Compare commits
133 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 69b3d9f7dd | |||
| 1fb32e8861 | |||
| 4d2e8dab0b | |||
| 89dbbf18a5 | |||
| 02590b9cd1 | |||
| b12f21dce6 | |||
| d08f181564 | |||
| 442d950545 | |||
| fa9298f427 | |||
| 3d32726bbe | |||
| 6662aa21ef | |||
| 9b9a91f6ef | |||
| 94b805de0b | |||
| eded585f79 | |||
| a552321287 | |||
| f98bfbbdcd | |||
| e9ab548445 | |||
| 15184d1a79 | |||
| 0c6bbde25f | |||
| 13aa353765 | |||
| ac941a801a | |||
| f261d44edb | |||
| 849cfdcdfa | |||
| 1470331f7e | |||
| dcaa96a79f | |||
| 101a966922 | |||
| 57940f38ea | |||
| 22f8c339fc | |||
| c896acb729 | |||
| 7f1bc490d6 | |||
| 1091c30845 | |||
| 12d9e454db | |||
| 82b973a8b1 | |||
| 71ee31a014 | |||
| 69b4ec81b6 | |||
| 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 |
+243
-6
@@ -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
|
||||||
@@ -52,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
|
||||||
@@ -124,9 +136,33 @@ jobs:
|
|||||||
|
|
||||||
- name: Cache CI dependencies
|
- name: Cache CI dependencies
|
||||||
run: |
|
run: |
|
||||||
bin/nix-build-and-cache ci
|
# 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
|
||||||
|
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: |
|
||||||
@@ -180,6 +216,10 @@ jobs:
|
|||||||
echo '```' >> $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
|
||||||
|
|
||||||
@@ -244,12 +287,39 @@ jobs:
|
|||||||
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 }}-all-features
|
# 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 }}-all-features
|
# 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
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -47,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
|
||||||
|
|
||||||
@@ -107,7 +110,23 @@ jobs:
|
|||||||
|
|
||||||
- name: Cache CI dependencies
|
- name: Cache CI dependencies
|
||||||
run: |
|
run: |
|
||||||
bin/nix-build-and-cache ci
|
# 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
|
||||||
|
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: |
|
||||||
@@ -116,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
|
||||||
cp -r --dereference result public
|
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
|
||||||
|
|
||||||
- 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:
|
||||||
|
|||||||
@@ -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
+562
-418
File diff suppressed because it is too large
Load Diff
+96
-40
@@ -14,20 +14,36 @@ 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.1"
|
||||||
version = "0.4.5"
|
version = "0.4.6"
|
||||||
|
|
||||||
[workspace.metadata.crane]
|
[workspace.metadata.crane]
|
||||||
name = "conduit"
|
name = "conduit"
|
||||||
|
|
||||||
|
[workspace.dependencies.arrayvec]
|
||||||
|
version = "0.7.4"
|
||||||
|
|
||||||
[workspace.dependencies.const-str]
|
[workspace.dependencies.const-str]
|
||||||
version = "0.5.7"
|
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"
|
||||||
|
|
||||||
@@ -53,7 +69,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.1"
|
version = "1.7.1"
|
||||||
|
|
||||||
[workspace.dependencies.http-body-util]
|
[workspace.dependencies.http-body-util]
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
@@ -62,7 +78,7 @@ version = "0.1.1"
|
|||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
|
||||||
[workspace.dependencies.regex]
|
[workspace.dependencies.regex]
|
||||||
version = "1.10.4"
|
version = "1.10.6"
|
||||||
|
|
||||||
[workspace.dependencies.axum]
|
[workspace.dependencies.axum]
|
||||||
version = "0.7.5"
|
version = "0.7.5"
|
||||||
@@ -83,18 +99,21 @@ default-features = false
|
|||||||
features = ["typed-header", "tracing"]
|
features = ["typed-header", "tracing"]
|
||||||
|
|
||||||
[workspace.dependencies.axum-server]
|
[workspace.dependencies.axum-server]
|
||||||
version = "0.6.0"
|
version = "0.7.1"
|
||||||
|
default-features = false
|
||||||
features = ["tls-rustls"]
|
features = ["tls-rustls"]
|
||||||
|
|
||||||
[workspace.dependencies.axum-client-ip]
|
[workspace.dependencies.axum-client-ip]
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
|
|
||||||
[workspace.dependencies.tower]
|
[workspace.dependencies.tower]
|
||||||
version = "0.4.13"
|
version = "0.5.0"
|
||||||
|
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",
|
||||||
@@ -117,10 +136,12 @@ features = [
|
|||||||
|
|
||||||
[workspace.dependencies.serde]
|
[workspace.dependencies.serde]
|
||||||
version = "1.0.204"
|
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.124"
|
||||||
|
default-features = false
|
||||||
features = ["raw_value"]
|
features = ["raw_value"]
|
||||||
|
|
||||||
# Used for appservice registration files
|
# Used for appservice registration files
|
||||||
@@ -172,7 +193,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.9"
|
version = "4.5.15"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = [
|
features = [
|
||||||
"std",
|
"std",
|
||||||
@@ -188,7 +209,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",
|
||||||
@@ -204,11 +226,12 @@ features = [
|
|||||||
version = "0.3.1"
|
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
|
||||||
@@ -218,7 +241,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",
|
||||||
@@ -227,6 +251,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",
|
||||||
@@ -238,11 +263,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]
|
||||||
@@ -251,7 +278,7 @@ default-features = false
|
|||||||
|
|
||||||
# Used for conduit::Error type
|
# Used for conduit::Error type
|
||||||
[workspace.dependencies.thiserror]
|
[workspace.dependencies.thiserror]
|
||||||
version = "1.0.62"
|
version = "1.0.63"
|
||||||
|
|
||||||
# Used when hashing the state
|
# Used when hashing the state
|
||||||
[workspace.dependencies.ring]
|
[workspace.dependencies.ring]
|
||||||
@@ -280,7 +307,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 = "11cbd52af1039f3d4a0b96333acd1d1687ba01aa"
|
||||||
features = [
|
features = [
|
||||||
"compat",
|
"compat",
|
||||||
"rand",
|
"rand",
|
||||||
@@ -293,6 +320,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",
|
||||||
@@ -307,10 +335,6 @@ features = [
|
|||||||
"unstable-extensible-events",
|
"unstable-extensible-events",
|
||||||
]
|
]
|
||||||
|
|
||||||
[workspace.dependencies.ruma-identifiers-validation]
|
|
||||||
git = "https://github.com/girlbossceo/ruwuma"
|
|
||||||
rev = "c51ccb2c68d2e3557eb12b1a49036531711ec0e5"
|
|
||||||
|
|
||||||
[workspace.dependencies.rust-rocksdb]
|
[workspace.dependencies.rust-rocksdb]
|
||||||
path = "deps/rust-rocksdb"
|
path = "deps/rust-rocksdb"
|
||||||
package = "rust-rocksdb-uwu"
|
package = "rust-rocksdb-uwu"
|
||||||
@@ -372,24 +396,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.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,16 +431,27 @@ features = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[workspace.dependencies.rustyline-async]
|
[workspace.dependencies.rustyline-async]
|
||||||
version = "0.4.2"
|
version = "0.4.3"
|
||||||
default-features = false
|
default-features = false
|
||||||
|
|
||||||
[workspace.dependencies.termimad]
|
[workspace.dependencies.termimad]
|
||||||
version = "0.29.4"
|
version = "0.30.0"
|
||||||
default-features = false
|
default-features = false
|
||||||
|
|
||||||
[workspace.dependencies.checked_ops]
|
[workspace.dependencies.checked_ops]
|
||||||
version = "0.1"
|
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
|
||||||
@@ -436,17 +473,11 @@ rev = "4d78a14a5e03f539b8c6b475aefa08bb14e4de91"
|
|||||||
git = "https://github.com/girlbossceo/tracing"
|
git = "https://github.com/girlbossceo/tracing"
|
||||||
rev = "4d78a14a5e03f539b8c6b475aefa08bb14e4de91"
|
rev = "4d78a14a5e03f539b8c6b475aefa08bb14e4de91"
|
||||||
|
|
||||||
# fixes hyper graceful shutdowns [https://github.com/programatik29/axum-server/issues/114]
|
|
||||||
# https://github.com/girlbossceo/axum-server/commit/8e3368d899079818934e61cc9c839abcbbcada8a
|
|
||||||
[patch.crates-io.axum-server]
|
|
||||||
git = "https://github.com/girlbossceo/axum-server"
|
|
||||||
rev = "8e3368d899079818934e61cc9c839abcbbcada8a"
|
|
||||||
|
|
||||||
# adds a tab completion callback: https://github.com/girlbossceo/rustyline-async/commit/de26100b0db03e419a3d8e1dd26895d170d1fe50
|
# 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
|
# adds event for CTRL+\: https://github.com/girlbossceo/rustyline-async/commit/67d8c49aeac03a5ef4e818f663eaa94dd7bf339b
|
||||||
[patch.crates-io.rustyline-async]
|
[patch.crates-io.rustyline-async]
|
||||||
git = "https://github.com/girlbossceo/rustyline-async"
|
git = "https://github.com/girlbossceo/rustyline-async"
|
||||||
rev = "de26100b0db03e419a3d8e1dd26895d170d1fe50"
|
rev = "9654cc84e19241f6e19021eb8e677892656f5071"
|
||||||
|
|
||||||
#
|
#
|
||||||
# Our crates
|
# Our crates
|
||||||
@@ -482,6 +513,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
|
||||||
@@ -530,7 +566,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',
|
||||||
#]
|
#]
|
||||||
|
|
||||||
@@ -659,7 +705,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
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
@@ -721,19 +776,19 @@ 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
|
||||||
@@ -743,7 +798,7 @@ 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
|
||||||
doc_markdown = { level = "allow", priority = 1 }
|
doc_markdown = { level = "allow", priority = 1 }
|
||||||
@@ -762,12 +817,12 @@ 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"
|
||||||
|
|
||||||
allow_attributes = "warn"
|
#allow_attributes = "warn" # UNSTABLE
|
||||||
arithmetic_side_effects = "warn"
|
arithmetic_side_effects = "warn"
|
||||||
as_conversions = "warn"
|
as_conversions = "warn"
|
||||||
as_underscore = "warn"
|
as_underscore = "warn"
|
||||||
@@ -812,14 +867,15 @@ unseparated_literal_suffix = "warn"
|
|||||||
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 }
|
||||||
|
|||||||
+34
-6
@@ -52,9 +52,10 @@
|
|||||||
|
|
||||||
# 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.
|
# Whether to attach a stacktrace to Sentry reports.
|
||||||
@@ -223,9 +224,11 @@ registration_token = "change this token for something specific to your server"
|
|||||||
# No default.
|
# No default.
|
||||||
# forbidden_alias_names = []
|
# forbidden_alias_names = []
|
||||||
|
|
||||||
# List of forbidden server names that we will block all client room joins, incoming federated room directory requests, incoming federated invites for, and incoming federated joins. This check is applied on the room ID, room alias, sender server name, and sender user's server name.
|
# List of forbidden server names that we will block incoming AND outgoing federation with, and block client room joins / remote user invites.
|
||||||
# Basically "global" ACLs. For our user (client) checks, admin users are allowed.
|
#
|
||||||
# No default.
|
# This check is applied on the room ID, room alias, sender server name, sender user's server name, inbound federation X-Matrix origin, and outbound federation handler.
|
||||||
|
#
|
||||||
|
# Basically "global" ACLs. No default.
|
||||||
# forbidden_remote_server_names = []
|
# forbidden_remote_server_names = []
|
||||||
|
|
||||||
# List of forbidden server names that we will block all outgoing federated room directory requests for. Useful for preventing our users from wandering into bad servers or spaces.
|
# List of forbidden server names that we will block all outgoing federated room directory requests for. Useful for preventing our users from wandering into bad servers or spaces.
|
||||||
@@ -401,7 +404,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
|
||||||
#
|
#
|
||||||
@@ -513,6 +516,31 @@ allow_profile_lookup_federation_requests = true
|
|||||||
# Defaults to false as this uses more CPU when compressing.
|
# Defaults to false as this uses more CPU when compressing.
|
||||||
#rocksdb_bottommost_compression = false
|
#rocksdb_bottommost_compression = false
|
||||||
|
|
||||||
|
# Level of statistics collection. Some admin commands to display database statistics may require
|
||||||
|
# this option to be set. Database performance may be impacted by higher settings.
|
||||||
|
#
|
||||||
|
# Option is a number ranging from 0 to 6:
|
||||||
|
# 0 = No statistics.
|
||||||
|
# 1 = No statistics in release mode (default).
|
||||||
|
# 2 to 3 = Statistics with no performance impact.
|
||||||
|
# 3 to 5 = Statistics with possible performance impact.
|
||||||
|
# 6 = All statistics.
|
||||||
|
#
|
||||||
|
# Defaults to 1 (No statistics, except in debug-mode)
|
||||||
|
#rocksdb_stats_level = 1
|
||||||
|
|
||||||
|
# Database repair mode (for RocksDB SST corruption)
|
||||||
|
#
|
||||||
|
# Use this option when the server reports corruption while running or panics. If the server refuses
|
||||||
|
# to start use the recovery mode options first. Corruption errors containing the acronym 'SST' which
|
||||||
|
# occur after startup will likely require this option.
|
||||||
|
#
|
||||||
|
# - Backing up your database directory is recommended prior to running the repair.
|
||||||
|
# - Disabling repair mode and restarting the server is recommended after running the repair.
|
||||||
|
#
|
||||||
|
# Defaults to false
|
||||||
|
#rocksdb_repair = false
|
||||||
|
|
||||||
# Database recovery mode (for RocksDB WAL corruption)
|
# Database recovery mode (for RocksDB WAL corruption)
|
||||||
#
|
#
|
||||||
# Use this option when the server reports corruption and refuses to start. Set mode 2 (PointInTime)
|
# Use this option when the server reports corruption and refuses to start. Set mode 2 (PointInTime)
|
||||||
|
|||||||
Vendored
-1
@@ -16,7 +16,6 @@ case "$1" in
|
|||||||
--home "$CONDUWUIT_DATABASE_PATH" \
|
--home "$CONDUWUIT_DATABASE_PATH" \
|
||||||
--disabled-login \
|
--disabled-login \
|
||||||
--shell "/usr/sbin/nologin" \
|
--shell "/usr/sbin/nologin" \
|
||||||
--verbose \
|
|
||||||
conduwuit
|
conduwuit
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
- [Docker](deploying/docker.md)
|
- [Docker](deploying/docker.md)
|
||||||
- [Arch Linux](deploying/arch-linux.md)
|
- [Arch Linux](deploying/arch-linux.md)
|
||||||
- [Debian](deploying/debian.md)
|
- [Debian](deploying/debian.md)
|
||||||
|
- [FreeBSD](deploying/freebsd.md)
|
||||||
- [TURN](turn.md)
|
- [TURN](turn.md)
|
||||||
- [Appservices](appservices.md)
|
- [Appservices](appservices.md)
|
||||||
- [Maintenance](maintenance.md)
|
- [Maintenance](maintenance.md)
|
||||||
|
|||||||
+24
-4
@@ -4,15 +4,35 @@ This chapter describes various ways to configure conduwuit.
|
|||||||
|
|
||||||
## Basics
|
## Basics
|
||||||
|
|
||||||
Conduwuit uses a config file for the majority of the settings. Please refer to the
|
conduwuit uses a config file for the majority of the settings, but also supports setting individual config options via commandline.
|
||||||
[example config file](./configuration/examples.md#example-configuration) for all of those settings.
|
|
||||||
The config file to use can either be specified on the command line when running conduwuit by specifying the
|
Please refer to the [example config file](./configuration/examples.md#example-configuration) for all of those settings.
|
||||||
|
|
||||||
|
The config file to use can be specified on the commandline when running conduwuit by specifying the
|
||||||
`-c`, `--config` flag. Alternatively, you can use the environment variable `CONDUWUIT_CONFIG` to specify the config
|
`-c`, `--config` flag. Alternatively, you can use the environment variable `CONDUWUIT_CONFIG` to specify the config
|
||||||
file to used.
|
file to used. Conduit's environment variables are supported for backwards compatibility.
|
||||||
|
|
||||||
|
## Option commandline flag
|
||||||
|
|
||||||
|
conduwuit supports setting individual config options in TOML format from the `-O` / `--option` flag. For example, you can set your server name via `-O server_name=\"example.com\"`.
|
||||||
|
|
||||||
|
Note that the config is parsed as TOML, and shells like bash will remove quotes. So unfortunately it is required to escape quotes if the config option takes a string.
|
||||||
|
This does not apply to options that take booleans or numbers:
|
||||||
|
- `--option allow_registration=true` works ✅
|
||||||
|
- `-O max_request_size=99999999` works ✅
|
||||||
|
- `-O server_name=example.com` does not work ❌
|
||||||
|
- `--option log=\"debug\"` works ✅
|
||||||
|
- `--option server_name='"example.com'"` works ✅
|
||||||
|
|
||||||
|
|
||||||
## Environment variables
|
## Environment variables
|
||||||
|
|
||||||
All of the settings that are found in the config file can be specified by using environment variables.
|
All of the settings that are found in the config file can be specified by using environment variables.
|
||||||
The environment variable names should be all caps and prefixed with `CONDUWUIT_`.
|
The environment variable names should be all caps and prefixed with `CONDUWUIT_`.
|
||||||
|
|
||||||
For example, if the setting you are changing is `max_request_size`, then the environment variable to set is
|
For example, if the setting you are changing is `max_request_size`, then the environment variable to set is
|
||||||
`CONDUWUIT_MAX_REQUEST_SIZE`.
|
`CONDUWUIT_MAX_REQUEST_SIZE`.
|
||||||
|
|
||||||
|
To modify config options not in the `[global]` context such as `[global.well_known]`, use the `__` suffix split: `CONDUWUIT_WELL_KNOWN__SERVER`
|
||||||
|
|
||||||
|
Conduit's environment variables are supported for backwards compatibility (e.g. `CONDUIT_SERVER_NAME`).
|
||||||
|
|||||||
@@ -17,9 +17,9 @@ OCI images for conduwuit are available in the registries listed below.
|
|||||||
| GitLab Registry | [registry.gitlab.com/conduwuit/conduwuit:main][gl] | ![Image Size][shield-main] | Stable main branch. |
|
| GitLab Registry | [registry.gitlab.com/conduwuit/conduwuit:main][gl] | ![Image Size][shield-main] | Stable main branch. |
|
||||||
| Docker Hub | [docker.io/girlbossceo/conduwuit:main][dh] | ![Image Size][shield-main] | Stable main branch. |
|
| Docker Hub | [docker.io/girlbossceo/conduwuit:main][dh] | ![Image Size][shield-main] | Stable main branch. |
|
||||||
|
|
||||||
[dh]: https://hub.docker.com/repository/docker/girlbossceo/conduwuit
|
[dh]: https://hub.docker.com/r/girlbossceo/conduwuit
|
||||||
[gh]: https://github.com/girlbossceo/conduwuit/pkgs/container/conduwuit
|
[gh]: https://github.com/girlbossceo/conduwuit/pkgs/container/conduwuit
|
||||||
[gl]: https://gitlab.com/conduwuit/conduwuit/container_registry/6351657
|
[gl]: https://gitlab.com/conduwuit/conduwuit/container_registry/6369729
|
||||||
[shield-latest]: https://img.shields.io/docker/image-size/girlbossceo/conduwuit/latest
|
[shield-latest]: https://img.shields.io/docker/image-size/girlbossceo/conduwuit/latest
|
||||||
[shield-main]: https://img.shields.io/docker/image-size/girlbossceo/conduwuit/main
|
[shield-main]: https://img.shields.io/docker/image-size/girlbossceo/conduwuit/main
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
# conduwuit for FreeBSD
|
||||||
|
|
||||||
|
conduwuit at the moment does not provide FreeBSD builds. Building conduwuit on FreeBSD requires a specific environment variable to use the
|
||||||
|
system prebuilt RocksDB library instead of rust-rocksdb / rust-librocksdb-sys which does *not* work and will cause a build error or coredump.
|
||||||
|
|
||||||
|
Use the following environment variable: `ROCKSDB_LIB_DIR=/usr/local/lib`
|
||||||
|
|
||||||
|
Such example commandline with it can be: `ROCKSDB_LIB_DIR=/usr/local/lib cargo build --release`
|
||||||
@@ -9,14 +9,12 @@
|
|||||||
|
|
||||||
You may simply download the binary that fits your machine. Run `uname -m` to see what you need.
|
You may simply download the binary that fits your machine. Run `uname -m` to see what you need.
|
||||||
|
|
||||||
Prebuilt binaries can be downloaded from the latest tagged release [here](https://github.com/girlbossceo/conduwuit/releases/latest).
|
Prebuilt fully static musl binaries can be downloaded from the latest tagged release [here](https://github.com/girlbossceo/conduwuit/releases/latest) or `main` CI branch workflow artifact output. These also include Debian packages. These binaries have jemalloc and io_uring statically linked and included with them.
|
||||||
|
|
||||||
The latest tagged release also includes the Debian packages.
|
|
||||||
|
|
||||||
Alternatively, you may compile the binary yourself. We recommend using [Lix](https://lix.systems) to build conduwuit as this has the most guaranteed
|
Alternatively, you may compile the binary yourself. We recommend using [Lix](https://lix.systems) to build conduwuit as this has the most guaranteed
|
||||||
reproducibiltiy and easiest to get a build environment and output going.
|
reproducibiltiy and easiest to get a build environment and output going.
|
||||||
|
|
||||||
Otherwise, follow standard Rust project build guides (installing git and cloning the repo, getting the Rust toolchain via rustup, installing LLVM toolchain + libclang, installing liburing for io_uring and RocksDB, etc).
|
Otherwise, follow standard Rust project build guides (installing git and cloning the repo, getting the Rust toolchain via rustup, installing LLVM toolchain + libclang for RocksDB, installing liburing for io_uring and RocksDB, etc).
|
||||||
|
|
||||||
## Adding a conduwuit user
|
## Adding a conduwuit user
|
||||||
|
|
||||||
|
|||||||
+11
-2
@@ -8,6 +8,17 @@
|
|||||||
>
|
>
|
||||||
> If there are things like Compose file issues or Dockerhub image issues, those can still be mentioned as long as they're something we can fix.
|
> If there are things like Compose file issues or Dockerhub image issues, those can still be mentioned as long as they're something we can fix.
|
||||||
|
|
||||||
|
## General potential issues
|
||||||
|
|
||||||
|
#### Potential DNS issues when using Docker
|
||||||
|
|
||||||
|
Docker has issues with its default DNS setup that may cause DNS to not be properly functional when running conduwuit, resulting in federation issues.
|
||||||
|
The symptoms of this have shown in excessively long room joins (30+ minutes) from very long DNS timeouts, log entries of "mismatching responding nameservers", and/or partial or non-functional inbound/outbound federation.
|
||||||
|
|
||||||
|
This is **not** a conduwuit issue, and is purely a Docker issue. It is not sustainable for heavy DNS activity which is normal for Matrix federation. The workarounds for this are:
|
||||||
|
- Use DNS over TCP via the config option `query_over_tcp_only = true`
|
||||||
|
- Don't use Docker's default DNS setup and instead allow the container to use and communicate with your host's DNS servers (host's `/etc/resolv.conf`)
|
||||||
|
|
||||||
## Rocksdb / database issues
|
## Rocksdb / database issues
|
||||||
|
|
||||||
#### Direct IO
|
#### Direct IO
|
||||||
@@ -38,8 +49,6 @@ With this in mind:
|
|||||||
- Leave your conduwuit running in `PointInTime` for at least 30-60 minutes so as much possible corruption is restored
|
- Leave your conduwuit running in `PointInTime` for at least 30-60 minutes so as much possible corruption is restored
|
||||||
- If all goes will, you should be able to restore back to using `TolerateCorruptedTailRecords` and you have successfully recovered your database
|
- If all goes will, you should be able to restore back to using `TolerateCorruptedTailRecords` and you have successfully recovered your database
|
||||||
|
|
||||||
## Media
|
|
||||||
|
|
||||||
## Debugging
|
## Debugging
|
||||||
|
|
||||||
Note that users should not really be debugging things. If you find yourself debugging and find the issue, please let us know and/or how we can fix it. Various debug commands can be found in `!admin debug`.
|
Note that users should not really be debugging things. If you find yourself debugging and find the issue, please let us know and/or how we can fix it. Various debug commands can be found in `!admin debug`.
|
||||||
|
|||||||
+18
-3
@@ -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 \
|
||||||
-- \
|
-- \
|
||||||
@@ -185,9 +198,11 @@ name = "nix-default"
|
|||||||
group = "tests"
|
group = "tests"
|
||||||
script = """
|
script = """
|
||||||
env DIRENV_DEVSHELL=dynamic \
|
env DIRENV_DEVSHELL=dynamic \
|
||||||
|
CARGO_PROFILE="test" \
|
||||||
direnv exec . \
|
direnv exec . \
|
||||||
bin/nix-build-and-cache just .#default
|
bin/nix-build-and-cache just .#default-test
|
||||||
env DIRENV_DEVSHELL=dynamic \
|
env DIRENV_DEVSHELL=dynamic \
|
||||||
|
CARGO_PROFILE="test" \
|
||||||
direnv exec . \
|
direnv exec . \
|
||||||
nix run -L .#default -- --help
|
nix run -L .#default-test -- --help && nix run -L .#default-test -- --version
|
||||||
"""
|
"""
|
||||||
|
|||||||
Generated
+66
-79
@@ -9,11 +9,11 @@
|
|||||||
"nixpkgs-stable": "nixpkgs-stable"
|
"nixpkgs-stable": "nixpkgs-stable"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1720542474,
|
"lastModified": 1723558887,
|
||||||
"narHash": "sha256-aKjJ/4l2I9+wNGTaOGRsuS3M1+IoTibqgEMPDikXm04=",
|
"narHash": "sha256-FBaRmikNnCX+HQJXAfNJyiOl3sM1NrlxvcFwQDqwCyk=",
|
||||||
"owner": "zhaofengli",
|
"owner": "zhaofengli",
|
||||||
"repo": "attic",
|
"repo": "attic",
|
||||||
"rev": "6139576a3ce6bb992e0f6c3022528ec233e45f00",
|
"rev": "26b9417bde03edc6280d1f7ce709cd619cdb72d4",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -27,15 +27,15 @@
|
|||||||
"inputs": {
|
"inputs": {
|
||||||
"devenv": "devenv",
|
"devenv": "devenv",
|
||||||
"flake-compat": "flake-compat_3",
|
"flake-compat": "flake-compat_3",
|
||||||
"nixpkgs": "nixpkgs_3",
|
"git-hooks": "git-hooks",
|
||||||
"pre-commit-hooks": "pre-commit-hooks"
|
"nixpkgs": "nixpkgs_3"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1719923519,
|
"lastModified": 1723252655,
|
||||||
"narHash": "sha256-7Rhljj2fsklFRsu+eq7N683Z9qukmreMEj5C1GqCrSA=",
|
"narHash": "sha256-uOxcrNEMTOHvGHYyqDWnUpkhkY29l01KhjkgVQU2gXg=",
|
||||||
"owner": "cachix",
|
"owner": "cachix",
|
||||||
"repo": "cachix",
|
"repo": "cachix",
|
||||||
"rev": "4e9e71f78b9500fa6210cf1eaa4d75bdbab777c3",
|
"rev": "bd558fd32f55d6df1c3d496bade90335a1a6044d",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -81,11 +81,11 @@
|
|||||||
"complement": {
|
"complement": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1720637557,
|
"lastModified": 1722323564,
|
||||||
"narHash": "sha256-oZz6nCmFmdJZpC+K1iOG2KkzTI6rlAmndxANPDVU7X0=",
|
"narHash": "sha256-6w6/N8walz4Ayc9zu7iySqJRmGFukhkaICLn4dweAcA=",
|
||||||
"owner": "matrix-org",
|
"owner": "matrix-org",
|
||||||
"repo": "complement",
|
"repo": "complement",
|
||||||
"rev": "0d14432e010482ea9e13a6f7c47c1533c0c9d62f",
|
"rev": "6e4426a9e63233f9821a4d2382bfed145244183f",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -123,11 +123,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1720546058,
|
"lastModified": 1722960479,
|
||||||
"narHash": "sha256-iU2yVaPIZm5vMGdlT0+57vdB/aPq/V5oZFBRwYw+HBM=",
|
"narHash": "sha256-NhCkJJQhD5GUib8zN9JrmYGMwt4lCRp6ZVNzIiYCl0Y=",
|
||||||
"owner": "ipetkov",
|
"owner": "ipetkov",
|
||||||
"repo": "crane",
|
"repo": "crane",
|
||||||
"rev": "2d83156f23c43598cf44e152c33a59d3892f8b29",
|
"rev": "4c6c77920b8d44cd6660c1621dea6b3fc4b4c4f4",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -151,15 +151,15 @@
|
|||||||
],
|
],
|
||||||
"pre-commit-hooks": [
|
"pre-commit-hooks": [
|
||||||
"cachix",
|
"cachix",
|
||||||
"pre-commit-hooks"
|
"git-hooks"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1719759336,
|
"lastModified": 1723156315,
|
||||||
"narHash": "sha256-3a34VL/QnHprl5gMy9xlx6d8J+iNp+W88Ex8smkgH9M=",
|
"narHash": "sha256-0JrfahRMJ37Rf1i0iOOn+8Z4CLvbcGNwa2ChOAVrp/8=",
|
||||||
"owner": "cachix",
|
"owner": "cachix",
|
||||||
"repo": "devenv",
|
"repo": "devenv",
|
||||||
"rev": "bb32aa986f2f695385e54428d0eaf7d05b31466e",
|
"rev": "ff5eb4f2accbcda963af67f1a1159e3f6c7f5f91",
|
||||||
"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": 1720852044,
|
"lastModified": 1723530607,
|
||||||
"narHash": "sha256-3NBYz8VuXuKU+8ONd9NFafCNjPEGHIZQ2Mdoam1a4mY=",
|
"narHash": "sha256-FaXZZLLDW1D+pj7UgrIslDS8XjMMG3Pus5gAvUYWQS0=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "fenix",
|
"repo": "fenix",
|
||||||
"rev": "5087b12a595ee73131a944d922f24d81dae05725",
|
"rev": "296d44c440302980824c5f3b67e477cf0522e0c1",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -272,22 +272,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flake-compat_4": {
|
"flake-compat_4": {
|
||||||
"flake": false,
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1696426674,
|
|
||||||
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
|
|
||||||
"owner": "edolstra",
|
|
||||||
"repo": "flake-compat",
|
|
||||||
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "edolstra",
|
|
||||||
"repo": "flake-compat",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"flake-compat_5": {
|
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1696426674,
|
"lastModified": 1696426674,
|
||||||
@@ -356,11 +340,38 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"git-hooks": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-compat": [
|
||||||
|
"cachix",
|
||||||
|
"flake-compat"
|
||||||
|
],
|
||||||
|
"gitignore": "gitignore",
|
||||||
|
"nixpkgs": [
|
||||||
|
"cachix",
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"nixpkgs-stable": "nixpkgs-stable_2"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1723202784,
|
||||||
|
"narHash": "sha256-qbhjc/NEGaDbyy0ucycubq4N3//gDFFH3DOmp1D3u1Q=",
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "git-hooks.nix",
|
||||||
|
"rev": "c7012d0c18567c889b948781bc74a501e92275d1",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "git-hooks.nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"gitignore": {
|
"gitignore": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
"cachix",
|
"cachix",
|
||||||
"pre-commit-hooks",
|
"git-hooks",
|
||||||
"nixpkgs"
|
"nixpkgs"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -381,11 +392,11 @@
|
|||||||
"liburing": {
|
"liburing": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1720798442,
|
"lastModified": 1723553052,
|
||||||
"narHash": "sha256-gtPppAoksMLW4GuruQ36nf4EAqIA1Bs6V9Xcx8dBxrQ=",
|
"narHash": "sha256-sjqmYP/tvQKmsTD6FOd8m7aEja2hhEyyB3aTxVXEbuE=",
|
||||||
"owner": "axboe",
|
"owner": "axboe",
|
||||||
"repo": "liburing",
|
"repo": "liburing",
|
||||||
"rev": "1d674f83b7d0f07553ac44d99a401b05853d9dbe",
|
"rev": "401b3e4bde2316d35e93b548269c72bbb64adc02",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -558,16 +569,16 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs-stable_2": {
|
"nixpkgs-stable_2": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1718811006,
|
"lastModified": 1720386169,
|
||||||
"narHash": "sha256-0Y8IrGhRmBmT7HHXlxxepg2t8j1X90++qRN3lukGaIk=",
|
"narHash": "sha256-NGKVY4PjzwAa4upkGtAMz1npHGoRzWotlSnVlqI40mo=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "03d771e513ce90147b65fe922d87d3a0356fc125",
|
"rev": "194846768975b7ad2c4988bdb82572c00222c0d7",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"ref": "nixos-23.11",
|
"ref": "nixos-24.05",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
@@ -590,11 +601,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs_3": {
|
"nixpkgs_3": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1719848872,
|
"lastModified": 1722813957,
|
||||||
"narHash": "sha256-H3+EC5cYuq+gQW8y0lSrrDZfH71LB4DAf+TDFyvwCNA=",
|
"narHash": "sha256-IAoYyYnED7P8zrBFMnmp7ydaJfwTnwcnqxUElC1I26Y=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "00d80d13810dbfea8ab4ed1009b09100cca86ba8",
|
"rev": "cb9a96f23c491c081b38eab96d22fa958043c9fa",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -606,11 +617,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs_4": {
|
"nixpkgs_4": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1720768451,
|
"lastModified": 1723362943,
|
||||||
"narHash": "sha256-EYekUHJE2gxeo2pM/zM9Wlqw1Uw2XTJXOSAO79ksc4Y=",
|
"narHash": "sha256-dFZRVSgmJkyM0bkPpaYRtG/kRMRTorUIDj8BxoOt1T4=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "7e7c39ea35c5cdd002cd4588b03a3fb9ece6fad9",
|
"rev": "a58bc8ad779655e790115244571758e8de055e3d",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -646,30 +657,6 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"pre-commit-hooks": {
|
|
||||||
"inputs": {
|
|
||||||
"flake-compat": "flake-compat_4",
|
|
||||||
"gitignore": "gitignore",
|
|
||||||
"nixpkgs": [
|
|
||||||
"cachix",
|
|
||||||
"nixpkgs"
|
|
||||||
],
|
|
||||||
"nixpkgs-stable": "nixpkgs-stable_2"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1719259945,
|
|
||||||
"narHash": "sha256-F1h+XIsGKT9TkGO3omxDLEb/9jOOsI6NnzsXFsZhry4=",
|
|
||||||
"owner": "cachix",
|
|
||||||
"repo": "pre-commit-hooks.nix",
|
|
||||||
"rev": "0ff4381bbb8f7a52ca4a851660fc7a437a4c6e07",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "cachix",
|
|
||||||
"repo": "pre-commit-hooks.nix",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"rocksdb": {
|
"rocksdb": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
@@ -694,7 +681,7 @@
|
|||||||
"complement": "complement",
|
"complement": "complement",
|
||||||
"crane": "crane_2",
|
"crane": "crane_2",
|
||||||
"fenix": "fenix",
|
"fenix": "fenix",
|
||||||
"flake-compat": "flake-compat_5",
|
"flake-compat": "flake-compat_4",
|
||||||
"flake-utils": "flake-utils_3",
|
"flake-utils": "flake-utils_3",
|
||||||
"liburing": "liburing",
|
"liburing": "liburing",
|
||||||
"nix-filter": "nix-filter",
|
"nix-filter": "nix-filter",
|
||||||
@@ -705,11 +692,11 @@
|
|||||||
"rust-analyzer-src": {
|
"rust-analyzer-src": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1720717809,
|
"lastModified": 1723473250,
|
||||||
"narHash": "sha256-6I+fm+nTLF/iaj7ffiFGlSY7POmubwUaPA/Wq0Bm53M=",
|
"narHash": "sha256-Ls0e6R4FmGUFXZlUcm6ZQaVNJ4Yj/nua4SSctXIopao=",
|
||||||
"owner": "rust-lang",
|
"owner": "rust-lang",
|
||||||
"repo": "rust-analyzer",
|
"repo": "rust-analyzer",
|
||||||
"rev": "ffbc5ad993d5cd2f3b8bcf9a511165470944ab91",
|
"rev": "32a86cb1dad2b208e8f36f1bb50c2e4806b0371f",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|||||||
@@ -24,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-3jVIIf5XPnUU1CRaTyAiO0XHVbJl12MSx3eucTXCjtE=";
|
||||||
};
|
};
|
||||||
|
|
||||||
mkScope = pkgs: pkgs.lib.makeScope pkgs.newScope (self: {
|
mkScope = pkgs: pkgs.lib.makeScope pkgs.newScope (self: {
|
||||||
@@ -119,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
|
||||||
|
|
||||||
@@ -133,6 +136,12 @@
|
|||||||
|
|
||||||
# Useful for editing the book locally
|
# Useful for editing the book locally
|
||||||
mdbook
|
mdbook
|
||||||
|
|
||||||
|
# used for rust caching in CI to speed it up
|
||||||
|
sccache
|
||||||
|
|
||||||
|
# needed so we can get rid of gcc and other unused deps that bloat OCI images
|
||||||
|
removeReferencesTo
|
||||||
])
|
])
|
||||||
++ scope.main.buildInputs
|
++ scope.main.buildInputs
|
||||||
++ scope.main.propagatedBuildInputs
|
++ scope.main.propagatedBuildInputs
|
||||||
@@ -144,6 +153,15 @@
|
|||||||
{
|
{
|
||||||
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 = scopeHost.main.override {
|
||||||
all_features = true;
|
all_features = true;
|
||||||
disable_features = [
|
disable_features = [
|
||||||
@@ -153,6 +171,18 @@
|
|||||||
"experimental"
|
"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;
|
||||||
@@ -167,6 +197,20 @@
|
|||||||
];
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
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"];
|
||||||
@@ -201,6 +245,26 @@
|
|||||||
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`
|
# An output for a statically-linked binary with `--all-features`
|
||||||
{
|
{
|
||||||
name = "${binaryName}-all-features";
|
name = "${binaryName}-all-features";
|
||||||
@@ -215,6 +279,23 @@
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# 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";
|
||||||
@@ -229,6 +310,18 @@
|
|||||||
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`
|
# An output for an OCI image based on that binary with `--all-features`
|
||||||
{
|
{
|
||||||
name = "oci-image-${crossSystem}-all-features";
|
name = "oci-image-${crossSystem}-all-features";
|
||||||
@@ -245,6 +338,25 @@
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# 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";
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse = true
|
|||||||
ip_range_denylist = []
|
ip_range_denylist = []
|
||||||
url_preview_domain_contains_allowlist = ["*"]
|
url_preview_domain_contains_allowlist = ["*"]
|
||||||
media_compat_file_link = false
|
media_compat_file_link = false
|
||||||
media_statup_check = false
|
media_startup_check = false
|
||||||
rocksdb_direct_io = false
|
rocksdb_direct_io = false
|
||||||
|
|
||||||
[global.tls]
|
[global.tls]
|
||||||
|
|||||||
@@ -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" ''
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
, liburing
|
, liburing
|
||||||
, pkgsBuildHost
|
, pkgsBuildHost
|
||||||
, rocksdb
|
, rocksdb
|
||||||
|
, removeReferencesTo
|
||||||
, rust
|
, rust
|
||||||
, rust-jemalloc-sys
|
, rust-jemalloc-sys
|
||||||
, stdenv
|
, stdenv
|
||||||
@@ -51,6 +52,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";
|
||||||
});
|
});
|
||||||
@@ -90,8 +95,8 @@ buildDepsOnlyEnv =
|
|||||||
else if stdenv.targetPlatform.isAarch64
|
else if stdenv.targetPlatform.isAarch64
|
||||||
then lib.subtractLists [ "-DPORTABLE=1" ] old.cmakeFlags
|
then lib.subtractLists [ "-DPORTABLE=1" ] old.cmakeFlags
|
||||||
++ lib.optionals stdenv.targetPlatform.isAarch64 [
|
++ lib.optionals stdenv.targetPlatform.isAarch64 [
|
||||||
# cortex-a55 == ARMv8.2-a
|
# cortex-a73 == ARMv8-A
|
||||||
"-DPORTABLE=armv8.2-a"
|
"-DPORTABLE=armv8-a"
|
||||||
]
|
]
|
||||||
else old.cmakeFlags;
|
else old.cmakeFlags;
|
||||||
});
|
});
|
||||||
@@ -124,7 +129,7 @@ buildPackageEnv = {
|
|||||||
+ lib.optionalString stdenv.targetPlatform.isx86_64
|
+ lib.optionalString stdenv.targetPlatform.isx86_64
|
||||||
" -Ctarget-cpu=x86-64-v2"
|
" -Ctarget-cpu=x86-64-v2"
|
||||||
+ lib.optionalString stdenv.targetPlatform.isAarch64
|
+ lib.optionalString stdenv.targetPlatform.isAarch64
|
||||||
" -Ctarget-cpu=cortex-a55"; # cortex-a55 == ARMv8.2-a
|
" -Ctarget-cpu=cortex-a73"; # cortex-a73 == ARMv8-A
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -149,7 +154,8 @@ commonAttrs = {
|
|||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
dontStrip = profile == "dev";
|
dontStrip = profile == "dev" || profile == "test";
|
||||||
|
dontPatchELF = profile == "dev" || profile == "test";
|
||||||
|
|
||||||
buildInputs = lib.optional (featureEnabled "jemalloc") rust-jemalloc-sys';
|
buildInputs = lib.optional (featureEnabled "jemalloc") rust-jemalloc-sys';
|
||||||
|
|
||||||
@@ -164,6 +170,9 @@ commonAttrs = {
|
|||||||
# differing values for `NIX_CFLAGS_COMPILE`, which contributes to spurious
|
# differing values for `NIX_CFLAGS_COMPILE`, which contributes to spurious
|
||||||
# rebuilds of bindgen and its depedents.
|
# rebuilds of bindgen and its depedents.
|
||||||
jq
|
jq
|
||||||
|
|
||||||
|
# needed so we can get rid of gcc and other unused deps that bloat OCI images
|
||||||
|
removeReferencesTo
|
||||||
]
|
]
|
||||||
++ lib.optionals stdenv.isDarwin [
|
++ lib.optionals stdenv.isDarwin [
|
||||||
# https://github.com/NixOS/nixpkgs/issues/206242
|
# https://github.com/NixOS/nixpkgs/issues/206242
|
||||||
@@ -173,6 +182,13 @@ commonAttrs = {
|
|||||||
# https://discourse.nixos.org/t/compile-a-rust-binary-on-macos-dbcrossbar/8612
|
# https://discourse.nixos.org/t/compile-a-rust-binary-on-macos-dbcrossbar/8612
|
||||||
pkgsBuildHost.darwin.apple_sdk.frameworks.Security
|
pkgsBuildHost.darwin.apple_sdk.frameworks.Security
|
||||||
];
|
];
|
||||||
|
|
||||||
|
# for some reason gcc and other weird deps are added to OCI images and bloats it up
|
||||||
|
#
|
||||||
|
# <https://github.com/input-output-hk/haskell.nix/issues/829>
|
||||||
|
postInstall = with pkgsBuildHost; ''
|
||||||
|
find "$out" -type f -exec remove-references-to -t ${stdenv.cc} -t ${gcc} -t ${libgcc} -t ${linuxHeaders} -t ${libidn2} -t ${libunistring} '{}' +
|
||||||
|
'';
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
|
|
||||||
|
|||||||
+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.1"
|
||||||
components = [
|
components = [
|
||||||
# For rust-analyzer
|
# For rust-analyzer
|
||||||
"rust-src",
|
"rust-src",
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ 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-macros.workspace = true
|
||||||
conduit-service.workspace = true
|
conduit-service.workspace = true
|
||||||
const-str.workspace = true
|
const-str.workspace = true
|
||||||
futures-util.workspace = true
|
futures-util.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,9 @@
|
|||||||
|
use std::time::SystemTime;
|
||||||
|
|
||||||
|
use conduit_service::Services;
|
||||||
|
|
||||||
|
pub(crate) struct Command<'a> {
|
||||||
|
pub(crate) services: &'a Services,
|
||||||
|
pub(crate) body: &'a [&'a str],
|
||||||
|
pub(crate) timer: SystemTime,
|
||||||
|
}
|
||||||
+275
-133
@@ -1,33 +1,33 @@
|
|||||||
use std::{
|
use std::{
|
||||||
collections::{BTreeMap, HashMap},
|
collections::{BTreeMap, HashMap},
|
||||||
sync::{Arc, Mutex},
|
fmt::Write,
|
||||||
time::Instant,
|
sync::Arc,
|
||||||
|
time::{Instant, SystemTime},
|
||||||
};
|
};
|
||||||
|
|
||||||
use api::client::validate_and_add_event_id;
|
use api::client::validate_and_add_event_id;
|
||||||
use conduit::{
|
use conduit::{debug, debug_error, err, info, trace, utils, warn, Error, PduEvent, Result};
|
||||||
debug, info, log,
|
|
||||||
log::{capture, Capture},
|
|
||||||
warn, Error, 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 +35,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 +54,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().saturating_sub(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 +84,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,69 +113,84 @@ 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<_>>();
|
||||||
|
|
||||||
|
let mut failed_count: usize = 0;
|
||||||
|
let mut success_count: usize = 0;
|
||||||
|
|
||||||
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()
|
failed_count = failed_count.saturating_add(1);
|
||||||
|
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}"
|
||||||
)))
|
)))
|
||||||
.await;
|
.await;
|
||||||
warn!(%e, "Failed to get remote PDU, ignoring error");
|
warn!("Failed to get remote PDU, ignoring error: {e}");
|
||||||
|
} else {
|
||||||
|
success_count = success_count.saturating_add(1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
get_remote_pdu(Vec::new(), Box::from(pdu), server.clone()).await?;
|
self.get_remote_pdu(Box::from(pdu), server.clone()).await?;
|
||||||
|
success_count = success_count.saturating_add(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(RoomMessageEventContent::text_plain("Fetched list of remote PDUs."))
|
Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
|
"Fetched {success_count} remote PDUs successfully with {failed_count} failures"
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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,
|
||||||
@@ -187,9 +209,13 @@ pub(super) async fn get_remote_pdu(
|
|||||||
Error::BadRequest(ErrorKind::Unknown, "Received response from server but failed to parse PDU")
|
Error::BadRequest(ErrorKind::Unknown, "Received response from server but failed to parse PDU")
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
debug!("Attempting to parse PDU: {:?}", &response.pdu);
|
trace!("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 +233,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 +242,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 +261,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 +290,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 +300,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,23 +330,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"];
|
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!(
|
||||||
@@ -326,7 +355,8 @@ pub(super) async fn change_log_level(
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
match services()
|
match self
|
||||||
|
.services
|
||||||
.server
|
.server
|
||||||
.log
|
.log
|
||||||
.reload
|
.reload
|
||||||
@@ -335,7 +365,7 @@ pub(super) async fn change_log_level(
|
|||||||
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) => {
|
||||||
@@ -356,7 +386,8 @@ pub(super) async fn change_log_level(
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
match services()
|
match self
|
||||||
|
.services
|
||||||
.server
|
.server
|
||||||
.log
|
.log
|
||||||
.reload
|
.reload
|
||||||
@@ -376,19 +407,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");
|
||||||
@@ -399,21 +432,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?;
|
||||||
|
|
||||||
@@ -429,19 +463,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)?
|
||||||
@@ -450,19 +487,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)?
|
||||||
@@ -471,32 +511,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,
|
||||||
@@ -510,7 +554,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}");
|
||||||
@@ -520,9 +564,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?;
|
||||||
|
|
||||||
@@ -530,23 +573,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)?;
|
||||||
@@ -558,32 +602,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().rooms.state.mutex.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)
|
||||||
@@ -593,7 +639,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);
|
||||||
|
|
||||||
@@ -602,61 +651,99 @@ 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.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let filter: &capture::Filter = &|data| {
|
let actual = self
|
||||||
data.level() <= log::Level::DEBUG
|
.services
|
||||||
&& data.mod_name().starts_with("conduit")
|
.resolver
|
||||||
&& matches!(data.span_name(), "actual" | "well-known" | "srv")
|
.resolve_actual_dest(&server_name, !no_cache)
|
||||||
};
|
.await?;
|
||||||
|
|
||||||
let state = &services().server.log.capture;
|
let msg = format!("Destination: {}\nHostname URI: {}", actual.dest, actual.host,);
|
||||||
let logs = Arc::new(Mutex::new(String::new()));
|
|
||||||
let capture = Capture::new(state, Some(filter), capture::fmt_markdown(logs.clone()));
|
|
||||||
|
|
||||||
let capture_scope = capture.start();
|
|
||||||
let actual = resolve_actual_dest(&server_name, !no_cache).await?;
|
|
||||||
drop(capture_scope);
|
|
||||||
|
|
||||||
let msg = format!(
|
|
||||||
"{}\nDestination: {}\nHostname URI: {}",
|
|
||||||
logs.lock().expect("locked"),
|
|
||||||
actual.dest,
|
|
||||||
actual.host,
|
|
||||||
);
|
|
||||||
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_none() {
|
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.expect("string result"),
|
html_body.expect("string result"),
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(tokio_unstable)]
|
#[cfg(tokio_unstable)]
|
||||||
pub(super) async fn runtime_metrics(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
#[admin_command]
|
||||||
let out = services().server.metrics.runtime_metrics().map_or_else(
|
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(),
|
|| "Runtime metrics are not available.".to_owned(),
|
||||||
|metrics| format!("```rs\n{metrics:#?}\n```"),
|
|metrics| format!("```rs\n{metrics:#?}\n```"),
|
||||||
);
|
);
|
||||||
@@ -665,15 +752,17 @@ pub(super) async fn runtime_metrics(_body: Vec<&str>) -> Result<RoomMessageEvent
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(tokio_unstable))]
|
#[cfg(not(tokio_unstable))]
|
||||||
pub(super) async fn runtime_metrics(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
#[admin_command]
|
||||||
|
pub(super) async fn runtime_metrics(&self) -> Result<RoomMessageEventContent> {
|
||||||
Ok(RoomMessageEventContent::text_markdown(
|
Ok(RoomMessageEventContent::text_markdown(
|
||||||
"Runtime metrics require building with `tokio_unstable`.",
|
"Runtime metrics require building with `tokio_unstable`.",
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(tokio_unstable)]
|
#[cfg(tokio_unstable)]
|
||||||
pub(super) async fn runtime_interval(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
#[admin_command]
|
||||||
let out = services().server.metrics.runtime_interval().map_or_else(
|
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(),
|
|| "Runtime metrics are not available.".to_owned(),
|
||||||
|metrics| format!("```rs\n{metrics:#?}\n```"),
|
|metrics| format!("```rs\n{metrics:#?}\n```"),
|
||||||
);
|
);
|
||||||
@@ -682,8 +771,61 @@ pub(super) async fn runtime_interval(_body: Vec<&str>) -> Result<RoomMessageEven
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(tokio_unstable))]
|
#[cfg(not(tokio_unstable))]
|
||||||
pub(super) async fn runtime_interval(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
#[admin_command]
|
||||||
|
pub(super) async fn runtime_interval(&self) -> Result<RoomMessageEventContent> {
|
||||||
Ok(RoomMessageEventContent::text_markdown(
|
Ok(RoomMessageEventContent::text_markdown(
|
||||||
"Runtime metrics require building with `tokio_unstable`.",
|
"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))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[admin_command]
|
||||||
|
pub(super) async fn database_stats(
|
||||||
|
&self, property: Option<String>, map: Option<String>,
|
||||||
|
) -> Result<RoomMessageEventContent> {
|
||||||
|
let property = property.unwrap_or_else(|| "rocksdb.stats".to_owned());
|
||||||
|
let map_name = map.as_ref().map_or(utils::string::EMPTY, String::as_str);
|
||||||
|
|
||||||
|
let mut out = String::new();
|
||||||
|
for (name, map) in self.services.db.iter_maps() {
|
||||||
|
if !map_name.is_empty() && *map_name != *name {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = map.property(&property)?;
|
||||||
|
let res = res.trim();
|
||||||
|
writeln!(out, "##### {name}:\n```\n{res}\n```")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(RoomMessageEventContent::notice_markdown(out))
|
||||||
|
}
|
||||||
|
|||||||
+31
-59
@@ -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
|
||||||
@@ -167,61 +175,25 @@ pub(super) enum DebugCommand {
|
|||||||
/// invocation.
|
/// invocation.
|
||||||
RuntimeInterval,
|
RuntimeInterval,
|
||||||
|
|
||||||
|
/// - Print the current time
|
||||||
|
Time,
|
||||||
|
|
||||||
|
/// - List dependencies
|
||||||
|
ListDependencies {
|
||||||
|
#[arg(short, long)]
|
||||||
|
names: bool,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// - Get database statistics
|
||||||
|
DatabaseStats {
|
||||||
|
property: Option<String>,
|
||||||
|
|
||||||
|
#[arg(short, long, alias("column"))]
|
||||||
|
map: Option<String>,
|
||||||
|
},
|
||||||
|
|
||||||
/// - 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::RuntimeMetrics => runtime_metrics(body).await?,
|
|
||||||
DebugCommand::RuntimeInterval => runtime_interval(body).await?,
|
|
||||||
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,21 +1,26 @@
|
|||||||
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> {
|
||||||
|
let map = self
|
||||||
|
.services
|
||||||
.rooms
|
.rooms
|
||||||
.event_handler
|
.event_handler
|
||||||
.federation_handletime
|
.federation_handletime
|
||||||
@@ -31,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"))
|
||||||
@@ -73,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?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|||||||
+97
-108
@@ -1,7 +1,21 @@
|
|||||||
use std::{panic::AssertUnwindSafe, time::Instant};
|
use std::{
|
||||||
|
panic::AssertUnwindSafe,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
time::SystemTime,
|
||||||
|
};
|
||||||
|
|
||||||
use clap::{CommandFactory, Parser};
|
use clap::{CommandFactory, Parser};
|
||||||
use conduit::{error, trace, Error};
|
use conduit::{
|
||||||
|
debug, error,
|
||||||
|
log::{
|
||||||
|
capture,
|
||||||
|
capture::Capture,
|
||||||
|
fmt::{markdown_table, markdown_table_head},
|
||||||
|
},
|
||||||
|
trace,
|
||||||
|
utils::string::{collect_stream, common_prefix},
|
||||||
|
Error, Result,
|
||||||
|
};
|
||||||
use futures_util::future::FutureExt;
|
use futures_util::future::FutureExt;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
events::{
|
events::{
|
||||||
@@ -10,82 +24,49 @@ use ruma::{
|
|||||||
},
|
},
|
||||||
OwnedEventId,
|
OwnedEventId,
|
||||||
};
|
};
|
||||||
|
use service::{
|
||||||
extern crate conduit_service as service;
|
admin::{CommandInput, CommandOutput, HandlerFuture, HandlerResult},
|
||||||
|
Services,
|
||||||
use conduit::{utils::string::common_prefix, 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;
|
use tracing::Level;
|
||||||
|
|
||||||
#[derive(Parser)]
|
use crate::{admin, admin::AdminCommand, Command};
|
||||||
#[command(name = "admin", version = env!("CARGO_PKG_VERSION"))]
|
|
||||||
pub(crate) enum AdminCommand {
|
|
||||||
#[command(subcommand)]
|
|
||||||
/// - Commands for managing appservices
|
|
||||||
Appservices(AppserviceCommand),
|
|
||||||
|
|
||||||
#[command(subcommand)]
|
#[must_use]
|
||||||
/// - Commands for managing local users
|
pub(super) fn complete(line: &str) -> String { complete_command(AdminCommand::command(), line) }
|
||||||
Users(UserCommand),
|
|
||||||
|
|
||||||
#[command(subcommand)]
|
#[must_use]
|
||||||
/// - Commands for managing rooms
|
pub(super) fn handle(services: Arc<Services>, command: CommandInput) -> HandlerFuture {
|
||||||
Rooms(RoomCommand),
|
Box::pin(handle_command(services, command))
|
||||||
|
|
||||||
#[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]
|
|
||||||
pub(crate) fn handle(command: Command) -> HandlerResult { Box::pin(handle_command(command)) }
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub(crate) fn complete(line: &str) -> String { complete_admin_command(AdminCommand::command(), line) }
|
|
||||||
|
|
||||||
#[tracing::instrument(skip_all, name = "admin")]
|
#[tracing::instrument(skip_all, name = "admin")]
|
||||||
async fn handle_command(command: Command) -> CommandResult {
|
async fn handle_command(services: Arc<Services>, command: CommandInput) -> HandlerResult {
|
||||||
AssertUnwindSafe(process_command(&command))
|
AssertUnwindSafe(Box::pin(process_command(services, &command)))
|
||||||
.catch_unwind()
|
.catch_unwind()
|
||||||
.await
|
.await
|
||||||
.map_err(Error::from_panic)
|
.map_err(Error::from_panic)
|
||||||
.or_else(|error| handle_panic(&error, command))
|
.or_else(|error| handle_panic(&error, command))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn process_command(command: &Command) -> CommandOutput {
|
async fn process_command(services: Arc<Services>, input: &CommandInput) -> CommandOutput {
|
||||||
process_admin_message(&command.command)
|
let (command, args, body) = match parse(&services, input) {
|
||||||
|
Err(error) => return error,
|
||||||
|
Ok(parsed) => parsed,
|
||||||
|
};
|
||||||
|
|
||||||
|
let context = Command {
|
||||||
|
services: &services,
|
||||||
|
body: &body,
|
||||||
|
timer: SystemTime::now(),
|
||||||
|
};
|
||||||
|
|
||||||
|
process(&context, command, &args)
|
||||||
.await
|
.await
|
||||||
.and_then(|content| reply(content, command.reply_id.clone()))
|
.and_then(|content| reply(content, input.reply_id.clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_panic(error: &Error, command: Command) -> CommandResult {
|
fn handle_panic(error: &Error, command: CommandInput) -> HandlerResult {
|
||||||
let link = "Please submit a [bug report](https://github.com/girlbossceo/conduwuit/issues/new). 🥺";
|
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 msg = format!("Panic occurred while processing command:\n```\n{error:#?}\n```\n{link}");
|
||||||
let content = RoomMessageEventContent::notice_markdown(msg);
|
let content = RoomMessageEventContent::notice_markdown(msg);
|
||||||
@@ -104,56 +85,65 @@ fn reply(mut content: RoomMessageEventContent, reply_id: Option<OwnedEventId>) -
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Parse and process a message from the admin room
|
// Parse and process a message from the admin room
|
||||||
async fn process_admin_message(msg: &str) -> CommandOutput {
|
async fn process(context: &Command<'_>, command: AdminCommand, args: &[String]) -> CommandOutput {
|
||||||
let mut lines = msg.lines().filter(|l| !l.trim().is_empty());
|
let filter: &capture::Filter =
|
||||||
let command = lines.next().expect("each string has at least one line");
|
&|data| data.level() <= Level::DEBUG && data.our_modules() && data.scope.contains(&"admin");
|
||||||
let body = lines.collect::<Vec<_>>();
|
let logs = Arc::new(Mutex::new(
|
||||||
let parsed = match parse_admin_command(command) {
|
collect_stream(|s| markdown_table_head(s)).expect("markdown table header"),
|
||||||
Ok(parsed) => parsed,
|
));
|
||||||
Err(error) => {
|
|
||||||
let server_name = services().globals.server_name();
|
let capture = Capture::new(
|
||||||
let message = error.replace("server.name", server_name.as_str());
|
&context.services.server.log.capture,
|
||||||
return Some(RoomMessageEventContent::notice_markdown(message));
|
Some(filter),
|
||||||
},
|
capture::fmt(markdown_table, logs.clone()),
|
||||||
|
);
|
||||||
|
|
||||||
|
let capture_scope = capture.start();
|
||||||
|
let result = Box::pin(admin::process(command, context)).await;
|
||||||
|
drop(capture_scope);
|
||||||
|
|
||||||
|
debug!(
|
||||||
|
ok = result.is_ok(),
|
||||||
|
elapsed = ?context.timer.elapsed(),
|
||||||
|
command = ?args,
|
||||||
|
"command processed"
|
||||||
|
);
|
||||||
|
|
||||||
|
let logs = logs.lock().expect("locked");
|
||||||
|
let output = match result {
|
||||||
|
Err(error) => format!("{logs}\nEncountered an error while handling the command:\n```\n{error:#?}\n```"),
|
||||||
|
Ok(reply) => format!("{logs}\n{}", reply.body()), //TODO: content is recreated to add logs
|
||||||
};
|
};
|
||||||
|
|
||||||
let timer = Instant::now();
|
Some(RoomMessageEventContent::notice_markdown(output))
|
||||||
let result = process_admin_command(parsed, body).await;
|
|
||||||
let elapsed = timer.elapsed();
|
|
||||||
conduit::debug!(?command, ok = result.is_ok(), "command processed in {elapsed:?}");
|
|
||||||
match result {
|
|
||||||
Ok(reply) => Some(reply),
|
|
||||||
Err(error) => Some(RoomMessageEventContent::notice_markdown(format!(
|
|
||||||
"Encountered an error while handling the command:\n```\n{error:#?}\n```"
|
|
||||||
))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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<'a>(
|
||||||
let argv = parse_command_line(command_line);
|
services: &Arc<Services>, input: &'a CommandInput,
|
||||||
AdminCommand::try_parse_from(argv).map_err(|error| error.to_string())
|
) -> Result<(AdminCommand, Vec<String>, Vec<&'a str>), CommandOutput> {
|
||||||
|
let lines = input.command.lines().filter(|line| !line.trim().is_empty());
|
||||||
|
let command_line = lines.clone().next().expect("command missing first line");
|
||||||
|
let body = lines.skip(1).collect();
|
||||||
|
match parse_command(command_line) {
|
||||||
|
Ok((command, args)) => Ok((command, args, body)),
|
||||||
|
Err(error) => {
|
||||||
|
let message = error
|
||||||
|
.to_string()
|
||||||
|
.replace("server.name", services.globals.server_name().as_str());
|
||||||
|
Err(Some(RoomMessageEventContent::notice_markdown(message)))
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn complete_admin_command(mut cmd: clap::Command, line: &str) -> String {
|
fn parse_command(line: &str) -> Result<(AdminCommand, Vec<String>)> {
|
||||||
let argv = parse_command_line(line);
|
let argv = parse_line(line);
|
||||||
|
let command = AdminCommand::try_parse_from(&argv)?;
|
||||||
|
Ok((command, argv))
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
let mut ret = Vec::<String>::with_capacity(argv.len().saturating_add(1));
|
||||||
|
|
||||||
'token: for token in argv.into_iter().skip(1) {
|
'token: for token in argv.into_iter().skip(1) {
|
||||||
@@ -196,7 +186,7 @@ fn complete_admin_command(mut cmd: clap::Command, line: &str) -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Parse chat messages from the admin room into an AdminCommand object
|
// Parse chat messages from the admin room into an AdminCommand object
|
||||||
fn parse_command_line(command_line: &str) -> Vec<String> {
|
fn parse_line(command_line: &str) -> Vec<String> {
|
||||||
let mut argv = command_line
|
let mut argv = command_line
|
||||||
.split_whitespace()
|
.split_whitespace()
|
||||||
.map(str::to_owned)
|
.map(str::to_owned)
|
||||||
@@ -208,8 +198,7 @@ fn parse_command_line(command_line: &str) -> Vec<String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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".to_owned());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+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?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|||||||
+22
-21
@@ -1,54 +1,55 @@
|
|||||||
|
#![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;
|
||||||
mod tests;
|
|
||||||
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(crate) use service::{services, user_is_local};
|
pub(crate) use conduit_macros::{admin_command, admin_command_dispatch};
|
||||||
|
|
||||||
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! {}
|
|
||||||
|
conduit::mod_ctor! {}
|
||||||
|
conduit::mod_dtor! {}
|
||||||
|
conduit::rustc_flags_capture! {}
|
||||||
|
|
||||||
/// Install the admin command handler
|
/// Install the admin command handler
|
||||||
pub async fn init() {
|
pub async fn init(admin_service: &service::admin::Service) {
|
||||||
_ = services()
|
_ = admin_service
|
||||||
.admin
|
|
||||||
.complete
|
.complete
|
||||||
.write()
|
.write()
|
||||||
.expect("locked for writing")
|
.expect("locked for writing")
|
||||||
.insert(handler::complete);
|
.insert(handler::complete);
|
||||||
_ = services()
|
_ = admin_service.handle.write().await.insert(handler::handle);
|
||||||
.admin
|
|
||||||
.handle
|
|
||||||
.write()
|
|
||||||
.await
|
|
||||||
.insert(handler::handle);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Uninstall the admin command handler
|
/// Uninstall the admin command handler
|
||||||
pub async fn fini() {
|
pub async fn fini(admin_service: &service::admin::Service) {
|
||||||
_ = services().admin.handle.write().await.take();
|
_ = admin_service.handle.write().await.take();
|
||||||
_ = services()
|
_ = admin_service
|
||||||
.admin
|
|
||||||
.complete
|
.complete
|
||||||
.write()
|
.write()
|
||||||
.expect("locked for writing")
|
.expect("locked for writing")
|
||||||
|
|||||||
@@ -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().updates.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,15 +1,18 @@
|
|||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
|
use conduit::Result;
|
||||||
use ruma::events::room::message::RoomMessageEventContent;
|
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(
|
#[admin_command]
|
||||||
_body: Vec<&str>, page: Option<usize>, exclude_disabled: bool, exclude_banned: bool,
|
pub(super) async fn list_rooms(
|
||||||
|
&self, page: Option<usize>, exclude_disabled: bool, exclude_banned: bool,
|
||||||
) -> Result<RoomMessageEventContent> {
|
) -> 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()
|
||||||
@@ -18,7 +21,8 @@ pub(super) async fn list(
|
|||||||
.ok()
|
.ok()
|
||||||
.filter(|room_id| {
|
.filter(|room_id| {
|
||||||
if exclude_disabled
|
if exclude_disabled
|
||||||
&& services()
|
&& self
|
||||||
|
.services
|
||||||
.rooms
|
.rooms
|
||||||
.metadata
|
.metadata
|
||||||
.is_disabled(room_id)
|
.is_disabled(room_id)
|
||||||
@@ -28,7 +32,8 @@ pub(super) async fn list(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if exclude_banned
|
if exclude_banned
|
||||||
&& services()
|
&& self
|
||||||
|
.services
|
||||||
.rooms
|
.rooms
|
||||||
.metadata
|
.metadata
|
||||||
.is_banned(room_id)
|
.is_banned(room_id)
|
||||||
@@ -39,7 +44,7 @@ pub(super) async fn list(
|
|||||||
|
|
||||||
true
|
true
|
||||||
})
|
})
|
||||||
.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);
|
||||||
@@ -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```"
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
+13
-160
@@ -1,20 +1,23 @@
|
|||||||
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
|
/// Excludes rooms that we have federation disabled with
|
||||||
@@ -42,153 +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,
|
|
||||||
exclude_disabled,
|
|
||||||
exclude_banned,
|
|
||||||
} => list(body, page, exclude_disabled, exclude_banned).await?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,37 +1,77 @@
|
|||||||
use api::client::leave_room;
|
use api::client::leave_room;
|
||||||
|
use clap::Subcommand;
|
||||||
|
use conduit::{debug, error, info, warn, Result};
|
||||||
use ruma::{events::room::message::RoomMessageEventContent, OwnedRoomId, RoomAliasId, RoomId, RoomOrAliasId};
|
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."));
|
||||||
}
|
}
|
||||||
@@ -50,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() {
|
||||||
@@ -69,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)
|
||||||
@@ -92,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 {
|
||||||
@@ -104,20 +145,21 @@ 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))
|
||||||
@@ -128,30 +170,31 @@ async fn ban_room(
|
|||||||
&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))
|
||||||
})
|
})
|
||||||
}) {
|
}) {
|
||||||
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
|
||||||
@@ -166,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.",
|
||||||
));
|
));
|
||||||
@@ -178,19 +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
|
let rooms_s = self
|
||||||
.clone()
|
.body
|
||||||
.drain(1..body.len().saturating_sub(1))
|
.to_vec()
|
||||||
|
.drain(1..self.body.len().saturating_sub(1))
|
||||||
.collect::<Vec<_>>();
|
.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();
|
||||||
@@ -198,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;
|
||||||
@@ -231,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!(
|
||||||
@@ -239,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)
|
||||||
@@ -303,28 +350,35 @@ 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))
|
||||||
@@ -334,31 +388,32 @@ async fn ban_list_of_rooms(body: Vec<&str>, force: bool, disable_federation: boo
|
|||||||
"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))
|
||||||
})
|
})
|
||||||
}) {
|
}) {
|
||||||
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
|
||||||
@@ -374,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)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -390,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,
|
||||||
@@ -406,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() {
|
||||||
@@ -425,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)
|
||||||
@@ -448,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 {
|
||||||
@@ -459,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."));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -469,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()
|
||||||
@@ -484,13 +541,13 @@ 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();
|
||||||
|
|
||||||
let output_plain = format!(
|
let output_plain = format!(
|
||||||
"Rooms Banned ({}):\n```\n{}```",
|
"Rooms Banned ({}):\n```\n{}\n```",
|
||||||
rooms.len(),
|
rooms.len(),
|
||||||
rooms
|
rooms
|
||||||
.iter()
|
.iter()
|
||||||
@@ -1,10 +1,14 @@
|
|||||||
use conduit::{utils::time, warn, Err, 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 elapsed = services()
|
pub(super) async fn uptime(&self) -> Result<RoomMessageEventContent> {
|
||||||
|
let elapsed = self
|
||||||
|
.services
|
||||||
.server
|
.server
|
||||||
.started
|
.started
|
||||||
.elapsed()
|
.elapsed()
|
||||||
@@ -14,14 +18,58 @@ pub(super) async fn uptime(_body: Vec<&str>) -> Result<RoomMessageEventContent>
|
|||||||
Ok(RoomMessageEventContent::notice_plain(format!("{result}.")))
|
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 services_usage = services().memory_usage().await?;
|
pub(super) async fn list_features(
|
||||||
let database_usage = services().db.db.memory_usage()?;
|
&self, available: bool, enabled: bool, comma: bool,
|
||||||
|
) -> 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}"));
|
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!(
|
||||||
@@ -29,14 +77,16 @@ pub(super) async fn memory_usage(_body: Vec<&str>) -> Result<RoomMessageEventCon
|
|||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
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."))
|
||||||
@@ -45,46 +95,51 @@ 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() {
|
||||||
@@ -94,14 +149,15 @@ pub(super) async fn restart(_body: Vec<&str>, force: bool) -> Result<RoomMessage
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
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?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|||||||
+1
-1
@@ -12,7 +12,7 @@ fn get_help_subcommand() { get_help_inner("help"); }
|
|||||||
fn get_help_inner(input: &str) {
|
fn get_help_inner(input: &str) {
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
|
||||||
use crate::handler::AdminCommand;
|
use crate::admin::AdminCommand;
|
||||||
|
|
||||||
let Err(error) = AdminCommand::try_parse_from(["argv[0] doesn't matter", input]) else {
|
let Err(error) = AdminCommand::try_parse_from(["argv[0] doesn't matter", input]) else {
|
||||||
panic!("no error!");
|
panic!("no error!");
|
||||||
|
|||||||
+113
-81
@@ -1,7 +1,7 @@
|
|||||||
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,
|
||||||
@@ -10,17 +10,17 @@ use ruma::{
|
|||||||
},
|
},
|
||||||
OwnedRoomId, OwnedRoomOrAliasId, 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").as_str();
|
plain_msg += users.join("\n").as_str();
|
||||||
@@ -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(
|
||||||
|
self.services,
|
||||||
&user_id,
|
&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,28 +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
|
let usernames = self
|
||||||
.clone()
|
.body
|
||||||
.drain(1..body.len().saturating_sub(1))
|
.to_vec()
|
||||||
|
.drain(1..self.body.len().saturating_sub(1))
|
||||||
.collect::<Vec<_>>();
|
.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"
|
||||||
@@ -218,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"
|
||||||
@@ -231,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}"
|
||||||
@@ -245,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;
|
||||||
@@ -283,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() {
|
||||||
@@ -334,41 +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))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[admin_command]
|
||||||
pub(super) async fn force_join_room(
|
pub(super) async fn force_join_room(
|
||||||
_body: Vec<&str>, user_id: String, room_id: OwnedRoomOrAliasId,
|
&self, user_id: String, room_id: OwnedRoomOrAliasId,
|
||||||
) -> Result<RoomMessageEventContent> {
|
) -> Result<RoomMessageEventContent> {
|
||||||
let user_id = parse_local_user_id(&user_id)?;
|
let user_id = parse_local_user_id(self.services, &user_id)?;
|
||||||
let room_id = services().rooms.alias.resolve(&room_id).await?;
|
let room_id = self.services.rooms.alias.resolve(&room_id).await?;
|
||||||
|
|
||||||
assert!(service::user_is_local(&user_id), "Parsed user_id must be a local user");
|
assert!(
|
||||||
join_room_by_id_helper(&user_id, &room_id, None, &[], None).await?;
|
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!(
|
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||||
"{user_id} has been joined to {room_id}.",
|
"{user_id} has been joined to {room_id}.",
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn make_user_admin(_body: Vec<&str>, user_id: String) -> Result<RoomMessageEventContent> {
|
#[admin_command]
|
||||||
let user_id = parse_local_user_id(&user_id)?;
|
pub(super) async fn make_user_admin(&self, user_id: String) -> Result<RoomMessageEventContent> {
|
||||||
let displayname = services()
|
let user_id = parse_local_user_id(self.services, &user_id)?;
|
||||||
|
let displayname = self
|
||||||
|
.services
|
||||||
.users
|
.users
|
||||||
.displayname(&user_id)?
|
.displayname(&user_id)?
|
||||||
.unwrap_or_else(|| user_id.to_string());
|
.unwrap_or_else(|| user_id.to_string());
|
||||||
|
|
||||||
assert!(service::user_is_local(&user_id), "Parsed user_id must be a local user");
|
assert!(
|
||||||
service::admin::make_user_admin(&user_id, displayname).await?;
|
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!(
|
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||||
"{user_id} has been granted admin privileges.",
|
"{user_id} has been granted admin privileges.",
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[admin_command]
|
||||||
pub(super) async fn put_room_tag(
|
pub(super) async fn put_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 +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,
|
||||||
@@ -398,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)?;
|
||||||
|
|
||||||
@@ -418,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,
|
||||||
@@ -430,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)?;
|
||||||
|
|
||||||
|
|||||||
+8
-51
@@ -2,15 +2,16 @@ mod commands;
|
|||||||
|
|
||||||
use clap::Subcommand;
|
use clap::Subcommand;
|
||||||
use conduit::Result;
|
use conduit::Result;
|
||||||
use ruma::{events::room::message::RoomMessageEventContent, OwnedRoomOrAliasId, 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
|
||||||
@@ -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
|
||||||
@@ -102,48 +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::ForceJoinRoom {
|
|
||||||
user_id,
|
|
||||||
room_id,
|
|
||||||
} => force_join_room(body, user_id, room_id).await?,
|
|
||||||
UserCommand::MakeUserAdmin {
|
|
||||||
user_id,
|
|
||||||
} => make_user_admin(body, user_id).await?,
|
|
||||||
UserCommand::PutRoomTag {
|
|
||||||
user_id,
|
|
||||||
room_id,
|
|
||||||
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?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|||||||
+14
-16
@@ -1,8 +1,6 @@
|
|||||||
use conduit_core::{err, Err};
|
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,16 +29,16 @@ 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| err!("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!("User {user_id:?} does not belong to our server.");
|
return Err!("User {user_id:?} does not belong to our server.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,14 +46,14 @@ pub(crate) fn parse_local_user_id(user_id: &str) -> Result<OwnedUserId> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 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!("User {user_id:?} 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!("User {user_id:?} is deactivated.");
|
return Err!("User {user_id:?} is deactivated.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,6 @@ hmac.workspace = true
|
|||||||
http.workspace = true
|
http.workspace = true
|
||||||
http-body-util.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
|
||||||
@@ -60,7 +59,6 @@ serde.workspace = true
|
|||||||
sha-1.workspace = true
|
sha-1.workspace = true
|
||||||
tokio.workspace = true
|
tokio.workspace = true
|
||||||
tracing.workspace = true
|
tracing.workspace = true
|
||||||
webpage.workspace = true
|
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|||||||
+80
-77
@@ -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() && body.appservice_info.is_none() {
|
||||||
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,16 +286,20 @@ 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.create_device(
|
||||||
.users
|
&user_id,
|
||||||
.create_device(&user_id, &device_id, &token, body.initial_device_display_name.clone())?;
|
&device_id,
|
||||||
|
&token,
|
||||||
|
body.initial_device_display_name.clone(),
|
||||||
|
Some(client.to_string()),
|
||||||
|
)?;
|
||||||
|
|
||||||
debug_info!(%user_id, %device_id, "User account was created");
|
debug_info!(%user_id, %device_id, "User account was created");
|
||||||
|
|
||||||
// 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,7 +308,7 @@ 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.");
|
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 {
|
||||||
@@ -317,7 +317,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 +325,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 +334,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 +346,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 +359,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 +374,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(
|
||||||
|
&services,
|
||||||
&user_id,
|
&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,7 +420,8 @@ 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> {
|
||||||
// Authentication for this endpoint was made optional, but we need
|
// Authentication for this endpoint was made optional, but we need
|
||||||
// authentication currently
|
// authentication currently
|
||||||
@@ -442,7 +442,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 {
|
||||||
@@ -451,7 +451,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));
|
||||||
@@ -459,24 +459,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."
|
||||||
@@ -491,14 +491,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(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -515,7 +517,8 @@ 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> {
|
||||||
// Authentication for this endpoint was made optional, but we need
|
// Authentication for this endpoint was made optional, but we need
|
||||||
// authentication currently
|
// authentication currently
|
||||||
@@ -536,7 +539,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 {
|
||||||
@@ -545,7 +548,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));
|
||||||
@@ -554,23 +557,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."
|
||||||
@@ -632,9 +635,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(),
|
||||||
|
|||||||
+24
-19
@@ -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,26 +15,30 @@ 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");
|
||||||
|
|
||||||
|
// some clients, at least element, seem to require knowledge of redundant
|
||||||
|
// members for "inline" profiles on the timeline to work properly
|
||||||
let (lazy_load_enabled, lazy_load_send_redundant) = match &body.filter.lazy_load_options {
|
let (lazy_load_enabled, lazy_load_send_redundant) = match &body.filter.lazy_load_options {
|
||||||
LazyLoadOptions::Enabled {
|
LazyLoadOptions::Enabled {
|
||||||
include_redundant_members,
|
include_redundant_members,
|
||||||
} => (true, *include_redundant_members),
|
} => (true, *include_redundant_members),
|
||||||
LazyLoadOptions::Disabled => (false, false),
|
LazyLoadOptions::Disabled => (false, cfg!(feature = "element_hacks")),
|
||||||
};
|
};
|
||||||
|
|
||||||
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 +46,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 +57,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 +72,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 +88,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 +108,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 +124,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 +135,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 +144,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 +152,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 +170,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 {})
|
||||||
|
|||||||
+41
-34
@@ -1,3 +1,4 @@
|
|||||||
|
use axum::extract::State;
|
||||||
use axum_client_ip::InsecureClientIp;
|
use axum_client_ip::InsecureClientIp;
|
||||||
use conduit::{err, info, warn, Error, Result};
|
use conduit::{err, info, warn, Error, Result};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
@@ -19,8 +20,9 @@ use ruma::{
|
|||||||
},
|
},
|
||||||
uint, RoomId, ServerName, UInt, UserId,
|
uint, RoomId, ServerName, UInt, UserId,
|
||||||
};
|
};
|
||||||
|
use service::Services;
|
||||||
|
|
||||||
use crate::{service::server_is_ours, services, Ruma};
|
use crate::Ruma;
|
||||||
|
|
||||||
/// # `POST /_matrix/client/v3/publicRooms`
|
/// # `POST /_matrix/client/v3/publicRooms`
|
||||||
///
|
///
|
||||||
@@ -29,10 +31,11 @@ use crate::{service::server_is_ours, services, 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)
|
||||||
@@ -45,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(),
|
||||||
@@ -67,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)
|
||||||
@@ -83,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(),
|
||||||
@@ -108,16 +114,17 @@ pub(crate) async fn get_public_rooms_route(
|
|||||||
/// Sets the visibility of a given room in the room directory.
|
/// Sets the visibility of a given room in the room directory.
|
||||||
#[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(sender_user, &body.room_id)? {
|
if !user_can_publish_room(&services, sender_user, &body.room_id)? {
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::forbidden(),
|
ErrorKind::forbidden(),
|
||||||
"User is not allowed to publish this room",
|
"User is not allowed to publish this room",
|
||||||
@@ -126,7 +133,7 @@ pub(crate) async fn set_room_visibility_route(
|
|||||||
|
|
||||||
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",
|
||||||
@@ -139,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,
|
||||||
@@ -158,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
|
||||||
@@ -175,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,
|
||||||
@@ -224,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()
|
||||||
@@ -232,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)?
|
||||||
@@ -247,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, "")?
|
||||||
@@ -282,7 +290,7 @@ pub(crate) async fn get_public_rooms_filtered_helper(
|
|||||||
.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)?,
|
||||||
@@ -361,12 +369,11 @@ pub(crate) async fn get_public_rooms_filtered_helper(
|
|||||||
|
|
||||||
/// Check whether the user can publish to the room directory via power levels of
|
/// Check whether the user can publish to the room directory via power levels of
|
||||||
/// room history visibility event or room creator
|
/// room history visibility event or room creator
|
||||||
fn user_can_publish_room(user_id: &UserId, room_id: &RoomId) -> Result<bool> {
|
fn user_can_publish_room(services: &Services, user_id: &UserId, room_id: &RoomId) -> Result<bool> {
|
||||||
if let Some(event) =
|
if let Some(event) = services
|
||||||
services()
|
.rooms
|
||||||
.rooms
|
.state_accessor
|
||||||
.state_accessor
|
.room_state_get(room_id, &StateEventType::RoomPowerLevels, "")?
|
||||||
.room_state_get(room_id, &StateEventType::RoomPowerLevels, "")?
|
|
||||||
{
|
{
|
||||||
serde_json::from_str(event.content.get())
|
serde_json::from_str(event.content.get())
|
||||||
.map_err(|_| Error::bad_database("Invalid event content for m.room.power_levels"))
|
.map_err(|_| Error::bad_database("Invalid event content for m.room.power_levels"))
|
||||||
@@ -374,7 +381,7 @@ fn user_can_publish_room(user_id: &UserId, room_id: &RoomId) -> Result<bool> {
|
|||||||
RoomPowerLevels::from(content).user_can_send_state(user_id, StateEventType::RoomHistoryVisibility)
|
RoomPowerLevels::from(content).user_can_send_state(user_id, StateEventType::RoomHistoryVisibility)
|
||||||
})
|
})
|
||||||
} else if let Some(event) =
|
} else if let Some(event) =
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.room_state_get(room_id, &StateEventType::RoomCreate, "")?
|
.room_state_get(room_id, &StateEventType::RoomCreate, "")?
|
||||||
|
|||||||
@@ -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)?,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
+51
-46
@@ -3,7 +3,8 @@ use std::{
|
|||||||
time::Instant,
|
time::Instant,
|
||||||
};
|
};
|
||||||
|
|
||||||
use conduit::{utils, utils::math::continue_exponential_backoff_secs, Error, Result};
|
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,11 +19,9 @@ use ruma::{
|
|||||||
DeviceKeyAlgorithm, OwnedDeviceId, OwnedUserId, UserId,
|
DeviceKeyAlgorithm, OwnedDeviceId, OwnedUserId, UserId,
|
||||||
};
|
};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use service::user_is_local;
|
|
||||||
use tracing::debug;
|
|
||||||
|
|
||||||
use super::SESSION_ID_LENGTH;
|
use super::SESSION_ID_LENGTH;
|
||||||
use crate::{services, Ruma};
|
use crate::{service::Services, Ruma};
|
||||||
|
|
||||||
/// # `POST /_matrix/client/r0/keys/upload`
|
/// # `POST /_matrix/client/r0/keys/upload`
|
||||||
///
|
///
|
||||||
@@ -31,12 +30,14 @@ use crate::{services, Ruma};
|
|||||||
/// - 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)?;
|
||||||
}
|
}
|
||||||
@@ -44,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)?,
|
||||||
})
|
})
|
||||||
@@ -70,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,
|
||||||
@@ -85,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`
|
||||||
@@ -95,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");
|
||||||
@@ -112,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 {
|
||||||
@@ -121,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));
|
||||||
@@ -130,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,
|
||||||
@@ -146,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");
|
||||||
|
|
||||||
@@ -173,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)?;
|
||||||
}
|
}
|
||||||
@@ -192,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(),
|
||||||
@@ -215,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(),
|
||||||
@@ -245,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();
|
||||||
@@ -258,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)
|
||||||
@@ -268,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."))?;
|
||||||
@@ -286,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(
|
||||||
@@ -303,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -326,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()
|
||||||
@@ -345,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()
|
||||||
@@ -356,8 +362,7 @@ pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool + Send>(
|
|||||||
const MIN: u64 = 5 * 60;
|
const MIN: u64 = 5 * 60;
|
||||||
const MAX: u64 = 60 * 60 * 24;
|
const MAX: u64 = 60 * 60 * 24;
|
||||||
if continue_exponential_backoff_secs(MIN, MAX, time.elapsed(), *tries) {
|
if continue_exponential_backoff_secs(MIN, MAX, time.elapsed(), *tries) {
|
||||||
debug!("Backing off query from {server:?}");
|
return (server, Err!(BadServerResponse("bad query from {server:?}, still backing off")));
|
||||||
return (server, Err(Error::BadServerResponse("bad query, still backing off")));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -369,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;
|
||||||
@@ -381,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 */
|
||||||
@@ -444,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)
|
||||||
@@ -460,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)?
|
||||||
{
|
{
|
||||||
@@ -483,7 +488,7 @@ pub(crate) async fn claim_keys_helper(
|
|||||||
}
|
}
|
||||||
(
|
(
|
||||||
server,
|
server,
|
||||||
services()
|
services
|
||||||
.sending
|
.sending
|
||||||
.send_federation_request(
|
.send_federation_request(
|
||||||
server,
|
server,
|
||||||
|
|||||||
+81
-334
@@ -1,37 +1,27 @@
|
|||||||
#![allow(deprecated)]
|
#![allow(deprecated)]
|
||||||
|
|
||||||
use std::{io::Cursor, sync::Arc, time::Duration};
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use axum::extract::State;
|
||||||
use axum_client_ip::InsecureClientIp;
|
use axum_client_ip::InsecureClientIp;
|
||||||
use conduit::{debug, error, utils::math::ruma_from_usize, warn};
|
use conduit::{
|
||||||
use image::io::Reader as ImgReader;
|
debug_info, debug_warn, err, info,
|
||||||
use ipaddress::IPAddress;
|
|
||||||
use reqwest::Url;
|
|
||||||
use ruma::api::client::{
|
|
||||||
error::{ErrorKind, RetryAfter},
|
|
||||||
media::{
|
|
||||||
create_content, get_content, get_content_as_filename, get_content_thumbnail, get_media_config,
|
|
||||||
get_media_preview,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
use webpage::HTML;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
debug_warn,
|
|
||||||
service::{
|
|
||||||
media::{FileMeta, UrlPreviewData},
|
|
||||||
server_is_ours,
|
|
||||||
},
|
|
||||||
services,
|
|
||||||
utils::{
|
utils::{
|
||||||
self,
|
self,
|
||||||
content_disposition::{content_disposition_type, make_content_disposition, sanitise_filename},
|
content_disposition::{content_disposition_type, make_content_disposition, sanitise_filename},
|
||||||
|
math::ruma_from_usize,
|
||||||
},
|
},
|
||||||
Error, Result, Ruma, RumaResponse,
|
warn, Err, Error, Result,
|
||||||
|
};
|
||||||
|
use ruma::api::client::media::{
|
||||||
|
create_content, get_content, get_content_as_filename, get_content_thumbnail, get_media_config, get_media_preview,
|
||||||
|
};
|
||||||
|
use service::{
|
||||||
|
media::{FileMeta, MXC_LENGTH},
|
||||||
|
Services,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// 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";
|
||||||
@@ -42,10 +32,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: ruma_from_usize(services().globals.config.max_request_size),
|
upload_size: ruma_from_usize(services.globals.config.max_request_size),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,9 +47,11 @@ 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`
|
||||||
@@ -67,41 +59,29 @@ pub(crate) async fn get_media_config_v1_route(
|
|||||||
/// Returns URL preview.
|
/// Returns URL preview.
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "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(
|
||||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_media_preview::v3::Request>,
|
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||||
|
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 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) {
|
||||||
warn!(%sender_user, "URL is not allowed to be previewed: {url}");
|
debug_info!(%sender_user, %url, "URL is not allowed to be previewed");
|
||||||
return Err(Error::BadRequest(ErrorKind::forbidden(), "URL is not allowed to be previewed"));
|
return Err!(Request(Forbidden("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!(%sender_user, "Failed to convert UrlPreviewData into a serde json value: {e}");
|
warn!(%sender_user, "Failed to convert UrlPreviewData into a serde json value: {e}");
|
||||||
Error::BadRequest(
|
err!(Request(Unknown("Failed to generate a URL preview")))
|
||||||
ErrorKind::LimitExceeded {
|
|
||||||
retry_after: Some(RetryAfter::Delay(Duration::from_secs(5))),
|
|
||||||
},
|
|
||||||
"Failed to generate a URL preview, try again later.",
|
|
||||||
)
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
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!(%sender_user, "Failed to generate a URL preview: {e}");
|
info!(%sender_user, "Failed to generate a URL preview: {e}");
|
||||||
|
Err!(Request(Unknown("Failed to generate a URL preview")))
|
||||||
// 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.
|
|
||||||
Err(Error::BadRequest(
|
|
||||||
ErrorKind::LimitExceeded {
|
|
||||||
retry_after: Some(RetryAfter::Delay(Duration::from_secs(5))),
|
|
||||||
},
|
|
||||||
"Failed to generate a URL preview, try again later.",
|
|
||||||
))
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -115,9 +95,10 @@ pub(crate) async fn get_media_preview_route(
|
|||||||
/// Returns URL preview.
|
/// Returns URL preview.
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "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(
|
||||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_media_preview::v3::Request>,
|
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||||
|
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(InsecureClientIp(client), body)
|
get_media_preview_route(State(services), InsecureClientIp(client), body)
|
||||||
.await
|
.await
|
||||||
.map(RumaResponse)
|
.map(RumaResponse)
|
||||||
}
|
}
|
||||||
@@ -130,17 +111,14 @@ pub(crate) async fn get_media_preview_v1_route(
|
|||||||
/// - 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")]
|
#[tracing::instrument(skip_all, fields(%client), name = "media_upload")]
|
||||||
pub(crate) async fn create_content_route(
|
pub(crate) async fn create_content_route(
|
||||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<create_content::v3::Request>,
|
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||||
|
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()),
|
||||||
@@ -178,9 +156,10 @@ pub(crate) async fn create_content_route(
|
|||||||
/// - 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")]
|
#[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(
|
||||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<create_content::v3::Request>,
|
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||||
|
body: Ruma<create_content::v3::Request>,
|
||||||
) -> Result<RumaResponse<create_content::v3::Response>> {
|
) -> Result<RumaResponse<create_content::v3::Response>> {
|
||||||
create_content_route(InsecureClientIp(client), body)
|
create_content_route(State(services), InsecureClientIp(client), body)
|
||||||
.await
|
.await
|
||||||
.map(RumaResponse)
|
.map(RumaResponse)
|
||||||
}
|
}
|
||||||
@@ -195,7 +174,8 @@ pub(crate) async fn create_content_v1_route(
|
|||||||
/// seconds
|
/// seconds
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "media_get")]
|
#[tracing::instrument(skip_all, fields(%client), name = "media_get")]
|
||||||
pub(crate) async fn get_content_route(
|
pub(crate) async fn get_content_route(
|
||||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_content::v3::Request>,
|
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||||
|
body: Ruma<get_content::v3::Request>,
|
||||||
) -> Result<get_content::v3::Response> {
|
) -> 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);
|
||||||
|
|
||||||
@@ -203,7 +183,7 @@ pub(crate) async fn get_content_route(
|
|||||||
content,
|
content,
|
||||||
content_type,
|
content_type,
|
||||||
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");
|
let file = content.expect("content");
|
||||||
@@ -215,8 +195,9 @@ pub(crate) async fn get_content_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 {
|
||||||
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(),
|
||||||
@@ -224,10 +205,7 @@ pub(crate) async fn get_content_route(
|
|||||||
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,
|
||||||
@@ -243,7 +221,7 @@ pub(crate) async fn get_content_route(
|
|||||||
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.")))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,9 +239,10 @@ pub(crate) async fn get_content_route(
|
|||||||
/// seconds
|
/// seconds
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "media_get")]
|
#[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(
|
||||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_content::v3::Request>,
|
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||||
|
body: Ruma<get_content::v3::Request>,
|
||||||
) -> Result<RumaResponse<get_content::v3::Response>> {
|
) -> Result<RumaResponse<get_content::v3::Response>> {
|
||||||
get_content_route(InsecureClientIp(client), body)
|
get_content_route(State(services), InsecureClientIp(client), body)
|
||||||
.await
|
.await
|
||||||
.map(RumaResponse)
|
.map(RumaResponse)
|
||||||
}
|
}
|
||||||
@@ -278,7 +257,8 @@ pub(crate) async fn get_content_v1_route(
|
|||||||
/// seconds
|
/// seconds
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "media_get")]
|
#[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(
|
||||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_content_as_filename::v3::Request>,
|
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||||
|
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);
|
||||||
|
|
||||||
@@ -286,7 +266,7 @@ pub(crate) async fn get_content_as_filename_route(
|
|||||||
content,
|
content,
|
||||||
content_type,
|
content_type,
|
||||||
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,
|
||||||
@@ -302,8 +282,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(),
|
||||||
@@ -327,13 +308,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.")))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -351,9 +329,10 @@ pub(crate) async fn get_content_as_filename_route(
|
|||||||
/// seconds
|
/// seconds
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "media_get")]
|
#[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(
|
||||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_content_as_filename::v3::Request>,
|
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||||
|
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(InsecureClientIp(client), body)
|
get_content_as_filename_route(State(services), InsecureClientIp(client), body)
|
||||||
.await
|
.await
|
||||||
.map(RumaResponse)
|
.map(RumaResponse)
|
||||||
}
|
}
|
||||||
@@ -368,7 +347,8 @@ pub(crate) async fn get_content_as_filename_v1_route(
|
|||||||
/// seconds
|
/// seconds
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "media_thumbnail_get")]
|
#[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(
|
||||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_content_thumbnail::v3::Request>,
|
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||||
|
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);
|
||||||
|
|
||||||
@@ -376,16 +356,16 @@ pub(crate) async fn get_content_thumbnail_route(
|
|||||||
content,
|
content,
|
||||||
content_type,
|
content_type,
|
||||||
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?
|
||||||
{
|
{
|
||||||
@@ -399,8 +379,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)
|
||||||
@@ -408,10 +388,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,
|
||||||
@@ -430,7 +410,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,
|
||||||
@@ -457,13 +437,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.")))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -481,17 +458,19 @@ pub(crate) async fn get_content_thumbnail_route(
|
|||||||
/// seconds
|
/// seconds
|
||||||
#[tracing::instrument(skip_all, fields(%client), name = "media_thumbnail_get")]
|
#[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(
|
||||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_content_thumbnail::v3::Request>,
|
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||||
|
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(InsecureClientIp(client), body)
|
get_content_thumbnail_route(State(services), InsecureClientIp(client), body)
|
||||||
.await
|
.await
|
||||||
.map(RumaResponse)
|
.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())
|
||||||
@@ -499,10 +478,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,
|
||||||
@@ -522,7 +501,7 @@ async fn get_remote_content(
|
|||||||
None,
|
None,
|
||||||
));
|
));
|
||||||
|
|
||||||
services()
|
services
|
||||||
.media
|
.media
|
||||||
.create(
|
.create(
|
||||||
None,
|
None,
|
||||||
@@ -541,235 +520,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
|
|
||||||
}
|
|
||||||
|
|||||||
+259
-239
File diff suppressed because it is too large
Load Diff
+35
-27
@@ -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,21 +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().rooms.state.mutex.lock(&body.room_id).await;
|
let state_lock = services.rooms.state.mutex.lock(&body.room_id).await;
|
||||||
|
|
||||||
// Forbid m.room.encrypted if encryption is disabled
|
// 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",
|
||||||
@@ -46,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)?
|
||||||
{
|
{
|
||||||
@@ -71,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(
|
||||||
@@ -82,6 +84,11 @@ pub(crate) async fn send_message_event_route(
|
|||||||
unsigned: Some(unsigned),
|
unsigned: Some(unsigned),
|
||||||
state_key: None,
|
state_key: None,
|
||||||
redacts: None,
|
redacts: None,
|
||||||
|
timestamp: if body.appservice_info.is_some() {
|
||||||
|
body.timestamp
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
},
|
},
|
||||||
sender_user,
|
sender_user,
|
||||||
&body.room_id,
|
&body.room_id,
|
||||||
@@ -89,7 +96,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())?;
|
||||||
|
|
||||||
@@ -105,7 +112,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");
|
||||||
@@ -123,7 +130,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)
|
||||||
@@ -139,12 +146,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`
|
||||||
@@ -157,7 +164,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,
|
||||||
@@ -181,17 +188,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();
|
||||||
@@ -202,7 +209,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,
|
||||||
@@ -229,11 +236,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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -241,7 +249,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)
|
||||||
@@ -252,8 +260,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;
|
||||||
}
|
}
|
||||||
|
|||||||
+79
-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,29 @@ 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()
|
let current_display_name = services.users.displayname(&user_id).unwrap_or_default();
|
||||||
|
|
||||||
|
if displayname == current_display_name {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
services
|
||||||
.users
|
.users
|
||||||
.set_displayname(&user_id, displayname.clone())
|
.set_displayname(&user_id, displayname.clone())
|
||||||
.await?;
|
.await?;
|
||||||
@@ -271,7 +279,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())?
|
||||||
@@ -287,6 +295,7 @@ pub async fn update_displayname(
|
|||||||
unsigned: None,
|
unsigned: None,
|
||||||
state_key: Some(user_id.to_string()),
|
state_key: Some(user_id.to_string()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
|
timestamp: None,
|
||||||
},
|
},
|
||||||
room_id,
|
room_id,
|
||||||
))
|
))
|
||||||
@@ -294,19 +303,27 @@ 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()
|
let current_avatar_url = services.users.avatar_url(&user_id).unwrap_or_default();
|
||||||
|
let current_blurhash = services.users.blurhash(&user_id).unwrap_or_default();
|
||||||
|
|
||||||
|
if current_avatar_url == avatar_url && current_blurhash == blurhash {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
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 +340,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())?
|
||||||
@@ -339,6 +356,7 @@ pub async fn update_avatar_url(
|
|||||||
unsigned: None,
|
unsigned: None,
|
||||||
state_key: Some(user_id.to_string()),
|
state_key: Some(user_id.to_string()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
|
timestamp: None,
|
||||||
},
|
},
|
||||||
room_id,
|
room_id,
|
||||||
))
|
))
|
||||||
@@ -346,15 +364,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().rooms.state.mutex.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,23 +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().rooms.state.mutex.lock(&body.room_id).await;
|
let state_lock = services.rooms.state.mutex.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(
|
||||||
@@ -31,6 +34,7 @@ pub(crate) async fn redact_event_route(body: Ruma<redact_event::v3::Request>) ->
|
|||||||
unsigned: None,
|
unsigned: None,
|
||||||
state_key: None,
|
state_key: None,
|
||||||
redacts: Some(body.event_id.into()),
|
redacts: Some(body.event_id.into()),
|
||||||
|
timestamp: None,
|
||||||
},
|
},
|
||||||
sender_user,
|
sender_user,
|
||||||
&body.room_id,
|
&body.room_id,
|
||||||
|
|||||||
+41
-49
@@ -1,30 +1,28 @@
|
|||||||
|
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
|
sender_user,
|
||||||
.pdu_metadata
|
&body.room_id,
|
||||||
.paginate_relations_with_filter(
|
&body.event_id,
|
||||||
sender_user,
|
&Some(body.event_type.clone()),
|
||||||
&body.room_id,
|
&Some(body.rel_type.clone()),
|
||||||
&body.event_id,
|
&body.from,
|
||||||
&Some(body.event_type.clone()),
|
&body.to,
|
||||||
&Some(body.rel_type.clone()),
|
&body.limit,
|
||||||
&body.from,
|
body.recurse,
|
||||||
&body.to,
|
body.dir,
|
||||||
&body.limit,
|
)?;
|
||||||
body.recurse,
|
|
||||||
body.dir,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(get_relating_events_with_rel_type_and_event_type::v1::Response {
|
Ok(get_relating_events_with_rel_type_and_event_type::v1::Response {
|
||||||
chunk: res.chunk,
|
chunk: res.chunk,
|
||||||
@@ -36,25 +34,22 @@ 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
|
sender_user,
|
||||||
.pdu_metadata
|
&body.room_id,
|
||||||
.paginate_relations_with_filter(
|
&body.event_id,
|
||||||
sender_user,
|
&None,
|
||||||
&body.room_id,
|
&Some(body.rel_type.clone()),
|
||||||
&body.event_id,
|
&body.from,
|
||||||
&None,
|
&body.to,
|
||||||
&Some(body.rel_type.clone()),
|
&body.limit,
|
||||||
&body.from,
|
body.recurse,
|
||||||
&body.to,
|
body.dir,
|
||||||
&body.limit,
|
)?;
|
||||||
body.recurse,
|
|
||||||
body.dir,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(get_relating_events_with_rel_type::v1::Response {
|
Ok(get_relating_events_with_rel_type::v1::Response {
|
||||||
chunk: res.chunk,
|
chunk: res.chunk,
|
||||||
@@ -66,23 +61,20 @@ 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
|
sender_user,
|
||||||
.pdu_metadata
|
&body.room_id,
|
||||||
.paginate_relations_with_filter(
|
&body.event_id,
|
||||||
sender_user,
|
&None,
|
||||||
&body.room_id,
|
&None,
|
||||||
&body.event_id,
|
&body.from,
|
||||||
&None,
|
&body.to,
|
||||||
&None,
|
&body.limit,
|
||||||
&body.from,
|
body.recurse,
|
||||||
&body.to,
|
body.dir,
|
||||||
&body.limit,
|
)
|
||||||
body.recurse,
|
|
||||||
body.dir,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
+89
-71
@@ -1,6 +1,7 @@
|
|||||||
use std::{cmp::max, collections::BTreeMap};
|
use std::{cmp::max, collections::BTreeMap};
|
||||||
|
|
||||||
use conduit::{debug_info, debug_warn};
|
use axum::extract::State;
|
||||||
|
use conduit::{debug_info, debug_warn, err};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::{
|
api::client::{
|
||||||
error::ErrorKind,
|
error::ErrorKind,
|
||||||
@@ -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().rooms.state.mutex.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,7 +116,7 @@ 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 {
|
||||||
@@ -184,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(
|
||||||
@@ -194,6 +197,7 @@ pub(crate) async fn create_room_route(body: Ruma<create_room::v3::Request>) -> R
|
|||||||
unsigned: None,
|
unsigned: None,
|
||||||
state_key: Some(String::new()),
|
state_key: Some(String::new()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
|
timestamp: None,
|
||||||
},
|
},
|
||||||
sender_user,
|
sender_user,
|
||||||
&room_id,
|
&room_id,
|
||||||
@@ -202,7 +206,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(
|
||||||
@@ -210,11 +214,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,
|
||||||
})
|
})
|
||||||
@@ -222,6 +226,7 @@ pub(crate) async fn create_room_route(body: Ruma<create_room::v3::Request>) -> R
|
|||||||
unsigned: None,
|
unsigned: None,
|
||||||
state_key: Some(sender_user.to_string()),
|
state_key: Some(sender_user.to_string()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
|
timestamp: None,
|
||||||
},
|
},
|
||||||
sender_user,
|
sender_user,
|
||||||
&room_id,
|
&room_id,
|
||||||
@@ -249,7 +254,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(
|
||||||
@@ -259,6 +264,7 @@ pub(crate) async fn create_room_route(body: Ruma<create_room::v3::Request>) -> R
|
|||||||
unsigned: None,
|
unsigned: None,
|
||||||
state_key: Some(String::new()),
|
state_key: Some(String::new()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
|
timestamp: None,
|
||||||
},
|
},
|
||||||
sender_user,
|
sender_user,
|
||||||
&room_id,
|
&room_id,
|
||||||
@@ -268,7 +274,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(
|
||||||
@@ -282,6 +288,7 @@ pub(crate) async fn create_room_route(body: Ruma<create_room::v3::Request>) -> R
|
|||||||
unsigned: None,
|
unsigned: None,
|
||||||
state_key: Some(String::new()),
|
state_key: Some(String::new()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
|
timestamp: None,
|
||||||
},
|
},
|
||||||
sender_user,
|
sender_user,
|
||||||
&room_id,
|
&room_id,
|
||||||
@@ -293,7 +300,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(
|
||||||
@@ -308,6 +315,7 @@ pub(crate) async fn create_room_route(body: Ruma<create_room::v3::Request>) -> R
|
|||||||
unsigned: None,
|
unsigned: None,
|
||||||
state_key: Some(String::new()),
|
state_key: Some(String::new()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
|
timestamp: None,
|
||||||
},
|
},
|
||||||
sender_user,
|
sender_user,
|
||||||
&room_id,
|
&room_id,
|
||||||
@@ -316,7 +324,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(
|
||||||
@@ -327,6 +335,7 @@ pub(crate) async fn create_room_route(body: Ruma<create_room::v3::Request>) -> R
|
|||||||
unsigned: None,
|
unsigned: None,
|
||||||
state_key: Some(String::new()),
|
state_key: Some(String::new()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
|
timestamp: None,
|
||||||
},
|
},
|
||||||
sender_user,
|
sender_user,
|
||||||
&room_id,
|
&room_id,
|
||||||
@@ -335,7 +344,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(
|
||||||
@@ -349,6 +358,7 @@ pub(crate) async fn create_room_route(body: Ruma<create_room::v3::Request>) -> R
|
|||||||
unsigned: None,
|
unsigned: None,
|
||||||
state_key: Some(String::new()),
|
state_key: Some(String::new()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
|
timestamp: None,
|
||||||
},
|
},
|
||||||
sender_user,
|
sender_user,
|
||||||
&room_id,
|
&room_id,
|
||||||
@@ -378,11 +388,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)
|
||||||
@@ -391,7 +401,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(
|
||||||
@@ -402,6 +412,7 @@ pub(crate) async fn create_room_route(body: Ruma<create_room::v3::Request>) -> R
|
|||||||
unsigned: None,
|
unsigned: None,
|
||||||
state_key: Some(String::new()),
|
state_key: Some(String::new()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
|
timestamp: None,
|
||||||
},
|
},
|
||||||
sender_user,
|
sender_user,
|
||||||
&room_id,
|
&room_id,
|
||||||
@@ -411,7 +422,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(
|
||||||
@@ -424,6 +435,7 @@ pub(crate) async fn create_room_route(body: Ruma<create_room::v3::Request>) -> R
|
|||||||
unsigned: None,
|
unsigned: None,
|
||||||
state_key: Some(String::new()),
|
state_key: Some(String::new()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
|
timestamp: None,
|
||||||
},
|
},
|
||||||
sender_user,
|
sender_user,
|
||||||
&room_id,
|
&room_id,
|
||||||
@@ -435,21 +447,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}");
|
||||||
@@ -464,20 +476,17 @@ 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)?
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| err!(Request(NotFound("Event {} not found.", &body.event_id))))?;
|
||||||
warn!("Event not found, event ID: {:?}", &body.event_id);
|
|
||||||
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)?
|
||||||
@@ -502,10 +511,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)?
|
||||||
@@ -517,7 +528,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)
|
||||||
@@ -536,10 +547,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)
|
||||||
@@ -551,19 +564,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().rooms.state.mutex.lock(&body.room_id).await;
|
let state_lock = services.rooms.state.mutex.lock(&body.room_id).await;
|
||||||
|
|
||||||
// Send a m.room.tombstone event to the old room to indicate that it is not
|
// 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(
|
||||||
@@ -577,6 +590,7 @@ pub(crate) async fn upgrade_room_route(body: Ruma<upgrade_room::v3::Request>) ->
|
|||||||
unsigned: None,
|
unsigned: None,
|
||||||
state_key: Some(String::new()),
|
state_key: Some(String::new()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
|
timestamp: None,
|
||||||
},
|
},
|
||||||
sender_user,
|
sender_user,
|
||||||
&body.room_id,
|
&body.room_id,
|
||||||
@@ -586,11 +600,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().rooms.state.mutex.lock(&replacement_room).await;
|
let state_lock = services.rooms.state.mutex.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, "")?
|
||||||
@@ -658,7 +672,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(
|
||||||
@@ -668,6 +682,7 @@ pub(crate) async fn upgrade_room_route(body: Ruma<upgrade_room::v3::Request>) ->
|
|||||||
unsigned: None,
|
unsigned: None,
|
||||||
state_key: Some(String::new()),
|
state_key: Some(String::new()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
|
timestamp: None,
|
||||||
},
|
},
|
||||||
sender_user,
|
sender_user,
|
||||||
&replacement_room,
|
&replacement_room,
|
||||||
@@ -676,7 +691,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(
|
||||||
@@ -684,11 +699,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,
|
||||||
})
|
})
|
||||||
@@ -696,6 +711,7 @@ pub(crate) async fn upgrade_room_route(body: Ruma<upgrade_room::v3::Request>) ->
|
|||||||
unsigned: None,
|
unsigned: None,
|
||||||
state_key: Some(sender_user.to_string()),
|
state_key: Some(sender_user.to_string()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
|
timestamp: None,
|
||||||
},
|
},
|
||||||
sender_user,
|
sender_user,
|
||||||
&replacement_room,
|
&replacement_room,
|
||||||
@@ -705,7 +721,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, "")?
|
||||||
@@ -714,7 +730,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(
|
||||||
@@ -724,6 +740,7 @@ pub(crate) async fn upgrade_room_route(body: Ruma<upgrade_room::v3::Request>) ->
|
|||||||
unsigned: None,
|
unsigned: None,
|
||||||
state_key: Some(String::new()),
|
state_key: Some(String::new()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
|
timestamp: None,
|
||||||
},
|
},
|
||||||
sender_user,
|
sender_user,
|
||||||
&replacement_room,
|
&replacement_room,
|
||||||
@@ -733,13 +750,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)?;
|
||||||
@@ -747,7 +764,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, "")?
|
||||||
@@ -772,7 +789,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(
|
||||||
@@ -782,6 +799,7 @@ pub(crate) async fn upgrade_room_route(body: Ruma<upgrade_room::v3::Request>) ->
|
|||||||
unsigned: None,
|
unsigned: None,
|
||||||
state_key: Some(String::new()),
|
state_key: Some(String::new()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
|
timestamp: None,
|
||||||
},
|
},
|
||||||
sender_user,
|
sender_user,
|
||||||
&body.room_id,
|
&body.room_id,
|
||||||
@@ -841,7 +859,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(':') {
|
||||||
@@ -858,7 +876,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)
|
||||||
@@ -866,13 +884,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)?
|
||||||
@@ -885,7 +903,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
|
||||||
@@ -899,9 +917,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)
|
||||||
@@ -922,7 +940,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)
|
||||||
|
|||||||
+39
-22
@@ -1,3 +1,5 @@
|
|||||||
|
use axum::extract::State;
|
||||||
|
use axum_client_ip::InsecureClientIp;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::{
|
api::client::{
|
||||||
error::ErrorKind,
|
error::ErrorKind,
|
||||||
@@ -20,7 +22,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 {
|
||||||
@@ -32,8 +34,9 @@ struct Claims {
|
|||||||
///
|
///
|
||||||
/// Get the supported login types of this server. One of these should be used as
|
/// Get the supported login types of this server. One of these should be used as
|
||||||
/// the `type` field when logging in.
|
/// the `type` field when logging in.
|
||||||
|
#[tracing::instrument(skip_all, fields(%client), name = "login")]
|
||||||
pub(crate) async fn get_login_types_route(
|
pub(crate) async fn get_login_types_route(
|
||||||
_body: Ruma<get_login_types::v3::Request>,
|
InsecureClientIp(client): InsecureClientIp, _body: Ruma<get_login_types::v3::Request>,
|
||||||
) -> Result<get_login_types::v3::Response> {
|
) -> Result<get_login_types::v3::Response> {
|
||||||
Ok(get_login_types::v3::Response::new(vec 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> {
|
#[tracing::instrument(skip_all, fields(%client), name = "login")]
|
||||||
|
pub(crate) async fn login_route(
|
||||||
|
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp, 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 +74,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 +83,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 +102,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 +112,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 +130,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 +170,26 @@ 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.create_device(
|
||||||
.users
|
&user_id,
|
||||||
.create_device(&user_id, &device_id, &token, body.initial_device_display_name.clone())?;
|
&device_id,
|
||||||
|
&token,
|
||||||
|
body.initial_device_display_name.clone(),
|
||||||
|
Some(client.to_string()),
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 +207,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 +221,17 @@ 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> {
|
#[tracing::instrument(skip_all, fields(%client), name = "logout")]
|
||||||
|
pub(crate) async fn logout_route(
|
||||||
|
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp, 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 +249,19 @@ 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> {
|
#[tracing::instrument(skip_all, fields(%client), name = "logout")]
|
||||||
|
pub(crate) async fn logout_all_route(
|
||||||
|
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||||
|
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(
|
||||||
|
|||||||
+37
-26
@@ -1,6 +1,7 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use conduit::{debug_info, error};
|
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,17 +31,23 @@ 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");
|
||||||
|
|
||||||
Ok(send_state_event::v3::Response {
|
Ok(send_state_event::v3::Response {
|
||||||
event_id: send_state_event_for_key_helper(
|
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(),
|
||||||
|
if body.appservice_info.is_some() {
|
||||||
|
body.timestamp
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
)
|
)
|
||||||
.await?
|
.await?
|
||||||
.into(),
|
.into(),
|
||||||
@@ -58,9 +63,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`
|
||||||
@@ -70,11 +77,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)?
|
||||||
@@ -86,7 +93,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)
|
||||||
@@ -106,11 +113,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)?
|
||||||
@@ -121,7 +128,7 @@ 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)?
|
||||||
@@ -161,17 +168,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, timestamp: Option<ruma::MilliSecondsSinceUnixEpoch>,
|
||||||
) -> 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().rooms.state.mutex.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(
|
||||||
@@ -181,6 +191,7 @@ async fn send_state_event_for_key_helper(
|
|||||||
unsigned: None,
|
unsigned: None,
|
||||||
state_key: Some(state_key),
|
state_key: Some(state_key),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
|
timestamp,
|
||||||
},
|
},
|
||||||
sender,
|
sender,
|
||||||
room_id,
|
room_id,
|
||||||
@@ -192,18 +203,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 {
|
||||||
@@ -218,7 +229,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())
|
||||||
@@ -243,8 +254,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)?
|
||||||
|
|||||||
+154
-142
@@ -5,6 +5,7 @@ use std::{
|
|||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use axum::extract::State;
|
||||||
use conduit::{
|
use conduit::{
|
||||||
error,
|
error,
|
||||||
utils::math::{ruma_from_u64, ruma_from_usize, usize_from_ruma, usize_from_u64_truncated},
|
utils::math::{ruma_from_u64, ruma_from_usize, usize_from_ruma, usize_from_u64_truncated},
|
||||||
@@ -17,7 +18,7 @@ use ruma::{
|
|||||||
self,
|
self,
|
||||||
v3::{
|
v3::{
|
||||||
Ephemeral, Filter, GlobalAccountData, InviteState, InvitedRoom, JoinedRoom, LeftRoom, Presence,
|
Ephemeral, Filter, GlobalAccountData, InviteState, InvitedRoom, JoinedRoom, LeftRoom, Presence,
|
||||||
RoomAccountData, RoomSummary, Rooms, State, Timeline, ToDevice,
|
RoomAccountData, RoomSummary, Rooms, State as RoomState, Timeline, ToDevice,
|
||||||
},
|
},
|
||||||
v4::SlidingOp,
|
v4::SlidingOp,
|
||||||
DeviceLists, UnreadNotificationsCount,
|
DeviceLists, UnreadNotificationsCount,
|
||||||
@@ -34,7 +35,10 @@ use ruma::{
|
|||||||
};
|
};
|
||||||
use tracing::{Instrument as _, Span};
|
use tracing::{Instrument as _, Span};
|
||||||
|
|
||||||
use crate::{service::pdu::EventHash, services, utils, Error, PduEvent, Result, Ruma, RumaResponse};
|
use crate::{
|
||||||
|
service::{pdu::EventHash, Services},
|
||||||
|
utils, Error, PduEvent, Result, Ruma, RumaResponse,
|
||||||
|
};
|
||||||
|
|
||||||
/// # `GET /_matrix/client/r0/sync`
|
/// # `GET /_matrix/client/r0/sync`
|
||||||
///
|
///
|
||||||
@@ -72,23 +76,23 @@ use crate::{service::pdu::EventHash, services, utils, Error, PduEvent, Result, R
|
|||||||
/// - If the user left after `since`: `prev_batch` token, empty state (TODO:
|
/// - If the user left after `since`: `prev_batch` token, empty state (TODO:
|
||||||
/// subset of the state at the point of the leave)
|
/// subset of the state at the point of the leave)
|
||||||
pub(crate) async fn sync_events_route(
|
pub(crate) async fn sync_events_route(
|
||||||
body: Ruma<sync_events::v3::Request>,
|
State(services): State<crate::State>, body: Ruma<sync_events::v3::Request>,
|
||||||
) -> Result<sync_events::v3::Response, RumaResponse<UiaaResponse>> {
|
) -> Result<sync_events::v3::Response, RumaResponse<UiaaResponse>> {
|
||||||
let sender_user = body.sender_user.expect("user is authenticated");
|
let sender_user = body.sender_user.expect("user is authenticated");
|
||||||
let sender_device = body.sender_device.expect("user is authenticated");
|
let sender_device = body.sender_device.expect("user is authenticated");
|
||||||
let body = body.body;
|
let body = body.body;
|
||||||
|
|
||||||
// Presence update
|
// Presence update
|
||||||
if services().globals.allow_local_presence() {
|
if services.globals.allow_local_presence() {
|
||||||
services()
|
services
|
||||||
.presence
|
.presence
|
||||||
.ping_presence(&sender_user, &body.set_presence)?;
|
.ping_presence(&sender_user, &body.set_presence)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup watchers, so if there's no response, we can wait for them
|
// Setup watchers, so if there's no response, we can wait for them
|
||||||
let watcher = services().globals.watch(&sender_user, &sender_device);
|
let watcher = services.globals.watch(&sender_user, &sender_device);
|
||||||
|
|
||||||
let next_batch = services().globals.current_count()?;
|
let next_batch = services.globals.current_count()?;
|
||||||
let next_batchcount = PduCount::Normal(next_batch);
|
let next_batchcount = PduCount::Normal(next_batch);
|
||||||
let next_batch_string = next_batch.to_string();
|
let next_batch_string = next_batch.to_string();
|
||||||
|
|
||||||
@@ -96,17 +100,19 @@ pub(crate) async fn sync_events_route(
|
|||||||
let filter = match body.filter {
|
let filter = match body.filter {
|
||||||
None => FilterDefinition::default(),
|
None => FilterDefinition::default(),
|
||||||
Some(Filter::FilterDefinition(filter)) => filter,
|
Some(Filter::FilterDefinition(filter)) => filter,
|
||||||
Some(Filter::FilterId(filter_id)) => services()
|
Some(Filter::FilterId(filter_id)) => services
|
||||||
.users
|
.users
|
||||||
.get_filter(&sender_user, &filter_id)?
|
.get_filter(&sender_user, &filter_id)?
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// some clients, at least element, seem to require knowledge of redundant
|
||||||
|
// members for "inline" profiles on the timeline to work properly
|
||||||
let (lazy_load_enabled, lazy_load_send_redundant) = match filter.room.state.lazy_load_options {
|
let (lazy_load_enabled, lazy_load_send_redundant) = match filter.room.state.lazy_load_options {
|
||||||
LazyLoadOptions::Enabled {
|
LazyLoadOptions::Enabled {
|
||||||
include_redundant_members: redundant,
|
include_redundant_members,
|
||||||
} => (true, redundant),
|
} => (true, include_redundant_members),
|
||||||
LazyLoadOptions::Disabled => (false, false),
|
LazyLoadOptions::Disabled => (false, cfg!(feature = "element_hacks")),
|
||||||
};
|
};
|
||||||
|
|
||||||
let full_state = body.full_state;
|
let full_state = body.full_state;
|
||||||
@@ -126,28 +132,29 @@ pub(crate) async fn sync_events_route(
|
|||||||
|
|
||||||
// Look for device list updates of this account
|
// Look for device list updates of this account
|
||||||
device_list_updates.extend(
|
device_list_updates.extend(
|
||||||
services()
|
services
|
||||||
.users
|
.users
|
||||||
.keys_changed(sender_user.as_ref(), since, None)
|
.keys_changed(sender_user.as_ref(), since, None)
|
||||||
.filter_map(Result::ok),
|
.filter_map(Result::ok),
|
||||||
);
|
);
|
||||||
|
|
||||||
if services().globals.allow_local_presence() {
|
if services.globals.allow_local_presence() {
|
||||||
process_presence_updates(&mut presence_updates, since, &sender_user).await?;
|
process_presence_updates(&services, &mut presence_updates, since, &sender_user).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let all_joined_rooms = services()
|
let all_joined_rooms = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.rooms_joined(&sender_user)
|
.rooms_joined(&sender_user)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
// Coalesce database writes for the remainder of this scope.
|
// Coalesce database writes for the remainder of this scope.
|
||||||
let _cork = services().db.cork_and_flush();
|
let _cork = services.db.cork_and_flush();
|
||||||
|
|
||||||
for room_id in all_joined_rooms {
|
for room_id in all_joined_rooms {
|
||||||
let room_id = room_id?;
|
let room_id = room_id?;
|
||||||
if let Ok(joined_room) = load_joined_room(
|
if let Ok(joined_room) = load_joined_room(
|
||||||
|
&services,
|
||||||
&sender_user,
|
&sender_user,
|
||||||
&sender_device,
|
&sender_device,
|
||||||
&room_id,
|
&room_id,
|
||||||
@@ -170,13 +177,14 @@ pub(crate) async fn sync_events_route(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut left_rooms = BTreeMap::new();
|
let mut left_rooms = BTreeMap::new();
|
||||||
let all_left_rooms: Vec<_> = services()
|
let all_left_rooms: Vec<_> = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.rooms_left(&sender_user)
|
.rooms_left(&sender_user)
|
||||||
.collect();
|
.collect();
|
||||||
for result in all_left_rooms {
|
for result in all_left_rooms {
|
||||||
handle_left_room(
|
handle_left_room(
|
||||||
|
&services,
|
||||||
since,
|
since,
|
||||||
&result?.0,
|
&result?.0,
|
||||||
&sender_user,
|
&sender_user,
|
||||||
@@ -190,7 +198,7 @@ pub(crate) async fn sync_events_route(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut invited_rooms = BTreeMap::new();
|
let mut invited_rooms = BTreeMap::new();
|
||||||
let all_invited_rooms: Vec<_> = services()
|
let all_invited_rooms: Vec<_> = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.rooms_invited(&sender_user)
|
.rooms_invited(&sender_user)
|
||||||
@@ -199,10 +207,10 @@ pub(crate) async fn sync_events_route(
|
|||||||
let (room_id, invite_state_events) = result?;
|
let (room_id, invite_state_events) = result?;
|
||||||
|
|
||||||
// Get and drop the lock to wait for remaining operations to finish
|
// Get and drop the lock to wait for remaining operations to finish
|
||||||
let insert_lock = services().rooms.timeline.mutex_insert.lock(&room_id).await;
|
let insert_lock = services.rooms.timeline.mutex_insert.lock(&room_id).await;
|
||||||
drop(insert_lock);
|
drop(insert_lock);
|
||||||
|
|
||||||
let invite_count = services()
|
let invite_count = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.get_invite_count(&room_id, &sender_user)?;
|
.get_invite_count(&room_id, &sender_user)?;
|
||||||
@@ -223,14 +231,14 @@ pub(crate) async fn sync_events_route(
|
|||||||
}
|
}
|
||||||
|
|
||||||
for user_id in left_encrypted_users {
|
for user_id in left_encrypted_users {
|
||||||
let dont_share_encrypted_room = services()
|
let dont_share_encrypted_room = services
|
||||||
.rooms
|
.rooms
|
||||||
.user
|
.user
|
||||||
.get_shared_rooms(vec![sender_user.clone(), user_id.clone()])?
|
.get_shared_rooms(vec![sender_user.clone(), user_id.clone()])?
|
||||||
.filter_map(Result::ok)
|
.filter_map(Result::ok)
|
||||||
.filter_map(|other_room_id| {
|
.filter_map(|other_room_id| {
|
||||||
Some(
|
Some(
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.room_state_get(&other_room_id, &StateEventType::RoomEncryption, "")
|
.room_state_get(&other_room_id, &StateEventType::RoomEncryption, "")
|
||||||
@@ -247,7 +255,7 @@ pub(crate) async fn sync_events_route(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Remove all to-device events the device received *last time*
|
// Remove all to-device events the device received *last time*
|
||||||
services()
|
services
|
||||||
.users
|
.users
|
||||||
.remove_to_device_events(&sender_user, &sender_device, since)?;
|
.remove_to_device_events(&sender_user, &sender_device, since)?;
|
||||||
|
|
||||||
@@ -266,7 +274,7 @@ pub(crate) async fn sync_events_route(
|
|||||||
.collect(),
|
.collect(),
|
||||||
},
|
},
|
||||||
account_data: GlobalAccountData {
|
account_data: GlobalAccountData {
|
||||||
events: services()
|
events: services
|
||||||
.account_data
|
.account_data
|
||||||
.changes_since(None, &sender_user, since)?
|
.changes_since(None, &sender_user, since)?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@@ -281,11 +289,11 @@ pub(crate) async fn sync_events_route(
|
|||||||
changed: device_list_updates.into_iter().collect(),
|
changed: device_list_updates.into_iter().collect(),
|
||||||
left: device_list_left.into_iter().collect(),
|
left: device_list_left.into_iter().collect(),
|
||||||
},
|
},
|
||||||
device_one_time_keys_count: services()
|
device_one_time_keys_count: services
|
||||||
.users
|
.users
|
||||||
.count_one_time_keys(&sender_user, &sender_device)?,
|
.count_one_time_keys(&sender_user, &sender_device)?,
|
||||||
to_device: ToDevice {
|
to_device: ToDevice {
|
||||||
events: services()
|
events: services
|
||||||
.users
|
.users
|
||||||
.get_to_device_events(&sender_user, &sender_device)?,
|
.get_to_device_events(&sender_user, &sender_device)?,
|
||||||
},
|
},
|
||||||
@@ -311,16 +319,18 @@ pub(crate) async fn sync_events_route(
|
|||||||
Ok(response)
|
Ok(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
#[tracing::instrument(skip_all, fields(user_id = %sender_user, room_id = %room_id), name = "left_room")]
|
#[tracing::instrument(skip_all, fields(user_id = %sender_user, room_id = %room_id), name = "left_room")]
|
||||||
async fn handle_left_room(
|
async fn handle_left_room(
|
||||||
since: u64, room_id: &RoomId, sender_user: &UserId, left_rooms: &mut BTreeMap<ruma::OwnedRoomId, LeftRoom>,
|
services: &Services, since: u64, room_id: &RoomId, sender_user: &UserId,
|
||||||
next_batch_string: &str, full_state: bool, lazy_load_enabled: bool,
|
left_rooms: &mut BTreeMap<ruma::OwnedRoomId, LeftRoom>, next_batch_string: &str, full_state: bool,
|
||||||
|
lazy_load_enabled: bool,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
// Get and drop the lock to wait for remaining operations to finish
|
// Get and drop the lock to wait for remaining operations to finish
|
||||||
let insert_lock = services().rooms.timeline.mutex_insert.lock(room_id).await;
|
let insert_lock = services.rooms.timeline.mutex_insert.lock(room_id).await;
|
||||||
drop(insert_lock);
|
drop(insert_lock);
|
||||||
|
|
||||||
let left_count = services()
|
let left_count = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.get_left_count(room_id, sender_user)?;
|
.get_left_count(room_id, sender_user)?;
|
||||||
@@ -330,11 +340,11 @@ async fn handle_left_room(
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
if !services().rooms.metadata.exists(room_id)? {
|
if !services.rooms.metadata.exists(room_id)? {
|
||||||
// This is just a rejected invite, not a room we know
|
// This is just a rejected invite, not a room we know
|
||||||
// Insert a leave event anyways
|
// Insert a leave event anyways
|
||||||
let event = PduEvent {
|
let event = PduEvent {
|
||||||
event_id: EventId::new(services().globals.server_name()).into(),
|
event_id: EventId::new(services.globals.server_name()).into(),
|
||||||
sender: sender_user.to_owned(),
|
sender: sender_user.to_owned(),
|
||||||
origin: None,
|
origin: None,
|
||||||
origin_server_ts: utils::millis_since_unix_epoch()
|
origin_server_ts: utils::millis_since_unix_epoch()
|
||||||
@@ -367,7 +377,7 @@ async fn handle_left_room(
|
|||||||
prev_batch: Some(next_batch_string.to_owned()),
|
prev_batch: Some(next_batch_string.to_owned()),
|
||||||
events: Vec::new(),
|
events: Vec::new(),
|
||||||
},
|
},
|
||||||
state: State {
|
state: RoomState {
|
||||||
events: vec![event.to_sync_state_event()],
|
events: vec![event.to_sync_state_event()],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -377,27 +387,27 @@ async fn handle_left_room(
|
|||||||
|
|
||||||
let mut left_state_events = Vec::new();
|
let mut left_state_events = Vec::new();
|
||||||
|
|
||||||
let since_shortstatehash = services()
|
let since_shortstatehash = services
|
||||||
.rooms
|
.rooms
|
||||||
.user
|
.user
|
||||||
.get_token_shortstatehash(room_id, since)?;
|
.get_token_shortstatehash(room_id, since)?;
|
||||||
|
|
||||||
let since_state_ids = match since_shortstatehash {
|
let since_state_ids = match since_shortstatehash {
|
||||||
Some(s) => services().rooms.state_accessor.state_full_ids(s).await?,
|
Some(s) => services.rooms.state_accessor.state_full_ids(s).await?,
|
||||||
None => HashMap::new(),
|
None => HashMap::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(left_event_id) = services().rooms.state_accessor.room_state_get_id(
|
let Some(left_event_id) =
|
||||||
room_id,
|
services
|
||||||
&StateEventType::RoomMember,
|
.rooms
|
||||||
sender_user.as_str(),
|
.state_accessor
|
||||||
)?
|
.room_state_get_id(room_id, &StateEventType::RoomMember, sender_user.as_str())?
|
||||||
else {
|
else {
|
||||||
error!("Left room but no left state event");
|
error!("Left room but no left state event");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(left_shortstatehash) = services()
|
let Some(left_shortstatehash) = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.pdu_shortstatehash(&left_event_id)?
|
.pdu_shortstatehash(&left_event_id)?
|
||||||
@@ -406,13 +416,13 @@ async fn handle_left_room(
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut left_state_ids = services()
|
let mut left_state_ids = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.state_full_ids(left_shortstatehash)
|
.state_full_ids(left_shortstatehash)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let leave_shortstatekey = services()
|
let leave_shortstatekey = services
|
||||||
.rooms
|
.rooms
|
||||||
.short
|
.short
|
||||||
.get_or_create_shortstatekey(&StateEventType::RoomMember, sender_user.as_str())?;
|
.get_or_create_shortstatekey(&StateEventType::RoomMember, sender_user.as_str())?;
|
||||||
@@ -422,7 +432,7 @@ async fn handle_left_room(
|
|||||||
let mut i: u8 = 0;
|
let mut i: u8 = 0;
|
||||||
for (key, id) in left_state_ids {
|
for (key, id) in left_state_ids {
|
||||||
if full_state || since_state_ids.get(&key) != Some(&id) {
|
if full_state || since_state_ids.get(&key) != Some(&id) {
|
||||||
let (event_type, state_key) = services().rooms.short.get_statekey_from_short(key)?;
|
let (event_type, state_key) = services.rooms.short.get_statekey_from_short(key)?;
|
||||||
|
|
||||||
if !lazy_load_enabled
|
if !lazy_load_enabled
|
||||||
|| event_type != StateEventType::RoomMember
|
|| event_type != StateEventType::RoomMember
|
||||||
@@ -430,7 +440,7 @@ async fn handle_left_room(
|
|||||||
// TODO: Delete the following line when this is resolved: https://github.com/vector-im/element-web/issues/22565
|
// TODO: Delete the following line when this is resolved: https://github.com/vector-im/element-web/issues/22565
|
||||||
|| (cfg!(feature = "element_hacks") && *sender_user == state_key)
|
|| (cfg!(feature = "element_hacks") && *sender_user == 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;
|
||||||
};
|
};
|
||||||
@@ -456,7 +466,7 @@ async fn handle_left_room(
|
|||||||
prev_batch: Some(next_batch_string.to_owned()),
|
prev_batch: Some(next_batch_string.to_owned()),
|
||||||
events: Vec::new(),
|
events: Vec::new(),
|
||||||
},
|
},
|
||||||
state: State {
|
state: RoomState {
|
||||||
events: left_state_events,
|
events: left_state_events,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -465,13 +475,11 @@ async fn handle_left_room(
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn process_presence_updates(
|
async fn process_presence_updates(
|
||||||
presence_updates: &mut HashMap<OwnedUserId, PresenceEvent>, since: u64, syncing_user: &UserId,
|
services: &Services, presence_updates: &mut HashMap<OwnedUserId, PresenceEvent>, since: u64, syncing_user: &UserId,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
use crate::service::presence::Presence;
|
|
||||||
|
|
||||||
// Take presence updates
|
// Take presence updates
|
||||||
for (user_id, _, presence_bytes) in services().presence.presence_since(since) {
|
for (user_id, _, presence_bytes) in services.presence.presence_since(since) {
|
||||||
if !services()
|
if !services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.user_sees_user(syncing_user, &user_id)?
|
.user_sees_user(syncing_user, &user_id)?
|
||||||
@@ -479,7 +487,9 @@ async fn process_presence_updates(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let presence_event = Presence::from_json_bytes_to_event(&presence_bytes, &user_id)?;
|
let presence_event = services
|
||||||
|
.presence
|
||||||
|
.from_json_bytes_to_event(&presence_bytes, &user_id)?;
|
||||||
match presence_updates.entry(user_id) {
|
match presence_updates.entry(user_id) {
|
||||||
Entry::Vacant(slot) => {
|
Entry::Vacant(slot) => {
|
||||||
slot.insert(presence_event);
|
slot.insert(presence_event);
|
||||||
@@ -513,19 +523,20 @@ async fn process_presence_updates(
|
|||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
async fn load_joined_room(
|
async fn load_joined_room(
|
||||||
sender_user: &UserId, sender_device: &DeviceId, room_id: &RoomId, since: u64, sincecount: PduCount,
|
services: &Services, sender_user: &UserId, sender_device: &DeviceId, room_id: &RoomId, since: u64,
|
||||||
next_batch: u64, next_batchcount: PduCount, lazy_load_enabled: bool, lazy_load_send_redundant: bool,
|
sincecount: PduCount, next_batch: u64, next_batchcount: PduCount, lazy_load_enabled: bool,
|
||||||
full_state: bool, device_list_updates: &mut HashSet<OwnedUserId>, left_encrypted_users: &mut HashSet<OwnedUserId>,
|
lazy_load_send_redundant: bool, full_state: bool, device_list_updates: &mut HashSet<OwnedUserId>,
|
||||||
|
left_encrypted_users: &mut HashSet<OwnedUserId>,
|
||||||
) -> Result<JoinedRoom> {
|
) -> Result<JoinedRoom> {
|
||||||
// Get and drop the lock to wait for remaining operations to finish
|
// Get and drop the lock to wait for remaining operations to finish
|
||||||
// This will make sure the we have all events until next_batch
|
// This will make sure the we have all events until next_batch
|
||||||
let insert_lock = services().rooms.timeline.mutex_insert.lock(room_id).await;
|
let insert_lock = services.rooms.timeline.mutex_insert.lock(room_id).await;
|
||||||
drop(insert_lock);
|
drop(insert_lock);
|
||||||
|
|
||||||
let (timeline_pdus, limited) = load_timeline(sender_user, room_id, sincecount, 10)?;
|
let (timeline_pdus, limited) = load_timeline(services, sender_user, room_id, sincecount, 10)?;
|
||||||
|
|
||||||
let send_notification_counts = !timeline_pdus.is_empty()
|
let send_notification_counts = !timeline_pdus.is_empty()
|
||||||
|| services()
|
|| services
|
||||||
.rooms
|
.rooms
|
||||||
.user
|
.user
|
||||||
.last_notification_read(sender_user, room_id)?
|
.last_notification_read(sender_user, room_id)?
|
||||||
@@ -536,7 +547,7 @@ async fn load_joined_room(
|
|||||||
timeline_users.insert(event.sender.as_str().to_owned());
|
timeline_users.insert(event.sender.as_str().to_owned());
|
||||||
}
|
}
|
||||||
|
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.lazy_loading
|
.lazy_loading
|
||||||
.lazy_load_confirm_delivery(sender_user, sender_device, room_id, sincecount)
|
.lazy_load_confirm_delivery(sender_user, sender_device, room_id, sincecount)
|
||||||
@@ -544,11 +555,11 @@ async fn load_joined_room(
|
|||||||
|
|
||||||
// Database queries:
|
// Database queries:
|
||||||
|
|
||||||
let Some(current_shortstatehash) = services().rooms.state.get_room_shortstatehash(room_id)? else {
|
let Some(current_shortstatehash) = services.rooms.state.get_room_shortstatehash(room_id)? else {
|
||||||
return Err!(Database(error!("Room {room_id} has no state")));
|
return Err!(Database(error!("Room {room_id} has no state")));
|
||||||
};
|
};
|
||||||
|
|
||||||
let since_shortstatehash = services()
|
let since_shortstatehash = services
|
||||||
.rooms
|
.rooms
|
||||||
.user
|
.user
|
||||||
.get_token_shortstatehash(room_id, since)?;
|
.get_token_shortstatehash(room_id, since)?;
|
||||||
@@ -560,12 +571,12 @@ async fn load_joined_room(
|
|||||||
} else {
|
} else {
|
||||||
// Calculates joined_member_count, invited_member_count and heroes
|
// Calculates joined_member_count, invited_member_count and heroes
|
||||||
let calculate_counts = || {
|
let calculate_counts = || {
|
||||||
let joined_member_count = services()
|
let joined_member_count = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.room_joined_count(room_id)?
|
.room_joined_count(room_id)?
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
let invited_member_count = services()
|
let invited_member_count = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.room_invited_count(room_id)?
|
.room_invited_count(room_id)?
|
||||||
@@ -578,7 +589,7 @@ async fn load_joined_room(
|
|||||||
// Go through all PDUs and for each member event, check if the user is still
|
// Go through all PDUs and for each member event, check if the user is still
|
||||||
// joined or invited until we have 5 or we reach the end
|
// joined or invited until we have 5 or we reach the end
|
||||||
|
|
||||||
for hero in services()
|
for hero in services
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.all_pdus(sender_user, room_id)?
|
.all_pdus(sender_user, room_id)?
|
||||||
@@ -594,8 +605,8 @@ async fn load_joined_room(
|
|||||||
|
|
||||||
// The membership was and still is invite or join
|
// The membership was and still is invite or join
|
||||||
if matches!(content.membership, MembershipState::Join | MembershipState::Invite)
|
if matches!(content.membership, MembershipState::Join | MembershipState::Invite)
|
||||||
&& (services().rooms.state_cache.is_joined(&user_id, room_id)?
|
&& (services.rooms.state_cache.is_joined(&user_id, room_id)?
|
||||||
|| services().rooms.state_cache.is_invited(&user_id, room_id)?)
|
|| services.rooms.state_cache.is_invited(&user_id, room_id)?)
|
||||||
{
|
{
|
||||||
Ok::<_, Error>(Some(user_id))
|
Ok::<_, Error>(Some(user_id))
|
||||||
} else {
|
} else {
|
||||||
@@ -622,7 +633,7 @@ async fn load_joined_room(
|
|||||||
|
|
||||||
let since_sender_member: Option<RoomMemberEventContent> = since_shortstatehash
|
let since_sender_member: Option<RoomMemberEventContent> = since_shortstatehash
|
||||||
.and_then(|shortstatehash| {
|
.and_then(|shortstatehash| {
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.state_get(shortstatehash, &StateEventType::RoomMember, sender_user.as_str())
|
.state_get(shortstatehash, &StateEventType::RoomMember, sender_user.as_str())
|
||||||
@@ -643,7 +654,7 @@ async fn load_joined_room(
|
|||||||
|
|
||||||
let (joined_member_count, invited_member_count, heroes) = calculate_counts()?;
|
let (joined_member_count, invited_member_count, heroes) = calculate_counts()?;
|
||||||
|
|
||||||
let current_state_ids = services()
|
let current_state_ids = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.state_full_ids(current_shortstatehash)
|
.state_full_ids(current_shortstatehash)
|
||||||
@@ -654,13 +665,13 @@ async fn load_joined_room(
|
|||||||
|
|
||||||
let mut i: u8 = 0;
|
let mut i: u8 = 0;
|
||||||
for (shortstatekey, id) in current_state_ids {
|
for (shortstatekey, id) in current_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;
|
||||||
};
|
};
|
||||||
@@ -676,7 +687,7 @@ async fn load_joined_room(
|
|||||||
// TODO: Delete the following line when this is resolved: https://github.com/vector-im/element-web/issues/22565
|
// TODO: Delete the following line when this is resolved: https://github.com/vector-im/element-web/issues/22565
|
||||||
|| (cfg!(feature = "element_hacks") && *sender_user == state_key)
|
|| (cfg!(feature = "element_hacks") && *sender_user == 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;
|
||||||
};
|
};
|
||||||
@@ -695,14 +706,14 @@ async fn load_joined_room(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Reset lazy loading because this is an initial sync
|
// Reset lazy loading because this is an initial sync
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.lazy_loading
|
.lazy_loading
|
||||||
.lazy_load_reset(sender_user, sender_device, room_id)?;
|
.lazy_load_reset(sender_user, sender_device, room_id)?;
|
||||||
|
|
||||||
// The state_events above should contain all timeline_users, let's mark them as
|
// The state_events above should contain all timeline_users, let's mark them as
|
||||||
// lazy loaded.
|
// lazy loaded.
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.lazy_loading
|
.lazy_loading
|
||||||
.lazy_load_mark_sent(sender_user, sender_device, room_id, lazy_loaded, next_batchcount)
|
.lazy_load_mark_sent(sender_user, sender_device, room_id, lazy_loaded, next_batchcount)
|
||||||
@@ -716,12 +727,12 @@ async fn load_joined_room(
|
|||||||
let mut delta_state_events = Vec::new();
|
let mut delta_state_events = Vec::new();
|
||||||
|
|
||||||
if since_shortstatehash != current_shortstatehash {
|
if since_shortstatehash != current_shortstatehash {
|
||||||
let current_state_ids = services()
|
let current_state_ids = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.state_full_ids(current_shortstatehash)
|
.state_full_ids(current_shortstatehash)
|
||||||
.await?;
|
.await?;
|
||||||
let since_state_ids = services()
|
let since_state_ids = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.state_full_ids(since_shortstatehash)
|
.state_full_ids(since_shortstatehash)
|
||||||
@@ -729,7 +740,7 @@ async fn load_joined_room(
|
|||||||
|
|
||||||
for (key, id) in current_state_ids {
|
for (key, id) in current_state_ids {
|
||||||
if full_state || since_state_ids.get(&key) != Some(&id) {
|
if full_state || since_state_ids.get(&key) != Some(&id) {
|
||||||
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;
|
||||||
};
|
};
|
||||||
@@ -740,13 +751,13 @@ async fn load_joined_room(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let encrypted_room = services()
|
let encrypted_room = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.state_get(current_shortstatehash, &StateEventType::RoomEncryption, "")?
|
.state_get(current_shortstatehash, &StateEventType::RoomEncryption, "")?
|
||||||
.is_some();
|
.is_some();
|
||||||
|
|
||||||
let since_encryption = services().rooms.state_accessor.state_get(
|
let since_encryption = services.rooms.state_accessor.state_get(
|
||||||
since_shortstatehash,
|
since_shortstatehash,
|
||||||
&StateEventType::RoomEncryption,
|
&StateEventType::RoomEncryption,
|
||||||
"",
|
"",
|
||||||
@@ -781,7 +792,7 @@ async fn load_joined_room(
|
|||||||
match new_membership {
|
match new_membership {
|
||||||
MembershipState::Join => {
|
MembershipState::Join => {
|
||||||
// A new user joined an encrypted room
|
// A new user joined an encrypted room
|
||||||
if !share_encrypted_room(sender_user, &user_id, room_id)? {
|
if !share_encrypted_room(services, sender_user, &user_id, room_id)? {
|
||||||
device_list_updates.insert(user_id);
|
device_list_updates.insert(user_id);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -798,7 +809,7 @@ async fn load_joined_room(
|
|||||||
if joined_since_last_sync && encrypted_room || new_encrypted_room {
|
if joined_since_last_sync && encrypted_room || new_encrypted_room {
|
||||||
// If the user is in a new encrypted room, give them all joined users
|
// If the user is in a new encrypted room, give them all joined users
|
||||||
device_list_updates.extend(
|
device_list_updates.extend(
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.room_members(room_id)
|
.room_members(room_id)
|
||||||
@@ -810,7 +821,7 @@ async fn load_joined_room(
|
|||||||
.filter(|user_id| {
|
.filter(|user_id| {
|
||||||
// Only send keys if the sender doesn't share an encrypted room with the target
|
// Only send keys if the sender doesn't share an encrypted room with the target
|
||||||
// already
|
// already
|
||||||
!share_encrypted_room(sender_user, user_id, room_id).unwrap_or(false)
|
!share_encrypted_room(services, sender_user, user_id, room_id).unwrap_or(false)
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -848,14 +859,14 @@ async fn load_joined_room(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
||||||
&event.sender,
|
&event.sender,
|
||||||
)? || lazy_load_send_redundant
|
)? || lazy_load_send_redundant
|
||||||
{
|
{
|
||||||
if let Some(member_event) = services().rooms.state_accessor.room_state_get(
|
if let Some(member_event) = services.rooms.state_accessor.room_state_get(
|
||||||
room_id,
|
room_id,
|
||||||
&StateEventType::RoomMember,
|
&StateEventType::RoomMember,
|
||||||
event.sender.as_str(),
|
event.sender.as_str(),
|
||||||
@@ -866,7 +877,7 @@ async fn load_joined_room(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.lazy_loading
|
.lazy_loading
|
||||||
.lazy_load_mark_sent(sender_user, sender_device, room_id, lazy_loaded, next_batchcount)
|
.lazy_load_mark_sent(sender_user, sender_device, room_id, lazy_loaded, next_batchcount)
|
||||||
@@ -884,7 +895,7 @@ async fn load_joined_room(
|
|||||||
|
|
||||||
// Look for device list updates in this room
|
// Look for device list updates in this room
|
||||||
device_list_updates.extend(
|
device_list_updates.extend(
|
||||||
services()
|
services
|
||||||
.users
|
.users
|
||||||
.keys_changed(room_id.as_ref(), since, None)
|
.keys_changed(room_id.as_ref(), since, None)
|
||||||
.filter_map(Result::ok),
|
.filter_map(Result::ok),
|
||||||
@@ -892,7 +903,7 @@ async fn load_joined_room(
|
|||||||
|
|
||||||
let notification_count = if send_notification_counts {
|
let notification_count = if send_notification_counts {
|
||||||
Some(
|
Some(
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.user
|
.user
|
||||||
.notification_count(sender_user, room_id)?
|
.notification_count(sender_user, room_id)?
|
||||||
@@ -905,7 +916,7 @@ async fn load_joined_room(
|
|||||||
|
|
||||||
let highlight_count = if send_notification_counts {
|
let highlight_count = if send_notification_counts {
|
||||||
Some(
|
Some(
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.user
|
.user
|
||||||
.highlight_count(sender_user, room_id)?
|
.highlight_count(sender_user, room_id)?
|
||||||
@@ -933,7 +944,7 @@ async fn load_joined_room(
|
|||||||
.map(|(_, pdu)| pdu.to_sync_room_event())
|
.map(|(_, pdu)| pdu.to_sync_room_event())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut edus: Vec<_> = services()
|
let mut edus: Vec<_> = services
|
||||||
.rooms
|
.rooms
|
||||||
.read_receipt
|
.read_receipt
|
||||||
.readreceipts_since(room_id, since)
|
.readreceipts_since(room_id, since)
|
||||||
@@ -941,10 +952,10 @@ async fn load_joined_room(
|
|||||||
.map(|(_, _, v)| v)
|
.map(|(_, _, v)| v)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
if services().rooms.typing.last_typing_update(room_id).await? > since {
|
if services.rooms.typing.last_typing_update(room_id).await? > since {
|
||||||
edus.push(
|
edus.push(
|
||||||
serde_json::from_str(
|
serde_json::from_str(
|
||||||
&serde_json::to_string(&services().rooms.typing.typings_all(room_id).await?)
|
&serde_json::to_string(&services.rooms.typing.typings_all(room_id).await?)
|
||||||
.expect("event is valid, we just created it"),
|
.expect("event is valid, we just created it"),
|
||||||
)
|
)
|
||||||
.expect("event is valid, we just created it"),
|
.expect("event is valid, we just created it"),
|
||||||
@@ -953,14 +964,14 @@ async fn load_joined_room(
|
|||||||
|
|
||||||
// Save the state after this sync so we can send the correct state diff next
|
// Save the state after this sync so we can send the correct state diff next
|
||||||
// sync
|
// sync
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.user
|
.user
|
||||||
.associate_token_shortstatehash(room_id, next_batch, current_shortstatehash)?;
|
.associate_token_shortstatehash(room_id, next_batch, current_shortstatehash)?;
|
||||||
|
|
||||||
Ok(JoinedRoom {
|
Ok(JoinedRoom {
|
||||||
account_data: RoomAccountData {
|
account_data: RoomAccountData {
|
||||||
events: services()
|
events: services
|
||||||
.account_data
|
.account_data
|
||||||
.changes_since(Some(room_id), sender_user, since)?
|
.changes_since(Some(room_id), sender_user, since)?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@@ -985,7 +996,7 @@ async fn load_joined_room(
|
|||||||
prev_batch,
|
prev_batch,
|
||||||
events: room_events,
|
events: room_events,
|
||||||
},
|
},
|
||||||
state: State {
|
state: RoomState {
|
||||||
events: state_events
|
events: state_events
|
||||||
.iter()
|
.iter()
|
||||||
.map(|pdu| pdu.to_sync_state_event())
|
.map(|pdu| pdu.to_sync_state_event())
|
||||||
@@ -999,16 +1010,16 @@ async fn load_joined_room(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn load_timeline(
|
fn load_timeline(
|
||||||
sender_user: &UserId, room_id: &RoomId, roomsincecount: PduCount, limit: u64,
|
services: &Services, sender_user: &UserId, room_id: &RoomId, roomsincecount: PduCount, limit: u64,
|
||||||
) -> Result<(Vec<(PduCount, PduEvent)>, bool), Error> {
|
) -> Result<(Vec<(PduCount, PduEvent)>, bool), Error> {
|
||||||
let timeline_pdus;
|
let timeline_pdus;
|
||||||
let limited = if services()
|
let limited = if services
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.last_timeline_count(sender_user, room_id)?
|
.last_timeline_count(sender_user, room_id)?
|
||||||
> roomsincecount
|
> roomsincecount
|
||||||
{
|
{
|
||||||
let mut non_timeline_pdus = services()
|
let mut non_timeline_pdus = services
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.pdus_until(sender_user, room_id, PduCount::max())?
|
.pdus_until(sender_user, room_id, PduCount::max())?
|
||||||
@@ -1040,8 +1051,10 @@ fn load_timeline(
|
|||||||
Ok((timeline_pdus, limited))
|
Ok((timeline_pdus, limited))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn share_encrypted_room(sender_user: &UserId, user_id: &UserId, ignore_room: &RoomId) -> Result<bool> {
|
fn share_encrypted_room(
|
||||||
Ok(services()
|
services: &Services, sender_user: &UserId, user_id: &UserId, ignore_room: &RoomId,
|
||||||
|
) -> Result<bool> {
|
||||||
|
Ok(services
|
||||||
.rooms
|
.rooms
|
||||||
.user
|
.user
|
||||||
.get_shared_rooms(vec![sender_user.to_owned(), user_id.to_owned()])?
|
.get_shared_rooms(vec![sender_user.to_owned(), user_id.to_owned()])?
|
||||||
@@ -1049,7 +1062,7 @@ fn share_encrypted_room(sender_user: &UserId, user_id: &UserId, ignore_room: &Ro
|
|||||||
.filter(|room_id| room_id != ignore_room)
|
.filter(|room_id| room_id != ignore_room)
|
||||||
.filter_map(|other_room_id| {
|
.filter_map(|other_room_id| {
|
||||||
Some(
|
Some(
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.room_state_get(&other_room_id, &StateEventType::RoomEncryption, "")
|
.room_state_get(&other_room_id, &StateEventType::RoomEncryption, "")
|
||||||
@@ -1064,15 +1077,15 @@ fn share_encrypted_room(sender_user: &UserId, user_id: &UserId, ignore_room: &Ro
|
|||||||
///
|
///
|
||||||
/// Sliding Sync endpoint (future endpoint: `/_matrix/client/v4/sync`)
|
/// Sliding Sync endpoint (future endpoint: `/_matrix/client/v4/sync`)
|
||||||
pub(crate) async fn sync_events_v4_route(
|
pub(crate) async fn sync_events_v4_route(
|
||||||
body: Ruma<sync_events::v4::Request>,
|
State(services): State<crate::State>, body: Ruma<sync_events::v4::Request>,
|
||||||
) -> Result<sync_events::v4::Response, RumaResponse<UiaaResponse>> {
|
) -> Result<sync_events::v4::Response, RumaResponse<UiaaResponse>> {
|
||||||
let sender_user = body.sender_user.expect("user is authenticated");
|
let sender_user = body.sender_user.expect("user is authenticated");
|
||||||
let sender_device = body.sender_device.expect("user is authenticated");
|
let sender_device = body.sender_device.expect("user is authenticated");
|
||||||
let mut body = body.body;
|
let mut body = body.body;
|
||||||
// Setup watchers, so if there's no response, we can wait for them
|
// Setup watchers, so if there's no response, we can wait for them
|
||||||
let watcher = services().globals.watch(&sender_user, &sender_device);
|
let watcher = services.globals.watch(&sender_user, &sender_device);
|
||||||
|
|
||||||
let next_batch = services().globals.next_count()?;
|
let next_batch = services.globals.next_count()?;
|
||||||
|
|
||||||
let globalsince = body
|
let globalsince = body
|
||||||
.pos
|
.pos
|
||||||
@@ -1082,21 +1095,19 @@ pub(crate) async fn sync_events_v4_route(
|
|||||||
|
|
||||||
if globalsince == 0 {
|
if globalsince == 0 {
|
||||||
if let Some(conn_id) = &body.conn_id {
|
if let Some(conn_id) = &body.conn_id {
|
||||||
services().users.forget_sync_request_connection(
|
services
|
||||||
sender_user.clone(),
|
.users
|
||||||
sender_device.clone(),
|
.forget_sync_request_connection(sender_user.clone(), sender_device.clone(), conn_id.clone());
|
||||||
conn_id.clone(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get sticky parameters from cache
|
// Get sticky parameters from cache
|
||||||
let known_rooms =
|
let known_rooms =
|
||||||
services()
|
services
|
||||||
.users
|
.users
|
||||||
.update_sync_request_with_cache(sender_user.clone(), sender_device.clone(), &mut body);
|
.update_sync_request_with_cache(sender_user.clone(), sender_device.clone(), &mut body);
|
||||||
|
|
||||||
let all_joined_rooms = services()
|
let all_joined_rooms = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.rooms_joined(&sender_user)
|
.rooms_joined(&sender_user)
|
||||||
@@ -1104,7 +1115,7 @@ pub(crate) async fn sync_events_v4_route(
|
|||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
if body.extensions.to_device.enabled.unwrap_or(false) {
|
if body.extensions.to_device.enabled.unwrap_or(false) {
|
||||||
services()
|
services
|
||||||
.users
|
.users
|
||||||
.remove_to_device_events(&sender_user, &sender_device, globalsince)?;
|
.remove_to_device_events(&sender_user, &sender_device, globalsince)?;
|
||||||
}
|
}
|
||||||
@@ -1116,26 +1127,26 @@ pub(crate) async fn sync_events_v4_route(
|
|||||||
if body.extensions.e2ee.enabled.unwrap_or(false) {
|
if body.extensions.e2ee.enabled.unwrap_or(false) {
|
||||||
// Look for device list updates of this account
|
// Look for device list updates of this account
|
||||||
device_list_changes.extend(
|
device_list_changes.extend(
|
||||||
services()
|
services
|
||||||
.users
|
.users
|
||||||
.keys_changed(sender_user.as_ref(), globalsince, None)
|
.keys_changed(sender_user.as_ref(), globalsince, None)
|
||||||
.filter_map(Result::ok),
|
.filter_map(Result::ok),
|
||||||
);
|
);
|
||||||
|
|
||||||
for room_id in &all_joined_rooms {
|
for room_id in &all_joined_rooms {
|
||||||
let Some(current_shortstatehash) = services().rooms.state.get_room_shortstatehash(room_id)? else {
|
let Some(current_shortstatehash) = services.rooms.state.get_room_shortstatehash(room_id)? else {
|
||||||
error!("Room {} has no state", room_id);
|
error!("Room {} has no state", room_id);
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
let since_shortstatehash = services()
|
let since_shortstatehash = services
|
||||||
.rooms
|
.rooms
|
||||||
.user
|
.user
|
||||||
.get_token_shortstatehash(room_id, globalsince)?;
|
.get_token_shortstatehash(room_id, globalsince)?;
|
||||||
|
|
||||||
let since_sender_member: Option<RoomMemberEventContent> = since_shortstatehash
|
let since_sender_member: Option<RoomMemberEventContent> = since_shortstatehash
|
||||||
.and_then(|shortstatehash| {
|
.and_then(|shortstatehash| {
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.state_get(shortstatehash, &StateEventType::RoomMember, sender_user.as_str())
|
.state_get(shortstatehash, &StateEventType::RoomMember, sender_user.as_str())
|
||||||
@@ -1148,7 +1159,7 @@ pub(crate) async fn sync_events_v4_route(
|
|||||||
.ok()
|
.ok()
|
||||||
});
|
});
|
||||||
|
|
||||||
let encrypted_room = services()
|
let encrypted_room = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.state_get(current_shortstatehash, &StateEventType::RoomEncryption, "")?
|
.state_get(current_shortstatehash, &StateEventType::RoomEncryption, "")?
|
||||||
@@ -1160,7 +1171,7 @@ pub(crate) async fn sync_events_v4_route(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let since_encryption = services().rooms.state_accessor.state_get(
|
let since_encryption = services.rooms.state_accessor.state_get(
|
||||||
since_shortstatehash,
|
since_shortstatehash,
|
||||||
&StateEventType::RoomEncryption,
|
&StateEventType::RoomEncryption,
|
||||||
"",
|
"",
|
||||||
@@ -1171,12 +1182,12 @@ pub(crate) async fn sync_events_v4_route(
|
|||||||
|
|
||||||
let new_encrypted_room = encrypted_room && since_encryption.is_none();
|
let new_encrypted_room = encrypted_room && since_encryption.is_none();
|
||||||
if encrypted_room {
|
if encrypted_room {
|
||||||
let current_state_ids = services()
|
let current_state_ids = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.state_full_ids(current_shortstatehash)
|
.state_full_ids(current_shortstatehash)
|
||||||
.await?;
|
.await?;
|
||||||
let since_state_ids = services()
|
let since_state_ids = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.state_full_ids(since_shortstatehash)
|
.state_full_ids(since_shortstatehash)
|
||||||
@@ -1184,7 +1195,7 @@ pub(crate) async fn sync_events_v4_route(
|
|||||||
|
|
||||||
for (key, id) in current_state_ids {
|
for (key, id) in current_state_ids {
|
||||||
if since_state_ids.get(&key) != Some(&id) {
|
if since_state_ids.get(&key) != Some(&id) {
|
||||||
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;
|
||||||
};
|
};
|
||||||
@@ -1205,7 +1216,7 @@ pub(crate) async fn sync_events_v4_route(
|
|||||||
match new_membership {
|
match new_membership {
|
||||||
MembershipState::Join => {
|
MembershipState::Join => {
|
||||||
// A new user joined an encrypted room
|
// A new user joined an encrypted room
|
||||||
if !share_encrypted_room(&sender_user, &user_id, room_id)? {
|
if !share_encrypted_room(&services, &sender_user, &user_id, room_id)? {
|
||||||
device_list_changes.insert(user_id);
|
device_list_changes.insert(user_id);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -1222,7 +1233,7 @@ pub(crate) async fn sync_events_v4_route(
|
|||||||
if joined_since_last_sync || new_encrypted_room {
|
if joined_since_last_sync || new_encrypted_room {
|
||||||
// If the user is in a new encrypted room, give them all joined users
|
// If the user is in a new encrypted room, give them all joined users
|
||||||
device_list_changes.extend(
|
device_list_changes.extend(
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.room_members(room_id)
|
.room_members(room_id)
|
||||||
@@ -1234,7 +1245,7 @@ pub(crate) async fn sync_events_v4_route(
|
|||||||
.filter(|user_id| {
|
.filter(|user_id| {
|
||||||
// Only send keys if the sender doesn't share an encrypted room with the target
|
// Only send keys if the sender doesn't share an encrypted room with the target
|
||||||
// already
|
// already
|
||||||
!share_encrypted_room(&sender_user, user_id, room_id).unwrap_or(false)
|
!share_encrypted_room(&services, &sender_user, user_id, room_id).unwrap_or(false)
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1242,21 +1253,21 @@ pub(crate) async fn sync_events_v4_route(
|
|||||||
}
|
}
|
||||||
// Look for device list updates in this room
|
// Look for device list updates in this room
|
||||||
device_list_changes.extend(
|
device_list_changes.extend(
|
||||||
services()
|
services
|
||||||
.users
|
.users
|
||||||
.keys_changed(room_id.as_ref(), globalsince, None)
|
.keys_changed(room_id.as_ref(), globalsince, None)
|
||||||
.filter_map(Result::ok),
|
.filter_map(Result::ok),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
for user_id in left_encrypted_users {
|
for user_id in left_encrypted_users {
|
||||||
let dont_share_encrypted_room = services()
|
let dont_share_encrypted_room = services
|
||||||
.rooms
|
.rooms
|
||||||
.user
|
.user
|
||||||
.get_shared_rooms(vec![sender_user.clone(), user_id.clone()])?
|
.get_shared_rooms(vec![sender_user.clone(), user_id.clone()])?
|
||||||
.filter_map(Result::ok)
|
.filter_map(Result::ok)
|
||||||
.filter_map(|other_room_id| {
|
.filter_map(|other_room_id| {
|
||||||
Some(
|
Some(
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.room_state_get(&other_room_id, &StateEventType::RoomEncryption, "")
|
.room_state_get(&other_room_id, &StateEventType::RoomEncryption, "")
|
||||||
@@ -1336,7 +1347,7 @@ pub(crate) async fn sync_events_v4_route(
|
|||||||
);
|
);
|
||||||
|
|
||||||
if let Some(conn_id) = &body.conn_id {
|
if let Some(conn_id) = &body.conn_id {
|
||||||
services().users.update_sync_known_rooms(
|
services.users.update_sync_known_rooms(
|
||||||
sender_user.clone(),
|
sender_user.clone(),
|
||||||
sender_device.clone(),
|
sender_device.clone(),
|
||||||
conn_id.clone(),
|
conn_id.clone(),
|
||||||
@@ -1349,7 +1360,7 @@ pub(crate) async fn sync_events_v4_route(
|
|||||||
|
|
||||||
let mut known_subscription_rooms = BTreeSet::new();
|
let mut known_subscription_rooms = BTreeSet::new();
|
||||||
for (room_id, room) in &body.room_subscriptions {
|
for (room_id, room) in &body.room_subscriptions {
|
||||||
if !services().rooms.metadata.exists(room_id)? {
|
if !services.rooms.metadata.exists(room_id)? {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let todo_room = todo_rooms
|
let todo_room = todo_rooms
|
||||||
@@ -1375,7 +1386,7 @@ pub(crate) async fn sync_events_v4_route(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(conn_id) = &body.conn_id {
|
if let Some(conn_id) = &body.conn_id {
|
||||||
services().users.update_sync_known_rooms(
|
services.users.update_sync_known_rooms(
|
||||||
sender_user.clone(),
|
sender_user.clone(),
|
||||||
sender_device.clone(),
|
sender_device.clone(),
|
||||||
conn_id.clone(),
|
conn_id.clone(),
|
||||||
@@ -1386,7 +1397,7 @@ pub(crate) async fn sync_events_v4_route(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(conn_id) = &body.conn_id {
|
if let Some(conn_id) = &body.conn_id {
|
||||||
services().users.update_sync_subscriptions(
|
services.users.update_sync_subscriptions(
|
||||||
sender_user.clone(),
|
sender_user.clone(),
|
||||||
sender_device.clone(),
|
sender_device.clone(),
|
||||||
conn_id.clone(),
|
conn_id.clone(),
|
||||||
@@ -1398,7 +1409,8 @@ pub(crate) async fn sync_events_v4_route(
|
|||||||
for (room_id, (required_state_request, timeline_limit, roomsince)) in &todo_rooms {
|
for (room_id, (required_state_request, timeline_limit, roomsince)) in &todo_rooms {
|
||||||
let roomsincecount = PduCount::Normal(*roomsince);
|
let roomsincecount = PduCount::Normal(*roomsince);
|
||||||
|
|
||||||
let (timeline_pdus, limited) = load_timeline(&sender_user, room_id, roomsincecount, *timeline_limit)?;
|
let (timeline_pdus, limited) =
|
||||||
|
load_timeline(&services, &sender_user, room_id, roomsincecount, *timeline_limit)?;
|
||||||
|
|
||||||
if roomsince != &0 && timeline_pdus.is_empty() {
|
if roomsince != &0 && timeline_pdus.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
@@ -1431,7 +1443,7 @@ pub(crate) async fn sync_events_v4_route(
|
|||||||
let required_state = required_state_request
|
let required_state = required_state_request
|
||||||
.iter()
|
.iter()
|
||||||
.map(|state| {
|
.map(|state| {
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.room_state_get(room_id, &state.0, &state.1)
|
.room_state_get(room_id, &state.0, &state.1)
|
||||||
@@ -1442,7 +1454,7 @@ pub(crate) async fn sync_events_v4_route(
|
|||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// Heroes
|
// Heroes
|
||||||
let heroes = services()
|
let heroes = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.room_members(room_id)
|
.room_members(room_id)
|
||||||
@@ -1450,7 +1462,7 @@ pub(crate) async fn sync_events_v4_route(
|
|||||||
.filter(|member| member != &sender_user)
|
.filter(|member| member != &sender_user)
|
||||||
.map(|member| {
|
.map(|member| {
|
||||||
Ok::<_, Error>(
|
Ok::<_, Error>(
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.get_member(room_id, &member)?
|
.get_member(room_id, &member)?
|
||||||
@@ -1491,11 +1503,11 @@ pub(crate) async fn sync_events_v4_route(
|
|||||||
rooms.insert(
|
rooms.insert(
|
||||||
room_id.clone(),
|
room_id.clone(),
|
||||||
sync_events::v4::SlidingSyncRoom {
|
sync_events::v4::SlidingSyncRoom {
|
||||||
name: services().rooms.state_accessor.get_name(room_id)?.or(name),
|
name: services.rooms.state_accessor.get_name(room_id)?.or(name),
|
||||||
avatar: if let Some(heroes_avatar) = heroes_avatar {
|
avatar: if let Some(heroes_avatar) = heroes_avatar {
|
||||||
ruma::JsOption::Some(heroes_avatar)
|
ruma::JsOption::Some(heroes_avatar)
|
||||||
} else {
|
} else {
|
||||||
match services().rooms.state_accessor.get_avatar(room_id)? {
|
match services.rooms.state_accessor.get_avatar(room_id)? {
|
||||||
ruma::JsOption::Some(avatar) => ruma::JsOption::from_option(avatar.url),
|
ruma::JsOption::Some(avatar) => ruma::JsOption::from_option(avatar.url),
|
||||||
ruma::JsOption::Null => ruma::JsOption::Null,
|
ruma::JsOption::Null => ruma::JsOption::Null,
|
||||||
ruma::JsOption::Undefined => ruma::JsOption::Undefined,
|
ruma::JsOption::Undefined => ruma::JsOption::Undefined,
|
||||||
@@ -1506,7 +1518,7 @@ pub(crate) async fn sync_events_v4_route(
|
|||||||
invite_state: None,
|
invite_state: None,
|
||||||
unread_notifications: UnreadNotificationsCount {
|
unread_notifications: UnreadNotificationsCount {
|
||||||
highlight_count: Some(
|
highlight_count: Some(
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.user
|
.user
|
||||||
.highlight_count(&sender_user, room_id)?
|
.highlight_count(&sender_user, room_id)?
|
||||||
@@ -1514,7 +1526,7 @@ pub(crate) async fn sync_events_v4_route(
|
|||||||
.expect("notification count can't go that high"),
|
.expect("notification count can't go that high"),
|
||||||
),
|
),
|
||||||
notification_count: Some(
|
notification_count: Some(
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.user
|
.user
|
||||||
.notification_count(&sender_user, room_id)?
|
.notification_count(&sender_user, room_id)?
|
||||||
@@ -1527,7 +1539,7 @@ pub(crate) async fn sync_events_v4_route(
|
|||||||
prev_batch,
|
prev_batch,
|
||||||
limited,
|
limited,
|
||||||
joined_count: Some(
|
joined_count: Some(
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.room_joined_count(room_id)?
|
.room_joined_count(room_id)?
|
||||||
@@ -1536,7 +1548,7 @@ pub(crate) async fn sync_events_v4_route(
|
|||||||
.unwrap_or_else(|_| uint!(0)),
|
.unwrap_or_else(|_| uint!(0)),
|
||||||
),
|
),
|
||||||
invited_count: Some(
|
invited_count: Some(
|
||||||
services()
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.room_invited_count(room_id)?
|
.room_invited_count(room_id)?
|
||||||
@@ -1571,7 +1583,7 @@ pub(crate) async fn sync_events_v4_route(
|
|||||||
extensions: sync_events::v4::Extensions {
|
extensions: sync_events::v4::Extensions {
|
||||||
to_device: if body.extensions.to_device.enabled.unwrap_or(false) {
|
to_device: if body.extensions.to_device.enabled.unwrap_or(false) {
|
||||||
Some(sync_events::v4::ToDevice {
|
Some(sync_events::v4::ToDevice {
|
||||||
events: services()
|
events: services
|
||||||
.users
|
.users
|
||||||
.get_to_device_events(&sender_user, &sender_device)?,
|
.get_to_device_events(&sender_user, &sender_device)?,
|
||||||
next_batch: next_batch.to_string(),
|
next_batch: next_batch.to_string(),
|
||||||
@@ -1584,7 +1596,7 @@ pub(crate) async fn sync_events_v4_route(
|
|||||||
changed: device_list_changes.into_iter().collect(),
|
changed: device_list_changes.into_iter().collect(),
|
||||||
left: device_list_left.into_iter().collect(),
|
left: device_list_left.into_iter().collect(),
|
||||||
},
|
},
|
||||||
device_one_time_keys_count: services()
|
device_one_time_keys_count: services
|
||||||
.users
|
.users
|
||||||
.count_one_time_keys(&sender_user, &sender_device)?,
|
.count_one_time_keys(&sender_user, &sender_device)?,
|
||||||
// Fallback keys are not yet supported
|
// Fallback keys are not yet supported
|
||||||
@@ -1592,7 +1604,7 @@ pub(crate) async fn sync_events_v4_route(
|
|||||||
},
|
},
|
||||||
account_data: sync_events::v4::AccountData {
|
account_data: sync_events::v4::AccountData {
|
||||||
global: if body.extensions.account_data.enabled.unwrap_or(false) {
|
global: if body.extensions.account_data.enabled.unwrap_or(false) {
|
||||||
services()
|
services
|
||||||
.account_data
|
.account_data
|
||||||
.changes_since(None, &sender_user, globalsince)?
|
.changes_since(None, &sender_user, globalsince)?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
|||||||
+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)?;
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
+26
-26
@@ -1,3 +1,4 @@
|
|||||||
|
use axum::extract::State;
|
||||||
use axum_client_ip::InsecureClientIp;
|
use axum_client_ip::InsecureClientIp;
|
||||||
use conduit::warn;
|
use conduit::warn;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
@@ -6,7 +7,7 @@ use ruma::{
|
|||||||
OwnedRoomId,
|
OwnedRoomId,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{services, Error, Result, Ruma, RumaResponse};
|
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, RumaResponse};
|
|||||||
/// 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])
|
||||||
|
|||||||
+16
-10
@@ -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, Err};
|
||||||
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,19 @@ 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();
|
// MSC4166: return M_NOT_FOUND 404 if no TURN URIs are specified in any way
|
||||||
|
if services.server.config.turn_uris.is_empty() {
|
||||||
|
return Err!(Request(NotFound("Not Found")));
|
||||||
|
}
|
||||||
|
|
||||||
|
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 +37,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 +52,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()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-4
@@ -1,3 +1,5 @@
|
|||||||
|
#![recursion_limit = "192"]
|
||||||
|
|
||||||
pub mod client;
|
pub mod client;
|
||||||
pub mod router;
|
pub mod router;
|
||||||
pub mod server;
|
pub mod server;
|
||||||
@@ -5,11 +7,10 @@ pub mod server;
|
|||||||
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::{debug_info, debug_warn, pdu::PduEvent, utils, Error, Result};
|
pub(crate) use conduit::{debug_info, pdu::PduEvent, utils, Error, Result};
|
||||||
pub(crate) use service::{services, user_is_local};
|
pub(crate) use service::services;
|
||||||
|
|
||||||
pub use crate::router::State;
|
pub(crate) use self::router::{Ruma, RumaResponse, State};
|
||||||
pub(crate) use crate::router::{Ruma, RumaResponse};
|
|
||||||
|
|
||||||
conduit::mod_ctor! {}
|
conduit::mod_ctor! {}
|
||||||
conduit::mod_dtor! {}
|
conduit::mod_dtor! {}
|
||||||
|
|||||||
+2
-3
@@ -3,6 +3,7 @@ mod auth;
|
|||||||
mod handler;
|
mod handler;
|
||||||
mod request;
|
mod request;
|
||||||
mod response;
|
mod response;
|
||||||
|
pub mod state;
|
||||||
|
|
||||||
use axum::{
|
use axum::{
|
||||||
response::IntoResponse,
|
response::IntoResponse,
|
||||||
@@ -13,11 +14,9 @@ use conduit::{err, Server};
|
|||||||
use http::Uri;
|
use http::Uri;
|
||||||
|
|
||||||
use self::handler::RouterExt;
|
use self::handler::RouterExt;
|
||||||
pub(super) use self::{args::Args as Ruma, response::RumaResponse};
|
pub(super) use self::{args::Args as Ruma, response::RumaResponse, state::State};
|
||||||
use crate::{client, server};
|
use crate::{client, server};
|
||||||
|
|
||||||
pub type State = &'static service::Services;
|
|
||||||
|
|
||||||
pub fn build(router: Router<State>, server: &Server) -> Router<State> {
|
pub fn build(router: Router<State>, server: &Server) -> Router<State> {
|
||||||
let config = &server.config;
|
let config = &server.config;
|
||||||
let router = router
|
let router = router
|
||||||
|
|||||||
+15
-11
@@ -2,11 +2,12 @@ use std::{mem, ops::Deref};
|
|||||||
|
|
||||||
use axum::{async_trait, body::Body, extract::FromRequest};
|
use axum::{async_trait, body::Body, extract::FromRequest};
|
||||||
use bytes::{BufMut, BytesMut};
|
use bytes::{BufMut, BytesMut};
|
||||||
use conduit::{debug, err, trace, Error, Result};
|
use conduit::{debug, err, trace, utils::string::EMPTY, Error, Result};
|
||||||
use ruma::{api::IncomingRequest, CanonicalJsonValue, OwnedDeviceId, OwnedServerName, OwnedUserId, UserId};
|
use ruma::{api::IncomingRequest, CanonicalJsonValue, OwnedDeviceId, OwnedServerName, OwnedUserId, UserId};
|
||||||
|
use service::Services;
|
||||||
|
|
||||||
use super::{auth, auth::Auth, request, request::Request};
|
use super::{auth, auth::Auth, request, request::Request};
|
||||||
use crate::{service::appservice::RegistrationInfo, services};
|
use crate::{service::appservice::RegistrationInfo, State};
|
||||||
|
|
||||||
/// Extractor for Ruma request structs
|
/// Extractor for Ruma request structs
|
||||||
pub(crate) struct Args<T> {
|
pub(crate) struct Args<T> {
|
||||||
@@ -35,18 +36,18 @@ pub(crate) struct Args<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl<T, S> FromRequest<S, Body> for Args<T>
|
impl<T> FromRequest<State, Body> for Args<T>
|
||||||
where
|
where
|
||||||
T: IncomingRequest,
|
T: IncomingRequest,
|
||||||
{
|
{
|
||||||
type Rejection = Error;
|
type Rejection = Error;
|
||||||
|
|
||||||
async fn from_request(request: hyper::Request<Body>, _: &S) -> Result<Self, Self::Rejection> {
|
async fn from_request(request: hyper::Request<Body>, services: &State) -> Result<Self, Self::Rejection> {
|
||||||
let mut request = request::from(request).await?;
|
let mut request = request::from(services, request).await?;
|
||||||
let mut json_body = serde_json::from_slice::<CanonicalJsonValue>(&request.body).ok();
|
let mut json_body = serde_json::from_slice::<CanonicalJsonValue>(&request.body).ok();
|
||||||
let auth = auth::auth(&mut request, &json_body, &T::METADATA).await?;
|
let auth = auth::auth(services, &mut request, &json_body, &T::METADATA).await?;
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
body: make_body::<T>(&mut request, &mut json_body, &auth)?,
|
body: make_body::<T>(services, &mut request, &mut json_body, &auth)?,
|
||||||
origin: auth.origin,
|
origin: auth.origin,
|
||||||
sender_user: auth.sender_user,
|
sender_user: auth.sender_user,
|
||||||
sender_device: auth.sender_device,
|
sender_device: auth.sender_device,
|
||||||
@@ -62,13 +63,16 @@ impl<T> Deref for Args<T> {
|
|||||||
fn deref(&self) -> &Self::Target { &self.body }
|
fn deref(&self) -> &Self::Target { &self.body }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_body<T>(request: &mut Request, json_body: &mut Option<CanonicalJsonValue>, auth: &Auth) -> Result<T>
|
fn make_body<T>(
|
||||||
|
services: &Services, request: &mut Request, json_body: &mut Option<CanonicalJsonValue>, auth: &Auth,
|
||||||
|
) -> Result<T>
|
||||||
where
|
where
|
||||||
T: IncomingRequest,
|
T: IncomingRequest,
|
||||||
{
|
{
|
||||||
let body = if let Some(CanonicalJsonValue::Object(json_body)) = json_body {
|
let body = if let Some(CanonicalJsonValue::Object(json_body)) = json_body {
|
||||||
let user_id = auth.sender_user.clone().unwrap_or_else(|| {
|
let user_id = auth.sender_user.clone().unwrap_or_else(|| {
|
||||||
UserId::parse_with_server_name("", services().globals.server_name()).expect("we know this is valid")
|
let server_name = services.globals.server_name();
|
||||||
|
UserId::parse_with_server_name(EMPTY, server_name).expect("valid user_id")
|
||||||
});
|
});
|
||||||
|
|
||||||
let uiaa_request = json_body
|
let uiaa_request = json_body
|
||||||
@@ -77,9 +81,9 @@ where
|
|||||||
.and_then(|auth| auth.get("session"))
|
.and_then(|auth| auth.get("session"))
|
||||||
.and_then(|session| session.as_str())
|
.and_then(|session| session.as_str())
|
||||||
.and_then(|session| {
|
.and_then(|session| {
|
||||||
services().uiaa.get_uiaa_request(
|
services.uiaa.get_uiaa_request(
|
||||||
&user_id,
|
&user_id,
|
||||||
&auth.sender_device.clone().unwrap_or_else(|| "".into()),
|
&auth.sender_device.clone().unwrap_or_else(|| EMPTY.into()),
|
||||||
session,
|
session,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|||||||
+31
-19
@@ -6,17 +6,17 @@ use axum_extra::{
|
|||||||
typed_header::TypedHeaderRejectionReason,
|
typed_header::TypedHeaderRejectionReason,
|
||||||
TypedHeader,
|
TypedHeader,
|
||||||
};
|
};
|
||||||
use conduit::Err;
|
use conduit::{debug_info, warn, Err, Error, Result};
|
||||||
use http::uri::PathAndQuery;
|
use http::uri::PathAndQuery;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::{client::error::ErrorKind, AuthScheme, Metadata},
|
api::{client::error::ErrorKind, AuthScheme, Metadata},
|
||||||
server_util::authorization::XMatrix,
|
server_util::authorization::XMatrix,
|
||||||
CanonicalJsonValue, OwnedDeviceId, OwnedServerName, OwnedUserId, UserId,
|
CanonicalJsonValue, OwnedDeviceId, OwnedServerName, OwnedUserId, UserId,
|
||||||
};
|
};
|
||||||
use tracing::warn;
|
use service::Services;
|
||||||
|
|
||||||
use super::request::Request;
|
use super::request::Request;
|
||||||
use crate::{service::appservice::RegistrationInfo, services, Error, Result};
|
use crate::service::appservice::RegistrationInfo;
|
||||||
|
|
||||||
enum Token {
|
enum Token {
|
||||||
Appservice(Box<RegistrationInfo>),
|
Appservice(Box<RegistrationInfo>),
|
||||||
@@ -33,7 +33,7 @@ pub(super) struct Auth {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn auth(
|
pub(super) async fn auth(
|
||||||
request: &mut Request, json_body: &Option<CanonicalJsonValue>, metadata: &Metadata,
|
services: &Services, request: &mut Request, json_body: &Option<CanonicalJsonValue>, metadata: &Metadata,
|
||||||
) -> Result<Auth> {
|
) -> Result<Auth> {
|
||||||
let bearer: Option<TypedHeader<Authorization<Bearer>>> = request.parts.extract().await?;
|
let bearer: Option<TypedHeader<Authorization<Bearer>>> = request.parts.extract().await?;
|
||||||
let token = match &bearer {
|
let token = match &bearer {
|
||||||
@@ -42,9 +42,9 @@ pub(super) async fn auth(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let token = if let Some(token) = token {
|
let token = if let Some(token) = token {
|
||||||
if let Some(reg_info) = services().appservice.find_from_token(token).await {
|
if let Some(reg_info) = services.appservice.find_from_token(token).await {
|
||||||
Token::Appservice(Box::new(reg_info))
|
Token::Appservice(Box::new(reg_info))
|
||||||
} else if let Some((user_id, device_id)) = services().users.find_from_token(token)? {
|
} else if let Some((user_id, device_id)) = services.users.find_from_token(token)? {
|
||||||
Token::User((user_id, OwnedDeviceId::from(device_id)))
|
Token::User((user_id, OwnedDeviceId::from(device_id)))
|
||||||
} else {
|
} else {
|
||||||
Token::Invalid
|
Token::Invalid
|
||||||
@@ -57,7 +57,7 @@ pub(super) async fn auth(
|
|||||||
match request.parts.uri.path() {
|
match request.parts.uri.path() {
|
||||||
// TODO: can we check this better?
|
// TODO: can we check this better?
|
||||||
"/_matrix/client/v3/publicRooms" | "/_matrix/client/r0/publicRooms" => {
|
"/_matrix/client/v3/publicRooms" | "/_matrix/client/r0/publicRooms" => {
|
||||||
if !services()
|
if !services
|
||||||
.globals
|
.globals
|
||||||
.config
|
.config
|
||||||
.allow_public_room_directory_without_auth
|
.allow_public_room_directory_without_auth
|
||||||
@@ -98,7 +98,7 @@ pub(super) async fn auth(
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(AuthScheme::AccessToken, Token::Appservice(info)) => Ok(auth_appservice(request, info)?),
|
(AuthScheme::AccessToken, Token::Appservice(info)) => Ok(auth_appservice(services, request, info)?),
|
||||||
(AuthScheme::None | AuthScheme::AccessTokenOptional | AuthScheme::AppserviceToken, Token::Appservice(info)) => {
|
(AuthScheme::None | AuthScheme::AccessTokenOptional | AuthScheme::AppserviceToken, Token::Appservice(info)) => {
|
||||||
Ok(Auth {
|
Ok(Auth {
|
||||||
origin: None,
|
origin: None,
|
||||||
@@ -110,7 +110,7 @@ pub(super) async fn auth(
|
|||||||
(AuthScheme::AccessToken, Token::None) => match request.parts.uri.path() {
|
(AuthScheme::AccessToken, Token::None) => match request.parts.uri.path() {
|
||||||
// TODO: can we check this better?
|
// TODO: can we check this better?
|
||||||
"/_matrix/client/v3/voip/turnServer" | "/_matrix/client/r0/voip/turnServer" => {
|
"/_matrix/client/v3/voip/turnServer" | "/_matrix/client/r0/voip/turnServer" => {
|
||||||
if services().globals.config.turn_allow_guests {
|
if services.globals.config.turn_allow_guests {
|
||||||
Ok(Auth {
|
Ok(Auth {
|
||||||
origin: None,
|
origin: None,
|
||||||
sender_user: None,
|
sender_user: None,
|
||||||
@@ -132,7 +132,7 @@ pub(super) async fn auth(
|
|||||||
sender_device: Some(device_id),
|
sender_device: Some(device_id),
|
||||||
appservice_info: None,
|
appservice_info: None,
|
||||||
}),
|
}),
|
||||||
(AuthScheme::ServerSignatures, Token::None) => Ok(auth_server(request, json_body).await?),
|
(AuthScheme::ServerSignatures, Token::None) => Ok(auth_server(services, request, json_body).await?),
|
||||||
(AuthScheme::None | AuthScheme::AppserviceToken | AuthScheme::AccessTokenOptional, Token::None) => Ok(Auth {
|
(AuthScheme::None | AuthScheme::AppserviceToken | AuthScheme::AccessTokenOptional, Token::None) => Ok(Auth {
|
||||||
sender_user: None,
|
sender_user: None,
|
||||||
sender_device: None,
|
sender_device: None,
|
||||||
@@ -150,7 +150,7 @@ pub(super) async fn auth(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn auth_appservice(request: &Request, info: Box<RegistrationInfo>) -> Result<Auth> {
|
fn auth_appservice(services: &Services, request: &Request, info: Box<RegistrationInfo>) -> Result<Auth> {
|
||||||
let user_id = request
|
let user_id = request
|
||||||
.query
|
.query
|
||||||
.user_id
|
.user_id
|
||||||
@@ -159,7 +159,7 @@ fn auth_appservice(request: &Request, info: Box<RegistrationInfo>) -> Result<Aut
|
|||||||
|| {
|
|| {
|
||||||
UserId::parse_with_server_name(
|
UserId::parse_with_server_name(
|
||||||
info.registration.sender_localpart.as_str(),
|
info.registration.sender_localpart.as_str(),
|
||||||
services().globals.server_name(),
|
services.globals.server_name(),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
UserId::parse,
|
UserId::parse,
|
||||||
@@ -170,7 +170,7 @@ fn auth_appservice(request: &Request, info: Box<RegistrationInfo>) -> Result<Aut
|
|||||||
return Err(Error::BadRequest(ErrorKind::Exclusive, "User is not in namespace."));
|
return Err(Error::BadRequest(ErrorKind::Exclusive, "User is not in namespace."));
|
||||||
}
|
}
|
||||||
|
|
||||||
if !services().users.exists(&user_id)? {
|
if !services.users.exists(&user_id)? {
|
||||||
return Err(Error::BadRequest(ErrorKind::forbidden(), "User does not exist."));
|
return Err(Error::BadRequest(ErrorKind::forbidden(), "User does not exist."));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,8 +182,10 @@ fn auth_appservice(request: &Request, info: Box<RegistrationInfo>) -> Result<Aut
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn auth_server(request: &mut Request, json_body: &Option<CanonicalJsonValue>) -> Result<Auth> {
|
async fn auth_server(
|
||||||
if !services().globals.allow_federation() {
|
services: &Services, request: &mut Request, json_body: &Option<CanonicalJsonValue>,
|
||||||
|
) -> Result<Auth> {
|
||||||
|
if !services.server.config.allow_federation {
|
||||||
return Err!(Config("allow_federation", "Federation is disabled."));
|
return Err!(Config("allow_federation", "Federation is disabled."));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,6 +206,17 @@ async fn auth_server(request: &mut Request, json_body: &Option<CanonicalJsonValu
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
let origin = &x_matrix.origin;
|
let origin = &x_matrix.origin;
|
||||||
|
|
||||||
|
if services
|
||||||
|
.server
|
||||||
|
.config
|
||||||
|
.forbidden_remote_server_names
|
||||||
|
.contains(origin)
|
||||||
|
{
|
||||||
|
debug_info!("Refusing to accept inbound federation request to {origin}");
|
||||||
|
return Err!(Request(Forbidden("Federation with this homeserver is not allowed.")));
|
||||||
|
}
|
||||||
|
|
||||||
let signatures =
|
let signatures =
|
||||||
BTreeMap::from_iter([(x_matrix.key.clone(), CanonicalJsonValue::String(x_matrix.sig.to_string()))]);
|
BTreeMap::from_iter([(x_matrix.key.clone(), CanonicalJsonValue::String(x_matrix.sig.to_string()))]);
|
||||||
let signatures = BTreeMap::from_iter([(
|
let signatures = BTreeMap::from_iter([(
|
||||||
@@ -216,7 +229,7 @@ async fn auth_server(request: &mut Request, json_body: &Option<CanonicalJsonValu
|
|||||||
),
|
),
|
||||||
)]);
|
)]);
|
||||||
|
|
||||||
let server_destination = services().globals.server_name().as_str().to_owned();
|
let server_destination = services.globals.server_name().as_str().to_owned();
|
||||||
if let Some(destination) = x_matrix.destination.as_ref() {
|
if let Some(destination) = x_matrix.destination.as_ref() {
|
||||||
if destination != &server_destination {
|
if destination != &server_destination {
|
||||||
return Err(Error::BadRequest(ErrorKind::forbidden(), "Invalid authorization."));
|
return Err(Error::BadRequest(ErrorKind::forbidden(), "Invalid authorization."));
|
||||||
@@ -247,9 +260,8 @@ async fn auth_server(request: &mut Request, json_body: &Option<CanonicalJsonValu
|
|||||||
request_map.insert("content".to_owned(), json_body.clone());
|
request_map.insert("content".to_owned(), json_body.clone());
|
||||||
};
|
};
|
||||||
|
|
||||||
let keys_result = services()
|
let keys_result = services
|
||||||
.rooms
|
.server_keys
|
||||||
.event_handler
|
|
||||||
.fetch_signing_keys_for_server(origin, vec![x_matrix.key.to_string()])
|
.fetch_signing_keys_for_server(origin, vec![x_matrix.key.to_string()])
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
|||||||
@@ -2,11 +2,10 @@ use std::str;
|
|||||||
|
|
||||||
use axum::{extract::Path, RequestExt, RequestPartsExt};
|
use axum::{extract::Path, RequestExt, RequestPartsExt};
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use conduit::err;
|
use conduit::{err, Result};
|
||||||
use http::request::Parts;
|
use http::request::Parts;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
use service::Services;
|
||||||
use crate::{services, Result};
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub(super) struct QueryParams {
|
pub(super) struct QueryParams {
|
||||||
@@ -21,7 +20,7 @@ pub(super) struct Request {
|
|||||||
pub(super) parts: Parts,
|
pub(super) parts: Parts,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) async fn from(request: hyper::Request<axum::body::Body>) -> Result<Request> {
|
pub(super) async fn from(services: &Services, request: hyper::Request<axum::body::Body>) -> Result<Request> {
|
||||||
let limited = request.with_limited_body();
|
let limited = request.with_limited_body();
|
||||||
let (mut parts, body) = limited.into_parts();
|
let (mut parts, body) = limited.into_parts();
|
||||||
|
|
||||||
@@ -30,7 +29,7 @@ pub(super) async fn from(request: hyper::Request<axum::body::Body>) -> Result<Re
|
|||||||
let query =
|
let query =
|
||||||
serde_html_form::from_str(query).map_err(|e| err!(Request(Unknown("Failed to read query parameters: {e}"))))?;
|
serde_html_form::from_str(query).map_err(|e| err!(Request(Unknown("Failed to read query parameters: {e}"))))?;
|
||||||
|
|
||||||
let max_body_size = services().globals.config.max_request_size;
|
let max_body_size = services.globals.config.max_request_size;
|
||||||
|
|
||||||
let body = axum::body::to_bytes(body, max_body_size)
|
let body = axum::body::to_bytes(body, max_body_size)
|
||||||
.await
|
.await
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
use std::{ops::Deref, sync::Arc};
|
||||||
|
|
||||||
|
use conduit_service::Services;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct State {
|
||||||
|
services: *const Services,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Guard {
|
||||||
|
services: Arc<Services>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create(services: Arc<Services>) -> (State, Guard) {
|
||||||
|
let state = State {
|
||||||
|
services: Arc::into_raw(services.clone()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let guard = Guard {
|
||||||
|
services,
|
||||||
|
};
|
||||||
|
|
||||||
|
(state, guard)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Guard {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let ptr = Arc::as_ptr(&self.services);
|
||||||
|
// SAFETY: Parity with Arc::into_raw() called in create(). This revivifies the
|
||||||
|
// Arc lost to State so it can be dropped, otherwise Services will leak.
|
||||||
|
let arc = unsafe { Arc::from_raw(ptr) };
|
||||||
|
debug_assert!(
|
||||||
|
Arc::strong_count(&arc) > 1,
|
||||||
|
"Services usually has more than one reference and is not dropped here"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for State {
|
||||||
|
type Target = Services;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
deref(&self.services).expect("dereferenced Services pointer in State must not be null")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// SAFETY: State is a thin wrapper containing a raw const pointer to Services
|
||||||
|
/// in lieu of an Arc. Services is internally threadsafe. If State contains
|
||||||
|
/// additional fields this notice should be reevaluated.
|
||||||
|
unsafe impl Send for State {}
|
||||||
|
|
||||||
|
/// SAFETY: State is a thin wrapper containing a raw const pointer to Services
|
||||||
|
/// in lieu of an Arc. Services is internally threadsafe. If State contains
|
||||||
|
/// additional fields this notice should be reevaluated.
|
||||||
|
unsafe impl Sync for State {}
|
||||||
|
|
||||||
|
fn deref(services: &*const Services) -> Option<&Services> {
|
||||||
|
// SAFETY: We replaced Arc<Services> with *const Services in State. This is
|
||||||
|
// worth about 10 clones (20 reference count updates) for each request handled.
|
||||||
|
// Though this is not an incredibly large quantity, it's woefully unnecessary
|
||||||
|
// given the context as explained below; though it is not currently known to be
|
||||||
|
// a performance bottleneck, the front-line position justifies preempting it.
|
||||||
|
//
|
||||||
|
// Services is created prior to the axum/tower stack and Router, and prior
|
||||||
|
// to serving any requests through the handlers in this crate. It is then
|
||||||
|
// dropped only after all requests have completed, the listening sockets
|
||||||
|
// have been closed, axum/tower has been dropped. Thus Services is
|
||||||
|
// expected to live at least as long as any instance of State, making the
|
||||||
|
// constant updates to the prior Arc unnecessary to keep Services alive.
|
||||||
|
//
|
||||||
|
// Nevertheless if it is possible to accomplish this by annotating State
|
||||||
|
// with a lifetime to hold a reference (and be aware I have made a
|
||||||
|
// significant effort trying to make this work) this unsafety may not be
|
||||||
|
// necessary. It is either very difficult or impossible to get a
|
||||||
|
// lifetime'ed reference through Router / RumaHandler; though it is
|
||||||
|
// possible to pass a reference through axum's `with_state()` in trivial
|
||||||
|
// configurations as the only requirement of a State is Clone.
|
||||||
|
unsafe { services.as_ref() }
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user