Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 39d6f5bac6 |
+11
-251
@@ -27,16 +27,6 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
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
|
||||
TERM: ansi
|
||||
# Publishing to my nix binary cache
|
||||
@@ -48,11 +38,8 @@ env:
|
||||
# Custom nix binary cache if fork is being used
|
||||
ATTIC_ENDPOINT: ${{ vars.ATTIC_ENDPOINT }}
|
||||
ATTIC_PUBLIC_KEY: ${{ vars.ATTIC_PUBLIC_KEY }}
|
||||
# Get error output from nix that we can actually use, and use our binary caches for the earlier CI steps
|
||||
NIX_CONFIG: |
|
||||
show-trace = true
|
||||
extra-substituters = https://attic.kennel.juneis.dog/conduit https://attic.kennel.juneis.dog/conduwuit https://cache.lix.systems https://conduwuit.cachix.org
|
||||
extra-trusted-public-keys = conduit:eEKoUwlQGDdYmAI/Q/0slVlegqh/QmAvQd7HBSm21Wk= conduwuit:BbycGUgTISsltcmH0qNjFR9dbrQNYgdIAcmViSGoVTE= cache.lix.systems:aBnZUw8zA7H35Cz2RyKFVs3H4PlGTLawyY5KRbvJR8o= conduwuit.cachix.org-1:MFRm6jcnfTf0jSAbmvLfhO3KBMt4px+1xaereWXp8Xg=
|
||||
# Get error output from nix that we can actually use
|
||||
NIX_CONFIG: show-trace = true
|
||||
|
||||
permissions:
|
||||
packages: write
|
||||
@@ -62,8 +49,6 @@ jobs:
|
||||
tests:
|
||||
name: Test
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CARGO_PROFILE: "test"
|
||||
steps:
|
||||
- name: Free Disk Space (Ubuntu)
|
||||
uses: jlumbroso/free-disk-space@main
|
||||
@@ -72,7 +57,7 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Tag comparison check
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/v') && !endsWith(github.ref, '-rc') }}
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
run: |
|
||||
# Tag mismatch with latest repo tag check to prevent potential downgrades
|
||||
LATEST_TAG=$(git describe --tags `git rev-list --tags --max-count=1`)
|
||||
@@ -130,39 +115,15 @@ jobs:
|
||||
- name: Prepare build environment
|
||||
run: |
|
||||
echo 'source $HOME/.nix-profile/share/nix-direnv/direnvrc' > "$HOME/.direnvrc"
|
||||
nix profile install --inputs-from . nixpkgs#direnv nixpkgs#nix-direnv
|
||||
nix profile install --impure --inputs-from . nixpkgs#direnv nixpkgs#nix-direnv
|
||||
direnv allow
|
||||
nix develop .#all-features --command true
|
||||
|
||||
- name: Cache CI dependencies
|
||||
run: |
|
||||
# attic nix binary cache server is very, very terribly flakey. nothing i can do to fix it other than retry multiple times here
|
||||
ATTEMPTS=3
|
||||
SUCCESS=false
|
||||
while (( ATTEMPTS-- > 0 ))
|
||||
do
|
||||
bin/nix-build-and-cache ci
|
||||
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
|
||||
with:
|
||||
cache-all-crates: "true"
|
||||
|
||||
- name: Run CI tests
|
||||
run: |
|
||||
@@ -216,10 +177,6 @@ jobs:
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
- name: Run cargo clean test artifacts
|
||||
run: |
|
||||
cargo clean --profile test
|
||||
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
@@ -230,9 +187,6 @@ jobs:
|
||||
- target: aarch64-unknown-linux-musl
|
||||
- target: x86_64-unknown-linux-musl
|
||||
steps:
|
||||
- name: Free Disk Space (Ubuntu)
|
||||
uses: jlumbroso/free-disk-space@main
|
||||
|
||||
- name: Sync repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
@@ -283,43 +237,16 @@ jobs:
|
||||
- name: Prepare build environment
|
||||
run: |
|
||||
echo 'source $HOME/.nix-profile/share/nix-direnv/direnvrc' > "$HOME/.direnvrc"
|
||||
nix profile install --inputs-from . nixpkgs#direnv nixpkgs#nix-direnv
|
||||
nix profile install --impure --inputs-from . nixpkgs#direnv nixpkgs#nix-direnv
|
||||
direnv allow
|
||||
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 }}
|
||||
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
|
||||
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/$CARGO_DEB_TARGET_TUPLE/release/
|
||||
cp -v -f result/bin/conduit target/release/conduwuit
|
||||
@@ -329,66 +256,6 @@ jobs:
|
||||
mv -v target/release/conduwuit static-${{ matrix.target }}
|
||||
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 }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
@@ -404,65 +271,11 @@ jobs:
|
||||
if-no-files-found: error
|
||||
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 }}
|
||||
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
|
||||
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
|
||||
|
||||
- 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 }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
@@ -471,14 +284,6 @@ jobs:
|
||||
if-no-files-found: error
|
||||
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:
|
||||
name: Docker publish
|
||||
runs-on: ubuntu-latest
|
||||
@@ -488,15 +293,15 @@ jobs:
|
||||
DOCKER_ARM64: docker.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-arm64v8
|
||||
DOCKER_AMD64: docker.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-amd64
|
||||
DOCKER_TAG: docker.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}
|
||||
DOCKER_BRANCH: docker.io/${{ github.repository }}:${{ (startsWith(github.ref, 'refs/tags/v') && !endsWith(github.ref, '-rc') && 'latest') || (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}
|
||||
DOCKER_BRANCH: docker.io/${{ github.repository }}:${{ (startsWith(github.ref, 'refs/tags/v') && 'latest') || (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}
|
||||
GHCR_ARM64: ghcr.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-arm64v8
|
||||
GHCR_AMD64: ghcr.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-amd64
|
||||
GHCR_TAG: ghcr.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}
|
||||
GHCR_BRANCH: ghcr.io/${{ github.repository }}:${{ (startsWith(github.ref, 'refs/tags/v') && !endsWith(github.ref, '-rc') && 'latest') || (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}
|
||||
GHCR_BRANCH: ghcr.io/${{ github.repository }}:${{ (startsWith(github.ref, 'refs/tags/v') && 'latest') || (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}
|
||||
GLCR_ARM64: registry.gitlab.com/conduwuit/conduwuit:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-arm64v8
|
||||
GLCR_AMD64: registry.gitlab.com/conduwuit/conduwuit:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-amd64
|
||||
GLCR_TAG: registry.gitlab.com/conduwuit/conduwuit:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}
|
||||
GLCR_BRANCH: registry.gitlab.com/conduwuit/conduwuit:${{ (startsWith(github.ref, 'refs/tags/v') && !endsWith(github.ref, '-rc') && 'latest') || (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}
|
||||
GLCR_BRANCH: registry.gitlab.com/conduwuit/conduwuit:${{ (startsWith(github.ref, 'refs/tags/v') && 'latest') || (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}
|
||||
|
||||
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
GITLAB_TOKEN: ${{ secrets.GITLAB_TOKEN }}
|
||||
@@ -529,10 +334,8 @@ jobs:
|
||||
|
||||
- name: Move OCI images into position
|
||||
run: |
|
||||
mv -v oci-image-x86_64-unknown-linux-musl/*.tar.gz oci-image-amd64.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
|
||||
mv -v oci-image-x86_64-*/*.tar.gz oci-image-amd64.tar.gz
|
||||
mv -v oci-image-aarch64-*/*.tar.gz oci-image-arm64v8.tar.gz
|
||||
|
||||
- name: Load and push amd64 image
|
||||
if: ${{ (vars.DOCKER_USERNAME != '') && (env.DOCKERHUB_TOKEN != '') }}
|
||||
@@ -556,28 +359,6 @@ jobs:
|
||||
docker push ${{ env.GHCR_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
|
||||
run: |
|
||||
# Dockerhub Container Registry
|
||||
@@ -587,21 +368,9 @@ jobs:
|
||||
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 }}
|
||||
# GitLab Container Registry
|
||||
docker manifest create ${{ env.GLCR_TAG }} --amend ${{ env.GLCR_ARM64 }} --amend ${{ env.GLCR_AMD64 }}
|
||||
docker manifest create ${{ env.GLCR_TAG }} --amend ${{ env.GLCR_ARM64 }} --amend ${{ env.GCCR_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
|
||||
if: ${{ (vars.DOCKER_USERNAME != '') && (env.DOCKERHUB_TOKEN != '') }}
|
||||
run: |
|
||||
@@ -611,12 +380,6 @@ jobs:
|
||||
docker manifest push ${{ env.GHCR_BRANCH }}
|
||||
docker manifest push ${{ env.GLCR_TAG }}
|
||||
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
|
||||
if: ${{ (vars.DOCKER_USERNAME != '') && (env.DOCKERHUB_TOKEN != '') }}
|
||||
@@ -624,6 +387,3 @@ jobs:
|
||||
echo "- \`docker pull ${{ env.DOCKER_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.DOCKER_TAG }}-debug\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- \`docker pull ${{ env.GHCR_TAG }}-debug\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- \`docker pull ${{ env.GLCR_TAG }}-debug\`" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
@@ -21,11 +21,8 @@ env:
|
||||
# Custom nix binary cache if fork is being used
|
||||
ATTIC_ENDPOINT: ${{ vars.ATTIC_ENDPOINT }}
|
||||
ATTIC_PUBLIC_KEY: ${{ vars.ATTIC_PUBLIC_KEY }}
|
||||
# Get error output from nix that we can actually use, and use our binary caches for the earlier CI steps
|
||||
NIX_CONFIG: |
|
||||
show-trace = true
|
||||
extra-substituters = https://attic.kennel.juneis.dog/conduit https://attic.kennel.juneis.dog/conduwuit https://cache.lix.systems https://conduwuit.cachix.org
|
||||
extra-trusted-public-keys = conduit:eEKoUwlQGDdYmAI/Q/0slVlegqh/QmAvQd7HBSm21Wk= conduwuit:BbycGUgTISsltcmH0qNjFR9dbrQNYgdIAcmViSGoVTE= cache.lix.systems:aBnZUw8zA7H35Cz2RyKFVs3H4PlGTLawyY5KRbvJR8o= conduwuit.cachix.org-1:MFRm6jcnfTf0jSAbmvLfhO3KBMt4px+1xaereWXp8Xg=
|
||||
# Get error output from nix that we can actually use
|
||||
NIX_CONFIG: show-trace = true
|
||||
|
||||
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
|
||||
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
|
||||
@@ -47,9 +44,6 @@ jobs:
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
|
||||
steps:
|
||||
- name: Free Disk Space (Ubuntu)
|
||||
uses: jlumbroso/free-disk-space@main
|
||||
|
||||
- name: Sync repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
@@ -104,29 +98,13 @@ jobs:
|
||||
- name: Prepare build environment
|
||||
run: |
|
||||
echo 'source $HOME/.nix-profile/share/nix-direnv/direnvrc' > "$HOME/.direnvrc"
|
||||
nix profile install --inputs-from . nixpkgs#direnv nixpkgs#nix-direnv
|
||||
nix profile install --impure --inputs-from . nixpkgs#direnv nixpkgs#nix-direnv
|
||||
direnv allow
|
||||
nix develop --command true
|
||||
|
||||
- name: Cache CI dependencies
|
||||
run: |
|
||||
# attic nix binary cache server is very, very terribly flakey. nothing i can do to fix it other than retry multiple times here
|
||||
ATTEMPTS=3
|
||||
SUCCESS=false
|
||||
while (( ATTEMPTS-- > 0 ))
|
||||
do
|
||||
bin/nix-build-and-cache ci
|
||||
if [[ $? == 0 ]]; then
|
||||
SUCCESS=true
|
||||
break
|
||||
else
|
||||
sleep 3
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $SUCCESS == "false" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Run lychee and markdownlint
|
||||
run: |
|
||||
@@ -135,26 +113,8 @@ jobs:
|
||||
|
||||
- name: Build documentation (book)
|
||||
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 .#book
|
||||
if [[ $? == 0 ]]; then
|
||||
SUCCESS=true
|
||||
break
|
||||
else
|
||||
sleep 3
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $SUCCESS == "false" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
./bin/nix-build-and-cache just .#book
|
||||
cp -r --dereference result public
|
||||
|
||||
- name: Upload generated documentation (book) as normal artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
|
||||
@@ -39,7 +39,6 @@ The following binaries are used in [`engage.toml`][engage.toml]:
|
||||
- [`cargo-deb`][cargo-deb]
|
||||
- [`lychee`][lychee]
|
||||
- [`markdownlint-cli`][markdownlint-cli]
|
||||
- `dpkg`
|
||||
|
||||
### Matrix tests
|
||||
|
||||
|
||||
Generated
+366
-316
File diff suppressed because it is too large
Load Diff
+35
-100
@@ -14,13 +14,13 @@ categories = ["network-programming"]
|
||||
description = "a very cool fork of Conduit, a Matrix homeserver written in Rust"
|
||||
edition = "2021"
|
||||
homepage = "https://conduwuit.puppyirl.gay/"
|
||||
keywords = ["chat", "matrix", "server", "uwu"]
|
||||
keywords = ["chat", "matrix", "server"]
|
||||
license = "Apache-2.0"
|
||||
# See also `rust-toolchain.toml`
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/girlbossceo/conduwuit"
|
||||
rust-version = "1.80.0"
|
||||
version = "0.4.6"
|
||||
rust-version = "1.77.0"
|
||||
version = "0.4.5"
|
||||
|
||||
[workspace.metadata.crane]
|
||||
name = "conduit"
|
||||
@@ -28,19 +28,6 @@ name = "conduit"
|
||||
[workspace.dependencies.const-str]
|
||||
version = "0.5.7"
|
||||
|
||||
[workspace.dependencies.ctor]
|
||||
version = "0.2.8"
|
||||
|
||||
[workspace.dependencies.cargo_toml]
|
||||
version = "0.20"
|
||||
default-features = false
|
||||
features = ["features"]
|
||||
|
||||
[workspace.dependencies.toml]
|
||||
version = "0.8.14"
|
||||
default-features = false
|
||||
features = ["parse"]
|
||||
|
||||
[workspace.dependencies.sanitize-filename]
|
||||
version = "0.5.0"
|
||||
|
||||
@@ -97,7 +84,6 @@ features = ["typed-header", "tracing"]
|
||||
|
||||
[workspace.dependencies.axum-server]
|
||||
version = "0.6.0"
|
||||
default-features = false
|
||||
features = ["tls-rustls"]
|
||||
|
||||
[workspace.dependencies.axum-client-ip]
|
||||
@@ -105,12 +91,10 @@ version = "0.6.0"
|
||||
|
||||
[workspace.dependencies.tower]
|
||||
version = "0.4.13"
|
||||
default-features = false
|
||||
features = ["util"]
|
||||
|
||||
[workspace.dependencies.tower-http]
|
||||
version = "0.5.2"
|
||||
default-features = false
|
||||
features = [
|
||||
"add-extension",
|
||||
"cors",
|
||||
@@ -133,12 +117,10 @@ features = [
|
||||
|
||||
[workspace.dependencies.serde]
|
||||
version = "1.0.204"
|
||||
default-features = false
|
||||
features = ["rc"]
|
||||
|
||||
[workspace.dependencies.serde_json]
|
||||
version = "1.0.121"
|
||||
default-features = false
|
||||
version = "1.0.120"
|
||||
features = ["raw_value"]
|
||||
|
||||
# Used for appservice registration files
|
||||
@@ -206,8 +188,7 @@ version = "0.3.30"
|
||||
default-features = false
|
||||
|
||||
[workspace.dependencies.tokio]
|
||||
version = "1.39.2"
|
||||
default-features = false
|
||||
version = "1.38.0"
|
||||
features = [
|
||||
"fs",
|
||||
"net",
|
||||
@@ -223,12 +204,11 @@ features = [
|
||||
version = "0.3.1"
|
||||
|
||||
[workspace.dependencies.libloading]
|
||||
version = "0.8.5"
|
||||
version = "0.8.3"
|
||||
|
||||
# Validating urls in config, was already a transitive dependency
|
||||
[workspace.dependencies.url]
|
||||
version = "2.5.0"
|
||||
default-features = false
|
||||
features = ["serde"]
|
||||
|
||||
# standard date and time tools
|
||||
@@ -238,8 +218,7 @@ features = ["alloc", "std"]
|
||||
default-features = false
|
||||
|
||||
[workspace.dependencies.hyper]
|
||||
version = "1.4.1"
|
||||
default-features = false
|
||||
version = "1.4.0"
|
||||
features = [
|
||||
"server",
|
||||
"http1",
|
||||
@@ -248,7 +227,6 @@ features = [
|
||||
|
||||
[workspace.dependencies.hyper-util]
|
||||
version = "0.1.6"
|
||||
default-features = false
|
||||
features = [
|
||||
"client",
|
||||
"server-auto",
|
||||
@@ -260,13 +238,11 @@ features = [
|
||||
# to support multiple variations of setting a config option
|
||||
[workspace.dependencies.either]
|
||||
version = "1.11.0"
|
||||
default-features = false
|
||||
features = ["serde"]
|
||||
|
||||
# Used for reading the configuration from conduwuit.toml & environment variables
|
||||
[workspace.dependencies.figment]
|
||||
version = "0.10.18"
|
||||
default-features = false
|
||||
features = ["env", "toml"]
|
||||
|
||||
[workspace.dependencies.hickory-resolver]
|
||||
@@ -275,7 +251,7 @@ default-features = false
|
||||
|
||||
# Used for conduit::Error type
|
||||
[workspace.dependencies.thiserror]
|
||||
version = "1.0.63"
|
||||
version = "1.0.62"
|
||||
|
||||
# Used when hashing the state
|
||||
[workspace.dependencies.ring]
|
||||
@@ -304,7 +280,7 @@ version = "0.1.2"
|
||||
[workspace.dependencies.ruma]
|
||||
git = "https://github.com/girlbossceo/ruwuma"
|
||||
#branch = "conduwuit-changes"
|
||||
rev = "c76e2873c1593a3308d4ba3e0e4a1db65acf8536"
|
||||
rev = "c51ccb2c68d2e3557eb12b1a49036531711ec0e5"
|
||||
features = [
|
||||
"compat",
|
||||
"rand",
|
||||
@@ -317,7 +293,6 @@ features = [
|
||||
"server-util",
|
||||
"unstable-exhaustive-types",
|
||||
"ring-compat",
|
||||
"identifiers-validation",
|
||||
"unstable-unspecified",
|
||||
"unstable-msc2448",
|
||||
"unstable-msc2666",
|
||||
@@ -332,6 +307,10 @@ features = [
|
||||
"unstable-extensible-events",
|
||||
]
|
||||
|
||||
[workspace.dependencies.ruma-identifiers-validation]
|
||||
git = "https://github.com/girlbossceo/ruwuma"
|
||||
rev = "fd686e77950680462377c9105dfb4136dd49c7a0"
|
||||
|
||||
[workspace.dependencies.rust-rocksdb]
|
||||
path = "deps/rust-rocksdb"
|
||||
package = "rust-rocksdb-uwu"
|
||||
@@ -354,7 +333,7 @@ version = "0.10.8"
|
||||
|
||||
# optional opentelemetry, performance measurements, flamegraphs, etc for performance measurements and monitoring
|
||||
[workspace.dependencies.opentelemetry]
|
||||
version = "0.21.0"
|
||||
version = "0.24.0"
|
||||
|
||||
[workspace.dependencies.tracing-flame]
|
||||
version = "0.2.0"
|
||||
@@ -363,11 +342,11 @@ version = "0.2.0"
|
||||
version = "0.22.0"
|
||||
|
||||
[workspace.dependencies.opentelemetry_sdk]
|
||||
version = "0.21.2"
|
||||
version = "0.24.0"
|
||||
features = ["rt-tokio"]
|
||||
|
||||
[workspace.dependencies.opentelemetry-jaeger]
|
||||
version = "0.20.0"
|
||||
version = "0.22.0"
|
||||
features = ["rt-tokio"]
|
||||
|
||||
# optional sentry metrics for crash/panic reporting
|
||||
@@ -393,26 +372,24 @@ version = "0.34.0"
|
||||
version = "0.34.0"
|
||||
|
||||
# jemalloc usage
|
||||
# locked to 0.5.4 due to static binary linking breakage
|
||||
[workspace.dependencies.tikv-jemalloc-sys]
|
||||
version = "=0.5.4"
|
||||
version = "0.5.4"
|
||||
default-features = false
|
||||
features = ["stats", "unprefixed_malloc_on_supported_platforms"]
|
||||
[workspace.dependencies.tikv-jemallocator]
|
||||
version = "=0.5.4"
|
||||
version = "0.5.4"
|
||||
default-features = false
|
||||
features = ["stats", "unprefixed_malloc_on_supported_platforms"]
|
||||
[workspace.dependencies.tikv-jemalloc-ctl]
|
||||
version = "=0.5.4"
|
||||
version = "0.5.4"
|
||||
default-features = false
|
||||
features = ["use_std"]
|
||||
|
||||
[workspace.dependencies.console-subscriber]
|
||||
version = "0.4"
|
||||
version = "0.3"
|
||||
|
||||
[workspace.dependencies.nix]
|
||||
version = "0.29.0"
|
||||
default-features = false
|
||||
features = ["resource"]
|
||||
|
||||
[workspace.dependencies.sd-notify]
|
||||
@@ -428,8 +405,8 @@ features = [
|
||||
]
|
||||
|
||||
[workspace.dependencies.rustyline-async]
|
||||
version = "0.4.2"
|
||||
default-features = false
|
||||
git = "https://github.com/girlbossceo/rustyline-async"
|
||||
rev = "de26100b0db03e419a3d8e1dd26895d170d1fe50"
|
||||
|
||||
[workspace.dependencies.termimad]
|
||||
version = "0.29.4"
|
||||
@@ -438,17 +415,6 @@ default-features = false
|
||||
[workspace.dependencies.checked_ops]
|
||||
version = "0.1"
|
||||
|
||||
[workspace.dependencies.syn]
|
||||
version = "2.0.72"
|
||||
default-features = false
|
||||
features = ["full", "extra-traits"]
|
||||
|
||||
[workspace.dependencies.quote]
|
||||
version = "1.0.36"
|
||||
|
||||
[workspace.dependencies.proc-macro2]
|
||||
version = "1.0.86"
|
||||
|
||||
|
||||
#
|
||||
# Patches
|
||||
@@ -459,16 +425,16 @@ version = "1.0.86"
|
||||
# https://github.com/girlbossceo/tracing/commit/b348dca742af641c47bc390261f60711c2af573c
|
||||
[patch.crates-io.tracing-subscriber]
|
||||
git = "https://github.com/girlbossceo/tracing"
|
||||
rev = "4d78a14a5e03f539b8c6b475aefa08bb14e4de91"
|
||||
rev = "b348dca742af641c47bc390261f60711c2af573c"
|
||||
[patch.crates-io.tracing]
|
||||
git = "https://github.com/girlbossceo/tracing"
|
||||
rev = "4d78a14a5e03f539b8c6b475aefa08bb14e4de91"
|
||||
rev = "b348dca742af641c47bc390261f60711c2af573c"
|
||||
[patch.crates-io.tracing-core]
|
||||
git = "https://github.com/girlbossceo/tracing"
|
||||
rev = "4d78a14a5e03f539b8c6b475aefa08bb14e4de91"
|
||||
rev = "b348dca742af641c47bc390261f60711c2af573c"
|
||||
[patch.crates-io.tracing-log]
|
||||
git = "https://github.com/girlbossceo/tracing"
|
||||
rev = "4d78a14a5e03f539b8c6b475aefa08bb14e4de91"
|
||||
rev = "b348dca742af641c47bc390261f60711c2af573c"
|
||||
|
||||
# fixes hyper graceful shutdowns [https://github.com/programatik29/axum-server/issues/114]
|
||||
# https://github.com/girlbossceo/axum-server/commit/8e3368d899079818934e61cc9c839abcbbcada8a
|
||||
@@ -476,12 +442,6 @@ rev = "4d78a14a5e03f539b8c6b475aefa08bb14e4de91"
|
||||
git = "https://github.com/girlbossceo/axum-server"
|
||||
rev = "8e3368d899079818934e61cc9c839abcbbcada8a"
|
||||
|
||||
# adds a tab completion callback: https://github.com/girlbossceo/rustyline-async/commit/de26100b0db03e419a3d8e1dd26895d170d1fe50
|
||||
# adds event for CTRL+\: https://github.com/girlbossceo/rustyline-async/commit/67d8c49aeac03a5ef4e818f663eaa94dd7bf339b
|
||||
[patch.crates-io.rustyline-async]
|
||||
git = "https://github.com/girlbossceo/rustyline-async"
|
||||
rev = "de26100b0db03e419a3d8e1dd26895d170d1fe50"
|
||||
|
||||
#
|
||||
# Our crates
|
||||
#
|
||||
@@ -516,11 +476,6 @@ package = "conduit_core"
|
||||
path = "src/core"
|
||||
default-features = false
|
||||
|
||||
[workspace.dependencies.conduit-macros]
|
||||
package = "conduit_macros"
|
||||
path = "src/macros"
|
||||
default-features = false
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# Release profiles
|
||||
@@ -569,17 +524,7 @@ lto = "fat"
|
||||
[profile.release-max-perf.build-override]
|
||||
inherits = "release-max-perf"
|
||||
opt-level = 0
|
||||
codegen-units = 32
|
||||
#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',
|
||||
#]
|
||||
|
||||
@@ -708,16 +653,7 @@ opt-level = 'z'
|
||||
# '-Clink-arg=-Wl,-z,nodelete',
|
||||
#]
|
||||
|
||||
# primarily used for CI
|
||||
[profile.test]
|
||||
inherits = "dev"
|
||||
codegen-units = 16
|
||||
incremental = false
|
||||
|
||||
[profile.test.package.'*']
|
||||
inherits = "dev"
|
||||
debug = 0
|
||||
codegen-units = 16
|
||||
incremental = false
|
||||
|
||||
###############################################################################
|
||||
@@ -779,19 +715,19 @@ variant_size_differences = "allow"
|
||||
[workspace.lints.clippy]
|
||||
|
||||
###################
|
||||
cargo = { level = "warn", priority = -1 }
|
||||
cargo = "warn"
|
||||
|
||||
## some sadness
|
||||
multiple_crate_versions = { level = "allow", priority = 1 }
|
||||
|
||||
###################
|
||||
complexity = { level = "warn", priority = -1 }
|
||||
complexity = "warn"
|
||||
|
||||
###################
|
||||
correctness = { level = "warn", priority = -1 }
|
||||
correctness = "warn"
|
||||
|
||||
###################
|
||||
nursery = { level = "warn", priority = -1 }
|
||||
nursery = "warn"
|
||||
|
||||
## some sadness
|
||||
missing_const_for_fn = { level = "allow", priority = 1 } # TODO
|
||||
@@ -801,7 +737,7 @@ significant_drop_in_scrutinee = { level = "allow", priority = 1 } # TODO
|
||||
significant_drop_tightening = { level = "allow", priority = 1 } # TODO
|
||||
|
||||
###################
|
||||
pedantic = { level = "warn", priority = -1 }
|
||||
pedantic = "warn"
|
||||
|
||||
## some sadness
|
||||
doc_markdown = { level = "allow", priority = 1 }
|
||||
@@ -820,12 +756,12 @@ unnecessary_wraps = { level = "allow", priority = 1 }
|
||||
unused_async = { level = "allow", priority = 1 }
|
||||
|
||||
###################
|
||||
perf = { level = "warn", priority = -1 }
|
||||
perf = "warn"
|
||||
|
||||
###################
|
||||
#restriction = "warn"
|
||||
|
||||
#allow_attributes = "warn" # UNSTABLE
|
||||
allow_attributes = "warn"
|
||||
arithmetic_side_effects = "warn"
|
||||
as_conversions = "warn"
|
||||
as_underscore = "warn"
|
||||
@@ -870,15 +806,14 @@ unseparated_literal_suffix = "warn"
|
||||
verbose_file_reads = "warn"
|
||||
|
||||
###################
|
||||
style = { level = "warn", priority = -1 }
|
||||
style = "warn"
|
||||
|
||||
## some sadness
|
||||
# trivial assertions are quite alright
|
||||
assertions_on_constants = { level = "allow", priority = 1 }
|
||||
module_inception = { level = "allow", priority = 1 }
|
||||
|
||||
###################
|
||||
suspicious = { level = "warn", priority = -1 }
|
||||
suspicious = "warn"
|
||||
|
||||
## some sadness
|
||||
let_underscore_future = { level = "allow", priority = 1 }
|
||||
|
||||
@@ -52,10 +52,9 @@
|
||||
|
||||
# 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 (0%)
|
||||
# This value is read as a percentage to Sentry, represented as a decimal
|
||||
# Note that too high values may impact performance, and can be disabled by setting it to 0.0
|
||||
#
|
||||
# Defaults to 15% of traces (0.15)
|
||||
# Defaults to 0.15
|
||||
#sentry_traces_sample_rate = 0.15
|
||||
|
||||
# Whether to attach a stacktrace to Sentry reports.
|
||||
@@ -402,7 +401,7 @@ allow_profile_lookup_federation_requests = true
|
||||
# setting this to false may reduce startup time.
|
||||
#
|
||||
# Enabled by default.
|
||||
#media_startup_check = true
|
||||
#media_statup_check = true
|
||||
|
||||
# OpenID token expiration/TTL in seconds
|
||||
#
|
||||
@@ -422,11 +421,8 @@ allow_profile_lookup_federation_requests = true
|
||||
|
||||
# Set this to any float value to multiply conduwuit's in-memory LRU caches with.
|
||||
# May be useful if you have significant memory to spare to increase performance.
|
||||
#
|
||||
# This was previously called `conduit_cache_capacity_modifier`
|
||||
#
|
||||
# Defaults to 1.0.
|
||||
#cache_capacity_modifier = 1.0
|
||||
#conduit_cache_capacity_modifier = 1.0
|
||||
|
||||
# Set this to any float value in megabytes for conduwuit to tell the database engine that this much memory is available for database-related caches.
|
||||
# May be useful if you have significant memory to spare to increase performance.
|
||||
|
||||
Vendored
+1
-1
@@ -27,7 +27,7 @@ malloc-usable-size = ["rust-rocksdb/malloc-usable-size"]
|
||||
|
||||
[dependencies.rust-rocksdb]
|
||||
git = "https://github.com/zaidoon1/rust-rocksdb"
|
||||
rev = "4056a3b0f823013fec49f6d0b3e5698856e6476a"
|
||||
rev = "db1ba33c2b78ad228e2525e8902d059c24fc81a1"
|
||||
#branch = "master"
|
||||
default-features = false
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ Otherwise, follow standard Rust project build guides (installing git and cloning
|
||||
While conduwuit can run as any user it is better to use dedicated users for different services. This also allows
|
||||
you to make sure that the file permissions are correctly set up.
|
||||
|
||||
In Debian or Fedora/RHEL, you can use this command to create a conduwuit user:
|
||||
In Debian or RHEL, you can use this command to create a conduwuit user:
|
||||
|
||||
```bash
|
||||
sudo adduser --system conduwuit --group --disabled-login --no-create-home
|
||||
@@ -53,11 +53,13 @@ RocksDB is the only supported database backend.
|
||||
|
||||
## Setting the correct file permissions
|
||||
|
||||
If you are using a dedicated user for conduwuit, you will need to allow it to read the config. To do that you can run this:
|
||||
If you are using a dedicated user for conduwuit, you will need to allow it to read the config. To do that you can run this command on
|
||||
|
||||
Debian or RHEL:
|
||||
|
||||
```bash
|
||||
sudo chown -R root:root /etc/conduwuit
|
||||
sudo chmod -R 755 /etc/conduwuit
|
||||
sudo chmod 755 /etc/conduwuit
|
||||
```
|
||||
|
||||
If you use the default database path you also need to run this:
|
||||
|
||||
+3
-22
@@ -60,11 +60,6 @@ name = "markdownlint"
|
||||
group = "versions"
|
||||
script = "markdownlint --version"
|
||||
|
||||
[[task]]
|
||||
name = "dpkg"
|
||||
group = "versions"
|
||||
script = "dpkg --version"
|
||||
|
||||
[[task]]
|
||||
name = "cargo-audit"
|
||||
group = "security"
|
||||
@@ -73,9 +68,7 @@ script = "cargo audit -D warnings -D unmaintained -D unsound -D yanked"
|
||||
[[task]]
|
||||
name = "cargo-fmt"
|
||||
group = "lints"
|
||||
script = """
|
||||
cargo fmt --check -- --color=always
|
||||
"""
|
||||
script = "cargo fmt --check -- --color=always"
|
||||
|
||||
[[task]]
|
||||
name = "cargo-doc"
|
||||
@@ -86,7 +79,6 @@ env DIRENV_DEVSHELL=all-features \
|
||||
direnv exec . \
|
||||
cargo doc \
|
||||
--workspace \
|
||||
--profile test \
|
||||
--all-features \
|
||||
--no-deps \
|
||||
--document-private-items \
|
||||
@@ -99,7 +91,6 @@ group = "lints"
|
||||
script = """
|
||||
cargo clippy \
|
||||
--workspace \
|
||||
--profile test \
|
||||
--all-targets \
|
||||
--color=always \
|
||||
-- \
|
||||
@@ -114,7 +105,6 @@ env DIRENV_DEVSHELL=all-features \
|
||||
direnv exec . \
|
||||
cargo clippy \
|
||||
--workspace \
|
||||
--profile test \
|
||||
--all-targets \
|
||||
--all-features \
|
||||
--color=always \
|
||||
@@ -128,7 +118,6 @@ group = "lints"
|
||||
script = """
|
||||
cargo clippy \
|
||||
--workspace \
|
||||
--profile test \
|
||||
--features jemalloc \
|
||||
--all-targets \
|
||||
--color=always \
|
||||
@@ -167,7 +156,6 @@ env DIRENV_DEVSHELL=all-features \
|
||||
direnv exec . \
|
||||
cargo test \
|
||||
--workspace \
|
||||
--profile test \
|
||||
--all-targets \
|
||||
--all-features \
|
||||
--color=always \
|
||||
@@ -181,7 +169,6 @@ group = "tests"
|
||||
script = """
|
||||
cargo test \
|
||||
--workspace \
|
||||
--profile test \
|
||||
--all-targets \
|
||||
--color=always \
|
||||
-- \
|
||||
@@ -197,12 +184,6 @@ cargo test \
|
||||
name = "nix-default"
|
||||
group = "tests"
|
||||
script = """
|
||||
env DIRENV_DEVSHELL=dynamic \
|
||||
CARGO_PROFILE="test" \
|
||||
direnv exec . \
|
||||
bin/nix-build-and-cache just .#default-test
|
||||
env DIRENV_DEVSHELL=dynamic \
|
||||
CARGO_PROFILE="test" \
|
||||
direnv exec . \
|
||||
nix run -L .#default-test -- --help && nix run -L .#default-test -- --version
|
||||
bin/nix-build-and-cache just .#default
|
||||
nix run -L .#default -- --help
|
||||
"""
|
||||
|
||||
Generated
+25
-25
@@ -9,11 +9,11 @@
|
||||
"nixpkgs-stable": "nixpkgs-stable"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1720542474,
|
||||
"narHash": "sha256-aKjJ/4l2I9+wNGTaOGRsuS3M1+IoTibqgEMPDikXm04=",
|
||||
"lastModified": 1717279440,
|
||||
"narHash": "sha256-kH04ReTjxOpQumgWnqy40vvQLSnLGxWP6RF3nq5Esrk=",
|
||||
"owner": "zhaofengli",
|
||||
"repo": "attic",
|
||||
"rev": "6139576a3ce6bb992e0f6c3022528ec233e45f00",
|
||||
"rev": "717cc95983cdc357bc347d70be20ced21f935843",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -81,11 +81,11 @@
|
||||
"complement": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1720637557,
|
||||
"narHash": "sha256-oZz6nCmFmdJZpC+K1iOG2KkzTI6rlAmndxANPDVU7X0=",
|
||||
"lastModified": 1719903368,
|
||||
"narHash": "sha256-PPzgxM4Bir+Zh9FUV/v+RBxEYeJxYVmi/BYo3uqt268=",
|
||||
"owner": "matrix-org",
|
||||
"repo": "complement",
|
||||
"rev": "0d14432e010482ea9e13a6f7c47c1533c0c9d62f",
|
||||
"rev": "bc97f1ddc1cd7485faf80c8935ee2641f3e1b57c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -123,11 +123,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1720546058,
|
||||
"narHash": "sha256-iU2yVaPIZm5vMGdlT0+57vdB/aPq/V5oZFBRwYw+HBM=",
|
||||
"lastModified": 1720226507,
|
||||
"narHash": "sha256-yHVvNsgrpyNTXZBEokL8uyB2J6gB1wEx0KOJzoeZi1A=",
|
||||
"owner": "ipetkov",
|
||||
"repo": "crane",
|
||||
"rev": "2d83156f23c43598cf44e152c33a59d3892f8b29",
|
||||
"rev": "0aed560c5c0a61c9385bddff471a13036203e11c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -209,11 +209,11 @@
|
||||
"rust-analyzer-src": "rust-analyzer-src"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1720852044,
|
||||
"narHash": "sha256-3NBYz8VuXuKU+8ONd9NFafCNjPEGHIZQ2Mdoam1a4mY=",
|
||||
"lastModified": 1720420198,
|
||||
"narHash": "sha256-OIuDb6pHDyGpo7YMFyuRzMLcHm7mRvlYOz0Ht7ps2sU=",
|
||||
"owner": "nix-community",
|
||||
"repo": "fenix",
|
||||
"rev": "5087b12a595ee73131a944d922f24d81dae05725",
|
||||
"rev": "abc0549e3560189462a7d394cc9d50af4608d103",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -381,11 +381,11 @@
|
||||
"liburing": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1720798442,
|
||||
"narHash": "sha256-gtPppAoksMLW4GuruQ36nf4EAqIA1Bs6V9Xcx8dBxrQ=",
|
||||
"lastModified": 1719025212,
|
||||
"narHash": "sha256-kD0yhjNStqC6uFqC1AxBwUpc/HlSFtiKrV+gwDyroDc=",
|
||||
"owner": "axboe",
|
||||
"repo": "liburing",
|
||||
"rev": "1d674f83b7d0f07553ac44d99a401b05853d9dbe",
|
||||
"rev": "7b3245583069bd481190c9da18f22e9fc8c3a805",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -606,11 +606,11 @@
|
||||
},
|
||||
"nixpkgs_4": {
|
||||
"locked": {
|
||||
"lastModified": 1720768451,
|
||||
"narHash": "sha256-EYekUHJE2gxeo2pM/zM9Wlqw1Uw2XTJXOSAO79ksc4Y=",
|
||||
"lastModified": 1720418205,
|
||||
"narHash": "sha256-cPJoFPXU44GlhWg4pUk9oUPqurPlCFZ11ZQPk21GTPU=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "7e7c39ea35c5cdd002cd4588b03a3fb9ece6fad9",
|
||||
"rev": "655a58a72a6601292512670343087c2d75d859c1",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -673,16 +673,16 @@
|
||||
"rocksdb": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1720900786,
|
||||
"narHash": "sha256-Vta9Um/RRuWwZ46BjXftV06iWLm/j/9MX39emXUvSAY=",
|
||||
"lastModified": 1719949653,
|
||||
"narHash": "sha256-DYx7XHH2GEh17GukKhXs6laM6l+eugCmRkF0adpi9wk=",
|
||||
"owner": "girlbossceo",
|
||||
"repo": "rocksdb",
|
||||
"rev": "911f4243e69c2e320a7a209bf1f5f3ff5f825495",
|
||||
"rev": "a935c0273e1ba44eacf88ce3685a9b9831486155",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "girlbossceo",
|
||||
"ref": "v9.4.0",
|
||||
"ref": "v9.3.1",
|
||||
"repo": "rocksdb",
|
||||
"type": "github"
|
||||
}
|
||||
@@ -705,11 +705,11 @@
|
||||
"rust-analyzer-src": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1720717809,
|
||||
"narHash": "sha256-6I+fm+nTLF/iaj7ffiFGlSY7POmubwUaPA/Wq0Bm53M=",
|
||||
"lastModified": 1720344064,
|
||||
"narHash": "sha256-STmaV9Zu74QtkGGrbr9uMhskwagfCjJqOAYapXabiuk=",
|
||||
"owner": "rust-lang",
|
||||
"repo": "rust-analyzer",
|
||||
"rev": "ffbc5ad993d5cd2f3b8bcf9a511165470944ab91",
|
||||
"rev": "a5b21ea0aa644dffd7cf958b43f11f221d53404e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
flake-utils.url = "github:numtide/flake-utils?ref=main";
|
||||
nix-filter.url = "github:numtide/nix-filter?ref=main";
|
||||
nixpkgs.url = "github:NixOS/nixpkgs?ref=nixos-unstable";
|
||||
rocksdb = { url = "github:girlbossceo/rocksdb?ref=v9.4.0"; flake = false; };
|
||||
# https://github.com/girlbossceo/rocksdb/commit/db6df0b185774778457dabfcbd822cb81760cade
|
||||
rocksdb = { url = "github:girlbossceo/rocksdb?ref=v9.3.1"; flake = false; };
|
||||
liburing = { url = "github:axboe/liburing?ref=master"; flake = false; };
|
||||
};
|
||||
|
||||
@@ -24,7 +25,7 @@
|
||||
file = ./rust-toolchain.toml;
|
||||
|
||||
# See also `rust-toolchain.toml`
|
||||
sha256 = "sha256-6eN/GKzjVSjEhGO9FhWObkRFaE1Jf+uqMSdQnb8lcB4=";
|
||||
sha256 = "sha256-+syqAd2kX8KVa8/U2gz3blIQTTsYYt3U63xBWaGOSc8";
|
||||
};
|
||||
|
||||
mkScope = pkgs: pkgs.lib.makeScope pkgs.newScope (self: {
|
||||
@@ -44,34 +45,10 @@
|
||||
# we have this already at https://github.com/girlbossceo/rocksdb/commit/a935c0273e1ba44eacf88ce3685a9b9831486155
|
||||
# unsetting this so i don't have to revert it and make this nix exclusive
|
||||
patches = [];
|
||||
cmakeFlags = pkgs.lib.subtractLists
|
||||
[
|
||||
# no real reason to have snappy, no one uses this
|
||||
"-DWITH_SNAPPY=1"
|
||||
# we dont need to use ldb or sst_dump (core_tools)
|
||||
"-DWITH_CORE_TOOLS=1"
|
||||
# we dont need to build rocksdb tests
|
||||
"-DWITH_TESTS=1"
|
||||
# we use rust-rocksdb via C interface and dont need C++ RTTI
|
||||
"-DUSE_RTTI=1"
|
||||
]
|
||||
old.cmakeFlags
|
||||
++ [
|
||||
# we dont need to use ldb or sst_dump (core_tools)
|
||||
"-DWITH_CORE_TOOLS=0"
|
||||
# we dont need trace tools
|
||||
"-DWITH_TRACE_TOOLS=0"
|
||||
# we dont need to build rocksdb tests
|
||||
"-DWITH_TESTS=0"
|
||||
# we use rust-rocksdb via C interface and dont need C++ RTTI
|
||||
"-DUSE_RTTI=0"
|
||||
];
|
||||
|
||||
# outputs has "tools" which we dont need or use
|
||||
outputs = [ "out" ];
|
||||
|
||||
# preInstall hooks has stuff for messing with ldb/sst_dump which we dont need or use
|
||||
preInstall = "";
|
||||
cmakeFlags = pkgs.lib.subtractLists
|
||||
[ "-DWITH_SNAPPY=1" ]
|
||||
old.cmakeFlags;
|
||||
});
|
||||
# TODO: remove once https://github.com/NixOS/nixpkgs/pull/314945 is available
|
||||
liburing = pkgs.liburing.overrideAttrs (old: {
|
||||
@@ -119,9 +96,6 @@
|
||||
# Needed for producing Debian packages
|
||||
cargo-deb
|
||||
|
||||
# Needed for CI to check validity of produced Debian packages (dpkg-deb)
|
||||
dpkg
|
||||
|
||||
# Needed for Complement
|
||||
go
|
||||
|
||||
@@ -136,8 +110,6 @@
|
||||
|
||||
# Useful for editing the book locally
|
||||
mdbook
|
||||
|
||||
sccache
|
||||
])
|
||||
++ scope.main.buildInputs
|
||||
++ scope.main.propagatedBuildInputs
|
||||
@@ -149,35 +121,10 @@
|
||||
{
|
||||
packages = {
|
||||
default = scopeHost.main;
|
||||
default-debug = scopeHost.main.override {
|
||||
profile = "dev";
|
||||
# debug build users expect full logs
|
||||
disable_release_max_log_level = true;
|
||||
};
|
||||
default-test = scopeHost.main.override {
|
||||
profile = "test";
|
||||
disable_release_max_log_level = true;
|
||||
};
|
||||
all-features = scopeHost.main.override {
|
||||
all_features = true;
|
||||
disable_features = [
|
||||
# this is non-functional on nix for some reason
|
||||
"hardened_malloc"
|
||||
# dont include experimental features
|
||||
"experimental"
|
||||
];
|
||||
};
|
||||
all-features-debug = scopeHost.main.override {
|
||||
profile = "dev";
|
||||
all_features = true;
|
||||
# debug build users expect full logs
|
||||
disable_release_max_log_level = true;
|
||||
disable_features = [
|
||||
# this is non-functional on nix for some reason
|
||||
"hardened_malloc"
|
||||
# dont include experimental features
|
||||
"experimental"
|
||||
];
|
||||
disable_features = ["hardened_malloc"];
|
||||
};
|
||||
hmalloc = scopeHost.main.override { features = ["hardened_malloc"]; };
|
||||
|
||||
@@ -185,26 +132,8 @@
|
||||
oci-image-all-features = scopeHost.oci-image.override {
|
||||
main = scopeHost.main.override {
|
||||
all_features = true;
|
||||
disable_features = [
|
||||
# this is non-functional on nix for some reason
|
||||
"hardened_malloc"
|
||||
# dont include experimental features
|
||||
"experimental"
|
||||
];
|
||||
};
|
||||
};
|
||||
oci-image-all-features-debug = scopeHost.oci-image.override {
|
||||
main = scopeHost.main.override {
|
||||
profile = "dev";
|
||||
all_features = true;
|
||||
# debug build users expect full logs
|
||||
disable_release_max_log_level = true;
|
||||
disable_features = [
|
||||
# this is non-functional on nix for some reason
|
||||
"hardened_malloc"
|
||||
# dont include experimental features
|
||||
"experimental"
|
||||
];
|
||||
disable_features = ["hardened_malloc"];
|
||||
};
|
||||
};
|
||||
oci-image-hmalloc = scopeHost.oci-image.override {
|
||||
@@ -241,54 +170,13 @@
|
||||
value = scopeCrossStatic.main;
|
||||
}
|
||||
|
||||
# An output for a statically-linked unstripped debug ("dev") binary
|
||||
{
|
||||
name = "${binaryName}-debug";
|
||||
value = scopeCrossStatic.main.override {
|
||||
profile = "dev";
|
||||
# debug build users expect full logs
|
||||
disable_release_max_log_level = true;
|
||||
};
|
||||
}
|
||||
|
||||
# An output for a statically-linked unstripped debug binary with the
|
||||
# "test" profile (for CI usage only)
|
||||
{
|
||||
name = "${binaryName}-test";
|
||||
value = scopeCrossStatic.main.override {
|
||||
profile = "test";
|
||||
disable_release_max_log_level = true;
|
||||
};
|
||||
}
|
||||
|
||||
# An output for a statically-linked binary with `--all-features`
|
||||
{
|
||||
name = "${binaryName}-all-features";
|
||||
value = scopeCrossStatic.main.override {
|
||||
all_features = true;
|
||||
disable_features = [
|
||||
# this is non-functional on nix for some reason
|
||||
"hardened_malloc"
|
||||
# dont include experimental features
|
||||
"experimental"
|
||||
];
|
||||
};
|
||||
}
|
||||
|
||||
# An output for a statically-linked unstripped debug ("dev") binary with `--all-features`
|
||||
{
|
||||
name = "${binaryName}-all-features-debug";
|
||||
value = scopeCrossStatic.main.override {
|
||||
profile = "dev";
|
||||
all_features = true;
|
||||
# debug build users expect full logs
|
||||
disable_release_max_log_level = true;
|
||||
disable_features = [
|
||||
# this is non-functional on nix for some reason
|
||||
"hardened_malloc"
|
||||
# dont include experimental features
|
||||
"experimental"
|
||||
];
|
||||
disable_features = ["hardened_malloc"];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -306,49 +194,14 @@
|
||||
value = scopeCrossStatic.oci-image;
|
||||
}
|
||||
|
||||
# An output for an OCI image based on that unstripped debug ("dev") binary
|
||||
{
|
||||
name = "oci-image-${crossSystem}-debug";
|
||||
value = scopeCrossStatic.oci-image.override {
|
||||
main = scopeCrossStatic.main.override {
|
||||
profile = "dev";
|
||||
# debug build users expect full logs
|
||||
disable_release_max_log_level = true;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
# An output for an OCI image based on that binary with `--all-features`
|
||||
{
|
||||
name = "oci-image-${crossSystem}-all-features";
|
||||
value = scopeCrossStatic.oci-image.override {
|
||||
main = scopeCrossStatic.main.override {
|
||||
all_features = true;
|
||||
disable_features = [
|
||||
# this is non-functional on nix for some reason
|
||||
"hardened_malloc"
|
||||
# dont include experimental features
|
||||
"experimental"
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
# An output for an OCI image based on that unstripped debug ("dev") binary with `--all-features`
|
||||
{
|
||||
name = "oci-image-${crossSystem}-all-features-debug";
|
||||
value = scopeCrossStatic.oci-image.override {
|
||||
main = scopeCrossStatic.main.override {
|
||||
profile = "dev";
|
||||
all_features = true;
|
||||
# debug build users expect full logs
|
||||
disable_release_max_log_level = true;
|
||||
disable_features = [
|
||||
# this is non-functional on nix for some reason
|
||||
"hardened_malloc"
|
||||
# dont include experimental features
|
||||
"experimental"
|
||||
];
|
||||
disable_features = ["hardened_malloc"];
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -376,12 +229,8 @@
|
||||
(scopeHostStatic.overrideScope (final: prev: {
|
||||
main = prev.main.override {
|
||||
all_features = true;
|
||||
disable_features = [
|
||||
# this is non-functional on nix for some reason
|
||||
"hardened_malloc"
|
||||
# dont include experimental features
|
||||
"experimental"
|
||||
];
|
||||
disable_features = ["hardened_malloc"];
|
||||
};
|
||||
}));
|
||||
devShells.no-features = mkDevShell
|
||||
|
||||
@@ -14,7 +14,7 @@ yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse = true
|
||||
ip_range_denylist = []
|
||||
url_preview_domain_contains_allowlist = ["*"]
|
||||
media_compat_file_link = false
|
||||
media_startup_check = false
|
||||
media_statup_check = false
|
||||
rocksdb_direct_io = false
|
||||
|
||||
[global.tls]
|
||||
|
||||
@@ -14,15 +14,8 @@
|
||||
|
||||
let
|
||||
main' = main.override {
|
||||
profile = "test";
|
||||
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"
|
||||
];
|
||||
profile = "dev";
|
||||
features = ["axum_dual_protocol"];
|
||||
};
|
||||
|
||||
start = writeShellScriptBin "start" ''
|
||||
|
||||
@@ -13,12 +13,6 @@ lib.optionalAttrs stdenv.hostPlatform.isStatic {
|
||||
lib.concatStringsSep
|
||||
" "
|
||||
([]
|
||||
++ lib.optionals
|
||||
stdenv.targetPlatform.isx86_64
|
||||
[ "-C" "target-cpu=x86-64-v2" ]
|
||||
++ lib.optionals
|
||||
stdenv.targetPlatform.isAarch64
|
||||
[ "-C" "target-cpu=cortex-a55" ] # cortex-a55 == ARMv8.2-a
|
||||
# This disables PIE for static builds, which isn't great in terms
|
||||
# of security. Unfortunately, my hand is forced because nixpkgs'
|
||||
# `libstdc++.a` is built without `-fPIE`, which precludes us from
|
||||
|
||||
@@ -51,10 +51,6 @@ rust-jemalloc-sys' = (rust-jemalloc-sys.override {
|
||||
unprefixed = true;
|
||||
}).overrideAttrs (old: {
|
||||
configureFlags = old.configureFlags ++
|
||||
# we dont need docs
|
||||
[ "--disable-doc" ] ++
|
||||
# we dont need cxx/C++ integration
|
||||
[ "--disable-cxx" ] ++
|
||||
# tikv-jemalloc-sys/profiling feature
|
||||
lib.optional (featureEnabled "jemalloc_prof") "--enable-prof";
|
||||
});
|
||||
@@ -75,29 +71,10 @@ buildDepsOnlyEnv =
|
||||
# which breaks Darwin entirely
|
||||
enableLiburing = enableLiburing;
|
||||
}).overrideAttrs (old: {
|
||||
# TODO: static rocksdb fails to build on darwin, also see <https://github.com/NixOS/nixpkgs/issues/320448>
|
||||
# TODO: static rocksdb fails to build on darwin
|
||||
# build log at <https://girlboss.ceo/~strawberry/pb/JjGH>
|
||||
meta.broken = stdenv.hostPlatform.isStatic && stdenv.isDarwin;
|
||||
|
||||
enableLiburing = enableLiburing;
|
||||
|
||||
sse42Support = stdenv.targetPlatform.isx86_64;
|
||||
|
||||
cmakeFlags = if stdenv.targetPlatform.isx86_64
|
||||
then lib.subtractLists [ "-DPORTABLE=1" ] old.cmakeFlags
|
||||
++ lib.optionals stdenv.targetPlatform.isx86_64 [
|
||||
"-DPORTABLE=x86-64-v2"
|
||||
"-DUSE_SSE=1"
|
||||
"-DHAVE_SSE=1"
|
||||
"-DHAVE_SSE42=1"
|
||||
]
|
||||
else if stdenv.targetPlatform.isAarch64
|
||||
then lib.subtractLists [ "-DPORTABLE=1" ] old.cmakeFlags
|
||||
++ lib.optionals stdenv.targetPlatform.isAarch64 [
|
||||
# cortex-a55 == ARMv8.2-a
|
||||
"-DPORTABLE=armv8.2-a"
|
||||
]
|
||||
else old.cmakeFlags;
|
||||
});
|
||||
in
|
||||
{
|
||||
@@ -124,11 +101,7 @@ buildPackageEnv = {
|
||||
# Only needed in static stdenv because these are transitive dependencies of rocksdb
|
||||
CARGO_BUILD_RUSTFLAGS = buildDepsOnlyEnv.CARGO_BUILD_RUSTFLAGS
|
||||
+ lib.optionalString (enableLiburing && stdenv.hostPlatform.isStatic)
|
||||
" -L${lib.getLib liburing}/lib -luring"
|
||||
+ lib.optionalString stdenv.targetPlatform.isx86_64
|
||||
" -Ctarget-cpu=x86-64-v2"
|
||||
+ lib.optionalString stdenv.targetPlatform.isAarch64
|
||||
" -Ctarget-cpu=cortex-a55"; # cortex-a55 == ARMv8.2-a
|
||||
" -L${lib.getLib liburing}/lib -luring";
|
||||
};
|
||||
|
||||
|
||||
@@ -153,8 +126,6 @@ commonAttrs = {
|
||||
];
|
||||
};
|
||||
|
||||
dontStrip = profile == "dev" || profile == "test";
|
||||
|
||||
buildInputs = lib.optional (featureEnabled "jemalloc") rust-jemalloc-sys';
|
||||
|
||||
nativeBuildInputs = [
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@
|
||||
# If you're having trouble making the relevant changes, bug a maintainer.
|
||||
|
||||
[toolchain]
|
||||
channel = "1.80.0"
|
||||
channel = "1.77.0"
|
||||
components = [
|
||||
# For rust-analyzer
|
||||
"rust-src",
|
||||
|
||||
@@ -29,7 +29,6 @@ release_max_log_level = [
|
||||
clap.workspace = true
|
||||
conduit-api.workspace = true
|
||||
conduit-core.workspace = true
|
||||
conduit-macros.workspace = true
|
||||
conduit-service.workspace = true
|
||||
const-str.workspace = true
|
||||
futures-util.workspace = true
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
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,20 +1,18 @@
|
||||
use ruma::{api::appservice::Registration, events::room::message::RoomMessageEventContent};
|
||||
|
||||
use crate::{admin_command, Result};
|
||||
use crate::{services, Result};
|
||||
|
||||
#[admin_command]
|
||||
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() != "```"
|
||||
{
|
||||
pub(super) async fn register(body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
if body.len() < 2 || !body[0].trim().starts_with("```") || body.last().unwrap_or(&"").trim() != "```" {
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
"Expected code block in command body. Add --help for details.",
|
||||
));
|
||||
}
|
||||
|
||||
let appservice_config = self.body[1..self.body.len().checked_sub(1).unwrap()].join("\n");
|
||||
let appservice_config = body[1..body.len().checked_sub(1).unwrap()].join("\n");
|
||||
let parsed_config = serde_yaml::from_str::<Registration>(&appservice_config);
|
||||
match parsed_config {
|
||||
Ok(yaml) => match self.services.appservice.register_appservice(yaml).await {
|
||||
Ok(yaml) => match services().appservice.register_appservice(yaml).await {
|
||||
Ok(id) => Ok(RoomMessageEventContent::text_plain(format!(
|
||||
"Appservice registered with ID: {id}."
|
||||
))),
|
||||
@@ -28,10 +26,8 @@ pub(super) async fn register(&self) -> Result<RoomMessageEventContent> {
|
||||
}
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn unregister(&self, appservice_identifier: String) -> Result<RoomMessageEventContent> {
|
||||
match self
|
||||
.services
|
||||
pub(super) async fn unregister(_body: Vec<&str>, appservice_identifier: String) -> Result<RoomMessageEventContent> {
|
||||
match services()
|
||||
.appservice
|
||||
.unregister_appservice(&appservice_identifier)
|
||||
.await
|
||||
@@ -43,10 +39,8 @@ pub(super) async fn unregister(&self, appservice_identifier: String) -> Result<R
|
||||
}
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn show_appservice_config(&self, appservice_identifier: String) -> Result<RoomMessageEventContent> {
|
||||
match self
|
||||
.services
|
||||
pub(super) async fn show(_body: Vec<&str>, appservice_identifier: String) -> Result<RoomMessageEventContent> {
|
||||
match services()
|
||||
.appservice
|
||||
.get_registration(&appservice_identifier)
|
||||
.await
|
||||
@@ -60,9 +54,8 @@ pub(super) async fn show_appservice_config(&self, appservice_identifier: String)
|
||||
}
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn list_registered(&self) -> Result<RoomMessageEventContent> {
|
||||
let appservices = self.services.appservice.iter_ids().await;
|
||||
pub(super) async fn list(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
let appservices = services().appservice.iter_ids().await;
|
||||
let output = format!("Appservices ({}): {}", appservices.len(), appservices.join(", "));
|
||||
Ok(RoomMessageEventContent::text_plain(output))
|
||||
}
|
||||
|
||||
@@ -2,11 +2,12 @@ mod commands;
|
||||
|
||||
use clap::Subcommand;
|
||||
use conduit::Result;
|
||||
use ruma::events::room::message::RoomMessageEventContent;
|
||||
|
||||
use crate::admin_command_dispatch;
|
||||
use self::commands::*;
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
#[admin_command_dispatch]
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
#[derive(Subcommand)]
|
||||
pub(super) enum AppserviceCommand {
|
||||
/// - Register an appservice using its registration YAML
|
||||
///
|
||||
@@ -28,13 +29,24 @@ pub(super) enum AppserviceCommand {
|
||||
/// - Show an appservice's config using its ID
|
||||
///
|
||||
/// You can find the ID using the `list-appservices` command.
|
||||
#[clap(alias("show"))]
|
||||
ShowAppserviceConfig {
|
||||
Show {
|
||||
/// The appservice to show
|
||||
appservice_identifier: String,
|
||||
},
|
||||
|
||||
/// - List all the currently registered appservices
|
||||
#[clap(alias("list"))]
|
||||
ListRegistered,
|
||||
List,
|
||||
}
|
||||
|
||||
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,16 +1,14 @@
|
||||
use conduit::Result;
|
||||
use conduit_macros::implement;
|
||||
use ruma::events::room::message::RoomMessageEventContent;
|
||||
|
||||
use crate::Command;
|
||||
use crate::services;
|
||||
|
||||
/// 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
|
||||
/// errors if there were any, etc
|
||||
#[implement(Command, params = "<'_>")]
|
||||
pub(super) async fn check_all_users(&self) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn check_all_users(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = self.services.users.db.iter();
|
||||
let results = services().users.db.iter();
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
let users = results.collect::<Vec<_>>();
|
||||
|
||||
@@ -4,15 +4,16 @@ use clap::Subcommand;
|
||||
use conduit::Result;
|
||||
use ruma::events::room::message::RoomMessageEventContent;
|
||||
|
||||
use crate::Command;
|
||||
use self::commands::*;
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
#[derive(Subcommand)]
|
||||
pub(super) enum CheckCommand {
|
||||
AllUsers,
|
||||
}
|
||||
|
||||
pub(super) async fn process(command: CheckCommand, context: &Command<'_>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn process(command: CheckCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
Ok(match command {
|
||||
CheckCommand::AllUsers => context.check_all_users().await?,
|
||||
CheckCommand::AllUsers => check_all_users(body).await?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
use service::Services;
|
||||
|
||||
pub(crate) struct Command<'a> {
|
||||
pub(crate) services: &'a Services,
|
||||
pub(crate) body: &'a [&'a str],
|
||||
}
|
||||
+109
-241
@@ -1,37 +1,33 @@
|
||||
use std::{
|
||||
collections::{BTreeMap, HashMap},
|
||||
fmt::Write,
|
||||
sync::{Arc, Mutex},
|
||||
time::{Instant, SystemTime},
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
use api::client::validate_and_add_event_id;
|
||||
use conduit::{
|
||||
debug, debug_error, err, info, log,
|
||||
debug, info, log,
|
||||
log::{capture, Capture},
|
||||
utils, warn, Error, PduEvent, Result,
|
||||
warn, Error, Result,
|
||||
};
|
||||
use ruma::{
|
||||
api::{client::error::ErrorKind, federation::event::get_room_state},
|
||||
events::room::message::RoomMessageEventContent,
|
||||
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 tracing_subscriber::EnvFilter;
|
||||
|
||||
use crate::admin_command;
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn echo(&self, message: Vec<String>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn echo(_body: Vec<&str>, message: Vec<String>) -> Result<RoomMessageEventContent> {
|
||||
let message = message.join(" ");
|
||||
|
||||
Ok(RoomMessageEventContent::notice_plain(message))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn get_auth_chain(&self, event_id: Box<EventId>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn get_auth_chain(_body: Vec<&str>, event_id: Box<EventId>) -> Result<RoomMessageEventContent> {
|
||||
let event_id = Arc::<EventId>::from(event_id);
|
||||
if let Some(event) = self.services.rooms.timeline.get_pdu_json(&event_id)? {
|
||||
if let Some(event) = services().rooms.timeline.get_pdu_json(&event_id)? {
|
||||
let room_id_str = event
|
||||
.get("room_id")
|
||||
.and_then(|val| val.as_str())
|
||||
@@ -39,16 +35,13 @@ pub(super) async fn get_auth_chain(&self, event_id: Box<EventId>) -> Result<Room
|
||||
|
||||
let room_id = <&RoomId>::try_from(room_id_str)
|
||||
.map_err(|_| Error::bad_database("Invalid room id field in event in database"))?;
|
||||
|
||||
let start = Instant::now();
|
||||
let count = self
|
||||
.services
|
||||
let count = services()
|
||||
.rooms
|
||||
.auth_chain
|
||||
.event_ids_iter(room_id, vec![event_id])
|
||||
.await?
|
||||
.count();
|
||||
|
||||
let elapsed = start.elapsed();
|
||||
Ok(RoomMessageEventContent::text_plain(format!(
|
||||
"Loaded auth chain with length {count} in {elapsed:?}"
|
||||
@@ -58,16 +51,14 @@ pub(super) async fn get_auth_chain(&self, event_id: Box<EventId>) -> Result<Room
|
||||
}
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
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() != "```"
|
||||
{
|
||||
pub(super) async fn parse_pdu(body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
if body.len() < 2 || !body[0].trim().starts_with("```") || body.last().unwrap_or(&"").trim() != "```" {
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
"Expected code block in command body. Add --help for details.",
|
||||
));
|
||||
}
|
||||
|
||||
let string = self.body[1..self.body.len().saturating_sub(1)].join("\n");
|
||||
let string = body[1..body.len().saturating_sub(1)].join("\n");
|
||||
match serde_json::from_str(&string) {
|
||||
Ok(value) => match ruma::signatures::reference_hash(&value, &RoomVersionId::V6) {
|
||||
Ok(hash) => {
|
||||
@@ -88,17 +79,15 @@ pub(super) async fn parse_pdu(&self) -> Result<RoomMessageEventContent> {
|
||||
}
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn get_pdu(&self, event_id: Box<EventId>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn get_pdu(_body: Vec<&str>, event_id: Box<EventId>) -> Result<RoomMessageEventContent> {
|
||||
let mut outlier = false;
|
||||
let mut pdu_json = self
|
||||
.services
|
||||
let mut pdu_json = services()
|
||||
.rooms
|
||||
.timeline
|
||||
.get_non_outlier_pdu_json(&event_id)?;
|
||||
if pdu_json.is_none() {
|
||||
outlier = true;
|
||||
pdu_json = self.services.rooms.timeline.get_pdu_json(&event_id)?;
|
||||
pdu_json = services().rooms.timeline.get_pdu_json(&event_id)?;
|
||||
}
|
||||
match pdu_json {
|
||||
Some(json) => {
|
||||
@@ -117,42 +106,38 @@ pub(super) async fn get_pdu(&self, event_id: Box<EventId>) -> Result<RoomMessage
|
||||
}
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn get_remote_pdu_list(
|
||||
&self, server: Box<ServerName>, force: bool,
|
||||
body: Vec<&str>, server: Box<ServerName>, force: bool,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
if !self.services.globals.config.allow_federation {
|
||||
if !services().globals.config.allow_federation {
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
"Federation is disabled on this homeserver.",
|
||||
));
|
||||
}
|
||||
|
||||
if server == self.services.globals.server_name() {
|
||||
if server == services().globals.server_name() {
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
"Not allowed to send federation requests to ourselves. Please use `get-pdu` for fetching local PDUs from \
|
||||
the database.",
|
||||
));
|
||||
}
|
||||
|
||||
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(
|
||||
"Expected code block in command body. Add --help for details.",
|
||||
));
|
||||
}
|
||||
|
||||
let list = self
|
||||
.body
|
||||
.iter()
|
||||
.collect::<Vec<_>>()
|
||||
.drain(1..self.body.len().saturating_sub(1))
|
||||
let list = body
|
||||
.clone()
|
||||
.drain(1..body.len().checked_sub(1).unwrap())
|
||||
.filter_map(|pdu| EventId::parse(pdu).ok())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for pdu in list {
|
||||
if force {
|
||||
if let Err(e) = self.get_remote_pdu(Box::from(pdu), server.clone()).await {
|
||||
self.services
|
||||
if let Err(e) = get_remote_pdu(Vec::new(), Box::from(pdu), server.clone()).await {
|
||||
services()
|
||||
.admin
|
||||
.send_message(RoomMessageEventContent::text_plain(format!(
|
||||
"Failed to get remote PDU, ignoring error: {e}"
|
||||
@@ -161,31 +146,29 @@ pub(super) async fn get_remote_pdu_list(
|
||||
warn!(%e, "Failed to get remote PDU, ignoring error");
|
||||
}
|
||||
} else {
|
||||
self.get_remote_pdu(Box::from(pdu), server.clone()).await?;
|
||||
get_remote_pdu(Vec::new(), Box::from(pdu), server.clone()).await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(RoomMessageEventContent::text_plain("Fetched list of remote PDUs."))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn get_remote_pdu(
|
||||
&self, event_id: Box<EventId>, server: Box<ServerName>,
|
||||
_body: Vec<&str>, event_id: Box<EventId>, server: Box<ServerName>,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
if !self.services.globals.config.allow_federation {
|
||||
if !services().globals.config.allow_federation {
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
"Federation is disabled on this homeserver.",
|
||||
));
|
||||
}
|
||||
|
||||
if server == self.services.globals.server_name() {
|
||||
if server == services().globals.server_name() {
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
"Not allowed to send federation requests to ourselves. Please use `get-pdu` for fetching local PDUs.",
|
||||
));
|
||||
}
|
||||
|
||||
match self
|
||||
.services
|
||||
match services()
|
||||
.sending
|
||||
.send_federation_request(
|
||||
&server,
|
||||
@@ -206,11 +189,7 @@ pub(super) async fn get_remote_pdu(
|
||||
|
||||
debug!("Attempting to parse PDU: {:?}", &response.pdu);
|
||||
let parsed_pdu = {
|
||||
let parsed_result = self
|
||||
.services
|
||||
.rooms
|
||||
.event_handler
|
||||
.parse_incoming_pdu(&response.pdu);
|
||||
let parsed_result = parse_incoming_pdu(&response.pdu);
|
||||
let (event_id, value, room_id) = match parsed_result {
|
||||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
@@ -228,8 +207,9 @@ pub(super) async fn get_remote_pdu(
|
||||
let pub_key_map = RwLock::new(BTreeMap::new());
|
||||
|
||||
debug!("Attempting to fetch homeserver signing keys for {server}");
|
||||
self.services
|
||||
.server_keys
|
||||
services()
|
||||
.rooms
|
||||
.event_handler
|
||||
.fetch_required_signing_keys(parsed_pdu.iter().map(|(_event_id, event, _room_id)| event), &pub_key_map)
|
||||
.await
|
||||
.unwrap_or_else(|e| {
|
||||
@@ -237,7 +217,7 @@ pub(super) async fn get_remote_pdu(
|
||||
});
|
||||
|
||||
info!("Attempting to handle event ID {event_id} as backfilled PDU");
|
||||
self.services
|
||||
services()
|
||||
.rooms
|
||||
.timeline
|
||||
.backfill_pdu(&server, response.pdu, &pub_key_map)
|
||||
@@ -256,11 +236,9 @@ pub(super) async fn get_remote_pdu(
|
||||
}
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn get_room_state(&self, room: OwnedRoomOrAliasId) -> Result<RoomMessageEventContent> {
|
||||
let room_id = self.services.rooms.alias.resolve(&room).await?;
|
||||
let room_state = self
|
||||
.services
|
||||
pub(super) async fn get_room_state(_body: Vec<&str>, room: OwnedRoomOrAliasId) -> Result<RoomMessageEventContent> {
|
||||
let room_id = services().rooms.alias.resolve(&room).await?;
|
||||
let room_state = services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_full(&room_id)
|
||||
@@ -285,9 +263,8 @@ pub(super) async fn get_room_state(&self, room: OwnedRoomOrAliasId) -> Result<Ro
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!("```json\n{json}\n```")))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn ping(&self, server: Box<ServerName>) -> Result<RoomMessageEventContent> {
|
||||
if server == self.services.globals.server_name() {
|
||||
pub(super) async fn ping(_body: Vec<&str>, server: Box<ServerName>) -> Result<RoomMessageEventContent> {
|
||||
if server == services().globals.server_name() {
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
"Not allowed to send federation requests to ourselves.",
|
||||
));
|
||||
@@ -295,8 +272,7 @@ pub(super) async fn ping(&self, server: Box<ServerName>) -> Result<RoomMessageEv
|
||||
|
||||
let timer = tokio::time::Instant::now();
|
||||
|
||||
match self
|
||||
.services
|
||||
match services()
|
||||
.sending
|
||||
.send_federation_request(&server, ruma::api::federation::discovery::get_server_version::v1::Request {})
|
||||
.await
|
||||
@@ -325,23 +301,23 @@ pub(super) async fn ping(&self, server: Box<ServerName>) -> Result<RoomMessageEv
|
||||
}
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn force_device_list_updates(&self) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn force_device_list_updates(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
// Force E2EE device list updates for all users
|
||||
for user_id in self.services.users.iter().filter_map(Result::ok) {
|
||||
self.services.users.mark_device_key_update(&user_id)?;
|
||||
for user_id in services().users.iter().filter_map(Result::ok) {
|
||||
services().users.mark_device_key_update(&user_id)?;
|
||||
}
|
||||
Ok(RoomMessageEventContent::text_plain(
|
||||
"Marked all devices for all users as having new keys to update",
|
||||
))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn change_log_level(&self, filter: Option<String>, reset: bool) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn change_log_level(
|
||||
_body: Vec<&str>, filter: Option<String>, reset: bool,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
let handles = &["console"];
|
||||
|
||||
if reset {
|
||||
let old_filter_layer = match EnvFilter::try_new(&self.services.globals.config.log) {
|
||||
let old_filter_layer = match EnvFilter::try_new(&services().globals.config.log) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
return Ok(RoomMessageEventContent::text_plain(format!(
|
||||
@@ -350,8 +326,7 @@ pub(super) async fn change_log_level(&self, filter: Option<String>, reset: bool)
|
||||
},
|
||||
};
|
||||
|
||||
match self
|
||||
.services
|
||||
match services()
|
||||
.server
|
||||
.log
|
||||
.reload
|
||||
@@ -360,7 +335,7 @@ pub(super) async fn change_log_level(&self, filter: Option<String>, reset: bool)
|
||||
Ok(()) => {
|
||||
return Ok(RoomMessageEventContent::text_plain(format!(
|
||||
"Successfully changed log level back to config value {}",
|
||||
self.services.globals.config.log
|
||||
services().globals.config.log
|
||||
)));
|
||||
},
|
||||
Err(e) => {
|
||||
@@ -381,8 +356,7 @@ pub(super) async fn change_log_level(&self, filter: Option<String>, reset: bool)
|
||||
},
|
||||
};
|
||||
|
||||
match self
|
||||
.services
|
||||
match services()
|
||||
.server
|
||||
.log
|
||||
.reload
|
||||
@@ -402,21 +376,19 @@ pub(super) async fn change_log_level(&self, filter: Option<String>, reset: bool)
|
||||
Ok(RoomMessageEventContent::text_plain("No log level was specified."))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
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() != "```"
|
||||
{
|
||||
pub(super) async fn sign_json(body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
if body.len() < 2 || !body[0].trim().starts_with("```") || body.last().unwrap_or(&"").trim() != "```" {
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
"Expected code block in command body. Add --help for details.",
|
||||
));
|
||||
}
|
||||
|
||||
let string = self.body[1..self.body.len().checked_sub(1).unwrap()].join("\n");
|
||||
let string = body[1..body.len().checked_sub(1).unwrap()].join("\n");
|
||||
match serde_json::from_str(&string) {
|
||||
Ok(mut value) => {
|
||||
ruma::signatures::sign_json(
|
||||
self.services.globals.server_name().as_str(),
|
||||
self.services.globals.keypair(),
|
||||
services().globals.server_name().as_str(),
|
||||
services().globals.keypair(),
|
||||
&mut value,
|
||||
)
|
||||
.expect("our request json is what ruma expects");
|
||||
@@ -427,22 +399,21 @@ pub(super) async fn sign_json(&self) -> Result<RoomMessageEventContent> {
|
||||
}
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
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() != "```"
|
||||
{
|
||||
pub(super) async fn verify_json(body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
if body.len() < 2 || !body[0].trim().starts_with("```") || body.last().unwrap_or(&"").trim() != "```" {
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
"Expected code block in command body. Add --help for details.",
|
||||
));
|
||||
}
|
||||
|
||||
let string = self.body[1..self.body.len().checked_sub(1).unwrap()].join("\n");
|
||||
let string = body[1..body.len().checked_sub(1).unwrap()].join("\n");
|
||||
match serde_json::from_str(&string) {
|
||||
Ok(value) => {
|
||||
let pub_key_map = RwLock::new(BTreeMap::new());
|
||||
|
||||
self.services
|
||||
.server_keys
|
||||
services()
|
||||
.rooms
|
||||
.event_handler
|
||||
.fetch_required_signing_keys([&value], &pub_key_map)
|
||||
.await?;
|
||||
|
||||
@@ -458,22 +429,19 @@ pub(super) async fn verify_json(&self) -> Result<RoomMessageEventContent> {
|
||||
}
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
#[tracing::instrument(skip(self))]
|
||||
pub(super) async fn first_pdu_in_room(&self, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
|
||||
if !self
|
||||
.services
|
||||
#[tracing::instrument(skip(_body))]
|
||||
pub(super) async fn first_pdu_in_room(_body: Vec<&str>, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
|
||||
if !services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.server_in_room(&self.services.globals.config.server_name, &room_id)?
|
||||
.server_in_room(&services().globals.config.server_name, &room_id)?
|
||||
{
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
"We are not participating in the room / we don't know about the room ID.",
|
||||
));
|
||||
}
|
||||
|
||||
let first_pdu = self
|
||||
.services
|
||||
let first_pdu = services()
|
||||
.rooms
|
||||
.timeline
|
||||
.first_pdu_in_room(&room_id)?
|
||||
@@ -482,22 +450,19 @@ pub(super) async fn first_pdu_in_room(&self, room_id: Box<RoomId>) -> Result<Roo
|
||||
Ok(RoomMessageEventContent::text_plain(format!("{first_pdu:?}")))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
#[tracing::instrument(skip(self))]
|
||||
pub(super) async fn latest_pdu_in_room(&self, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
|
||||
if !self
|
||||
.services
|
||||
#[tracing::instrument(skip(_body))]
|
||||
pub(super) async fn latest_pdu_in_room(_body: Vec<&str>, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
|
||||
if !services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.server_in_room(&self.services.globals.config.server_name, &room_id)?
|
||||
.server_in_room(&services().globals.config.server_name, &room_id)?
|
||||
{
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
"We are not participating in the room / we don't know about the room ID.",
|
||||
));
|
||||
}
|
||||
|
||||
let latest_pdu = self
|
||||
.services
|
||||
let latest_pdu = services()
|
||||
.rooms
|
||||
.timeline
|
||||
.latest_pdu_in_room(&room_id)?
|
||||
@@ -506,36 +471,32 @@ pub(super) async fn latest_pdu_in_room(&self, room_id: Box<RoomId>) -> Result<Ro
|
||||
Ok(RoomMessageEventContent::text_plain(format!("{latest_pdu:?}")))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
#[tracing::instrument(skip(self))]
|
||||
#[tracing::instrument(skip(_body))]
|
||||
pub(super) async fn force_set_room_state_from_server(
|
||||
&self, room_id: Box<RoomId>, server_name: Box<ServerName>,
|
||||
_body: Vec<&str>, server_name: Box<ServerName>, room_id: Box<RoomId>,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
if !self
|
||||
.services
|
||||
if !services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.server_in_room(&self.services.globals.config.server_name, &room_id)?
|
||||
.server_in_room(&services().globals.config.server_name, &room_id)?
|
||||
{
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
"We are not participating in the room / we don't know about the room ID.",
|
||||
));
|
||||
}
|
||||
|
||||
let first_pdu = self
|
||||
.services
|
||||
let first_pdu = services()
|
||||
.rooms
|
||||
.timeline
|
||||
.latest_pdu_in_room(&room_id)?
|
||||
.ok_or_else(|| Error::bad_database("Failed to find the latest PDU in database"))?;
|
||||
|
||||
let room_version = self.services.rooms.state.get_room_version(&room_id)?;
|
||||
let room_version = services().rooms.state.get_room_version(&room_id)?;
|
||||
|
||||
let mut state: HashMap<u64, Arc<EventId>> = HashMap::new();
|
||||
let pub_key_map = RwLock::new(BTreeMap::new());
|
||||
|
||||
let remote_state_response = self
|
||||
.services
|
||||
let remote_state_response = services()
|
||||
.sending
|
||||
.send_federation_request(
|
||||
&server_name,
|
||||
@@ -549,7 +510,7 @@ pub(super) async fn force_set_room_state_from_server(
|
||||
let mut events = Vec::with_capacity(remote_state_response.pdus.len());
|
||||
|
||||
for pdu in remote_state_response.pdus.clone() {
|
||||
events.push(match self.services.rooms.event_handler.parse_incoming_pdu(&pdu) {
|
||||
events.push(match parse_incoming_pdu(&pdu) {
|
||||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
warn!("Could not parse PDU, ignoring: {e}");
|
||||
@@ -559,8 +520,9 @@ pub(super) async fn force_set_room_state_from_server(
|
||||
}
|
||||
|
||||
info!("Fetching required signing keys for all the state events we got");
|
||||
self.services
|
||||
.server_keys
|
||||
services()
|
||||
.rooms
|
||||
.event_handler
|
||||
.fetch_required_signing_keys(events.iter().map(|(_event_id, event, _room_id)| event), &pub_key_map)
|
||||
.await?;
|
||||
|
||||
@@ -568,24 +530,23 @@ pub(super) async fn force_set_room_state_from_server(
|
||||
for result in remote_state_response
|
||||
.pdus
|
||||
.iter()
|
||||
.map(|pdu| validate_and_add_event_id(self.services, pdu, &room_version, &pub_key_map))
|
||||
.map(|pdu| validate_and_add_event_id(pdu, &room_version, &pub_key_map))
|
||||
{
|
||||
let Ok((event_id, value)) = result.await else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let pdu = PduEvent::from_id_val(&event_id, value.clone()).map_err(|e| {
|
||||
debug_error!("Invalid PDU in fetching remote room state PDUs response: {value:#?}");
|
||||
err!(BadServerResponse(debug_error!("Invalid PDU in send_join response: {e:?}")))
|
||||
warn!("Invalid PDU in fetching remote room state PDUs response: {} {:?}", e, value);
|
||||
Error::BadServerResponse("Invalid PDU in send_join response.")
|
||||
})?;
|
||||
|
||||
self.services
|
||||
services()
|
||||
.rooms
|
||||
.outlier
|
||||
.add_pdu_outlier(&event_id, &value)?;
|
||||
if let Some(state_key) = &pdu.state_key {
|
||||
let shortstatekey = self
|
||||
.services
|
||||
let shortstatekey = services()
|
||||
.rooms
|
||||
.short
|
||||
.get_or_create_shortstatekey(&pdu.kind.to_string().into(), state_key)?;
|
||||
@@ -597,34 +558,32 @@ pub(super) async fn force_set_room_state_from_server(
|
||||
for result in remote_state_response
|
||||
.auth_chain
|
||||
.iter()
|
||||
.map(|pdu| validate_and_add_event_id(self.services, pdu, &room_version, &pub_key_map))
|
||||
.map(|pdu| validate_and_add_event_id(pdu, &room_version, &pub_key_map))
|
||||
{
|
||||
let Ok((event_id, value)) = result.await else {
|
||||
continue;
|
||||
};
|
||||
|
||||
self.services
|
||||
services()
|
||||
.rooms
|
||||
.outlier
|
||||
.add_pdu_outlier(&event_id, &value)?;
|
||||
}
|
||||
|
||||
let new_room_state = self
|
||||
.services
|
||||
let new_room_state = services()
|
||||
.rooms
|
||||
.event_handler
|
||||
.resolve_state(room_id.clone().as_ref(), &room_version, state)
|
||||
.await?;
|
||||
|
||||
info!("Forcing new room state");
|
||||
let (short_state_hash, new, removed) = self
|
||||
.services
|
||||
let (short_state_hash, new, removed) = services()
|
||||
.rooms
|
||||
.state_compressor
|
||||
.save_state(room_id.clone().as_ref(), new_room_state)?;
|
||||
|
||||
let state_lock = self.services.rooms.state.mutex.lock(&room_id).await;
|
||||
self.services
|
||||
let state_lock = services().rooms.state.mutex.lock(&room_id).await;
|
||||
services()
|
||||
.rooms
|
||||
.state
|
||||
.force_state(room_id.clone().as_ref(), short_state_hash, new, removed, &state_lock)
|
||||
@@ -634,10 +593,7 @@ 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 \
|
||||
m.room.member state"
|
||||
);
|
||||
self.services
|
||||
.rooms
|
||||
.state_cache
|
||||
.update_joined_count(&room_id)?;
|
||||
services().rooms.state_cache.update_joined_count(&room_id)?;
|
||||
|
||||
drop(state_lock);
|
||||
|
||||
@@ -646,63 +602,16 @@ pub(super) async fn force_set_room_state_from_server(
|
||||
))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn get_signing_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 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,
|
||||
_body: Vec<&str>, server_name: Box<ServerName>, no_cache: bool,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
if !self.services.globals.config.allow_federation {
|
||||
if !services().globals.config.allow_federation {
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
"Federation is disabled on this homeserver.",
|
||||
));
|
||||
}
|
||||
|
||||
if server_name == self.services.globals.config.server_name {
|
||||
if server_name == services().globals.config.server_name {
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
"Not allowed to send federation requests to ourselves. Please use `get-pdu` for fetching local PDUs.",
|
||||
));
|
||||
@@ -714,16 +623,12 @@ pub(super) async fn resolve_true_destination(
|
||||
&& matches!(data.span_name(), "actual" | "well-known" | "srv")
|
||||
};
|
||||
|
||||
let state = &self.services.server.log.capture;
|
||||
let state = &services().server.log.capture;
|
||||
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 = self
|
||||
.services
|
||||
.resolver
|
||||
.resolve_actual_dest(&server_name, !no_cache)
|
||||
.await?;
|
||||
let actual = resolve_actual_dest(&server_name, !no_cache).await?;
|
||||
drop(capture_scope);
|
||||
|
||||
let msg = format!(
|
||||
@@ -735,26 +640,23 @@ pub(super) async fn resolve_true_destination(
|
||||
Ok(RoomMessageEventContent::text_markdown(msg))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn memory_stats(&self) -> Result<RoomMessageEventContent> {
|
||||
#[must_use]
|
||||
pub(super) fn memory_stats() -> RoomMessageEventContent {
|
||||
let html_body = conduit::alloc::memory_stats();
|
||||
|
||||
if html_body.is_none() {
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
"malloc stats are not supported on your compiled malloc.",
|
||||
));
|
||||
return RoomMessageEventContent::text_plain("malloc stats are not supported on your compiled malloc.");
|
||||
}
|
||||
|
||||
Ok(RoomMessageEventContent::text_html(
|
||||
RoomMessageEventContent::text_html(
|
||||
"This command's output can only be viewed by clients that render HTML.".to_owned(),
|
||||
html_body.expect("string result"),
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(tokio_unstable)]
|
||||
#[admin_command]
|
||||
pub(super) async fn runtime_metrics(&self) -> Result<RoomMessageEventContent> {
|
||||
let out = self.services.server.metrics.runtime_metrics().map_or_else(
|
||||
pub(super) async fn runtime_metrics(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
let out = services().server.metrics.runtime_metrics().map_or_else(
|
||||
|| "Runtime metrics are not available.".to_owned(),
|
||||
|metrics| format!("```rs\n{metrics:#?}\n```"),
|
||||
);
|
||||
@@ -763,17 +665,15 @@ pub(super) async fn runtime_metrics(&self) -> Result<RoomMessageEventContent> {
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_unstable))]
|
||||
#[admin_command]
|
||||
pub(super) async fn runtime_metrics(&self) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn runtime_metrics(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
Ok(RoomMessageEventContent::text_markdown(
|
||||
"Runtime metrics require building with `tokio_unstable`.",
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(tokio_unstable)]
|
||||
#[admin_command]
|
||||
pub(super) async fn runtime_interval(&self) -> Result<RoomMessageEventContent> {
|
||||
let out = self.services.server.metrics.runtime_interval().map_or_else(
|
||||
pub(super) async fn runtime_interval(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
let out = services().server.metrics.runtime_interval().map_or_else(
|
||||
|| "Runtime metrics are not available.".to_owned(),
|
||||
|metrics| format!("```rs\n{metrics:#?}\n```"),
|
||||
);
|
||||
@@ -782,40 +682,8 @@ pub(super) async fn runtime_interval(&self) -> Result<RoomMessageEventContent> {
|
||||
}
|
||||
|
||||
#[cfg(not(tokio_unstable))]
|
||||
#[admin_command]
|
||||
pub(super) async fn runtime_interval(&self) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn runtime_interval(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
Ok(RoomMessageEventContent::text_markdown(
|
||||
"Runtime metrics require building with `tokio_unstable`.",
|
||||
))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn time(&self) -> Result<RoomMessageEventContent> {
|
||||
let now = SystemTime::now();
|
||||
Ok(RoomMessageEventContent::text_markdown(utils::time::format(now, "%+")))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn list_dependencies(&self, names: bool) -> Result<RoomMessageEventContent> {
|
||||
if names {
|
||||
let out = info::cargo::dependencies_names().join(" ");
|
||||
return Ok(RoomMessageEventContent::notice_markdown(out));
|
||||
}
|
||||
|
||||
let deps = info::cargo::dependencies();
|
||||
let mut out = String::new();
|
||||
writeln!(out, "| name | version | features |")?;
|
||||
writeln!(out, "| ---- | ------- | -------- |")?;
|
||||
for (name, dep) in deps {
|
||||
let version = dep.try_req().unwrap_or("*");
|
||||
let feats = dep.req_features();
|
||||
let feats = if !feats.is_empty() {
|
||||
feats.join(" ")
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
writeln!(out, "{name} | {version} | {feats}")?;
|
||||
}
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(out))
|
||||
}
|
||||
|
||||
+59
-23
@@ -3,13 +3,13 @@ pub(crate) mod tester;
|
||||
|
||||
use clap::Subcommand;
|
||||
use conduit::Result;
|
||||
use ruma::{EventId, OwnedRoomOrAliasId, RoomId, ServerName};
|
||||
use ruma::{events::room::message::RoomMessageEventContent, EventId, OwnedRoomOrAliasId, RoomId, ServerName};
|
||||
use tester::TesterCommand;
|
||||
|
||||
use self::tester::TesterCommand;
|
||||
use crate::admin_command_dispatch;
|
||||
use self::commands::*;
|
||||
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
#[derive(Subcommand)]
|
||||
pub(super) enum DebugCommand {
|
||||
/// - Echo input of admin command
|
||||
Echo {
|
||||
@@ -76,14 +76,6 @@ pub(super) enum DebugCommand {
|
||||
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
|
||||
/// `/_matrix/federation/v1/version` endpoint and measures the latency it
|
||||
/// took for the server to respond
|
||||
@@ -175,17 +167,61 @@ pub(super) enum DebugCommand {
|
||||
/// invocation.
|
||||
RuntimeInterval,
|
||||
|
||||
/// - Print the current time
|
||||
Time,
|
||||
|
||||
/// - List dependencies
|
||||
ListDependencies {
|
||||
#[arg(short, long)]
|
||||
names: bool,
|
||||
},
|
||||
|
||||
/// - Developer test stubs
|
||||
#[command(subcommand)]
|
||||
#[allow(non_snake_case)]
|
||||
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,29 +1,34 @@
|
||||
use ruma::events::room::message::RoomMessageEventContent;
|
||||
|
||||
use crate::{admin_command, admin_command_dispatch, Result};
|
||||
use crate::Result;
|
||||
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, clap::Subcommand)]
|
||||
#[derive(clap::Subcommand)]
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
pub(crate) enum TesterCommand {
|
||||
Tester,
|
||||
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)]
|
||||
#[rustfmt::skip]
|
||||
#[allow(unused_variables)]
|
||||
#[admin_command]
|
||||
async fn tester(&self) -> Result<RoomMessageEventContent> {
|
||||
async fn tester(body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
|
||||
Ok(RoomMessageEventContent::notice_plain("completed"))
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
#[rustfmt::skip]
|
||||
#[admin_command]
|
||||
async fn timer(&self) -> Result<RoomMessageEventContent> {
|
||||
async fn timer(body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
let started = std::time::Instant::now();
|
||||
timed(self.body);
|
||||
timed(&body);
|
||||
|
||||
let elapsed = started.elapsed();
|
||||
Ok(RoomMessageEventContent::notice_plain(format!("completed in {elapsed:#?}")))
|
||||
|
||||
@@ -1,26 +1,21 @@
|
||||
use std::fmt::Write;
|
||||
|
||||
use conduit::Result;
|
||||
use ruma::{events::room::message::RoomMessageEventContent, OwnedRoomId, RoomId, ServerName, UserId};
|
||||
|
||||
use crate::{admin_command, escape_html, get_room_info};
|
||||
use crate::{escape_html, get_room_info, services, Result};
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn disable_room(&self, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
|
||||
self.services.rooms.metadata.disable_room(&room_id, true)?;
|
||||
pub(super) async fn disable_room(_body: Vec<&str>, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
|
||||
services().rooms.metadata.disable_room(&room_id, true)?;
|
||||
Ok(RoomMessageEventContent::text_plain("Room disabled."))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn enable_room(&self, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
|
||||
self.services.rooms.metadata.disable_room(&room_id, false)?;
|
||||
pub(super) async fn enable_room(_body: Vec<&str>, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
|
||||
services().rooms.metadata.disable_room(&room_id, false)?;
|
||||
Ok(RoomMessageEventContent::text_plain("Room enabled."))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn incoming_federation(&self) -> Result<RoomMessageEventContent> {
|
||||
let map = self
|
||||
.services
|
||||
pub(super) async fn incoming_federation(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
let map = services()
|
||||
.rooms
|
||||
.event_handler
|
||||
.federation_handletime
|
||||
@@ -36,10 +31,11 @@ pub(super) async fn incoming_federation(&self) -> Result<RoomMessageEventContent
|
||||
Ok(RoomMessageEventContent::text_plain(&msg))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn fetch_support_well_known(&self, server_name: Box<ServerName>) -> Result<RoomMessageEventContent> {
|
||||
let response = self
|
||||
.services
|
||||
pub(super) async fn fetch_support_well_known(
|
||||
_body: Vec<&str>, server_name: Box<ServerName>,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
let response = services()
|
||||
.globals
|
||||
.client
|
||||
.default
|
||||
.get(format!("https://{server_name}/.well-known/matrix/support"))
|
||||
@@ -77,27 +73,25 @@ pub(super) async fn fetch_support_well_known(&self, server_name: Box<ServerName>
|
||||
)))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
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 {
|
||||
pub(super) async fn remote_user_in_rooms(_body: Vec<&str>, user_id: Box<UserId>) -> Result<RoomMessageEventContent> {
|
||||
if user_id.server_name() == services().globals.config.server_name {
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
"User belongs to our server, please use `list-joined-rooms` user admin command instead.",
|
||||
));
|
||||
}
|
||||
|
||||
if !self.services.users.exists(&user_id)? {
|
||||
if !services().users.exists(&user_id)? {
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
"Remote user does not exist in our database.",
|
||||
));
|
||||
}
|
||||
|
||||
let mut rooms: Vec<(OwnedRoomId, u64, String)> = self
|
||||
.services
|
||||
let mut rooms: Vec<(OwnedRoomId, u64, String)> = services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.rooms_joined(&user_id)
|
||||
.filter_map(Result::ok)
|
||||
.map(|room_id| get_room_info(self.services, &room_id))
|
||||
.map(|room_id| get_room_info(&room_id))
|
||||
.collect();
|
||||
|
||||
if rooms.is_empty() {
|
||||
|
||||
@@ -2,12 +2,12 @@ mod commands;
|
||||
|
||||
use clap::Subcommand;
|
||||
use conduit::Result;
|
||||
use ruma::{RoomId, ServerName, UserId};
|
||||
use ruma::{events::room::message::RoomMessageEventContent, RoomId, ServerName, UserId};
|
||||
|
||||
use crate::admin_command_dispatch;
|
||||
use self::commands::*;
|
||||
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
#[derive(Subcommand)]
|
||||
pub(super) enum FederationCommand {
|
||||
/// - List all rooms we are currently handling an incoming pdu from
|
||||
IncomingFederation,
|
||||
@@ -40,3 +40,21 @@ pub(super) enum FederationCommand {
|
||||
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?,
|
||||
})
|
||||
}
|
||||
|
||||
+93
-64
@@ -1,7 +1,7 @@
|
||||
use std::{panic::AssertUnwindSafe, sync::Arc, time::Instant};
|
||||
use std::{panic::AssertUnwindSafe, time::Instant};
|
||||
|
||||
use clap::{CommandFactory, Parser};
|
||||
use conduit::{checked, error, trace, utils::string::common_prefix, Error, Result};
|
||||
use conduit::{error, trace, Error};
|
||||
use futures_util::future::FutureExt;
|
||||
use ruma::{
|
||||
events::{
|
||||
@@ -10,37 +10,82 @@ use ruma::{
|
||||
},
|
||||
OwnedEventId,
|
||||
};
|
||||
use service::{
|
||||
admin::{CommandInput, CommandOutput, CommandResult, HandlerResult},
|
||||
Services,
|
||||
|
||||
extern crate conduit_service as service;
|
||||
|
||||
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 crate::{admin, admin::AdminCommand, Command};
|
||||
#[derive(Parser)]
|
||||
#[command(name = "admin", version = env!("CARGO_PKG_VERSION"))]
|
||||
pub(crate) enum AdminCommand {
|
||||
#[command(subcommand)]
|
||||
/// - Commands for managing appservices
|
||||
Appservices(AppserviceCommand),
|
||||
|
||||
#[must_use]
|
||||
pub(super) fn complete(line: &str) -> String { complete_command(AdminCommand::command(), line) }
|
||||
#[command(subcommand)]
|
||||
/// - Commands for managing local users
|
||||
Users(UserCommand),
|
||||
|
||||
#[must_use]
|
||||
pub(super) fn handle(services: Arc<Services>, command: CommandInput) -> HandlerResult {
|
||||
Box::pin(handle_command(services, command))
|
||||
#[command(subcommand)]
|
||||
/// - Commands for managing rooms
|
||||
Rooms(RoomCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - Commands for managing federation
|
||||
Federation(FederationCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - Commands for managing the server
|
||||
Server(ServerCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - Commands for managing media
|
||||
Media(MediaCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - Commands for checking integrity
|
||||
Check(CheckCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - Commands for debugging things
|
||||
Debug(DebugCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - Low-level queries for database getters and iterators
|
||||
Query(QueryCommand),
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub(crate) fn handle(command: Command) -> HandlerResult { Box::pin(handle_command(command)) }
|
||||
|
||||
#[must_use]
|
||||
pub(crate) fn complete(line: &str) -> String { complete_admin_command(AdminCommand::command(), line) }
|
||||
|
||||
#[tracing::instrument(skip_all, name = "admin")]
|
||||
async fn handle_command(services: Arc<Services>, command: CommandInput) -> CommandResult {
|
||||
AssertUnwindSafe(Box::pin(process_command(services, &command)))
|
||||
async fn handle_command(command: Command) -> CommandResult {
|
||||
AssertUnwindSafe(process_command(&command))
|
||||
.catch_unwind()
|
||||
.await
|
||||
.map_err(Error::from_panic)
|
||||
.or_else(|error| handle_panic(&error, command))
|
||||
}
|
||||
|
||||
async fn process_command(services: Arc<Services>, command: &CommandInput) -> CommandOutput {
|
||||
process(services, &command.command)
|
||||
async fn process_command(command: &Command) -> CommandOutput {
|
||||
process_admin_message(&command.command)
|
||||
.await
|
||||
.and_then(|content| reply(content, command.reply_id.clone()))
|
||||
}
|
||||
|
||||
fn handle_panic(error: &Error, command: CommandInput) -> CommandResult {
|
||||
fn handle_panic(error: &Error, command: Command) -> CommandResult {
|
||||
let link = "Please submit a [bug report](https://github.com/girlbossceo/conduwuit/issues/new). 🥺";
|
||||
let msg = format!("Panic occurred while processing command:\n```\n{error:#?}\n```\n{link}");
|
||||
let content = RoomMessageEventContent::notice_markdown(msg);
|
||||
@@ -59,28 +104,21 @@ fn reply(mut content: RoomMessageEventContent, reply_id: Option<OwnedEventId>) -
|
||||
}
|
||||
|
||||
// Parse and process a message from the admin room
|
||||
async fn process(services: Arc<Services>, msg: &str) -> CommandOutput {
|
||||
let lines = msg.lines().filter(|l| !l.trim().is_empty());
|
||||
let command = lines
|
||||
.clone()
|
||||
.next()
|
||||
.expect("each string has at least one line");
|
||||
let (parsed, body) = match parse_command(command) {
|
||||
async fn process_admin_message(msg: &str) -> CommandOutput {
|
||||
let mut lines = msg.lines().filter(|l| !l.trim().is_empty());
|
||||
let command = lines.next().expect("each string has at least one line");
|
||||
let body = lines.collect::<Vec<_>>();
|
||||
let parsed = match parse_admin_command(command) {
|
||||
Ok(parsed) => parsed,
|
||||
Err(error) => {
|
||||
let server_name = services.globals.server_name();
|
||||
let server_name = services().globals.server_name();
|
||||
let message = error.replace("server.name", server_name.as_str());
|
||||
return Some(RoomMessageEventContent::notice_markdown(message));
|
||||
},
|
||||
};
|
||||
|
||||
let body = parse_body(AdminCommand::command(), &body, lines.skip(1).collect()).expect("trailing body parsed");
|
||||
let context = Command {
|
||||
services: &services,
|
||||
body: &body,
|
||||
};
|
||||
let timer = Instant::now();
|
||||
let result = Box::pin(admin::process(parsed, &context)).await;
|
||||
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 {
|
||||
@@ -91,41 +129,31 @@ async fn process(services: Arc<Services>, msg: &str) -> CommandOutput {
|
||||
}
|
||||
}
|
||||
|
||||
#[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
|
||||
fn parse_command(command_line: &str) -> Result<(AdminCommand, Vec<String>), String> {
|
||||
let argv = parse_line(command_line);
|
||||
let com = AdminCommand::try_parse_from(&argv).map_err(|error| error.to_string())?;
|
||||
Ok((com, argv))
|
||||
fn parse_admin_command(command_line: &str) -> Result<AdminCommand, String> {
|
||||
let argv = parse_command_line(command_line);
|
||||
AdminCommand::try_parse_from(argv).map_err(|error| error.to_string())
|
||||
}
|
||||
|
||||
fn parse_body<'a>(mut cmd: clap::Command, body: &'a [String], lines: Vec<&'a str>) -> Result<Vec<&'a str>> {
|
||||
let mut start = 1;
|
||||
'token: for token in body.iter().skip(1) {
|
||||
let cmd_ = cmd.clone();
|
||||
for sub in cmd_.get_subcommands() {
|
||||
if sub.get_name() == *token {
|
||||
start = checked!(start + 1)?;
|
||||
cmd = sub.clone();
|
||||
continue 'token;
|
||||
}
|
||||
}
|
||||
|
||||
// positional arguments have to be skipped too
|
||||
let num_posargs = cmd_.get_positionals().count();
|
||||
start = checked!(start + num_posargs)?;
|
||||
break;
|
||||
}
|
||||
|
||||
Ok(body
|
||||
.iter()
|
||||
.skip(start)
|
||||
.map(String::as_str)
|
||||
.chain(lines)
|
||||
.collect::<Vec<&'a str>>())
|
||||
}
|
||||
|
||||
fn complete_command(mut cmd: clap::Command, line: &str) -> String {
|
||||
let argv = parse_line(line);
|
||||
fn complete_admin_command(mut cmd: clap::Command, line: &str) -> String {
|
||||
let argv = parse_command_line(line);
|
||||
let mut ret = Vec::<String>::with_capacity(argv.len().saturating_add(1));
|
||||
|
||||
'token: for token in argv.into_iter().skip(1) {
|
||||
@@ -168,7 +196,7 @@ fn complete_command(mut cmd: clap::Command, line: &str) -> String {
|
||||
}
|
||||
|
||||
// Parse chat messages from the admin room into an AdminCommand object
|
||||
fn parse_line(command_line: &str) -> Vec<String> {
|
||||
fn parse_command_line(command_line: &str) -> Vec<String> {
|
||||
let mut argv = command_line
|
||||
.split_whitespace()
|
||||
.map(str::to_owned)
|
||||
@@ -180,7 +208,8 @@ fn parse_line(command_line: &str) -> Vec<String> {
|
||||
}
|
||||
|
||||
// First indice has to be "admin" but for console convenience we add it here
|
||||
if !argv.is_empty() && !argv[0].ends_with("admin") && !argv[0].starts_with('@') {
|
||||
let server_user = services().globals.server_user.as_str();
|
||||
if !argv.is_empty() && !argv[0].ends_with("admin") && !argv[0].starts_with(server_user) {
|
||||
argv.insert(0, "admin".to_owned());
|
||||
}
|
||||
|
||||
|
||||
+17
-20
@@ -1,11 +1,11 @@
|
||||
use conduit::{debug, info, Result};
|
||||
use conduit::Result;
|
||||
use ruma::{events::room::message::RoomMessageEventContent, EventId, MxcUri};
|
||||
use tracing::{debug, info};
|
||||
|
||||
use crate::admin_command;
|
||||
use crate::services;
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn delete(
|
||||
&self, mxc: Option<Box<MxcUri>>, event_id: Option<Box<EventId>>,
|
||||
_body: Vec<&str>, mxc: Option<Box<MxcUri>>, event_id: Option<Box<EventId>>,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
if event_id.is_some() && mxc.is_some() {
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
@@ -15,7 +15,7 @@ pub(super) async fn delete(
|
||||
|
||||
if let Some(mxc) = mxc {
|
||||
debug!("Got MXC URL: {mxc}");
|
||||
self.services.media.delete(mxc.as_ref()).await?;
|
||||
services().media.delete(mxc.as_ref()).await?;
|
||||
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
"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;
|
||||
|
||||
// parsing the PDU for any MXC URLs begins here
|
||||
if let Some(event_json) = self.services.rooms.timeline.get_pdu_json(&event_id)? {
|
||||
if let Some(event_json) = services().rooms.timeline.get_pdu_json(&event_id)? {
|
||||
if let Some(content_key) = event_json.get("content") {
|
||||
debug!("Event ID has \"content\".");
|
||||
let content_obj = content_key.as_object();
|
||||
@@ -123,7 +123,7 @@ pub(super) async fn delete(
|
||||
}
|
||||
|
||||
for mxc_url in mxc_urls {
|
||||
self.services.media.delete(&mxc_url).await?;
|
||||
services().media.delete(&mxc_url).await?;
|
||||
mxc_deletion_count = mxc_deletion_count.saturating_add(1);
|
||||
}
|
||||
|
||||
@@ -138,26 +138,23 @@ pub(super) async fn delete(
|
||||
))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
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() != "```"
|
||||
{
|
||||
pub(super) async fn delete_list(body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
if body.len() < 2 || !body[0].trim().starts_with("```") || body.last().unwrap_or(&"").trim() != "```" {
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
"Expected code block in command body. Add --help for details.",
|
||||
));
|
||||
}
|
||||
|
||||
let mxc_list = self
|
||||
.body
|
||||
.to_vec()
|
||||
.drain(1..self.body.len().checked_sub(1).unwrap())
|
||||
let mxc_list = body
|
||||
.clone()
|
||||
.drain(1..body.len().checked_sub(1).unwrap())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut mxc_deletion_count: usize = 0;
|
||||
|
||||
for mxc in mxc_list {
|
||||
debug!("Deleting MXC {mxc} in bulk");
|
||||
self.services.media.delete(mxc).await?;
|
||||
services().media.delete(mxc).await?;
|
||||
mxc_deletion_count = mxc_deletion_count
|
||||
.checked_add(1)
|
||||
.expect("mxc_deletion_count should not get this high");
|
||||
@@ -168,10 +165,10 @@ pub(super) async fn delete_list(&self) -> Result<RoomMessageEventContent> {
|
||||
)))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn delete_past_remote_media(&self, duration: String, force: bool) -> Result<RoomMessageEventContent> {
|
||||
let deleted_count = self
|
||||
.services
|
||||
pub(super) async fn delete_past_remote_media(
|
||||
_body: Vec<&str>, duration: String, force: bool,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
let deleted_count = services()
|
||||
.media
|
||||
.delete_all_remote_media_at_after_time(duration, force)
|
||||
.await?;
|
||||
|
||||
+18
-4
@@ -2,12 +2,12 @@ mod commands;
|
||||
|
||||
use clap::Subcommand;
|
||||
use conduit::Result;
|
||||
use ruma::{EventId, MxcUri};
|
||||
use ruma::{events::room::message::RoomMessageEventContent, EventId, MxcUri};
|
||||
|
||||
use crate::admin_command_dispatch;
|
||||
use self::commands::*;
|
||||
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
#[derive(Subcommand)]
|
||||
pub(super) enum MediaCommand {
|
||||
/// - Deletes a single media file from our database and on the filesystem
|
||||
/// via a single MXC URL
|
||||
@@ -37,3 +37,17 @@ pub(super) enum MediaCommand {
|
||||
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?,
|
||||
})
|
||||
}
|
||||
|
||||
+21
-22
@@ -1,55 +1,54 @@
|
||||
#![recursion_limit = "192"]
|
||||
#![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 check;
|
||||
pub(crate) mod debug;
|
||||
pub(crate) mod federation;
|
||||
pub(crate) mod handler;
|
||||
pub(crate) mod media;
|
||||
pub(crate) mod query;
|
||||
pub(crate) mod room;
|
||||
pub(crate) mod server;
|
||||
mod tests;
|
||||
pub(crate) mod user;
|
||||
pub(crate) mod utils;
|
||||
|
||||
extern crate conduit_api as api;
|
||||
extern crate conduit_core as conduit;
|
||||
extern crate conduit_service as service;
|
||||
|
||||
pub(crate) use conduit::Result;
|
||||
pub(crate) use conduit_macros::{admin_command, admin_command_dispatch};
|
||||
pub(crate) use conduit::{mod_ctor, mod_dtor, Result};
|
||||
pub(crate) use service::{services, user_is_local};
|
||||
|
||||
pub(crate) use crate::{
|
||||
command::Command,
|
||||
handler::Service,
|
||||
utils::{escape_html, get_room_info},
|
||||
};
|
||||
|
||||
pub(crate) const PAGE_SIZE: usize = 100;
|
||||
|
||||
conduit::mod_ctor! {}
|
||||
conduit::mod_dtor! {}
|
||||
conduit::rustc_flags_capture! {}
|
||||
mod_ctor! {}
|
||||
mod_dtor! {}
|
||||
|
||||
/// Install the admin command handler
|
||||
pub async fn init(admin_service: &service::admin::Service) {
|
||||
_ = admin_service
|
||||
pub async fn init() {
|
||||
_ = services()
|
||||
.admin
|
||||
.complete
|
||||
.write()
|
||||
.expect("locked for writing")
|
||||
.insert(handler::complete);
|
||||
_ = admin_service.handle.write().await.insert(handler::handle);
|
||||
_ = services()
|
||||
.admin
|
||||
.handle
|
||||
.write()
|
||||
.await
|
||||
.insert(handler::handle);
|
||||
}
|
||||
|
||||
/// Uninstall the admin command handler
|
||||
pub async fn fini(admin_service: &service::admin::Service) {
|
||||
_ = admin_service.handle.write().await.take();
|
||||
_ = admin_service
|
||||
pub async fn fini() {
|
||||
_ = services().admin.handle.write().await.take();
|
||||
_ = services()
|
||||
.admin
|
||||
.complete
|
||||
.write()
|
||||
.expect("locked for writing")
|
||||
|
||||
@@ -1,48 +1,18 @@
|
||||
use clap::Subcommand;
|
||||
use conduit::Result;
|
||||
use ruma::{
|
||||
events::{room::message::RoomMessageEventContent, RoomAccountDataEventType},
|
||||
RoomId, UserId,
|
||||
};
|
||||
use ruma::events::room::message::RoomMessageEventContent;
|
||||
|
||||
use crate::Command;
|
||||
|
||||
#[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>>,
|
||||
},
|
||||
}
|
||||
use super::AccountData;
|
||||
use crate::{services, Result};
|
||||
|
||||
/// All the getters and iterators from src/database/key_value/account_data.rs
|
||||
pub(super) async fn process(subcommand: AccountDataCommand, context: &Command<'_>) -> Result<RoomMessageEventContent> {
|
||||
let services = context.services;
|
||||
|
||||
pub(super) async fn account_data(subcommand: AccountData) -> Result<RoomMessageEventContent> {
|
||||
match subcommand {
|
||||
AccountDataCommand::ChangesSince {
|
||||
AccountData::ChangesSince {
|
||||
user_id,
|
||||
since,
|
||||
room_id,
|
||||
} => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = services
|
||||
let results = services()
|
||||
.account_data
|
||||
.changes_since(room_id.as_deref(), &user_id, since)?;
|
||||
let query_time = timer.elapsed();
|
||||
@@ -51,13 +21,13 @@ pub(super) async fn process(subcommand: AccountDataCommand, context: &Command<'_
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
AccountDataCommand::Get {
|
||||
AccountData::Get {
|
||||
user_id,
|
||||
kind,
|
||||
room_id,
|
||||
} => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = services
|
||||
let results = services()
|
||||
.account_data
|
||||
.get(room_id.as_deref(), &user_id, kind)?;
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
@@ -1,32 +1,16 @@
|
||||
use clap::Subcommand;
|
||||
use conduit::Result;
|
||||
use ruma::events::room::message::RoomMessageEventContent;
|
||||
|
||||
use crate::Command;
|
||||
|
||||
#[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,
|
||||
}
|
||||
use super::Appservice;
|
||||
use crate::{services, Result};
|
||||
|
||||
/// All the getters and iterators from src/database/key_value/appservice.rs
|
||||
pub(super) async fn process(subcommand: AppserviceCommand, context: &Command<'_>) -> Result<RoomMessageEventContent> {
|
||||
let services = context.services;
|
||||
|
||||
pub(super) async fn appservice(subcommand: Appservice) -> Result<RoomMessageEventContent> {
|
||||
match subcommand {
|
||||
AppserviceCommand::GetRegistration {
|
||||
Appservice::GetRegistration {
|
||||
appservice_id,
|
||||
} => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = services
|
||||
let results = services()
|
||||
.appservice
|
||||
.db
|
||||
.get_registration(appservice_id.as_ref());
|
||||
@@ -36,9 +20,9 @@ pub(super) async fn process(subcommand: AppserviceCommand, context: &Command<'_>
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
AppserviceCommand::All => {
|
||||
Appservice::All => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = services.appservice.all();
|
||||
let results = services().appservice.all();
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
|
||||
+14
-35
@@ -1,73 +1,52 @@
|
||||
use clap::Subcommand;
|
||||
use conduit::Result;
|
||||
use ruma::{events::room::message::RoomMessageEventContent, ServerName};
|
||||
use ruma::events::room::message::RoomMessageEventContent;
|
||||
|
||||
use crate::Command;
|
||||
|
||||
#[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>,
|
||||
},
|
||||
}
|
||||
use super::Globals;
|
||||
use crate::{services, Result};
|
||||
|
||||
/// All the getters and iterators from src/database/key_value/globals.rs
|
||||
pub(super) async fn process(subcommand: GlobalsCommand, context: &Command<'_>) -> Result<RoomMessageEventContent> {
|
||||
let services = context.services;
|
||||
|
||||
pub(super) async fn globals(subcommand: Globals) -> Result<RoomMessageEventContent> {
|
||||
match subcommand {
|
||||
GlobalsCommand::DatabaseVersion => {
|
||||
Globals::DatabaseVersion => {
|
||||
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();
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
GlobalsCommand::CurrentCount => {
|
||||
Globals::CurrentCount => {
|
||||
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();
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
GlobalsCommand::LastCheckForUpdatesId => {
|
||||
Globals::LastCheckForUpdatesId => {
|
||||
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();
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
GlobalsCommand::LoadKeypair => {
|
||||
Globals::LoadKeypair => {
|
||||
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();
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
GlobalsCommand::SigningKeysFor {
|
||||
Globals::SigningKeysFor {
|
||||
origin,
|
||||
} => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = services.globals.db.verify_keys_for(&origin);
|
||||
let results = services().globals.db.signing_keys_for(&origin);
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
|
||||
+278
-15
@@ -10,51 +10,314 @@ mod users;
|
||||
|
||||
use clap::Subcommand;
|
||||
use conduit::Result;
|
||||
use room_state_cache::room_state_cache;
|
||||
use ruma::{
|
||||
events::{room::message::RoomMessageEventContent, RoomAccountDataEventType},
|
||||
OwnedServerName, RoomAliasId, RoomId, ServerName, UserId,
|
||||
};
|
||||
|
||||
use self::{
|
||||
account_data::AccountDataCommand, appservice::AppserviceCommand, globals::GlobalsCommand,
|
||||
presence::PresenceCommand, resolver::ResolverCommand, room_alias::RoomAliasCommand,
|
||||
room_state_cache::RoomStateCacheCommand, sending::SendingCommand, users::UsersCommand,
|
||||
account_data::account_data, appservice::appservice, globals::globals, presence::presence, resolver::resolver,
|
||||
room_alias::room_alias, sending::sending, users::users,
|
||||
};
|
||||
use crate::admin_command_dispatch;
|
||||
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
#[derive(Subcommand)]
|
||||
/// Query tables from database
|
||||
pub(super) enum QueryCommand {
|
||||
/// - account_data.rs iterators and getters
|
||||
#[command(subcommand)]
|
||||
AccountData(AccountDataCommand),
|
||||
AccountData(AccountData),
|
||||
|
||||
/// - appservice.rs iterators and getters
|
||||
#[command(subcommand)]
|
||||
Appservice(AppserviceCommand),
|
||||
Appservice(Appservice),
|
||||
|
||||
/// - presence.rs iterators and getters
|
||||
#[command(subcommand)]
|
||||
Presence(PresenceCommand),
|
||||
Presence(Presence),
|
||||
|
||||
/// - rooms/alias.rs iterators and getters
|
||||
#[command(subcommand)]
|
||||
RoomAlias(RoomAliasCommand),
|
||||
RoomAlias(RoomAlias),
|
||||
|
||||
/// - rooms/state_cache iterators and getters
|
||||
#[command(subcommand)]
|
||||
RoomStateCache(RoomStateCacheCommand),
|
||||
RoomStateCache(RoomStateCache),
|
||||
|
||||
/// - globals.rs iterators and getters
|
||||
#[command(subcommand)]
|
||||
Globals(GlobalsCommand),
|
||||
Globals(Globals),
|
||||
|
||||
/// - sending.rs iterators and getters
|
||||
#[command(subcommand)]
|
||||
Sending(SendingCommand),
|
||||
Sending(Sending),
|
||||
|
||||
/// - users.rs iterators and getters
|
||||
#[command(subcommand)]
|
||||
Users(UsersCommand),
|
||||
Users(Users),
|
||||
|
||||
/// - resolver service
|
||||
#[command(subcommand)]
|
||||
Resolver(ResolverCommand),
|
||||
Resolver(Resolver),
|
||||
}
|
||||
|
||||
#[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,47 +1,27 @@
|
||||
use clap::Subcommand;
|
||||
use conduit::Result;
|
||||
use ruma::{events::room::message::RoomMessageEventContent, UserId};
|
||||
use ruma::events::room::message::RoomMessageEventContent;
|
||||
|
||||
use crate::Command;
|
||||
|
||||
#[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,
|
||||
},
|
||||
}
|
||||
use super::Presence;
|
||||
use crate::{services, Result};
|
||||
|
||||
/// All the getters and iterators in key_value/presence.rs
|
||||
pub(super) async fn process(subcommand: PresenceCommand, context: &Command<'_>) -> Result<RoomMessageEventContent> {
|
||||
let services = context.services;
|
||||
|
||||
pub(super) async fn presence(subcommand: Presence) -> Result<RoomMessageEventContent> {
|
||||
match subcommand {
|
||||
PresenceCommand::GetPresence {
|
||||
Presence::GetPresence {
|
||||
user_id,
|
||||
} => {
|
||||
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();
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
PresenceCommand::PresenceSince {
|
||||
Presence::PresenceSince {
|
||||
since,
|
||||
} => {
|
||||
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 query_time = timer.elapsed();
|
||||
|
||||
|
||||
+20
-27
@@ -1,29 +1,25 @@
|
||||
use std::fmt::Write;
|
||||
|
||||
use clap::Subcommand;
|
||||
use conduit::{utils::time, Result};
|
||||
use ruma::{events::room::message::RoomMessageEventContent, OwnedServerName};
|
||||
|
||||
use crate::{admin_command, admin_command_dispatch};
|
||||
use super::Resolver;
|
||||
use crate::services;
|
||||
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
/// Resolver service and caches
|
||||
pub(crate) enum ResolverCommand {
|
||||
/// Query the destinations cache
|
||||
DestinationsCache {
|
||||
server_name: Option<OwnedServerName>,
|
||||
},
|
||||
|
||||
/// Query the overrides cache
|
||||
OverridesCache {
|
||||
name: Option<String>,
|
||||
},
|
||||
/// All the getters and iterators in key_value/users.rs
|
||||
pub(super) async fn resolver(subcommand: Resolver) -> Result<RoomMessageEventContent> {
|
||||
match subcommand {
|
||||
Resolver::DestinationsCache {
|
||||
server_name,
|
||||
} => destinations_cache(server_name).await,
|
||||
Resolver::OverridesCache {
|
||||
name,
|
||||
} => overrides_cache(name).await,
|
||||
}
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
async fn destinations_cache(&self, server_name: Option<OwnedServerName>) -> Result<RoomMessageEventContent> {
|
||||
use service::resolver::cache::CachedDest;
|
||||
async fn destinations_cache(server_name: Option<OwnedServerName>) -> Result<RoomMessageEventContent> {
|
||||
use service::sending::CachedDest;
|
||||
|
||||
let mut out = String::new();
|
||||
writeln!(out, "| Server Name | Destination | Hostname | Expires |")?;
|
||||
@@ -40,10 +36,9 @@ async fn destinations_cache(&self, server_name: Option<OwnedServerName>) -> Resu
|
||||
writeln!(out, "| {name} | {dest} | {host} | {expire} |").expect("wrote line");
|
||||
};
|
||||
|
||||
let map = self
|
||||
.services
|
||||
let map = services()
|
||||
.globals
|
||||
.resolver
|
||||
.cache
|
||||
.destinations
|
||||
.read()
|
||||
.expect("locked");
|
||||
@@ -57,9 +52,8 @@ async fn destinations_cache(&self, server_name: Option<OwnedServerName>) -> Resu
|
||||
Ok(RoomMessageEventContent::notice_markdown(out))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
async fn overrides_cache(&self, server_name: Option<String>) -> Result<RoomMessageEventContent> {
|
||||
use service::resolver::cache::CachedOverride;
|
||||
async fn overrides_cache(server_name: Option<String>) -> Result<RoomMessageEventContent> {
|
||||
use service::sending::CachedOverride;
|
||||
|
||||
let mut out = String::new();
|
||||
writeln!(out, "| Server Name | IP | Port | Expires |")?;
|
||||
@@ -76,10 +70,9 @@ async fn overrides_cache(&self, server_name: Option<String>) -> Result<RoomMessa
|
||||
writeln!(out, "| {name} | {ips:?} | {port} | {expire} |").expect("wrote line");
|
||||
};
|
||||
|
||||
let map = self
|
||||
.services
|
||||
let map = services()
|
||||
.globals
|
||||
.resolver
|
||||
.cache
|
||||
.overrides
|
||||
.read()
|
||||
.expect("locked");
|
||||
|
||||
@@ -1,48 +1,27 @@
|
||||
use clap::Subcommand;
|
||||
use conduit::Result;
|
||||
use ruma::{events::room::message::RoomMessageEventContent, RoomAliasId, RoomId};
|
||||
use ruma::events::room::message::RoomMessageEventContent;
|
||||
|
||||
use crate::Command;
|
||||
|
||||
#[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,
|
||||
}
|
||||
use super::RoomAlias;
|
||||
use crate::{services, Result};
|
||||
|
||||
/// All the getters and iterators in src/database/key_value/rooms/alias.rs
|
||||
pub(super) async fn process(subcommand: RoomAliasCommand, context: &Command<'_>) -> Result<RoomMessageEventContent> {
|
||||
let services = context.services;
|
||||
|
||||
pub(super) async fn room_alias(subcommand: RoomAlias) -> Result<RoomMessageEventContent> {
|
||||
match subcommand {
|
||||
RoomAliasCommand::ResolveLocalAlias {
|
||||
RoomAlias::ResolveLocalAlias {
|
||||
alias,
|
||||
} => {
|
||||
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();
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
RoomAliasCommand::LocalAliasesForRoom {
|
||||
RoomAlias::LocalAliasesForRoom {
|
||||
room_id,
|
||||
} => {
|
||||
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 query_time = timer.elapsed();
|
||||
|
||||
@@ -50,9 +29,9 @@ pub(super) async fn process(subcommand: RoomAliasCommand, context: &Command<'_>)
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{aliases:#?}\n```"
|
||||
)))
|
||||
},
|
||||
RoomAliasCommand::AllLocalAliases => {
|
||||
RoomAlias::AllLocalAliases => {
|
||||
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 query_time = timer.elapsed();
|
||||
|
||||
|
||||
@@ -1,136 +1,71 @@
|
||||
use clap::Subcommand;
|
||||
use conduit::Result;
|
||||
use ruma::{events::room::message::RoomMessageEventContent, RoomId, ServerName, UserId};
|
||||
use ruma::events::room::message::RoomMessageEventContent;
|
||||
|
||||
use crate::Command;
|
||||
|
||||
#[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;
|
||||
use super::RoomStateCache;
|
||||
use crate::{services, Result};
|
||||
|
||||
pub(super) async fn room_state_cache(subcommand: RoomStateCache) -> Result<RoomMessageEventContent> {
|
||||
match subcommand {
|
||||
RoomStateCacheCommand::ServerInRoom {
|
||||
RoomStateCache::ServerInRoom {
|
||||
server,
|
||||
room_id,
|
||||
} => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let result = services.rooms.state_cache.server_in_room(&server, &room_id);
|
||||
let result = services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.server_in_room(&server, &room_id);
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{result:#?}\n```"
|
||||
)))
|
||||
},
|
||||
RoomStateCacheCommand::RoomServers {
|
||||
RoomStateCache::RoomServers {
|
||||
room_id,
|
||||
} => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results: Result<Vec<_>> = services.rooms.state_cache.room_servers(&room_id).collect();
|
||||
let results: Result<Vec<_>> = services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_servers(&room_id)
|
||||
.collect();
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
RoomStateCacheCommand::ServerRooms {
|
||||
RoomStateCache::ServerRooms {
|
||||
server,
|
||||
} => {
|
||||
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();
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
RoomStateCacheCommand::RoomMembers {
|
||||
RoomStateCache::RoomMembers {
|
||||
room_id,
|
||||
} => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results: Result<Vec<_>> = services.rooms.state_cache.room_members(&room_id).collect();
|
||||
let results: Result<Vec<_>> = services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_members(&room_id)
|
||||
.collect();
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
RoomStateCacheCommand::LocalUsersInRoom {
|
||||
RoomStateCache::LocalUsersInRoom {
|
||||
room_id,
|
||||
} => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results: Vec<_> = services
|
||||
let results: Vec<_> = services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.local_users_in_room(&room_id)
|
||||
@@ -141,11 +76,11 @@ pub(super) async fn process(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
RoomStateCacheCommand::ActiveLocalUsersInRoom {
|
||||
RoomStateCache::ActiveLocalUsersInRoom {
|
||||
room_id,
|
||||
} => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results: Vec<_> = services
|
||||
let results: Vec<_> = services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.active_local_users_in_room(&room_id)
|
||||
@@ -156,33 +91,33 @@ pub(super) async fn process(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
RoomStateCacheCommand::RoomJoinedCount {
|
||||
RoomStateCache::RoomJoinedCount {
|
||||
room_id,
|
||||
} => {
|
||||
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();
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
RoomStateCacheCommand::RoomInvitedCount {
|
||||
RoomStateCache::RoomInvitedCount {
|
||||
room_id,
|
||||
} => {
|
||||
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();
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
RoomStateCacheCommand::RoomUserOnceJoined {
|
||||
RoomStateCache::RoomUserOnceJoined {
|
||||
room_id,
|
||||
} => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results: Result<Vec<_>> = services
|
||||
let results: Result<Vec<_>> = services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_useroncejoined(&room_id)
|
||||
@@ -193,11 +128,11 @@ pub(super) async fn process(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
RoomStateCacheCommand::RoomMembersInvited {
|
||||
RoomStateCache::RoomMembersInvited {
|
||||
room_id,
|
||||
} => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results: Result<Vec<_>> = services
|
||||
let results: Result<Vec<_>> = services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_members_invited(&room_id)
|
||||
@@ -208,12 +143,12 @@ pub(super) async fn process(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
RoomStateCacheCommand::GetInviteCount {
|
||||
RoomStateCache::GetInviteCount {
|
||||
room_id,
|
||||
user_id,
|
||||
} => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = services
|
||||
let results = services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.get_invite_count(&room_id, &user_id);
|
||||
@@ -223,12 +158,12 @@ pub(super) async fn process(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
RoomStateCacheCommand::GetLeftCount {
|
||||
RoomStateCache::GetLeftCount {
|
||||
room_id,
|
||||
user_id,
|
||||
} => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = services
|
||||
let results = services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.get_left_count(&room_id, &user_id);
|
||||
@@ -238,45 +173,56 @@ pub(super) async fn process(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
RoomStateCacheCommand::RoomsJoined {
|
||||
RoomStateCache::RoomsJoined {
|
||||
user_id,
|
||||
} => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results: Result<Vec<_>> = services.rooms.state_cache.rooms_joined(&user_id).collect();
|
||||
let results: Result<Vec<_>> = services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.rooms_joined(&user_id)
|
||||
.collect();
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
RoomStateCacheCommand::RoomsInvited {
|
||||
RoomStateCache::RoomsInvited {
|
||||
user_id,
|
||||
} => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results: Result<Vec<_>> = services.rooms.state_cache.rooms_invited(&user_id).collect();
|
||||
let results: Result<Vec<_>> = services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.rooms_invited(&user_id)
|
||||
.collect();
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
RoomStateCacheCommand::RoomsLeft {
|
||||
RoomStateCache::RoomsLeft {
|
||||
user_id,
|
||||
} => {
|
||||
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();
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
RoomStateCacheCommand::InviteState {
|
||||
RoomStateCache::InviteState {
|
||||
user_id,
|
||||
room_id,
|
||||
} => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = services.rooms.state_cache.invite_state(&user_id, &room_id);
|
||||
let results = services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.invite_state(&user_id, &room_id);
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
|
||||
+16
-75
@@ -1,73 +1,14 @@
|
||||
use clap::Subcommand;
|
||||
use conduit::Result;
|
||||
use ruma::{events::room::message::RoomMessageEventContent, ServerName, UserId};
|
||||
use service::sending::Destination;
|
||||
use ruma::events::room::message::RoomMessageEventContent;
|
||||
|
||||
use crate::Command;
|
||||
|
||||
#[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>,
|
||||
},
|
||||
}
|
||||
use super::Sending;
|
||||
use crate::{service::sending::Destination, services, Result};
|
||||
|
||||
/// All the getters and iterators in key_value/sending.rs
|
||||
pub(super) async fn process(subcommand: SendingCommand, context: &Command<'_>) -> Result<RoomMessageEventContent> {
|
||||
let services = context.services;
|
||||
|
||||
pub(super) async fn sending(subcommand: Sending) -> Result<RoomMessageEventContent> {
|
||||
match subcommand {
|
||||
SendingCommand::ActiveRequests => {
|
||||
Sending::ActiveRequests => {
|
||||
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 query_time = timer.elapsed();
|
||||
|
||||
@@ -75,7 +16,7 @@ pub(super) async fn process(subcommand: SendingCommand, context: &Command<'_>) -
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{active_requests:#?}\n```"
|
||||
)))
|
||||
},
|
||||
SendingCommand::QueuedRequests {
|
||||
Sending::QueuedRequests {
|
||||
appservice_id,
|
||||
server_name,
|
||||
user_id,
|
||||
@@ -97,12 +38,12 @@ pub(super) async fn process(subcommand: SendingCommand, context: &Command<'_>) -
|
||||
));
|
||||
}
|
||||
|
||||
services
|
||||
services()
|
||||
.sending
|
||||
.db
|
||||
.queued_requests(&Destination::Appservice(appservice_id))
|
||||
},
|
||||
(None, Some(server_name), None, None) => services
|
||||
(None, Some(server_name), None, None) => services()
|
||||
.sending
|
||||
.db
|
||||
.queued_requests(&Destination::Normal(server_name.into())),
|
||||
@@ -114,7 +55,7 @@ pub(super) async fn process(subcommand: SendingCommand, context: &Command<'_>) -
|
||||
));
|
||||
}
|
||||
|
||||
services
|
||||
services()
|
||||
.sending
|
||||
.db
|
||||
.queued_requests(&Destination::Push(user_id.into(), push_key))
|
||||
@@ -140,7 +81,7 @@ pub(super) async fn process(subcommand: SendingCommand, context: &Command<'_>) -
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{queued_requests:#?}\n```"
|
||||
)))
|
||||
},
|
||||
SendingCommand::ActiveRequestsFor {
|
||||
Sending::ActiveRequestsFor {
|
||||
appservice_id,
|
||||
server_name,
|
||||
user_id,
|
||||
@@ -163,12 +104,12 @@ pub(super) async fn process(subcommand: SendingCommand, context: &Command<'_>) -
|
||||
));
|
||||
}
|
||||
|
||||
services
|
||||
services()
|
||||
.sending
|
||||
.db
|
||||
.active_requests_for(&Destination::Appservice(appservice_id))
|
||||
},
|
||||
(None, Some(server_name), None, None) => services
|
||||
(None, Some(server_name), None, None) => services()
|
||||
.sending
|
||||
.db
|
||||
.active_requests_for(&Destination::Normal(server_name.into())),
|
||||
@@ -180,7 +121,7 @@ pub(super) async fn process(subcommand: SendingCommand, context: &Command<'_>) -
|
||||
));
|
||||
}
|
||||
|
||||
services
|
||||
services()
|
||||
.sending
|
||||
.db
|
||||
.active_requests_for(&Destination::Push(user_id.into(), push_key))
|
||||
@@ -206,11 +147,11 @@ pub(super) async fn process(subcommand: SendingCommand, context: &Command<'_>) -
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{active_requests:#?}\n```"
|
||||
)))
|
||||
},
|
||||
SendingCommand::GetLatestEduCount {
|
||||
Sending::GetLatestEduCount {
|
||||
server_name,
|
||||
} => {
|
||||
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();
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
|
||||
@@ -1,23 +1,14 @@
|
||||
use clap::Subcommand;
|
||||
use conduit::Result;
|
||||
use ruma::events::room::message::RoomMessageEventContent;
|
||||
|
||||
use crate::Command;
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
/// All the getters and iterators from src/database/key_value/users.rs
|
||||
pub(crate) enum UsersCommand {
|
||||
Iter,
|
||||
}
|
||||
use super::Users;
|
||||
use crate::{services, Result};
|
||||
|
||||
/// All the getters and iterators in key_value/users.rs
|
||||
pub(super) async fn process(subcommand: UsersCommand, context: &Command<'_>) -> Result<RoomMessageEventContent> {
|
||||
let services = context.services;
|
||||
|
||||
pub(super) async fn users(subcommand: Users) -> Result<RoomMessageEventContent> {
|
||||
match subcommand {
|
||||
UsersCommand::Iter => {
|
||||
Users::Iter => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = services.users.db.iter();
|
||||
let results = services().users.db.iter();
|
||||
let users = results.collect::<Vec<_>>();
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
|
||||
+160
-13
@@ -1,23 +1,20 @@
|
||||
mod alias;
|
||||
mod commands;
|
||||
mod directory;
|
||||
mod info;
|
||||
mod moderation;
|
||||
mod room_alias_commands;
|
||||
mod room_commands;
|
||||
mod room_directory_commands;
|
||||
mod room_info_commands;
|
||||
mod room_moderation_commands;
|
||||
|
||||
use clap::Subcommand;
|
||||
use conduit::Result;
|
||||
use ruma::{events::room::message::RoomMessageEventContent, RoomId, RoomOrAliasId};
|
||||
|
||||
use self::{
|
||||
alias::RoomAliasCommand, directory::RoomDirectoryCommand, info::RoomInfoCommand, moderation::RoomModerationCommand,
|
||||
};
|
||||
use crate::admin_command_dispatch;
|
||||
use self::room_commands::list;
|
||||
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
#[derive(Subcommand)]
|
||||
pub(super) enum RoomCommand {
|
||||
/// - List all rooms the server knows about
|
||||
#[clap(alias = "list")]
|
||||
ListRooms {
|
||||
List {
|
||||
page: Option<usize>,
|
||||
|
||||
/// Excludes rooms that we have federation disabled with
|
||||
@@ -45,3 +42,153 @@ pub(super) enum RoomCommand {
|
||||
/// - Manage the room directory
|
||||
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,49 +1,12 @@
|
||||
use std::fmt::Write;
|
||||
|
||||
use clap::Subcommand;
|
||||
use conduit::Result;
|
||||
use ruma::{events::room::message::RoomMessageEventContent, RoomAliasId, RoomId};
|
||||
use ruma::{events::room::message::RoomMessageEventContent, RoomAliasId};
|
||||
|
||||
use crate::{escape_html, Command};
|
||||
use super::RoomAliasCommand;
|
||||
use crate::{escape_html, services, Result};
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
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;
|
||||
pub(super) async fn process(command: RoomAliasCommand, _body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
let server_user = &services().globals.server_user;
|
||||
|
||||
match command {
|
||||
RoomAliasCommand::Set {
|
||||
@@ -56,7 +19,7 @@ pub(super) async fn process(command: RoomAliasCommand, context: &Command<'_>) ->
|
||||
| RoomAliasCommand::Which {
|
||||
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) {
|
||||
Ok(alias) => alias,
|
||||
Err(err) => return Ok(RoomMessageEventContent::text_plain(format!("Failed to parse alias: {err}"))),
|
||||
@@ -66,8 +29,8 @@ pub(super) async fn process(command: RoomAliasCommand, context: &Command<'_>) ->
|
||||
force,
|
||||
room_id,
|
||||
..
|
||||
} => match (force, services.rooms.alias.resolve_local_alias(&room_alias)) {
|
||||
(true, Ok(Some(id))) => match services
|
||||
} => match (force, services().rooms.alias.resolve_local_alias(&room_alias)) {
|
||||
(true, Ok(Some(id))) => match services()
|
||||
.rooms
|
||||
.alias
|
||||
.set_alias(&room_alias, &room_id, server_user)
|
||||
@@ -80,7 +43,7 @@ pub(super) async fn process(command: RoomAliasCommand, context: &Command<'_>) ->
|
||||
(false, Ok(Some(id))) => Ok(RoomMessageEventContent::text_plain(format!(
|
||||
"Refusing to overwrite in use alias for {id}, use -f or --force to overwrite"
|
||||
))),
|
||||
(_, Ok(None)) => match services
|
||||
(_, Ok(None)) => match services()
|
||||
.rooms
|
||||
.alias
|
||||
.set_alias(&room_alias, &room_id, server_user)
|
||||
@@ -92,8 +55,8 @@ pub(super) async fn process(command: RoomAliasCommand, context: &Command<'_>) ->
|
||||
},
|
||||
RoomAliasCommand::Remove {
|
||||
..
|
||||
} => match services.rooms.alias.resolve_local_alias(&room_alias) {
|
||||
Ok(Some(id)) => match services
|
||||
} => match services().rooms.alias.resolve_local_alias(&room_alias) {
|
||||
Ok(Some(id)) => match services()
|
||||
.rooms
|
||||
.alias
|
||||
.remove_alias(&room_alias, server_user)
|
||||
@@ -107,7 +70,7 @@ pub(super) async fn process(command: RoomAliasCommand, context: &Command<'_>) ->
|
||||
},
|
||||
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(None) => Ok(RoomMessageEventContent::text_plain("Alias isn't in use.")),
|
||||
Err(err) => Ok(RoomMessageEventContent::text_plain(format!("Unable to lookup alias: {err}"))),
|
||||
@@ -121,7 +84,7 @@ pub(super) async fn process(command: RoomAliasCommand, context: &Command<'_>) ->
|
||||
room_id,
|
||||
} => {
|
||||
if let Some(room_id) = room_id {
|
||||
let aliases = services
|
||||
let aliases = services()
|
||||
.rooms
|
||||
.alias
|
||||
.local_aliases_for_room(&room_id)
|
||||
@@ -146,14 +109,14 @@ pub(super) async fn process(command: RoomAliasCommand, context: &Command<'_>) ->
|
||||
Err(err) => Ok(RoomMessageEventContent::text_plain(format!("Unable to list aliases: {err}"))),
|
||||
}
|
||||
} else {
|
||||
let aliases = services
|
||||
let aliases = services()
|
||||
.rooms
|
||||
.alias
|
||||
.all_local_aliases()
|
||||
.collect::<Result<Vec<_>, _>>();
|
||||
match aliases {
|
||||
Ok(aliases) => {
|
||||
let server_name = services.globals.server_name();
|
||||
let server_name = services().globals.server_name();
|
||||
let plain_list = aliases
|
||||
.iter()
|
||||
.fold(String::new(), |mut output, (alias, id)| {
|
||||
@@ -1,18 +1,15 @@
|
||||
use std::fmt::Write;
|
||||
|
||||
use conduit::Result;
|
||||
use ruma::events::room::message::RoomMessageEventContent;
|
||||
|
||||
use crate::{admin_command, escape_html, get_room_info, PAGE_SIZE};
|
||||
use crate::{escape_html, get_room_info, handler::PAGE_SIZE, services, Result};
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn list_rooms(
|
||||
&self, page: Option<usize>, exclude_disabled: bool, exclude_banned: bool,
|
||||
pub(super) async fn list(
|
||||
_body: Vec<&str>, page: Option<usize>, exclude_disabled: bool, exclude_banned: bool,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
// TODO: i know there's a way to do this with clap, but i can't seem to find it
|
||||
let page = page.unwrap_or(1);
|
||||
let mut rooms = self
|
||||
.services
|
||||
let mut rooms = services()
|
||||
.rooms
|
||||
.metadata
|
||||
.iter_ids()
|
||||
@@ -21,8 +18,7 @@ pub(super) async fn list_rooms(
|
||||
.ok()
|
||||
.filter(|room_id| {
|
||||
if exclude_disabled
|
||||
&& self
|
||||
.services
|
||||
&& services()
|
||||
.rooms
|
||||
.metadata
|
||||
.is_disabled(room_id)
|
||||
@@ -32,8 +28,7 @@ pub(super) async fn list_rooms(
|
||||
}
|
||||
|
||||
if exclude_banned
|
||||
&& self
|
||||
.services
|
||||
&& services()
|
||||
.rooms
|
||||
.metadata
|
||||
.is_banned(room_id)
|
||||
@@ -44,7 +39,7 @@ pub(super) async fn list_rooms(
|
||||
|
||||
true
|
||||
})
|
||||
.map(|room_id| get_room_info(self.services, &room_id))
|
||||
.map(|room_id| get_room_info(&room_id))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
rooms.sort_by_key(|r| r.1);
|
||||
@@ -1,43 +1,21 @@
|
||||
use std::fmt::Write;
|
||||
|
||||
use clap::Subcommand;
|
||||
use conduit::Result;
|
||||
use ruma::{events::room::message::RoomMessageEventContent, OwnedRoomId, RoomId};
|
||||
use ruma::{events::room::message::RoomMessageEventContent, OwnedRoomId};
|
||||
|
||||
use crate::{escape_html, get_room_info, Command, PAGE_SIZE};
|
||||
use super::RoomDirectoryCommand;
|
||||
use crate::{escape_html, get_room_info, handler::PAGE_SIZE, services, Result};
|
||||
|
||||
#[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;
|
||||
pub(super) async fn process(command: RoomDirectoryCommand, _body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
match command {
|
||||
RoomDirectoryCommand::Publish {
|
||||
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")),
|
||||
Err(err) => Ok(RoomMessageEventContent::text_plain(format!("Unable to update room: {err}"))),
|
||||
},
|
||||
RoomDirectoryCommand::Unpublish {
|
||||
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")),
|
||||
Err(err) => Ok(RoomMessageEventContent::text_plain(format!("Unable to update room: {err}"))),
|
||||
},
|
||||
@@ -46,12 +24,12 @@ pub(super) async fn process(command: RoomDirectoryCommand, context: &Command<'_>
|
||||
} => {
|
||||
// TODO: i know there's a way to do this with clap, but i can't seem to find it
|
||||
let page = page.unwrap_or(1);
|
||||
let mut rooms = services
|
||||
let mut rooms = services()
|
||||
.rooms
|
||||
.directory
|
||||
.public_rooms()
|
||||
.filter_map(Result::ok)
|
||||
.map(|id: OwnedRoomId| get_room_info(services, &id))
|
||||
.map(|id: OwnedRoomId| get_room_info(&id))
|
||||
.collect::<Vec<_>>();
|
||||
rooms.sort_by_key(|r| r.1);
|
||||
rooms.reverse();
|
||||
@@ -1,30 +1,22 @@
|
||||
use clap::Subcommand;
|
||||
use conduit::Result;
|
||||
use ruma::{events::room::message::RoomMessageEventContent, RoomId};
|
||||
use service::services;
|
||||
|
||||
use crate::{admin_command, admin_command_dispatch};
|
||||
use super::RoomInfoCommand;
|
||||
use crate::Result;
|
||||
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub(crate) 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>,
|
||||
},
|
||||
pub(super) async fn process(command: RoomInfoCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
match command {
|
||||
RoomInfoCommand::ListJoinedMembers {
|
||||
room_id,
|
||||
} => list_joined_members(body, room_id).await,
|
||||
RoomInfoCommand::ViewRoomTopic {
|
||||
room_id,
|
||||
} => view_room_topic(body, room_id).await,
|
||||
}
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
async fn list_joined_members(&self, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
|
||||
let room_name = self
|
||||
.services
|
||||
async fn list_joined_members(_body: Vec<&str>, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
|
||||
let room_name = services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.get_name(&room_id)
|
||||
@@ -32,8 +24,7 @@ async fn list_joined_members(&self, room_id: Box<RoomId>) -> Result<RoomMessageE
|
||||
.flatten()
|
||||
.unwrap_or_else(|| room_id.to_string());
|
||||
|
||||
let members = self
|
||||
.services
|
||||
let members = services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_members(&room_id)
|
||||
@@ -44,7 +35,7 @@ async fn list_joined_members(&self, room_id: Box<RoomId>) -> Result<RoomMessageE
|
||||
.map(|user_id| {
|
||||
(
|
||||
user_id.clone(),
|
||||
self.services
|
||||
services()
|
||||
.users
|
||||
.displayname(&user_id)
|
||||
.unwrap_or(None)
|
||||
@@ -67,18 +58,12 @@ async fn list_joined_members(&self, room_id: Box<RoomId>) -> Result<RoomMessageE
|
||||
Ok(RoomMessageEventContent::notice_markdown(output_plain))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
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 {
|
||||
async fn view_room_topic(_body: Vec<&str>, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
|
||||
let Some(room_topic) = services().rooms.state_accessor.get_room_topic(&room_id)? else {
|
||||
return Ok(RoomMessageEventContent::text_plain("Room does not have a room topic set."));
|
||||
};
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Room topic:\n```\n{room_topic}\n```"
|
||||
"Room topic:\n\n```{room_topic}\n```"
|
||||
)))
|
||||
}
|
||||
@@ -1,77 +1,37 @@
|
||||
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 tracing::{debug, error, info, warn};
|
||||
|
||||
use crate::{admin_command, admin_command_dispatch, get_room_info};
|
||||
use super::{super::Service, RoomModerationCommand};
|
||||
use crate::{get_room_info, services, user_is_local, Result};
|
||||
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub(crate) 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: RoomModerationCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
match command {
|
||||
RoomModerationCommand::BanRoom {
|
||||
force,
|
||||
room,
|
||||
disable_federation,
|
||||
} => ban_room(body, force, room, disable_federation).await,
|
||||
RoomModerationCommand::BanListOfRooms {
|
||||
force,
|
||||
disable_federation,
|
||||
} => ban_list_of_rooms(body, force, disable_federation).await,
|
||||
RoomModerationCommand::UnbanRoom {
|
||||
room,
|
||||
enable_federation,
|
||||
} => unban_room(body, room, enable_federation).await,
|
||||
RoomModerationCommand::ListBannedRooms => list_banned_rooms(body).await,
|
||||
}
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
async fn ban_room(
|
||||
&self, force: bool, disable_federation: bool, room: Box<RoomOrAliasId>,
|
||||
_body: Vec<&str>, force: bool, room: Box<RoomOrAliasId>, disable_federation: bool,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
debug!("Got room alias or ID: {}", room);
|
||||
|
||||
let admin_room_alias = &self.services.globals.admin_alias;
|
||||
let admin_room_alias = &services().globals.admin_alias;
|
||||
|
||||
if let Some(admin_room_id) = self.services.admin.get_admin_room()? {
|
||||
if let Some(admin_room_id) = Service::get_admin_room()? {
|
||||
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."));
|
||||
}
|
||||
@@ -90,7 +50,7 @@ async fn ban_room(
|
||||
|
||||
debug!("Room specified is a room ID, banning room ID");
|
||||
|
||||
self.services.rooms.metadata.ban_room(&room_id, true)?;
|
||||
services().rooms.metadata.ban_room(&room_id, true)?;
|
||||
|
||||
room_id
|
||||
} else if room.is_room_alias_id() {
|
||||
@@ -109,13 +69,12 @@ async fn ban_room(
|
||||
get_alias_helper to fetch room ID remotely"
|
||||
);
|
||||
|
||||
let room_id = if let Some(room_id) = self.services.rooms.alias.resolve_local_alias(&room_alias)? {
|
||||
let room_id = if let Some(room_id) = services().rooms.alias.resolve_local_alias(&room_alias)? {
|
||||
room_id
|
||||
} else {
|
||||
debug!("We don't have this room alias to a room ID locally, attempting to fetch room ID over federation");
|
||||
|
||||
match self
|
||||
.services
|
||||
match services()
|
||||
.rooms
|
||||
.alias
|
||||
.resolve_alias(&room_alias, None)
|
||||
@@ -133,7 +92,7 @@ async fn ban_room(
|
||||
}
|
||||
};
|
||||
|
||||
self.services.rooms.metadata.ban_room(&room_id, true)?;
|
||||
services().rooms.metadata.ban_room(&room_id, true)?;
|
||||
|
||||
room_id
|
||||
} else {
|
||||
@@ -145,21 +104,20 @@ async fn ban_room(
|
||||
|
||||
debug!("Making all users leave the room {}", &room);
|
||||
if force {
|
||||
for local_user in self
|
||||
.services
|
||||
for local_user in services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_members(&room_id)
|
||||
.filter_map(|user| {
|
||||
user.ok().filter(|local_user| {
|
||||
self.services.globals.user_is_local(local_user)
|
||||
user_is_local(local_user)
|
||||
// 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)
|
||||
&& (self.services.globals.user_is_local(local_user)
|
||||
&& (user_is_local(local_user)
|
||||
// since this is a force operation, assume user is an admin
|
||||
// if somehow this fails
|
||||
&& self.services
|
||||
&& services()
|
||||
.users
|
||||
.is_admin(local_user)
|
||||
.unwrap_or(true))
|
||||
@@ -170,31 +128,30 @@ async fn ban_room(
|
||||
&local_user, &room_id
|
||||
);
|
||||
|
||||
if let Err(e) = leave_room(self.services, &local_user, &room_id, None).await {
|
||||
if let Err(e) = leave_room(&local_user, &room_id, None).await {
|
||||
warn!(%e, "Failed to leave room");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for local_user in self
|
||||
.services
|
||||
for local_user in services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_members(&room_id)
|
||||
.filter_map(|user| {
|
||||
user.ok().filter(|local_user| {
|
||||
local_user.server_name() == self.services.globals.server_name()
|
||||
local_user.server_name() == services().globals.server_name()
|
||||
// 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)
|
||||
&& (local_user.server_name()
|
||||
== self.services.globals.server_name()
|
||||
&& !self.services
|
||||
== services().globals.server_name()
|
||||
&& !services()
|
||||
.users
|
||||
.is_admin(local_user)
|
||||
.unwrap_or(false))
|
||||
})
|
||||
}) {
|
||||
debug!("Attempting leave for user {} in room {}", &local_user, &room_id);
|
||||
if let Err(e) = leave_room(self.services, &local_user, &room_id, None).await {
|
||||
if let Err(e) = leave_room(&local_user, &room_id, None).await {
|
||||
error!(
|
||||
"Error attempting to make local user {} leave room {} during room banning: {}",
|
||||
&local_user, &room_id, e
|
||||
@@ -209,7 +166,7 @@ async fn ban_room(
|
||||
}
|
||||
|
||||
if disable_federation {
|
||||
self.services.rooms.metadata.disable_room(&room_id, true)?;
|
||||
services().rooms.metadata.disable_room(&room_id, true)?;
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
"Room banned, removed all our local users, and disabled incoming federation with room.",
|
||||
));
|
||||
@@ -221,22 +178,19 @@ async fn ban_room(
|
||||
))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
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() != "```"
|
||||
{
|
||||
async fn ban_list_of_rooms(body: Vec<&str>, force: bool, disable_federation: bool) -> Result<RoomMessageEventContent> {
|
||||
if body.len() < 2 || !body[0].trim().starts_with("```") || body.last().unwrap_or(&"").trim() != "```" {
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
"Expected code block in command body. Add --help for details.",
|
||||
));
|
||||
}
|
||||
|
||||
let rooms_s = self
|
||||
.body
|
||||
.to_vec()
|
||||
.drain(1..self.body.len().saturating_sub(1))
|
||||
let rooms_s = body
|
||||
.clone()
|
||||
.drain(1..body.len().saturating_sub(1))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let admin_room_alias = &self.services.globals.admin_alias;
|
||||
let admin_room_alias = &services().globals.admin_alias;
|
||||
|
||||
let mut room_ban_count: usize = 0;
|
||||
let mut room_ids: Vec<OwnedRoomId> = Vec::new();
|
||||
@@ -244,7 +198,7 @@ async fn ban_list_of_rooms(&self, force: bool, disable_federation: bool) -> Resu
|
||||
for &room in &rooms_s {
|
||||
match <&RoomOrAliasId>::try_from(room) {
|
||||
Ok(room_alias_or_id) => {
|
||||
if let Some(admin_room_id) = self.services.admin.get_admin_room()? {
|
||||
if let Some(admin_room_id) = Service::get_admin_room()? {
|
||||
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");
|
||||
continue;
|
||||
@@ -277,7 +231,7 @@ async fn ban_list_of_rooms(&self, force: bool, disable_federation: bool) -> Resu
|
||||
match RoomAliasId::parse(room_alias_or_id) {
|
||||
Ok(room_alias) => {
|
||||
let room_id =
|
||||
if let Some(room_id) = self.services.rooms.alias.resolve_local_alias(&room_alias)? {
|
||||
if let Some(room_id) = services().rooms.alias.resolve_local_alias(&room_alias)? {
|
||||
room_id
|
||||
} else {
|
||||
debug!(
|
||||
@@ -285,8 +239,7 @@ async fn ban_list_of_rooms(&self, force: bool, disable_federation: bool) -> Resu
|
||||
ID over federation"
|
||||
);
|
||||
|
||||
match self
|
||||
.services
|
||||
match services()
|
||||
.rooms
|
||||
.alias
|
||||
.resolve_alias(&room_alias, None)
|
||||
@@ -350,35 +303,28 @@ async fn ban_list_of_rooms(&self, force: bool, disable_federation: bool) -> Resu
|
||||
}
|
||||
|
||||
for room_id in room_ids {
|
||||
if self
|
||||
.services
|
||||
.rooms
|
||||
.metadata
|
||||
.ban_room(&room_id, true)
|
||||
.is_ok()
|
||||
{
|
||||
if services().rooms.metadata.ban_room(&room_id, true).is_ok() {
|
||||
debug!("Banned {room_id} successfully");
|
||||
room_ban_count = room_ban_count.saturating_add(1);
|
||||
}
|
||||
|
||||
debug!("Making all users leave the room {}", &room_id);
|
||||
if force {
|
||||
for local_user in self
|
||||
.services
|
||||
for local_user in services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_members(&room_id)
|
||||
.filter_map(|user| {
|
||||
user.ok().filter(|local_user| {
|
||||
local_user.server_name() == self.services.globals.server_name()
|
||||
local_user.server_name() == services().globals.server_name()
|
||||
// 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)
|
||||
&& (local_user.server_name()
|
||||
== self.services.globals.server_name()
|
||||
== services().globals.server_name()
|
||||
// since this is a force operation, assume user is an
|
||||
// admin if somehow this fails
|
||||
&& self.services
|
||||
&& services()
|
||||
.users
|
||||
.is_admin(local_user)
|
||||
.unwrap_or(true))
|
||||
@@ -388,32 +334,31 @@ async fn ban_list_of_rooms(&self, force: bool, disable_federation: bool) -> Resu
|
||||
"Attempting leave for user {} in room {} (forced, ignoring all errors, evicting admins too)",
|
||||
&local_user, room_id
|
||||
);
|
||||
if let Err(e) = leave_room(self.services, &local_user, &room_id, None).await {
|
||||
if let Err(e) = leave_room(&local_user, &room_id, None).await {
|
||||
warn!(%e, "Failed to leave room");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for local_user in self
|
||||
.services
|
||||
for local_user in services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_members(&room_id)
|
||||
.filter_map(|user| {
|
||||
user.ok().filter(|local_user| {
|
||||
local_user.server_name() == self.services.globals.server_name()
|
||||
local_user.server_name() == services().globals.server_name()
|
||||
// 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)
|
||||
&& (local_user.server_name()
|
||||
== self.services.globals.server_name()
|
||||
&& !self.services
|
||||
== services().globals.server_name()
|
||||
&& !services()
|
||||
.users
|
||||
.is_admin(local_user)
|
||||
.unwrap_or(false))
|
||||
})
|
||||
}) {
|
||||
debug!("Attempting leave for user {} in room {}", &local_user, &room_id);
|
||||
if let Err(e) = leave_room(self.services, &local_user, &room_id, None).await {
|
||||
if let Err(e) = leave_room(&local_user, &room_id, None).await {
|
||||
error!(
|
||||
"Error attempting to make local user {} leave room {} during bulk room banning: {}",
|
||||
&local_user, &room_id, e
|
||||
@@ -429,7 +374,7 @@ async fn ban_list_of_rooms(&self, force: bool, disable_federation: bool) -> Resu
|
||||
}
|
||||
|
||||
if disable_federation {
|
||||
self.services.rooms.metadata.disable_room(&room_id, true)?;
|
||||
services().rooms.metadata.disable_room(&room_id, true)?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -445,8 +390,9 @@ async fn ban_list_of_rooms(&self, force: bool, disable_federation: bool) -> Resu
|
||||
}
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
async fn unban_room(&self, enable_federation: bool, room: Box<RoomOrAliasId>) -> Result<RoomMessageEventContent> {
|
||||
async fn unban_room(
|
||||
_body: Vec<&str>, room: Box<RoomOrAliasId>, enable_federation: bool,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
let room_id = if room.is_room_id() {
|
||||
let room_id = match RoomId::parse(&room) {
|
||||
Ok(room_id) => room_id,
|
||||
@@ -460,7 +406,7 @@ async fn unban_room(&self, enable_federation: bool, room: Box<RoomOrAliasId>) ->
|
||||
|
||||
debug!("Room specified is a room ID, unbanning room ID");
|
||||
|
||||
self.services.rooms.metadata.ban_room(&room_id, false)?;
|
||||
services().rooms.metadata.ban_room(&room_id, false)?;
|
||||
|
||||
room_id
|
||||
} else if room.is_room_alias_id() {
|
||||
@@ -479,13 +425,12 @@ async fn unban_room(&self, enable_federation: bool, room: Box<RoomOrAliasId>) ->
|
||||
get_alias_helper to fetch room ID remotely"
|
||||
);
|
||||
|
||||
let room_id = if let Some(room_id) = self.services.rooms.alias.resolve_local_alias(&room_alias)? {
|
||||
let room_id = if let Some(room_id) = services().rooms.alias.resolve_local_alias(&room_alias)? {
|
||||
room_id
|
||||
} else {
|
||||
debug!("We don't have this room alias to a room ID locally, attempting to fetch room ID over federation");
|
||||
|
||||
match self
|
||||
.services
|
||||
match services()
|
||||
.rooms
|
||||
.alias
|
||||
.resolve_alias(&room_alias, None)
|
||||
@@ -503,7 +448,7 @@ async fn unban_room(&self, enable_federation: bool, room: Box<RoomOrAliasId>) ->
|
||||
}
|
||||
};
|
||||
|
||||
self.services.rooms.metadata.ban_room(&room_id, false)?;
|
||||
services().rooms.metadata.ban_room(&room_id, false)?;
|
||||
|
||||
room_id
|
||||
} else {
|
||||
@@ -514,7 +459,7 @@ async fn unban_room(&self, enable_federation: bool, room: Box<RoomOrAliasId>) ->
|
||||
};
|
||||
|
||||
if enable_federation {
|
||||
self.services.rooms.metadata.disable_room(&room_id, false)?;
|
||||
services().rooms.metadata.disable_room(&room_id, false)?;
|
||||
return Ok(RoomMessageEventContent::text_plain("Room unbanned."));
|
||||
}
|
||||
|
||||
@@ -524,10 +469,8 @@ async fn unban_room(&self, enable_federation: bool, room: Box<RoomOrAliasId>) ->
|
||||
))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
async fn list_banned_rooms(&self) -> Result<RoomMessageEventContent> {
|
||||
let rooms = self
|
||||
.services
|
||||
async fn list_banned_rooms(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
let rooms = services()
|
||||
.rooms
|
||||
.metadata
|
||||
.list_banned_rooms()
|
||||
@@ -541,7 +484,7 @@ async fn list_banned_rooms(&self) -> Result<RoomMessageEventContent> {
|
||||
|
||||
let mut rooms = room_ids
|
||||
.into_iter()
|
||||
.map(|room_id| get_room_info(self.services, &room_id))
|
||||
.map(|room_id| get_room_info(&room_id))
|
||||
.collect::<Vec<_>>();
|
||||
rooms.sort_by_key(|r| r.1);
|
||||
rooms.reverse();
|
||||
@@ -1,14 +1,10 @@
|
||||
use std::{fmt::Write, sync::Arc};
|
||||
|
||||
use conduit::{info, utils::time, warn, Err, Result};
|
||||
use conduit::{utils::time, warn, Err, Result};
|
||||
use ruma::events::room::message::RoomMessageEventContent;
|
||||
|
||||
use crate::admin_command;
|
||||
use crate::services;
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn uptime(&self) -> Result<RoomMessageEventContent> {
|
||||
let elapsed = self
|
||||
.services
|
||||
pub(super) async fn uptime(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
let elapsed = services()
|
||||
.server
|
||||
.started
|
||||
.elapsed()
|
||||
@@ -18,58 +14,14 @@ pub(super) async fn uptime(&self) -> Result<RoomMessageEventContent> {
|
||||
Ok(RoomMessageEventContent::notice_plain(format!("{result}.")))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn show_config(&self) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn show_config(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
// Construct and send the response
|
||||
Ok(RoomMessageEventContent::text_plain(format!("{}", self.services.globals.config)))
|
||||
Ok(RoomMessageEventContent::text_plain(format!("{}", services().globals.config)))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn list_features(
|
||||
&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()?;
|
||||
pub(super) async fn memory_usage(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
let services_usage = services().memory_usage().await?;
|
||||
let database_usage = services().db.db.memory_usage()?;
|
||||
let allocator_usage = conduit::alloc::memory_usage().map_or(String::new(), |s| format!("\nAllocator:\n{s}"));
|
||||
|
||||
Ok(RoomMessageEventContent::text_plain(format!(
|
||||
@@ -77,16 +29,14 @@ pub(super) async fn memory_usage(&self) -> Result<RoomMessageEventContent> {
|
||||
)))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn clear_caches(&self) -> Result<RoomMessageEventContent> {
|
||||
self.services.clear_cache().await;
|
||||
pub(super) async fn clear_caches(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
services().clear_cache().await;
|
||||
|
||||
Ok(RoomMessageEventContent::text_plain("Done."))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn list_backups(&self) -> Result<RoomMessageEventContent> {
|
||||
let result = self.services.globals.db.backup_list()?;
|
||||
pub(super) async fn list_backups(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
let result = services().globals.db.backup_list()?;
|
||||
|
||||
if result.is_empty() {
|
||||
Ok(RoomMessageEventContent::text_plain("No backups found."))
|
||||
@@ -95,51 +45,46 @@ pub(super) async fn list_backups(&self) -> Result<RoomMessageEventContent> {
|
||||
}
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn backup_database(&self) -> Result<RoomMessageEventContent> {
|
||||
let globals = Arc::clone(&self.services.globals);
|
||||
let mut result = self
|
||||
.services
|
||||
pub(super) async fn backup_database(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
let mut result = services()
|
||||
.server
|
||||
.runtime()
|
||||
.spawn_blocking(move || match globals.db.backup() {
|
||||
.spawn_blocking(move || match services().globals.db.backup() {
|
||||
Ok(()) => String::new(),
|
||||
Err(e) => (*e).to_string(),
|
||||
})
|
||||
.await?;
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
if result.is_empty() {
|
||||
result = self.services.globals.db.backup_list()?;
|
||||
result = services().globals.db.backup_list()?;
|
||||
}
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(result))
|
||||
Ok(RoomMessageEventContent::text_plain(&result))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn list_database_files(&self) -> Result<RoomMessageEventContent> {
|
||||
let result = self.services.globals.db.file_list()?;
|
||||
pub(super) async fn list_database_files(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
let result = services().globals.db.file_list()?;
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(result))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn admin_notice(&self, message: Vec<String>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn admin_notice(_body: Vec<&str>, message: Vec<String>) -> Result<RoomMessageEventContent> {
|
||||
let message = message.join(" ");
|
||||
self.services.admin.send_text(&message).await;
|
||||
services().admin.send_text(&message).await;
|
||||
|
||||
Ok(RoomMessageEventContent::notice_plain("Notice was sent to #admins"))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn reload_mods(&self) -> Result<RoomMessageEventContent> {
|
||||
self.services.server.reload()?;
|
||||
#[cfg(conduit_mods)]
|
||||
pub(super) async fn reload(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
services().server.reload()?;
|
||||
|
||||
Ok(RoomMessageEventContent::notice_plain("Reloading server..."))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
#[cfg(unix)]
|
||||
pub(super) async fn restart(&self, force: bool) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn restart(_body: Vec<&str>, force: bool) -> Result<RoomMessageEventContent> {
|
||||
use conduit::utils::sys::current_exe_deleted;
|
||||
|
||||
if !force && current_exe_deleted() {
|
||||
@@ -149,15 +94,14 @@ pub(super) async fn restart(&self, force: bool) -> Result<RoomMessageEventConten
|
||||
);
|
||||
}
|
||||
|
||||
self.services.server.restart()?;
|
||||
services().server.restart()?;
|
||||
|
||||
Ok(RoomMessageEventContent::notice_plain("Restarting server..."))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn shutdown(&self) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn shutdown(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
warn!("shutdown command");
|
||||
self.services.server.shutdown()?;
|
||||
services().server.shutdown()?;
|
||||
|
||||
Ok(RoomMessageEventContent::notice_plain("Shutting down server..."))
|
||||
}
|
||||
|
||||
+28
-17
@@ -2,11 +2,12 @@ mod commands;
|
||||
|
||||
use clap::Subcommand;
|
||||
use conduit::Result;
|
||||
use ruma::events::room::message::RoomMessageEventContent;
|
||||
|
||||
use crate::admin_command_dispatch;
|
||||
use self::commands::*;
|
||||
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
#[derive(Subcommand)]
|
||||
pub(super) enum ServerCommand {
|
||||
/// - Time elapsed since startup
|
||||
Uptime,
|
||||
@@ -14,18 +15,6 @@ pub(super) enum ServerCommand {
|
||||
/// - Show configuration values
|
||||
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
|
||||
MemoryUsage,
|
||||
|
||||
@@ -47,9 +36,9 @@ pub(super) enum ServerCommand {
|
||||
message: Vec<String>,
|
||||
},
|
||||
|
||||
#[cfg(conduit_mods)]
|
||||
/// - Hot-reload the server
|
||||
#[clap(alias = "reload")]
|
||||
ReloadMods,
|
||||
Reload,
|
||||
|
||||
#[cfg(unix)]
|
||||
/// - Restart the server
|
||||
@@ -61,3 +50,25 @@ pub(super) enum ServerCommand {
|
||||
/// - Shutdown the server
|
||||
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) {
|
||||
use clap::Parser;
|
||||
|
||||
use crate::admin::AdminCommand;
|
||||
use crate::handler::AdminCommand;
|
||||
|
||||
let Err(error) = AdminCommand::try_parse_from(["argv[0] doesn't matter", input]) else {
|
||||
panic!("no error!");
|
||||
|
||||
+81
-113
@@ -1,7 +1,7 @@
|
||||
use std::{collections::BTreeMap, fmt::Write as _};
|
||||
|
||||
use api::client::{join_room_by_id_helper, leave_all_rooms, update_avatar_url, update_displayname};
|
||||
use conduit::{error, info, utils, warn, Result};
|
||||
use conduit::{utils, Result};
|
||||
use ruma::{
|
||||
events::{
|
||||
room::message::RoomMessageEventContent,
|
||||
@@ -10,17 +10,17 @@ use ruma::{
|
||||
},
|
||||
OwnedRoomId, OwnedRoomOrAliasId, OwnedUserId, RoomId,
|
||||
};
|
||||
use tracing::{error, info, warn};
|
||||
|
||||
use crate::{
|
||||
admin_command, escape_html, get_room_info,
|
||||
escape_html, get_room_info, services,
|
||||
utils::{parse_active_local_user_id, parse_local_user_id},
|
||||
};
|
||||
|
||||
const AUTO_GEN_PASSWORD_LENGTH: usize = 25;
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn list_users(&self) -> Result<RoomMessageEventContent> {
|
||||
match self.services.users.list_local_users() {
|
||||
pub(super) async fn list(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
match services().users.list_local_users() {
|
||||
Ok(users) => {
|
||||
let mut plain_msg = format!("Found {} local user account(s):\n```\n", users.len());
|
||||
plain_msg += users.join("\n").as_str();
|
||||
@@ -32,51 +32,43 @@ pub(super) async fn list_users(&self) -> Result<RoomMessageEventContent> {
|
||||
}
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn create_user(&self, username: String, password: Option<String>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn create(
|
||||
_body: Vec<&str>, username: String, password: Option<String>,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
// Validate user id
|
||||
let user_id = parse_local_user_id(self.services, &username)?;
|
||||
let user_id = parse_local_user_id(&username)?;
|
||||
|
||||
if self.services.users.exists(&user_id)? {
|
||||
if services().users.exists(&user_id)? {
|
||||
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));
|
||||
|
||||
// Create user
|
||||
self.services
|
||||
.users
|
||||
.create(&user_id, Some(password.as_str()))?;
|
||||
services().users.create(&user_id, Some(password.as_str()))?;
|
||||
|
||||
// Default to pretty displayname
|
||||
let mut displayname = user_id.localpart().to_owned();
|
||||
|
||||
// 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
|
||||
if !self
|
||||
.services
|
||||
if !services()
|
||||
.globals
|
||||
.config
|
||||
.new_user_displayname_suffix
|
||||
.is_empty()
|
||||
{
|
||||
write!(displayname, " {}", self.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");
|
||||
}
|
||||
|
||||
self.services
|
||||
services()
|
||||
.users
|
||||
.set_displayname(&user_id, Some(displayname))
|
||||
.await?;
|
||||
|
||||
// Initial account data
|
||||
self.services.account_data.update(
|
||||
services().account_data.update(
|
||||
None,
|
||||
&user_id,
|
||||
ruma::events::GlobalAccountDataEventType::PushRules
|
||||
@@ -90,13 +82,12 @@ pub(super) async fn create_user(&self, username: String, password: Option<String
|
||||
.expect("to json value always works"),
|
||||
)?;
|
||||
|
||||
if !self.services.globals.config.auto_join_rooms.is_empty() {
|
||||
for room in &self.services.globals.config.auto_join_rooms {
|
||||
if !self
|
||||
.services
|
||||
if !services().globals.config.auto_join_rooms.is_empty() {
|
||||
for room in &services().globals.config.auto_join_rooms {
|
||||
if !services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.server_in_room(self.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.");
|
||||
continue;
|
||||
@@ -104,11 +95,10 @@ pub(super) async fn create_user(&self, username: String, password: Option<String
|
||||
|
||||
if let Some(room_id_server_name) = room.server_name() {
|
||||
match join_room_by_id_helper(
|
||||
self.services,
|
||||
&user_id,
|
||||
room,
|
||||
Some("Automatically joining this room upon registration".to_owned()),
|
||||
&[room_id_server_name.to_owned(), self.services.globals.server_name().to_owned()],
|
||||
&[room_id_server_name.to_owned(), services().globals.server_name().to_owned()],
|
||||
None,
|
||||
)
|
||||
.await
|
||||
@@ -133,38 +123,38 @@ pub(super) async fn create_user(&self, username: String, password: Option<String
|
||||
)))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn deactivate(&self, no_leave_rooms: bool, user_id: String) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn deactivate(
|
||||
_body: Vec<&str>, no_leave_rooms: bool, user_id: String,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
// Validate user id
|
||||
let user_id = parse_local_user_id(self.services, &user_id)?;
|
||||
let user_id = parse_local_user_id(&user_id)?;
|
||||
|
||||
// don't deactivate the server service account
|
||||
if user_id == self.services.globals.server_user {
|
||||
if user_id == services().globals.server_user {
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
"Not allowed to deactivate the server service account.",
|
||||
));
|
||||
}
|
||||
|
||||
self.services.users.deactivate_account(&user_id)?;
|
||||
services().users.deactivate_account(&user_id)?;
|
||||
|
||||
if !no_leave_rooms {
|
||||
self.services
|
||||
services()
|
||||
.admin
|
||||
.send_message(RoomMessageEventContent::text_plain(format!(
|
||||
"Making {user_id} leave all rooms after deactivation..."
|
||||
)))
|
||||
.await;
|
||||
|
||||
let all_joined_rooms: Vec<OwnedRoomId> = self
|
||||
.services
|
||||
let all_joined_rooms: Vec<OwnedRoomId> = services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.rooms_joined(&user_id)
|
||||
.filter_map(Result::ok)
|
||||
.collect();
|
||||
update_displayname(self.services, user_id.clone(), None, all_joined_rooms.clone()).await?;
|
||||
update_avatar_url(self.services, user_id.clone(), None, None, all_joined_rooms).await?;
|
||||
leave_all_rooms(self.services, &user_id).await;
|
||||
update_displayname(user_id.clone(), None, all_joined_rooms.clone()).await?;
|
||||
update_avatar_url(user_id.clone(), None, None, all_joined_rooms).await?;
|
||||
leave_all_rooms(&user_id).await;
|
||||
}
|
||||
|
||||
Ok(RoomMessageEventContent::text_plain(format!(
|
||||
@@ -172,11 +162,10 @@ pub(super) async fn deactivate(&self, no_leave_rooms: bool, user_id: String) ->
|
||||
)))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn reset_password(&self, username: String) -> Result<RoomMessageEventContent> {
|
||||
let user_id = parse_local_user_id(self.services, &username)?;
|
||||
pub(super) async fn reset_password(_body: Vec<&str>, username: String) -> Result<RoomMessageEventContent> {
|
||||
let user_id = parse_local_user_id(&username)?;
|
||||
|
||||
if user_id == self.services.globals.server_user {
|
||||
if user_id == services().globals.server_user {
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
"Not allowed to set the password for the server account. Please use the emergency password config option.",
|
||||
));
|
||||
@@ -184,8 +173,7 @@ pub(super) async fn reset_password(&self, username: String) -> Result<RoomMessag
|
||||
|
||||
let new_password = utils::random_string(AUTO_GEN_PASSWORD_LENGTH);
|
||||
|
||||
match self
|
||||
.services
|
||||
match services()
|
||||
.users
|
||||
.set_password(&user_id, Some(new_password.as_str()))
|
||||
{
|
||||
@@ -198,29 +186,28 @@ pub(super) async fn reset_password(&self, username: String) -> Result<RoomMessag
|
||||
}
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn deactivate_all(&self, no_leave_rooms: bool, force: bool) -> Result<RoomMessageEventContent> {
|
||||
if self.body.len() < 2 || !self.body[0].trim().starts_with("```") || self.body.last().unwrap_or(&"").trim() != "```"
|
||||
{
|
||||
pub(super) async fn deactivate_all(
|
||||
body: Vec<&str>, no_leave_rooms: bool, force: bool,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
if body.len() < 2 || !body[0].trim().starts_with("```") || body.last().unwrap_or(&"").trim() != "```" {
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
"Expected code block in command body. Add --help for details.",
|
||||
));
|
||||
}
|
||||
|
||||
let usernames = self
|
||||
.body
|
||||
.to_vec()
|
||||
.drain(1..self.body.len().saturating_sub(1))
|
||||
let usernames = body
|
||||
.clone()
|
||||
.drain(1..body.len().saturating_sub(1))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut user_ids: Vec<OwnedUserId> = Vec::with_capacity(usernames.len());
|
||||
let mut admins = Vec::new();
|
||||
|
||||
for username in usernames {
|
||||
match parse_active_local_user_id(self.services, username) {
|
||||
match parse_active_local_user_id(username) {
|
||||
Ok(user_id) => {
|
||||
if self.services.users.is_admin(&user_id)? && !force {
|
||||
self.services
|
||||
if services().users.is_admin(&user_id)? && !force {
|
||||
services()
|
||||
.admin
|
||||
.send_message(RoomMessageEventContent::text_plain(format!(
|
||||
"{username} is an admin and --force is not set, skipping over"
|
||||
@@ -231,8 +218,8 @@ pub(super) async fn deactivate_all(&self, no_leave_rooms: bool, force: bool) ->
|
||||
}
|
||||
|
||||
// don't deactivate the server service account
|
||||
if user_id == self.services.globals.server_user {
|
||||
self.services
|
||||
if user_id == services().globals.server_user {
|
||||
services()
|
||||
.admin
|
||||
.send_message(RoomMessageEventContent::text_plain(format!(
|
||||
"{username} is the server service account, skipping over"
|
||||
@@ -244,7 +231,7 @@ pub(super) async fn deactivate_all(&self, no_leave_rooms: bool, force: bool) ->
|
||||
user_ids.push(user_id);
|
||||
},
|
||||
Err(e) => {
|
||||
self.services
|
||||
services()
|
||||
.admin
|
||||
.send_message(RoomMessageEventContent::text_plain(format!(
|
||||
"{username} is not a valid username, skipping over: {e}"
|
||||
@@ -258,25 +245,24 @@ pub(super) async fn deactivate_all(&self, no_leave_rooms: bool, force: bool) ->
|
||||
let mut deactivation_count: usize = 0;
|
||||
|
||||
for user_id in user_ids {
|
||||
match self.services.users.deactivate_account(&user_id) {
|
||||
match services().users.deactivate_account(&user_id) {
|
||||
Ok(()) => {
|
||||
deactivation_count = deactivation_count.saturating_add(1);
|
||||
if !no_leave_rooms {
|
||||
info!("Forcing user {user_id} to leave all rooms apart of deactivate-all");
|
||||
let all_joined_rooms: Vec<OwnedRoomId> = self
|
||||
.services
|
||||
let all_joined_rooms: Vec<OwnedRoomId> = services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.rooms_joined(&user_id)
|
||||
.filter_map(Result::ok)
|
||||
.collect();
|
||||
update_displayname(self.services, user_id.clone(), None, all_joined_rooms.clone()).await?;
|
||||
update_avatar_url(self.services, user_id.clone(), None, None, all_joined_rooms).await?;
|
||||
leave_all_rooms(self.services, &user_id).await;
|
||||
update_displayname(user_id.clone(), None, all_joined_rooms.clone()).await?;
|
||||
update_avatar_url(user_id.clone(), None, None, all_joined_rooms).await?;
|
||||
leave_all_rooms(&user_id).await;
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
self.services
|
||||
services()
|
||||
.admin
|
||||
.send_message(RoomMessageEventContent::text_plain(format!("Failed deactivating user: {e}")))
|
||||
.await;
|
||||
@@ -297,18 +283,16 @@ pub(super) async fn deactivate_all(&self, no_leave_rooms: bool, force: bool) ->
|
||||
}
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn list_joined_rooms(&self, user_id: String) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn list_joined_rooms(_body: Vec<&str>, user_id: String) -> Result<RoomMessageEventContent> {
|
||||
// Validate user id
|
||||
let user_id = parse_local_user_id(self.services, &user_id)?;
|
||||
let user_id = parse_local_user_id(&user_id)?;
|
||||
|
||||
let mut rooms: Vec<(OwnedRoomId, u64, String)> = self
|
||||
.services
|
||||
let mut rooms: Vec<(OwnedRoomId, u64, String)> = services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.rooms_joined(&user_id)
|
||||
.filter_map(Result::ok)
|
||||
.map(|room_id| get_room_info(self.services, &room_id))
|
||||
.map(|room_id| get_room_info(&room_id))
|
||||
.collect();
|
||||
|
||||
if rooms.is_empty() {
|
||||
@@ -350,55 +334,41 @@ pub(super) async fn list_joined_rooms(&self, user_id: String) -> Result<RoomMess
|
||||
Ok(RoomMessageEventContent::text_html(output_plain, output_html))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn force_join_room(
|
||||
&self, user_id: String, room_id: OwnedRoomOrAliasId,
|
||||
_body: Vec<&str>, user_id: String, room_id: OwnedRoomOrAliasId,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
let user_id = parse_local_user_id(self.services, &user_id)?;
|
||||
let room_id = self.services.rooms.alias.resolve(&room_id).await?;
|
||||
let user_id = parse_local_user_id(&user_id)?;
|
||||
let room_id = services().rooms.alias.resolve(&room_id).await?;
|
||||
|
||||
assert!(
|
||||
self.services.globals.user_is_local(&user_id),
|
||||
"Parsed user_id must be a local user"
|
||||
);
|
||||
join_room_by_id_helper(self.services, &user_id, &room_id, None, &[], None).await?;
|
||||
assert!(service::user_is_local(&user_id), "Parsed user_id must be a local user");
|
||||
join_room_by_id_helper(&user_id, &room_id, None, &[], None).await?;
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"{user_id} has been joined to {room_id}.",
|
||||
)))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn make_user_admin(&self, user_id: String) -> Result<RoomMessageEventContent> {
|
||||
let user_id = parse_local_user_id(self.services, &user_id)?;
|
||||
let displayname = self
|
||||
.services
|
||||
pub(super) async fn make_user_admin(_body: Vec<&str>, user_id: String) -> Result<RoomMessageEventContent> {
|
||||
let user_id = parse_local_user_id(&user_id)?;
|
||||
let displayname = services()
|
||||
.users
|
||||
.displayname(&user_id)?
|
||||
.unwrap_or_else(|| user_id.to_string());
|
||||
|
||||
assert!(
|
||||
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?;
|
||||
assert!(service::user_is_local(&user_id), "Parsed user_id must be a local user");
|
||||
service::admin::make_user_admin(&user_id, displayname).await?;
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"{user_id} has been granted admin privileges.",
|
||||
)))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn put_room_tag(
|
||||
&self, user_id: String, room_id: Box<RoomId>, tag: String,
|
||||
_body: Vec<&str>, user_id: String, room_id: Box<RoomId>, tag: String,
|
||||
) -> 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 = self
|
||||
.services
|
||||
let event = services()
|
||||
.account_data
|
||||
.get(Some(&room_id), &user_id, RoomAccountDataEventType::Tag)?;
|
||||
|
||||
@@ -416,7 +386,7 @@ pub(super) async fn put_room_tag(
|
||||
.tags
|
||||
.insert(tag.clone().into(), TagInfo::new());
|
||||
|
||||
self.services.account_data.update(
|
||||
services().account_data.update(
|
||||
Some(&room_id),
|
||||
&user_id,
|
||||
RoomAccountDataEventType::Tag,
|
||||
@@ -428,14 +398,12 @@ pub(super) async fn put_room_tag(
|
||||
)))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn delete_room_tag(
|
||||
&self, user_id: String, room_id: Box<RoomId>, tag: String,
|
||||
_body: Vec<&str>, user_id: String, room_id: Box<RoomId>, tag: String,
|
||||
) -> 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 = self
|
||||
.services
|
||||
let event = services()
|
||||
.account_data
|
||||
.get(Some(&room_id), &user_id, RoomAccountDataEventType::Tag)?;
|
||||
|
||||
@@ -450,7 +418,7 @@ pub(super) async fn delete_room_tag(
|
||||
|
||||
tags_event.content.tags.remove(&tag.clone().into());
|
||||
|
||||
self.services.account_data.update(
|
||||
services().account_data.update(
|
||||
Some(&room_id),
|
||||
&user_id,
|
||||
RoomAccountDataEventType::Tag,
|
||||
@@ -462,12 +430,12 @@ pub(super) async fn delete_room_tag(
|
||||
)))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn get_room_tags(&self, user_id: String, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
|
||||
let user_id = parse_active_local_user_id(self.services, &user_id)?;
|
||||
pub(super) async fn get_room_tags(
|
||||
_body: Vec<&str>, user_id: String, room_id: Box<RoomId>,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
let user_id = parse_active_local_user_id(&user_id)?;
|
||||
|
||||
let event = self
|
||||
.services
|
||||
let event = services()
|
||||
.account_data
|
||||
.get(Some(&room_id), &user_id, RoomAccountDataEventType::Tag)?;
|
||||
|
||||
|
||||
+51
-8
@@ -2,16 +2,15 @@ mod commands;
|
||||
|
||||
use clap::Subcommand;
|
||||
use conduit::Result;
|
||||
use ruma::{OwnedRoomOrAliasId, RoomId};
|
||||
use ruma::{events::room::message::RoomMessageEventContent, OwnedRoomOrAliasId, RoomId};
|
||||
|
||||
use crate::admin_command_dispatch;
|
||||
use self::commands::*;
|
||||
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
#[derive(Subcommand)]
|
||||
pub(super) enum UserCommand {
|
||||
/// - Create a new user
|
||||
#[clap(alias = "create")]
|
||||
CreateUser {
|
||||
Create {
|
||||
/// Username of the new user
|
||||
username: String,
|
||||
/// Password of the new user, if unspecified one is generated
|
||||
@@ -58,8 +57,7 @@ pub(super) enum UserCommand {
|
||||
},
|
||||
|
||||
/// - List local users in the database
|
||||
#[clap(alias = "list")]
|
||||
ListUsers,
|
||||
List,
|
||||
|
||||
/// - Lists all the rooms (local and remote) that the specified user is
|
||||
/// joined in
|
||||
@@ -104,3 +102,48 @@ pub(super) enum UserCommand {
|
||||
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?,
|
||||
})
|
||||
}
|
||||
|
||||
+16
-14
@@ -1,6 +1,8 @@
|
||||
use conduit_core::{err, Err, Result};
|
||||
use conduit_core::{err, Err};
|
||||
use ruma::{OwnedRoomId, OwnedUserId, RoomId, UserId};
|
||||
use service::Services;
|
||||
use service::user_is_local;
|
||||
|
||||
use crate::{services, Result};
|
||||
|
||||
pub(crate) fn escape_html(s: &str) -> String {
|
||||
s.replace('&', "&")
|
||||
@@ -8,17 +10,17 @@ pub(crate) fn escape_html(s: &str) -> String {
|
||||
.replace('>', ">")
|
||||
}
|
||||
|
||||
pub(crate) fn get_room_info(services: &Services, id: &RoomId) -> (OwnedRoomId, u64, String) {
|
||||
pub(crate) fn get_room_info(id: &RoomId) -> (OwnedRoomId, u64, String) {
|
||||
(
|
||||
id.into(),
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_joined_count(id)
|
||||
.ok()
|
||||
.flatten()
|
||||
.unwrap_or(0),
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.get_name(id)
|
||||
@@ -29,16 +31,16 @@ pub(crate) fn get_room_info(services: &Services, id: &RoomId) -> (OwnedRoomId, u
|
||||
}
|
||||
|
||||
/// Parses user ID
|
||||
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())
|
||||
pub(crate) fn parse_user_id(user_id: &str) -> Result<OwnedUserId> {
|
||||
UserId::parse_with_server_name(user_id.to_lowercase(), services().globals.server_name())
|
||||
.map_err(|e| err!("The supplied username is not a valid username: {e}"))
|
||||
}
|
||||
|
||||
/// Parses user ID as our local user
|
||||
pub(crate) fn parse_local_user_id(services: &Services, user_id: &str) -> Result<OwnedUserId> {
|
||||
let user_id = parse_user_id(services, user_id)?;
|
||||
pub(crate) fn parse_local_user_id(user_id: &str) -> Result<OwnedUserId> {
|
||||
let user_id = parse_user_id(user_id)?;
|
||||
|
||||
if !services.globals.user_is_local(&user_id) {
|
||||
if !user_is_local(&user_id) {
|
||||
return Err!("User {user_id:?} does not belong to our server.");
|
||||
}
|
||||
|
||||
@@ -46,14 +48,14 @@ pub(crate) fn parse_local_user_id(services: &Services, user_id: &str) -> Result<
|
||||
}
|
||||
|
||||
/// Parses user ID that is an active (not guest or deactivated) local user
|
||||
pub(crate) fn parse_active_local_user_id(services: &Services, user_id: &str) -> Result<OwnedUserId> {
|
||||
let user_id = parse_local_user_id(services, user_id)?;
|
||||
pub(crate) fn parse_active_local_user_id(user_id: &str) -> Result<OwnedUserId> {
|
||||
let user_id = parse_local_user_id(user_id)?;
|
||||
|
||||
if !services.users.exists(&user_id)? {
|
||||
if !services().users.exists(&user_id)? {
|
||||
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.");
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ hmac.workspace = true
|
||||
http.workspace = true
|
||||
http-body-util.workspace = true
|
||||
hyper.workspace = true
|
||||
image.workspace = true
|
||||
ipaddress.workspace = true
|
||||
jsonwebtoken.workspace = true
|
||||
log.workspace = true
|
||||
@@ -59,6 +60,7 @@ serde.workspace = true
|
||||
sha-1.workspace = true
|
||||
tokio.workspace = true
|
||||
tracing.workspace = true
|
||||
webpage.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
+75
-74
@@ -1,8 +1,7 @@
|
||||
use std::fmt::Write;
|
||||
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::InsecureClientIp;
|
||||
use conduit::{debug_info, error, info, utils, warn, Error, Result};
|
||||
use conduit::debug_info;
|
||||
use register::RegistrationKind;
|
||||
use ruma::{
|
||||
api::client::{
|
||||
@@ -18,9 +17,15 @@ use ruma::{
|
||||
events::{room::message::RoomMessageEventContent, GlobalAccountDataEventType},
|
||||
push, OwnedRoomId, UserId,
|
||||
};
|
||||
use tracing::{error, info, warn};
|
||||
|
||||
use super::{join_room_by_id_helper, DEVICE_ID_LENGTH, SESSION_ID_LENGTH, TOKEN_LENGTH};
|
||||
use crate::Ruma;
|
||||
use crate::{
|
||||
service::user_is_local,
|
||||
services,
|
||||
utils::{self},
|
||||
Error, Result, Ruma,
|
||||
};
|
||||
|
||||
const RANDOM_USER_ID_LENGTH: usize = 10;
|
||||
|
||||
@@ -37,21 +42,20 @@ const RANDOM_USER_ID_LENGTH: usize = 10;
|
||||
/// invalid when trying to register
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "register_available")]
|
||||
pub(crate) async fn get_register_available_route(
|
||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||
body: Ruma<get_username_availability::v3::Request>,
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_username_availability::v3::Request>,
|
||||
) -> Result<get_username_availability::v3::Response> {
|
||||
// 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()
|
||||
.filter(|user_id| !user_id.is_historical() && services.globals.user_is_local(user_id))
|
||||
.filter(|user_id| !user_id.is_historical() && user_is_local(user_id))
|
||||
.ok_or(Error::BadRequest(ErrorKind::InvalidUsername, "Username is invalid."))?;
|
||||
|
||||
// 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."));
|
||||
}
|
||||
|
||||
if services
|
||||
if services()
|
||||
.globals
|
||||
.forbidden_usernames()
|
||||
.is_match(user_id.localpart())
|
||||
@@ -87,9 +91,9 @@ pub(crate) async fn get_register_available_route(
|
||||
#[allow(clippy::doc_markdown)]
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "register")]
|
||||
pub(crate) async fn register_route(
|
||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp, body: Ruma<register::v3::Request>,
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<register::v3::Request>,
|
||||
) -> 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!(
|
||||
"Registration disabled and request not from known appservice, rejecting registration attempt for username \
|
||||
{:?}",
|
||||
@@ -101,8 +105,8 @@ pub(crate) async fn register_route(
|
||||
let is_guest = body.kind == RegistrationKind::Guest;
|
||||
|
||||
if is_guest
|
||||
&& (!services.globals.allow_guest_registration()
|
||||
|| (services.globals.allow_registration() && services.globals.config.registration_token.is_some()))
|
||||
&& (!services().globals.allow_guest_registration()
|
||||
|| (services().globals.allow_registration() && services().globals.config.registration_token.is_some()))
|
||||
{
|
||||
info!(
|
||||
"Guest registration disabled / registration enabled with token configured, rejecting guest registration \
|
||||
@@ -117,7 +121,7 @@ pub(crate) async fn register_route(
|
||||
|
||||
// forbid guests from registering if there is not a real admin user yet. give
|
||||
// generic user error.
|
||||
if is_guest && services.users.count()? < 2 {
|
||||
if is_guest && services().users.count()? < 2 {
|
||||
warn!(
|
||||
"Guest account attempted to register before a real admin user has been registered, rejecting \
|
||||
registration. Guest's initial device name: {:?}",
|
||||
@@ -129,16 +133,16 @@ pub(crate) async fn register_route(
|
||||
let user_id = match (&body.username, is_guest) {
|
||||
(Some(username), false) => {
|
||||
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()
|
||||
.filter(|user_id| !user_id.is_historical() && services.globals.user_is_local(user_id))
|
||||
.filter(|user_id| !user_id.is_historical() && user_is_local(user_id))
|
||||
.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."));
|
||||
}
|
||||
|
||||
if services
|
||||
if services()
|
||||
.globals
|
||||
.forbidden_usernames()
|
||||
.is_match(proposed_user_id.localpart())
|
||||
@@ -151,10 +155,10 @@ pub(crate) async fn register_route(
|
||||
_ => loop {
|
||||
let proposed_user_id = UserId::parse_with_server_name(
|
||||
utils::random_string(RANDOM_USER_ID_LENGTH).to_lowercase(),
|
||||
services.globals.server_name(),
|
||||
services().globals.server_name(),
|
||||
)
|
||||
.unwrap();
|
||||
if !services.users.exists(&proposed_user_id)? {
|
||||
if !services().users.exists(&proposed_user_id)? {
|
||||
break proposed_user_id;
|
||||
}
|
||||
},
|
||||
@@ -168,13 +172,13 @@ pub(crate) async fn register_route(
|
||||
} else {
|
||||
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."));
|
||||
}
|
||||
|
||||
// UIAA
|
||||
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
|
||||
uiaainfo = UiaaInfo {
|
||||
flows: vec![AuthFlow {
|
||||
@@ -202,8 +206,8 @@ pub(crate) async fn register_route(
|
||||
|
||||
if !skip_auth {
|
||||
if let Some(auth) = &body.auth {
|
||||
let (worked, uiaainfo) = services.uiaa.try_auth(
|
||||
&UserId::parse_with_server_name("", services.globals.server_name()).expect("we know this is valid"),
|
||||
let (worked, uiaainfo) = services().uiaa.try_auth(
|
||||
&UserId::parse_with_server_name("", services().globals.server_name()).expect("we know this is valid"),
|
||||
"".into(),
|
||||
auth,
|
||||
&uiaainfo,
|
||||
@@ -214,8 +218,8 @@ pub(crate) async fn register_route(
|
||||
// Success!
|
||||
} else if let Some(json) = body.json_body {
|
||||
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
||||
services.uiaa.create(
|
||||
&UserId::parse_with_server_name("", services.globals.server_name()).expect("we know this is valid"),
|
||||
services().uiaa.create(
|
||||
&UserId::parse_with_server_name("", services().globals.server_name()).expect("we know this is valid"),
|
||||
"".into(),
|
||||
&uiaainfo,
|
||||
&json,
|
||||
@@ -233,25 +237,25 @@ pub(crate) async fn register_route(
|
||||
};
|
||||
|
||||
// Create user
|
||||
services.users.create(&user_id, password)?;
|
||||
services().users.create(&user_id, password)?;
|
||||
|
||||
// Default to pretty displayname
|
||||
let mut displayname = user_id.localpart().to_owned();
|
||||
|
||||
// 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
|
||||
if !services.globals.new_user_displayname_suffix().is_empty() {
|
||||
write!(displayname, " {}", services.globals.config.new_user_displayname_suffix)
|
||||
if !services().globals.new_user_displayname_suffix().is_empty() {
|
||||
write!(displayname, " {}", services().globals.config.new_user_displayname_suffix)
|
||||
.expect("should be able to write to string buffer");
|
||||
}
|
||||
|
||||
services
|
||||
services()
|
||||
.users
|
||||
.set_displayname(&user_id, Some(displayname.clone()))
|
||||
.await?;
|
||||
|
||||
// Initial account data
|
||||
services.account_data.update(
|
||||
services().account_data.update(
|
||||
None,
|
||||
&user_id,
|
||||
GlobalAccountDataEventType::PushRules.to_string().into(),
|
||||
@@ -286,7 +290,7 @@ pub(crate) async fn register_route(
|
||||
let token = utils::random_string(TOKEN_LENGTH);
|
||||
|
||||
// Create device for this account
|
||||
services
|
||||
services()
|
||||
.users
|
||||
.create_device(&user_id, &device_id, &token, body.initial_device_display_name.clone())?;
|
||||
|
||||
@@ -295,7 +299,7 @@ pub(crate) async fn register_route(
|
||||
// log in conduit admin channel if a non-guest user registered
|
||||
if body.appservice_info.is_none() && !is_guest {
|
||||
info!("New user \"{user_id}\" registered on this server.");
|
||||
services
|
||||
services()
|
||||
.admin
|
||||
.send_message(RoomMessageEventContent::notice_plain(format!(
|
||||
"New user \"{user_id}\" registered on this server from IP {client}."
|
||||
@@ -304,7 +308,7 @@ pub(crate) async fn register_route(
|
||||
}
|
||||
|
||||
// log in conduit admin channel if a guest registered
|
||||
if body.appservice_info.is_none() && is_guest && services.globals.log_guest_registrations() {
|
||||
if body.appservice_info.is_none() && is_guest && services().globals.log_guest_registrations() {
|
||||
info!("New guest user \"{user_id}\" registered on this server.");
|
||||
|
||||
if let Some(device_display_name) = &body.initial_device_display_name {
|
||||
@@ -313,7 +317,7 @@ pub(crate) async fn register_route(
|
||||
.as_ref()
|
||||
.is_some_and(|device_display_name| !device_display_name.is_empty())
|
||||
{
|
||||
services
|
||||
services()
|
||||
.admin
|
||||
.send_message(RoomMessageEventContent::notice_plain(format!(
|
||||
"Guest user \"{user_id}\" with device display name `{device_display_name}` registered on this \
|
||||
@@ -321,7 +325,7 @@ pub(crate) async fn register_route(
|
||||
)))
|
||||
.await;
|
||||
} else {
|
||||
services
|
||||
services()
|
||||
.admin
|
||||
.send_message(RoomMessageEventContent::notice_plain(format!(
|
||||
"Guest user \"{user_id}\" with no device display name registered on this server from IP \
|
||||
@@ -330,7 +334,7 @@ pub(crate) async fn register_route(
|
||||
.await;
|
||||
}
|
||||
} else {
|
||||
services
|
||||
services()
|
||||
.admin
|
||||
.send_message(RoomMessageEventContent::notice_plain(format!(
|
||||
"Guest user \"{user_id}\" with no device display name registered on this server from IP {client}.",
|
||||
@@ -342,12 +346,14 @@ pub(crate) async fn register_route(
|
||||
// If this is the first real user, grant them admin privileges except for guest
|
||||
// users Note: the server user, @conduit:servername, is generated first
|
||||
if !is_guest {
|
||||
if let Some(admin_room) = services.admin.get_admin_room()? {
|
||||
if services.rooms.state_cache.room_joined_count(&admin_room)? == Some(1) {
|
||||
services
|
||||
.admin
|
||||
.make_user_admin(&user_id, displayname)
|
||||
.await?;
|
||||
if let Some(admin_room) = service::admin::Service::get_admin_room()? {
|
||||
if services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_joined_count(&admin_room)?
|
||||
== Some(1)
|
||||
{
|
||||
service::admin::make_user_admin(&user_id, displayname).await?;
|
||||
|
||||
warn!("Granting {user_id} admin privileges as the first user");
|
||||
}
|
||||
@@ -355,14 +361,14 @@ pub(crate) async fn register_route(
|
||||
}
|
||||
|
||||
if body.appservice_info.is_none()
|
||||
&& !services.globals.config.auto_join_rooms.is_empty()
|
||||
&& (services.globals.allow_guests_auto_join_rooms() || !is_guest)
|
||||
&& !services().globals.config.auto_join_rooms.is_empty()
|
||||
&& (services().globals.allow_guests_auto_join_rooms() || !is_guest)
|
||||
{
|
||||
for room in &services.globals.config.auto_join_rooms {
|
||||
if !services
|
||||
for room in &services().globals.config.auto_join_rooms {
|
||||
if !services()
|
||||
.rooms
|
||||
.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.");
|
||||
continue;
|
||||
@@ -370,11 +376,10 @@ pub(crate) async fn register_route(
|
||||
|
||||
if let Some(room_id_server_name) = room.server_name() {
|
||||
if let Err(e) = join_room_by_id_helper(
|
||||
&services,
|
||||
&user_id,
|
||||
room,
|
||||
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,
|
||||
)
|
||||
.await
|
||||
@@ -416,8 +421,7 @@ pub(crate) async fn register_route(
|
||||
/// - Triggers device list updates
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "change_password")]
|
||||
pub(crate) async fn change_password_route(
|
||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||
body: Ruma<change_password::v3::Request>,
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<change_password::v3::Request>,
|
||||
) -> Result<change_password::v3::Response> {
|
||||
// Authentication for this endpoint was made optional, but we need
|
||||
// authentication currently
|
||||
@@ -438,7 +442,7 @@ pub(crate) async fn change_password_route(
|
||||
};
|
||||
|
||||
if let Some(auth) = &body.auth {
|
||||
let (worked, uiaainfo) = services
|
||||
let (worked, uiaainfo) = services()
|
||||
.uiaa
|
||||
.try_auth(sender_user, sender_device, auth, &uiaainfo)?;
|
||||
if !worked {
|
||||
@@ -447,7 +451,7 @@ pub(crate) async fn change_password_route(
|
||||
// Success!
|
||||
} else if let Some(json) = body.json_body {
|
||||
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
||||
services
|
||||
services()
|
||||
.uiaa
|
||||
.create(sender_user, sender_device, &uiaainfo, &json)?;
|
||||
return Err(Error::Uiaa(uiaainfo));
|
||||
@@ -455,24 +459,24 @@ pub(crate) async fn change_password_route(
|
||||
return Err(Error::BadRequest(ErrorKind::NotJson, "Not json."));
|
||||
}
|
||||
|
||||
services
|
||||
services()
|
||||
.users
|
||||
.set_password(sender_user, Some(&body.new_password))?;
|
||||
|
||||
if body.logout_devices {
|
||||
// Logout all devices except the current one
|
||||
for id in services
|
||||
for id in services()
|
||||
.users
|
||||
.all_device_ids(sender_user)
|
||||
.filter_map(Result::ok)
|
||||
.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.");
|
||||
services
|
||||
services()
|
||||
.admin
|
||||
.send_message(RoomMessageEventContent::notice_plain(format!(
|
||||
"User {sender_user} changed their password."
|
||||
@@ -487,16 +491,14 @@ pub(crate) async fn change_password_route(
|
||||
/// Get `user_id` of the sender user.
|
||||
///
|
||||
/// Note: Also works for Application Services
|
||||
pub(crate) async fn whoami_route(
|
||||
State(services): State<crate::State>, body: Ruma<whoami::v3::Request>,
|
||||
) -> Result<whoami::v3::Response> {
|
||||
pub(crate) async fn whoami_route(body: Ruma<whoami::v3::Request>) -> Result<whoami::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let device_id = body.sender_device.clone();
|
||||
|
||||
Ok(whoami::v3::Response {
|
||||
user_id: sender_user.clone(),
|
||||
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(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -513,8 +515,7 @@ pub(crate) async fn whoami_route(
|
||||
/// - Removes ability to log in again
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "deactivate")]
|
||||
pub(crate) async fn deactivate_route(
|
||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||
body: Ruma<deactivate::v3::Request>,
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<deactivate::v3::Request>,
|
||||
) -> Result<deactivate::v3::Response> {
|
||||
// Authentication for this endpoint was made optional, but we need
|
||||
// authentication currently
|
||||
@@ -535,7 +536,7 @@ pub(crate) async fn deactivate_route(
|
||||
};
|
||||
|
||||
if let Some(auth) = &body.auth {
|
||||
let (worked, uiaainfo) = services
|
||||
let (worked, uiaainfo) = services()
|
||||
.uiaa
|
||||
.try_auth(sender_user, sender_device, auth, &uiaainfo)?;
|
||||
if !worked {
|
||||
@@ -544,7 +545,7 @@ pub(crate) async fn deactivate_route(
|
||||
// Success!
|
||||
} else if let Some(json) = body.json_body {
|
||||
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
||||
services
|
||||
services()
|
||||
.uiaa
|
||||
.create(sender_user, sender_device, &uiaainfo, &json)?;
|
||||
return Err(Error::Uiaa(uiaainfo));
|
||||
@@ -553,23 +554,23 @@ pub(crate) async fn deactivate_route(
|
||||
}
|
||||
|
||||
// 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
|
||||
let all_joined_rooms: Vec<OwnedRoomId> = services
|
||||
let all_joined_rooms: Vec<OwnedRoomId> = services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.rooms_joined(sender_user)
|
||||
.filter_map(Result::ok)
|
||||
.collect();
|
||||
super::update_displayname(&services, sender_user.clone(), None, all_joined_rooms.clone()).await?;
|
||||
super::update_avatar_url(&services, sender_user.clone(), None, None, all_joined_rooms).await?;
|
||||
super::update_displayname(sender_user.clone(), None, all_joined_rooms.clone()).await?;
|
||||
super::update_avatar_url(sender_user.clone(), None, None, all_joined_rooms).await?;
|
||||
|
||||
// Make the user leave all rooms before deactivation
|
||||
super::leave_all_rooms(&services, sender_user).await;
|
||||
super::leave_all_rooms(sender_user).await;
|
||||
|
||||
info!("User {sender_user} deactivated their account.");
|
||||
services
|
||||
services()
|
||||
.admin
|
||||
.send_message(RoomMessageEventContent::notice_plain(format!(
|
||||
"User {sender_user} deactivated their account."
|
||||
@@ -631,9 +632,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
|
||||
/// there is only one registration token allowed.
|
||||
pub(crate) async fn check_registration_token_validity(
|
||||
State(services): State<crate::State>, body: Ruma<check_registration_token_validity::v1::Request>,
|
||||
body: Ruma<check_registration_token_validity::v1::Request>,
|
||||
) -> 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(
|
||||
ErrorKind::forbidden(),
|
||||
"Server does not allow token registration.",
|
||||
|
||||
+18
-34
@@ -1,5 +1,3 @@
|
||||
use axum::extract::State;
|
||||
use conduit::{debug, Error, Result};
|
||||
use rand::seq::SliceRandom;
|
||||
use ruma::{
|
||||
api::client::{
|
||||
@@ -8,27 +6,21 @@ use ruma::{
|
||||
},
|
||||
OwnedServerName, RoomAliasId, RoomId,
|
||||
};
|
||||
use service::Services;
|
||||
use tracing::debug;
|
||||
|
||||
use crate::Ruma;
|
||||
use crate::{service::server_is_ours, services, Error, Result, Ruma};
|
||||
|
||||
/// # `PUT /_matrix/client/v3/directory/room/{roomAlias}`
|
||||
///
|
||||
/// Creates a new room alias on this server.
|
||||
pub(crate) async fn create_alias_route(
|
||||
State(services): State<crate::State>, body: Ruma<create_alias::v3::Request>,
|
||||
) -> Result<create_alias::v3::Response> {
|
||||
pub(crate) async fn create_alias_route(body: Ruma<create_alias::v3::Request>) -> Result<create_alias::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
services
|
||||
.rooms
|
||||
.alias
|
||||
.appservice_checks(&body.room_alias, &body.appservice_info)
|
||||
.await?;
|
||||
service::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
|
||||
// allow removing forbidden room aliases
|
||||
if services
|
||||
if services()
|
||||
.globals
|
||||
.forbidden_alias_names()
|
||||
.is_match(body.room_alias.alias())
|
||||
@@ -36,7 +28,7 @@ pub(crate) async fn create_alias_route(
|
||||
return Err(Error::BadRequest(ErrorKind::forbidden(), "Room alias is forbidden."));
|
||||
}
|
||||
|
||||
if services
|
||||
if services()
|
||||
.rooms
|
||||
.alias
|
||||
.resolve_local_alias(&body.room_alias)?
|
||||
@@ -45,7 +37,7 @@ pub(crate) async fn create_alias_route(
|
||||
return Err(Error::Conflict("Alias already exists."));
|
||||
}
|
||||
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.alias
|
||||
.set_alias(&body.room_alias, &body.room_id, sender_user)?;
|
||||
@@ -58,18 +50,12 @@ pub(crate) async fn create_alias_route(
|
||||
/// Deletes a room alias from this server.
|
||||
///
|
||||
/// - TODO: Update canonical alias event
|
||||
pub(crate) async fn delete_alias_route(
|
||||
State(services): State<crate::State>, body: Ruma<delete_alias::v3::Request>,
|
||||
) -> Result<delete_alias::v3::Response> {
|
||||
pub(crate) async fn delete_alias_route(body: Ruma<delete_alias::v3::Request>) -> Result<delete_alias::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
services
|
||||
.rooms
|
||||
.alias
|
||||
.appservice_checks(&body.room_alias, &body.appservice_info)
|
||||
.await?;
|
||||
service::rooms::alias::appservice_checks(&body.room_alias, &body.appservice_info).await?;
|
||||
|
||||
if services
|
||||
if services()
|
||||
.rooms
|
||||
.alias
|
||||
.resolve_local_alias(&body.room_alias)?
|
||||
@@ -78,7 +64,7 @@ pub(crate) async fn delete_alias_route(
|
||||
return Err(Error::BadRequest(ErrorKind::NotFound, "Alias does not exist."));
|
||||
}
|
||||
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.alias
|
||||
.remove_alias(&body.room_alias, sender_user)
|
||||
@@ -92,13 +78,11 @@ pub(crate) async fn delete_alias_route(
|
||||
/// # `GET /_matrix/client/v3/directory/room/{roomAlias}`
|
||||
///
|
||||
/// Resolve an alias locally or over federation.
|
||||
pub(crate) async fn get_alias_route(
|
||||
State(services): State<crate::State>, body: Ruma<get_alias::v3::Request>,
|
||||
) -> Result<get_alias::v3::Response> {
|
||||
pub(crate) async fn get_alias_route(body: Ruma<get_alias::v3::Request>) -> Result<get_alias::v3::Response> {
|
||||
let room_alias = body.body.room_alias;
|
||||
let servers = None;
|
||||
|
||||
let Ok((room_id, pre_servers)) = services
|
||||
let Ok((room_id, pre_servers)) = services()
|
||||
.rooms
|
||||
.alias
|
||||
.resolve_alias(&room_alias, servers.as_ref())
|
||||
@@ -107,17 +91,17 @@ pub(crate) async fn get_alias_route(
|
||||
return Err(Error::BadRequest(ErrorKind::NotFound, "Room with alias not found."));
|
||||
};
|
||||
|
||||
let servers = room_available_servers(&services, &room_id, &room_alias, &pre_servers);
|
||||
let servers = room_available_servers(&room_id, &room_alias, &pre_servers);
|
||||
debug!(?room_alias, ?room_id, "available servers: {servers:?}");
|
||||
|
||||
Ok(get_alias::v3::Response::new(room_id, servers))
|
||||
}
|
||||
|
||||
fn room_available_servers(
|
||||
services: &Services, room_id: &RoomId, room_alias: &RoomAliasId, pre_servers: &Option<Vec<OwnedServerName>>,
|
||||
room_id: &RoomId, room_alias: &RoomAliasId, pre_servers: &Option<Vec<OwnedServerName>>,
|
||||
) -> Vec<OwnedServerName> {
|
||||
// find active servers in room state cache to suggest
|
||||
let mut servers: Vec<OwnedServerName> = services
|
||||
let mut servers: Vec<OwnedServerName> = services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_servers(room_id)
|
||||
@@ -140,10 +124,10 @@ fn room_available_servers(
|
||||
// prefer the room alias server first
|
||||
if let Some(server_index) = servers
|
||||
.iter()
|
||||
.position(|server_name| services.globals.server_is_ours(server_name))
|
||||
.position(|server_name| server_is_ours(server_name))
|
||||
{
|
||||
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
|
||||
.iter()
|
||||
.position(|server| server == room_alias.server_name())
|
||||
|
||||
+62
-49
@@ -1,4 +1,3 @@
|
||||
use axum::extract::State;
|
||||
use ruma::{
|
||||
api::client::{
|
||||
backup::{
|
||||
@@ -12,16 +11,16 @@ use ruma::{
|
||||
UInt,
|
||||
};
|
||||
|
||||
use crate::{Error, Result, Ruma};
|
||||
use crate::{services, Error, Result, Ruma};
|
||||
|
||||
/// # `POST /_matrix/client/r0/room_keys/version`
|
||||
///
|
||||
/// Creates a new backup.
|
||||
pub(crate) async fn create_backup_version_route(
|
||||
State(services): State<crate::State>, body: Ruma<create_backup_version::v3::Request>,
|
||||
body: Ruma<create_backup_version::v3::Request>,
|
||||
) -> Result<create_backup_version::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let version = services
|
||||
let version = services()
|
||||
.key_backups
|
||||
.create_backup(sender_user, &body.algorithm)?;
|
||||
|
||||
@@ -35,10 +34,10 @@ pub(crate) async fn create_backup_version_route(
|
||||
/// Update information about an existing backup. Only `auth_data` can be
|
||||
/// modified.
|
||||
pub(crate) async fn update_backup_version_route(
|
||||
State(services): State<crate::State>, body: Ruma<update_backup_version::v3::Request>,
|
||||
body: Ruma<update_backup_version::v3::Request>,
|
||||
) -> Result<update_backup_version::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
services
|
||||
services()
|
||||
.key_backups
|
||||
.update_backup(sender_user, &body.version, &body.algorithm)?;
|
||||
|
||||
@@ -49,20 +48,20 @@ pub(crate) async fn update_backup_version_route(
|
||||
///
|
||||
/// Get information about the latest backup version.
|
||||
pub(crate) async fn get_latest_backup_info_route(
|
||||
State(services): State<crate::State>, body: Ruma<get_latest_backup_info::v3::Request>,
|
||||
body: Ruma<get_latest_backup_info::v3::Request>,
|
||||
) -> Result<get_latest_backup_info::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
let (version, algorithm) = services
|
||||
let (version, algorithm) = services()
|
||||
.key_backups
|
||||
.get_latest_backup(sender_user)?
|
||||
.ok_or_else(|| Error::BadRequest(ErrorKind::NotFound, "Key backup does not exist."))?;
|
||||
|
||||
Ok(get_latest_backup_info::v3::Response {
|
||||
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")),
|
||||
etag: services.key_backups.get_etag(sender_user, &version)?,
|
||||
etag: services().key_backups.get_etag(sender_user, &version)?,
|
||||
version,
|
||||
})
|
||||
}
|
||||
@@ -71,10 +70,10 @@ pub(crate) async fn get_latest_backup_info_route(
|
||||
///
|
||||
/// Get information about an existing backup.
|
||||
pub(crate) async fn get_backup_info_route(
|
||||
State(services): State<crate::State>, body: Ruma<get_backup_info::v3::Request>,
|
||||
body: Ruma<get_backup_info::v3::Request>,
|
||||
) -> Result<get_backup_info::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let algorithm = services
|
||||
let algorithm = services()
|
||||
.key_backups
|
||||
.get_backup(sender_user, &body.version)?
|
||||
.ok_or_else(|| Error::BadRequest(ErrorKind::NotFound, "Key backup does not exist."))?;
|
||||
@@ -82,12 +81,14 @@ pub(crate) async fn get_backup_info_route(
|
||||
Ok(get_backup_info::v3::Response {
|
||||
algorithm,
|
||||
count: (UInt::try_from(
|
||||
services
|
||||
services()
|
||||
.key_backups
|
||||
.count_keys(sender_user, &body.version)?,
|
||||
)
|
||||
.expect("user backup keys count should not be that high")),
|
||||
etag: services.key_backups.get_etag(sender_user, &body.version)?,
|
||||
etag: services()
|
||||
.key_backups
|
||||
.get_etag(sender_user, &body.version)?,
|
||||
version: body.version.clone(),
|
||||
})
|
||||
}
|
||||
@@ -99,11 +100,11 @@ pub(crate) async fn get_backup_info_route(
|
||||
/// - Deletes both information about the backup, as well as all key data related
|
||||
/// to the backup
|
||||
pub(crate) async fn delete_backup_version_route(
|
||||
State(services): State<crate::State>, body: Ruma<delete_backup_version::v3::Request>,
|
||||
body: Ruma<delete_backup_version::v3::Request>,
|
||||
) -> Result<delete_backup_version::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
services
|
||||
services()
|
||||
.key_backups
|
||||
.delete_backup(sender_user, &body.version)?;
|
||||
|
||||
@@ -119,12 +120,12 @@ pub(crate) async fn delete_backup_version_route(
|
||||
/// - Adds the keys to the backup
|
||||
/// - Returns the new number of keys in this backup and the etag
|
||||
pub(crate) async fn add_backup_keys_route(
|
||||
State(services): State<crate::State>, body: Ruma<add_backup_keys::v3::Request>,
|
||||
body: Ruma<add_backup_keys::v3::Request>,
|
||||
) -> Result<add_backup_keys::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
if Some(&body.version)
|
||||
!= services
|
||||
!= services()
|
||||
.key_backups
|
||||
.get_latest_backup_version(sender_user)?
|
||||
.as_ref()
|
||||
@@ -137,7 +138,7 @@ pub(crate) async fn add_backup_keys_route(
|
||||
|
||||
for (room_id, room) in &body.rooms {
|
||||
for (session_id, key_data) in &room.sessions {
|
||||
services
|
||||
services()
|
||||
.key_backups
|
||||
.add_key(sender_user, &body.version, room_id, session_id, key_data)?;
|
||||
}
|
||||
@@ -145,12 +146,14 @@ pub(crate) async fn add_backup_keys_route(
|
||||
|
||||
Ok(add_backup_keys::v3::Response {
|
||||
count: (UInt::try_from(
|
||||
services
|
||||
services()
|
||||
.key_backups
|
||||
.count_keys(sender_user, &body.version)?,
|
||||
)
|
||||
.expect("user backup keys count should not be that high")),
|
||||
etag: services.key_backups.get_etag(sender_user, &body.version)?,
|
||||
etag: services()
|
||||
.key_backups
|
||||
.get_etag(sender_user, &body.version)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -163,12 +166,12 @@ pub(crate) async fn add_backup_keys_route(
|
||||
/// - Adds the keys to the backup
|
||||
/// - Returns the new number of keys in this backup and the etag
|
||||
pub(crate) async fn add_backup_keys_for_room_route(
|
||||
State(services): State<crate::State>, body: Ruma<add_backup_keys_for_room::v3::Request>,
|
||||
body: Ruma<add_backup_keys_for_room::v3::Request>,
|
||||
) -> Result<add_backup_keys_for_room::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
if Some(&body.version)
|
||||
!= services
|
||||
!= services()
|
||||
.key_backups
|
||||
.get_latest_backup_version(sender_user)?
|
||||
.as_ref()
|
||||
@@ -180,19 +183,21 @@ pub(crate) async fn add_backup_keys_for_room_route(
|
||||
}
|
||||
|
||||
for (session_id, key_data) in &body.sessions {
|
||||
services
|
||||
services()
|
||||
.key_backups
|
||||
.add_key(sender_user, &body.version, &body.room_id, session_id, key_data)?;
|
||||
}
|
||||
|
||||
Ok(add_backup_keys_for_room::v3::Response {
|
||||
count: (UInt::try_from(
|
||||
services
|
||||
services()
|
||||
.key_backups
|
||||
.count_keys(sender_user, &body.version)?,
|
||||
)
|
||||
.expect("user backup keys count should not be that high")),
|
||||
etag: services.key_backups.get_etag(sender_user, &body.version)?,
|
||||
etag: services()
|
||||
.key_backups
|
||||
.get_etag(sender_user, &body.version)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -205,12 +210,12 @@ pub(crate) async fn add_backup_keys_for_room_route(
|
||||
/// - Adds the keys to the backup
|
||||
/// - Returns the new number of keys in this backup and the etag
|
||||
pub(crate) async fn add_backup_keys_for_session_route(
|
||||
State(services): State<crate::State>, body: Ruma<add_backup_keys_for_session::v3::Request>,
|
||||
body: Ruma<add_backup_keys_for_session::v3::Request>,
|
||||
) -> Result<add_backup_keys_for_session::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
if Some(&body.version)
|
||||
!= services
|
||||
!= services()
|
||||
.key_backups
|
||||
.get_latest_backup_version(sender_user)?
|
||||
.as_ref()
|
||||
@@ -221,18 +226,20 @@ pub(crate) async fn add_backup_keys_for_session_route(
|
||||
));
|
||||
}
|
||||
|
||||
services
|
||||
services()
|
||||
.key_backups
|
||||
.add_key(sender_user, &body.version, &body.room_id, &body.session_id, &body.session_data)?;
|
||||
|
||||
Ok(add_backup_keys_for_session::v3::Response {
|
||||
count: (UInt::try_from(
|
||||
services
|
||||
services()
|
||||
.key_backups
|
||||
.count_keys(sender_user, &body.version)?,
|
||||
)
|
||||
.expect("user backup keys count should not be that high")),
|
||||
etag: services.key_backups.get_etag(sender_user, &body.version)?,
|
||||
etag: services()
|
||||
.key_backups
|
||||
.get_etag(sender_user, &body.version)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -240,11 +247,11 @@ pub(crate) async fn add_backup_keys_for_session_route(
|
||||
///
|
||||
/// Retrieves all keys from the backup.
|
||||
pub(crate) async fn get_backup_keys_route(
|
||||
State(services): State<crate::State>, body: Ruma<get_backup_keys::v3::Request>,
|
||||
body: Ruma<get_backup_keys::v3::Request>,
|
||||
) -> Result<get_backup_keys::v3::Response> {
|
||||
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 {
|
||||
rooms,
|
||||
@@ -255,11 +262,11 @@ pub(crate) async fn get_backup_keys_route(
|
||||
///
|
||||
/// Retrieves all keys from the backup for a given room.
|
||||
pub(crate) async fn get_backup_keys_for_room_route(
|
||||
State(services): State<crate::State>, body: Ruma<get_backup_keys_for_room::v3::Request>,
|
||||
body: Ruma<get_backup_keys_for_room::v3::Request>,
|
||||
) -> Result<get_backup_keys_for_room::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
let sessions = services
|
||||
let sessions = services()
|
||||
.key_backups
|
||||
.get_room(sender_user, &body.version, &body.room_id)?;
|
||||
|
||||
@@ -272,11 +279,11 @@ pub(crate) async fn get_backup_keys_for_room_route(
|
||||
///
|
||||
/// Retrieves a key from the backup.
|
||||
pub(crate) async fn get_backup_keys_for_session_route(
|
||||
State(services): State<crate::State>, body: Ruma<get_backup_keys_for_session::v3::Request>,
|
||||
body: Ruma<get_backup_keys_for_session::v3::Request>,
|
||||
) -> Result<get_backup_keys_for_session::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
let key_data = services
|
||||
let key_data = services()
|
||||
.key_backups
|
||||
.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."))?;
|
||||
@@ -290,22 +297,24 @@ pub(crate) async fn get_backup_keys_for_session_route(
|
||||
///
|
||||
/// Delete the keys from the backup.
|
||||
pub(crate) async fn delete_backup_keys_route(
|
||||
State(services): State<crate::State>, body: Ruma<delete_backup_keys::v3::Request>,
|
||||
body: Ruma<delete_backup_keys::v3::Request>,
|
||||
) -> Result<delete_backup_keys::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
services
|
||||
services()
|
||||
.key_backups
|
||||
.delete_all_keys(sender_user, &body.version)?;
|
||||
|
||||
Ok(delete_backup_keys::v3::Response {
|
||||
count: (UInt::try_from(
|
||||
services
|
||||
services()
|
||||
.key_backups
|
||||
.count_keys(sender_user, &body.version)?,
|
||||
)
|
||||
.expect("user backup keys count should not be that high")),
|
||||
etag: services.key_backups.get_etag(sender_user, &body.version)?,
|
||||
etag: services()
|
||||
.key_backups
|
||||
.get_etag(sender_user, &body.version)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -313,22 +322,24 @@ pub(crate) async fn delete_backup_keys_route(
|
||||
///
|
||||
/// Delete the keys from the backup for a given room.
|
||||
pub(crate) async fn delete_backup_keys_for_room_route(
|
||||
State(services): State<crate::State>, body: Ruma<delete_backup_keys_for_room::v3::Request>,
|
||||
body: Ruma<delete_backup_keys_for_room::v3::Request>,
|
||||
) -> Result<delete_backup_keys_for_room::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
services
|
||||
services()
|
||||
.key_backups
|
||||
.delete_room_keys(sender_user, &body.version, &body.room_id)?;
|
||||
|
||||
Ok(delete_backup_keys_for_room::v3::Response {
|
||||
count: (UInt::try_from(
|
||||
services
|
||||
services()
|
||||
.key_backups
|
||||
.count_keys(sender_user, &body.version)?,
|
||||
)
|
||||
.expect("user backup keys count should not be that high")),
|
||||
etag: services.key_backups.get_etag(sender_user, &body.version)?,
|
||||
etag: services()
|
||||
.key_backups
|
||||
.get_etag(sender_user, &body.version)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -336,21 +347,23 @@ pub(crate) async fn delete_backup_keys_for_room_route(
|
||||
///
|
||||
/// Delete a key from the backup.
|
||||
pub(crate) async fn delete_backup_keys_for_session_route(
|
||||
State(services): State<crate::State>, body: Ruma<delete_backup_keys_for_session::v3::Request>,
|
||||
body: Ruma<delete_backup_keys_for_session::v3::Request>,
|
||||
) -> Result<delete_backup_keys_for_session::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
services
|
||||
services()
|
||||
.key_backups
|
||||
.delete_room_key(sender_user, &body.version, &body.room_id, &body.session_id)?;
|
||||
|
||||
Ok(delete_backup_keys_for_session::v3::Response {
|
||||
count: (UInt::try_from(
|
||||
services
|
||||
services()
|
||||
.key_backups
|
||||
.count_keys(sender_user, &body.version)?,
|
||||
)
|
||||
.expect("user backup keys count should not be that high")),
|
||||
etag: services.key_backups.get_etag(sender_user, &body.version)?,
|
||||
etag: services()
|
||||
.key_backups
|
||||
.get_etag(sender_user, &body.version)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,30 +1,29 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use axum::extract::State;
|
||||
use ruma::api::client::discovery::get_capabilities::{
|
||||
self, Capabilities, RoomVersionStability, RoomVersionsCapability, ThirdPartyIdChangesCapability,
|
||||
};
|
||||
|
||||
use crate::{Result, Ruma};
|
||||
use crate::{services, Result, Ruma};
|
||||
|
||||
/// # `GET /_matrix/client/v3/capabilities`
|
||||
///
|
||||
/// Get information on the supported feature set and other relevent capabilities
|
||||
/// of this server.
|
||||
pub(crate) async fn get_capabilities_route(
|
||||
State(services): State<crate::State>, _body: Ruma<get_capabilities::v3::Request>,
|
||||
_body: Ruma<get_capabilities::v3::Request>,
|
||||
) -> Result<get_capabilities::v3::Response> {
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
let mut capabilities = Capabilities::default();
|
||||
capabilities.room_versions = RoomVersionsCapability {
|
||||
default: services.globals.default_room_version(),
|
||||
default: services().globals.default_room_version(),
|
||||
available,
|
||||
};
|
||||
|
||||
|
||||
+10
-19
@@ -1,4 +1,3 @@
|
||||
use axum::extract::State;
|
||||
use ruma::{
|
||||
api::client::{
|
||||
config::{get_global_account_data, get_room_account_data, set_global_account_data, set_room_account_data},
|
||||
@@ -11,21 +10,15 @@ use ruma::{
|
||||
use serde::Deserialize;
|
||||
use serde_json::{json, value::RawValue as RawJsonValue};
|
||||
|
||||
use crate::{service::Services, Error, Result, Ruma};
|
||||
use crate::{services, Error, Result, Ruma};
|
||||
|
||||
/// # `PUT /_matrix/client/r0/user/{userId}/account_data/{type}`
|
||||
///
|
||||
/// Sets some account data for the sender user.
|
||||
pub(crate) async fn set_global_account_data_route(
|
||||
State(services): State<crate::State>, body: Ruma<set_global_account_data::v3::Request>,
|
||||
body: Ruma<set_global_account_data::v3::Request>,
|
||||
) -> Result<set_global_account_data::v3::Response> {
|
||||
set_account_data(
|
||||
&services,
|
||||
None,
|
||||
&body.sender_user,
|
||||
&body.event_type.to_string(),
|
||||
body.data.json(),
|
||||
)?;
|
||||
set_account_data(None, &body.sender_user, &body.event_type.to_string(), body.data.json())?;
|
||||
|
||||
Ok(set_global_account_data::v3::Response {})
|
||||
}
|
||||
@@ -34,10 +27,9 @@ pub(crate) async fn set_global_account_data_route(
|
||||
///
|
||||
/// Sets some room account data for the sender user.
|
||||
pub(crate) async fn set_room_account_data_route(
|
||||
State(services): State<crate::State>, body: Ruma<set_room_account_data::v3::Request>,
|
||||
body: Ruma<set_room_account_data::v3::Request>,
|
||||
) -> Result<set_room_account_data::v3::Response> {
|
||||
set_account_data(
|
||||
&services,
|
||||
Some(&body.room_id),
|
||||
&body.sender_user,
|
||||
&body.event_type.to_string(),
|
||||
@@ -51,11 +43,11 @@ pub(crate) async fn set_room_account_data_route(
|
||||
///
|
||||
/// Gets some account data for the sender user.
|
||||
pub(crate) async fn get_global_account_data_route(
|
||||
State(services): State<crate::State>, body: Ruma<get_global_account_data::v3::Request>,
|
||||
body: Ruma<get_global_account_data::v3::Request>,
|
||||
) -> Result<get_global_account_data::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
let event: Box<RawJsonValue> = services
|
||||
let event: Box<RawJsonValue> = services()
|
||||
.account_data
|
||||
.get(None, sender_user, body.event_type.to_string().into())?
|
||||
.ok_or_else(|| Error::BadRequest(ErrorKind::NotFound, "Data not found."))?;
|
||||
@@ -73,11 +65,11 @@ pub(crate) async fn get_global_account_data_route(
|
||||
///
|
||||
/// Gets some room account data for the sender user.
|
||||
pub(crate) async fn get_room_account_data_route(
|
||||
State(services): State<crate::State>, body: Ruma<get_room_account_data::v3::Request>,
|
||||
body: Ruma<get_room_account_data::v3::Request>,
|
||||
) -> Result<get_room_account_data::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
let event: Box<RawJsonValue> = services
|
||||
let event: Box<RawJsonValue> = services()
|
||||
.account_data
|
||||
.get(Some(&body.room_id), sender_user, body.event_type.clone())?
|
||||
.ok_or_else(|| Error::BadRequest(ErrorKind::NotFound, "Data not found."))?;
|
||||
@@ -92,15 +84,14 @@ pub(crate) async fn get_room_account_data_route(
|
||||
}
|
||||
|
||||
fn set_account_data(
|
||||
services: &Services, room_id: Option<&RoomId>, sender_user: &Option<OwnedUserId>, event_type: &str,
|
||||
data: &RawJsonValue,
|
||||
room_id: Option<&RoomId>, sender_user: &Option<OwnedUserId>, event_type: &str, data: &RawJsonValue,
|
||||
) -> Result<()> {
|
||||
let sender_user = sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
let data: serde_json::Value =
|
||||
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,
|
||||
sender_user,
|
||||
event_type.into(),
|
||||
|
||||
+18
-21
@@ -1,13 +1,12 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use axum::extract::State;
|
||||
use ruma::{
|
||||
api::client::{context::get_context, error::ErrorKind, filter::LazyLoadOptions},
|
||||
events::StateEventType,
|
||||
};
|
||||
use tracing::error;
|
||||
|
||||
use crate::{Error, Result, Ruma};
|
||||
use crate::{services, Error, Result, Ruma};
|
||||
|
||||
/// # `GET /_matrix/client/r0/rooms/{roomId}/context`
|
||||
///
|
||||
@@ -15,9 +14,7 @@ use crate::{Error, Result, Ruma};
|
||||
///
|
||||
/// - Only works if the user is joined (TODO: always allow, but only show events
|
||||
/// if the user was joined, depending on history_visibility)
|
||||
pub(crate) async fn get_context_route(
|
||||
State(services): State<crate::State>, body: Ruma<get_context::v3::Request>,
|
||||
) -> Result<get_context::v3::Response> {
|
||||
pub(crate) async fn get_context_route(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_device = body.sender_device.as_ref().expect("user is authenticated");
|
||||
|
||||
@@ -30,13 +27,13 @@ pub(crate) async fn get_context_route(
|
||||
|
||||
let mut lazy_loaded = HashSet::new();
|
||||
|
||||
let base_token = services
|
||||
let base_token = services()
|
||||
.rooms
|
||||
.timeline
|
||||
.get_pdu_count(&body.event_id)?
|
||||
.ok_or(Error::BadRequest(ErrorKind::NotFound, "Base event id not found."))?;
|
||||
|
||||
let base_event = services
|
||||
let base_event = services()
|
||||
.rooms
|
||||
.timeline
|
||||
.get_pdu(&body.event_id)?
|
||||
@@ -44,7 +41,7 @@ pub(crate) async fn get_context_route(
|
||||
|
||||
let room_id = base_event.room_id.clone();
|
||||
|
||||
if !services
|
||||
if !services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.user_can_see_event(sender_user, &room_id, &body.event_id)?
|
||||
@@ -55,7 +52,7 @@ pub(crate) async fn get_context_route(
|
||||
));
|
||||
}
|
||||
|
||||
if !services.rooms.lazy_loading.lazy_load_was_sent_before(
|
||||
if !services().rooms.lazy_loading.lazy_load_was_sent_before(
|
||||
sender_user,
|
||||
sender_device,
|
||||
&room_id,
|
||||
@@ -70,14 +67,14 @@ pub(crate) async fn get_context_route(
|
||||
|
||||
let base_event = base_event.to_room_event();
|
||||
|
||||
let events_before: Vec<_> = services
|
||||
let events_before: Vec<_> = services()
|
||||
.rooms
|
||||
.timeline
|
||||
.pdus_until(sender_user, &room_id, base_token)?
|
||||
.take(limit / 2)
|
||||
.filter_map(Result::ok) // Remove buggy events
|
||||
.filter(|(_, pdu)| {
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.user_can_see_event(sender_user, &room_id, &pdu.event_id)
|
||||
@@ -86,7 +83,7 @@ pub(crate) async fn get_context_route(
|
||||
.collect();
|
||||
|
||||
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_device,
|
||||
&room_id,
|
||||
@@ -106,14 +103,14 @@ pub(crate) async fn get_context_route(
|
||||
.map(|(_, pdu)| pdu.to_room_event())
|
||||
.collect();
|
||||
|
||||
let events_after: Vec<_> = services
|
||||
let events_after: Vec<_> = services()
|
||||
.rooms
|
||||
.timeline
|
||||
.pdus_after(sender_user, &room_id, base_token)?
|
||||
.take(limit / 2)
|
||||
.filter_map(Result::ok) // Remove buggy events
|
||||
.filter(|(_, pdu)| {
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.user_can_see_event(sender_user, &room_id, &pdu.event_id)
|
||||
@@ -122,7 +119,7 @@ pub(crate) async fn get_context_route(
|
||||
.collect();
|
||||
|
||||
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_device,
|
||||
&room_id,
|
||||
@@ -133,7 +130,7 @@ pub(crate) async fn get_context_route(
|
||||
}
|
||||
}
|
||||
|
||||
let shortstatehash = services
|
||||
let shortstatehash = services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.pdu_shortstatehash(
|
||||
@@ -142,7 +139,7 @@ pub(crate) async fn get_context_route(
|
||||
.map_or(&*body.event_id, |(_, e)| &*e.event_id),
|
||||
)?
|
||||
.map_or(
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.state
|
||||
.get_room_shortstatehash(&room_id)?
|
||||
@@ -150,7 +147,7 @@ pub(crate) async fn get_context_route(
|
||||
|hash| hash,
|
||||
);
|
||||
|
||||
let state_ids = services
|
||||
let state_ids = services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.state_full_ids(shortstatehash)
|
||||
@@ -168,20 +165,20 @@ pub(crate) async fn get_context_route(
|
||||
let mut state = Vec::with_capacity(state_ids.len());
|
||||
|
||||
for (shortstatekey, id) in state_ids {
|
||||
let (event_type, state_key) = services
|
||||
let (event_type, state_key) = services()
|
||||
.rooms
|
||||
.short
|
||||
.get_statekey_from_short(shortstatekey)?;
|
||||
|
||||
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);
|
||||
continue;
|
||||
};
|
||||
|
||||
state.push(pdu.to_state_event());
|
||||
} 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);
|
||||
continue;
|
||||
};
|
||||
|
||||
+18
-25
@@ -1,4 +1,3 @@
|
||||
use axum::extract::State;
|
||||
use ruma::api::client::{
|
||||
device::{self, delete_device, delete_devices, get_device, get_devices, update_device},
|
||||
error::ErrorKind,
|
||||
@@ -6,17 +5,15 @@ use ruma::api::client::{
|
||||
};
|
||||
|
||||
use super::SESSION_ID_LENGTH;
|
||||
use crate::{utils, Error, Result, Ruma};
|
||||
use crate::{services, utils, Error, Result, Ruma};
|
||||
|
||||
/// # `GET /_matrix/client/r0/devices`
|
||||
///
|
||||
/// Get metadata on all devices of the sender user.
|
||||
pub(crate) async fn get_devices_route(
|
||||
State(services): State<crate::State>, body: Ruma<get_devices::v3::Request>,
|
||||
) -> Result<get_devices::v3::Response> {
|
||||
pub(crate) async fn get_devices_route(body: Ruma<get_devices::v3::Request>) -> Result<get_devices::v3::Response> {
|
||||
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
|
||||
.all_devices_metadata(sender_user)
|
||||
.filter_map(Result::ok) // Filter out buggy devices
|
||||
@@ -30,12 +27,10 @@ pub(crate) async fn get_devices_route(
|
||||
/// # `GET /_matrix/client/r0/devices/{deviceId}`
|
||||
///
|
||||
/// Get metadata on a single device of the sender user.
|
||||
pub(crate) async fn get_device_route(
|
||||
State(services): State<crate::State>, body: Ruma<get_device::v3::Request>,
|
||||
) -> Result<get_device::v3::Response> {
|
||||
pub(crate) async fn get_device_route(body: Ruma<get_device::v3::Request>) -> Result<get_device::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
let device = services
|
||||
let device = services()
|
||||
.users
|
||||
.get_device_metadata(sender_user, &body.body.device_id)?
|
||||
.ok_or(Error::BadRequest(ErrorKind::NotFound, "Device not found."))?;
|
||||
@@ -48,19 +43,17 @@ pub(crate) async fn get_device_route(
|
||||
/// # `PUT /_matrix/client/r0/devices/{deviceId}`
|
||||
///
|
||||
/// Updates the metadata on a given device of the sender user.
|
||||
pub(crate) async fn update_device_route(
|
||||
State(services): State<crate::State>, body: Ruma<update_device::v3::Request>,
|
||||
) -> Result<update_device::v3::Response> {
|
||||
pub(crate) async fn update_device_route(body: Ruma<update_device::v3::Request>) -> Result<update_device::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
let mut device = services
|
||||
let mut device = services()
|
||||
.users
|
||||
.get_device_metadata(sender_user, &body.device_id)?
|
||||
.ok_or(Error::BadRequest(ErrorKind::NotFound, "Device not found."))?;
|
||||
|
||||
device.display_name.clone_from(&body.display_name);
|
||||
|
||||
services
|
||||
services()
|
||||
.users
|
||||
.update_device_metadata(sender_user, &body.device_id, &device)?;
|
||||
|
||||
@@ -77,9 +70,7 @@ pub(crate) async fn update_device_route(
|
||||
/// last seen ts)
|
||||
/// - Forgets to-device events
|
||||
/// - Triggers device list updates
|
||||
pub(crate) async fn delete_device_route(
|
||||
State(services): State<crate::State>, body: Ruma<delete_device::v3::Request>,
|
||||
) -> Result<delete_device::v3::Response> {
|
||||
pub(crate) async fn delete_device_route(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_device = body.sender_device.as_ref().expect("user is authenticated");
|
||||
|
||||
@@ -95,7 +86,7 @@ pub(crate) async fn delete_device_route(
|
||||
};
|
||||
|
||||
if let Some(auth) = &body.auth {
|
||||
let (worked, uiaainfo) = services
|
||||
let (worked, uiaainfo) = services()
|
||||
.uiaa
|
||||
.try_auth(sender_user, sender_device, auth, &uiaainfo)?;
|
||||
if !worked {
|
||||
@@ -104,7 +95,7 @@ pub(crate) async fn delete_device_route(
|
||||
// Success!
|
||||
} else if let Some(json) = body.json_body {
|
||||
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
||||
services
|
||||
services()
|
||||
.uiaa
|
||||
.create(sender_user, sender_device, &uiaainfo, &json)?;
|
||||
return Err(Error::Uiaa(uiaainfo));
|
||||
@@ -112,7 +103,9 @@ pub(crate) async fn delete_device_route(
|
||||
return Err(Error::BadRequest(ErrorKind::NotJson, "Not json."));
|
||||
}
|
||||
|
||||
services.users.remove_device(sender_user, &body.device_id)?;
|
||||
services()
|
||||
.users
|
||||
.remove_device(sender_user, &body.device_id)?;
|
||||
|
||||
Ok(delete_device::v3::Response {})
|
||||
}
|
||||
@@ -130,7 +123,7 @@ pub(crate) async fn delete_device_route(
|
||||
/// - Forgets to-device events
|
||||
/// - Triggers device list updates
|
||||
pub(crate) async fn delete_devices_route(
|
||||
State(services): State<crate::State>, body: Ruma<delete_devices::v3::Request>,
|
||||
body: Ruma<delete_devices::v3::Request>,
|
||||
) -> Result<delete_devices::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
||||
@@ -147,7 +140,7 @@ pub(crate) async fn delete_devices_route(
|
||||
};
|
||||
|
||||
if let Some(auth) = &body.auth {
|
||||
let (worked, uiaainfo) = services
|
||||
let (worked, uiaainfo) = services()
|
||||
.uiaa
|
||||
.try_auth(sender_user, sender_device, auth, &uiaainfo)?;
|
||||
if !worked {
|
||||
@@ -156,7 +149,7 @@ pub(crate) async fn delete_devices_route(
|
||||
// Success!
|
||||
} else if let Some(json) = body.json_body {
|
||||
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
||||
services
|
||||
services()
|
||||
.uiaa
|
||||
.create(sender_user, sender_device, &uiaainfo, &json)?;
|
||||
return Err(Error::Uiaa(uiaainfo));
|
||||
@@ -165,7 +158,7 @@ pub(crate) async fn delete_devices_route(
|
||||
}
|
||||
|
||||
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 {})
|
||||
|
||||
+33
-45
@@ -1,4 +1,3 @@
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::InsecureClientIp;
|
||||
use conduit::{err, info, warn, Error, Result};
|
||||
use ruma::{
|
||||
@@ -20,9 +19,8 @@ use ruma::{
|
||||
},
|
||||
uint, RoomId, ServerName, UInt, UserId,
|
||||
};
|
||||
use service::Services;
|
||||
|
||||
use crate::Ruma;
|
||||
use crate::{service::server_is_ours, services, Ruma};
|
||||
|
||||
/// # `POST /_matrix/client/v3/publicRooms`
|
||||
///
|
||||
@@ -31,11 +29,10 @@ use crate::Ruma;
|
||||
/// - Rooms are ordered by the number of joined members
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "publicrooms")]
|
||||
pub(crate) async fn get_public_rooms_filtered_route(
|
||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||
body: Ruma<get_public_rooms_filtered::v3::Request>,
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_public_rooms_filtered::v3::Request>,
|
||||
) -> Result<get_public_rooms_filtered::v3::Response> {
|
||||
if let Some(server) = &body.server {
|
||||
if services
|
||||
if services()
|
||||
.globals
|
||||
.forbidden_remote_room_directory_server_names()
|
||||
.contains(server)
|
||||
@@ -48,7 +45,6 @@ pub(crate) async fn get_public_rooms_filtered_route(
|
||||
}
|
||||
|
||||
let response = get_public_rooms_filtered_helper(
|
||||
&services,
|
||||
body.server.as_deref(),
|
||||
body.limit,
|
||||
body.since.as_deref(),
|
||||
@@ -71,11 +67,10 @@ pub(crate) async fn get_public_rooms_filtered_route(
|
||||
/// - Rooms are ordered by the number of joined members
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "publicrooms")]
|
||||
pub(crate) async fn get_public_rooms_route(
|
||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||
body: Ruma<get_public_rooms::v3::Request>,
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_public_rooms::v3::Request>,
|
||||
) -> Result<get_public_rooms::v3::Response> {
|
||||
if let Some(server) = &body.server {
|
||||
if services
|
||||
if services()
|
||||
.globals
|
||||
.forbidden_remote_room_directory_server_names()
|
||||
.contains(server)
|
||||
@@ -88,7 +83,6 @@ pub(crate) async fn get_public_rooms_route(
|
||||
}
|
||||
|
||||
let response = get_public_rooms_filtered_helper(
|
||||
&services,
|
||||
body.server.as_deref(),
|
||||
body.limit,
|
||||
body.since.as_deref(),
|
||||
@@ -114,26 +108,20 @@ pub(crate) async fn get_public_rooms_route(
|
||||
/// Sets the visibility of a given room in the room directory.
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "room_directory")]
|
||||
pub(crate) async fn set_room_visibility_route(
|
||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||
body: Ruma<set_room_visibility::v3::Request>,
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<set_room_visibility::v3::Request>,
|
||||
) -> Result<set_room_visibility::v3::Response> {
|
||||
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 Err(Error::BadRequest(ErrorKind::NotFound, "Room not found"));
|
||||
}
|
||||
|
||||
if !user_can_publish_room(&services, sender_user, &body.room_id)? {
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::forbidden(),
|
||||
"User is not allowed to publish this room",
|
||||
));
|
||||
}
|
||||
user_can_publish_room(sender_user, &body.room_id)?;
|
||||
|
||||
match &body.visibility {
|
||||
room::Visibility::Public => {
|
||||
if services.globals.config.lockdown_public_room_directory && !services.users.is_admin(sender_user)? {
|
||||
if services().globals.config.lockdown_public_room_directory && !services().users.is_admin(sender_user)? {
|
||||
info!(
|
||||
"Non-admin user {sender_user} tried to publish {0} to the room directory while \
|
||||
\"lockdown_public_room_directory\" is enabled",
|
||||
@@ -146,10 +134,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);
|
||||
},
|
||||
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(
|
||||
ErrorKind::InvalidParam,
|
||||
@@ -165,15 +153,15 @@ pub(crate) async fn set_room_visibility_route(
|
||||
///
|
||||
/// Gets the visibility of a given room in the room directory.
|
||||
pub(crate) async fn get_room_visibility_route(
|
||||
State(services): State<crate::State>, body: Ruma<get_room_visibility::v3::Request>,
|
||||
body: Ruma<get_room_visibility::v3::Request>,
|
||||
) -> 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 Err(Error::BadRequest(ErrorKind::NotFound, "Room not found"));
|
||||
}
|
||||
|
||||
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
|
||||
} else {
|
||||
room::Visibility::Private
|
||||
@@ -182,11 +170,10 @@ pub(crate) async fn get_room_visibility_route(
|
||||
}
|
||||
|
||||
pub(crate) async fn get_public_rooms_filtered_helper(
|
||||
services: &Services, server: Option<&ServerName>, limit: Option<UInt>, since: Option<&str>, filter: &Filter,
|
||||
_network: &RoomNetwork,
|
||||
server: Option<&ServerName>, limit: Option<UInt>, since: Option<&str>, filter: &Filter, _network: &RoomNetwork,
|
||||
) -> Result<get_public_rooms_filtered::v3::Response> {
|
||||
if let Some(other_server) = server.filter(|server_name| !services.globals.server_is_ours(server_name)) {
|
||||
let response = services
|
||||
if let Some(other_server) = server.filter(|server_name| !server_is_ours(server_name)) {
|
||||
let response = services()
|
||||
.sending
|
||||
.send_federation_request(
|
||||
other_server,
|
||||
@@ -232,7 +219,7 @@ pub(crate) async fn get_public_rooms_filtered_helper(
|
||||
}
|
||||
}
|
||||
|
||||
let mut all_rooms: Vec<_> = services
|
||||
let mut all_rooms: Vec<_> = services()
|
||||
.rooms
|
||||
.directory
|
||||
.public_rooms()
|
||||
@@ -240,12 +227,12 @@ pub(crate) async fn get_public_rooms_filtered_helper(
|
||||
let room_id = room_id?;
|
||||
|
||||
let chunk = PublicRoomsChunk {
|
||||
canonical_alias: services
|
||||
canonical_alias: services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.get_canonical_alias(&room_id)?,
|
||||
name: services.rooms.state_accessor.get_name(&room_id)?,
|
||||
num_joined_members: services
|
||||
name: services().rooms.state_accessor.get_name(&room_id)?,
|
||||
num_joined_members: services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_joined_count(&room_id)?
|
||||
@@ -255,24 +242,24 @@ pub(crate) async fn get_public_rooms_filtered_helper(
|
||||
})
|
||||
.try_into()
|
||||
.expect("user count should not be that big"),
|
||||
topic: services
|
||||
topic: services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.get_room_topic(&room_id)
|
||||
.unwrap_or(None),
|
||||
world_readable: services.rooms.state_accessor.is_world_readable(&room_id)?,
|
||||
guest_can_join: services
|
||||
world_readable: services().rooms.state_accessor.is_world_readable(&room_id)?,
|
||||
guest_can_join: services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.guest_can_join(&room_id)?,
|
||||
avatar_url: services
|
||||
avatar_url: services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.get_avatar(&room_id)?
|
||||
.into_option()
|
||||
.unwrap_or_default()
|
||||
.url,
|
||||
join_rule: services
|
||||
join_rule: services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get(&room_id, &StateEventType::RoomJoinRules, "")?
|
||||
@@ -290,7 +277,7 @@ pub(crate) async fn get_public_rooms_filtered_helper(
|
||||
.transpose()?
|
||||
.flatten()
|
||||
.ok_or_else(|| Error::bad_database("Missing room join rule event for room."))?,
|
||||
room_type: services
|
||||
room_type: services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.get_room_type(&room_id)?,
|
||||
@@ -369,8 +356,9 @@ pub(crate) async fn get_public_rooms_filtered_helper(
|
||||
|
||||
/// Check whether the user can publish to the room directory via power levels of
|
||||
/// room history visibility event or room creator
|
||||
fn user_can_publish_room(services: &Services, user_id: &UserId, room_id: &RoomId) -> Result<bool> {
|
||||
if let Some(event) = services
|
||||
fn user_can_publish_room(user_id: &UserId, room_id: &RoomId) -> Result<bool> {
|
||||
if let Some(event) =
|
||||
services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get(room_id, &StateEventType::RoomPowerLevels, "")?
|
||||
@@ -381,7 +369,7 @@ fn user_can_publish_room(services: &Services, user_id: &UserId, room_id: &RoomId
|
||||
RoomPowerLevels::from(content).user_can_send_state(user_id, StateEventType::RoomHistoryVisibility)
|
||||
})
|
||||
} else if let Some(event) =
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get(room_id, &StateEventType::RoomCreate, "")?
|
||||
@@ -389,8 +377,8 @@ fn user_can_publish_room(services: &Services, user_id: &UserId, room_id: &RoomId
|
||||
Ok(event.sender == user_id)
|
||||
} else {
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::forbidden(),
|
||||
"User is not allowed to publish this room",
|
||||
ErrorKind::Unauthorized,
|
||||
"You are not allowed to publish this room to the room directory",
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,18 @@
|
||||
use axum::extract::State;
|
||||
use ruma::api::client::{
|
||||
error::ErrorKind,
|
||||
filter::{create_filter, get_filter},
|
||||
};
|
||||
|
||||
use crate::{Error, Result, Ruma};
|
||||
use crate::{services, Error, Result, Ruma};
|
||||
|
||||
/// # `GET /_matrix/client/r0/user/{userId}/filter/{filterId}`
|
||||
///
|
||||
/// Loads a filter that was previously created.
|
||||
///
|
||||
/// - A user can only access their own filters
|
||||
pub(crate) async fn get_filter_route(
|
||||
State(services): State<crate::State>, body: Ruma<get_filter::v3::Request>,
|
||||
) -> Result<get_filter::v3::Response> {
|
||||
pub(crate) async fn get_filter_route(body: Ruma<get_filter::v3::Request>) -> Result<get_filter::v3::Response> {
|
||||
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."));
|
||||
};
|
||||
|
||||
@@ -25,11 +22,9 @@ pub(crate) async fn get_filter_route(
|
||||
/// # `PUT /_matrix/client/r0/user/{userId}/filter`
|
||||
///
|
||||
/// Creates a new filter to be used by other endpoints.
|
||||
pub(crate) async fn create_filter_route(
|
||||
State(services): State<crate::State>, body: Ruma<create_filter::v3::Request>,
|
||||
) -> Result<create_filter::v3::Response> {
|
||||
pub(crate) async fn create_filter_route(body: Ruma<create_filter::v3::Request>) -> Result<create_filter::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
Ok(create_filter::v3::Response::new(
|
||||
services.users.create_filter(sender_user, &body.filter)?,
|
||||
services().users.create_filter(sender_user, &body.filter)?,
|
||||
))
|
||||
}
|
||||
|
||||
+46
-51
@@ -3,8 +3,7 @@ use std::{
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
use axum::extract::State;
|
||||
use conduit::{utils, utils::math::continue_exponential_backoff_secs, Err, Error, Result};
|
||||
use conduit::{utils, utils::math::continue_exponential_backoff_secs, Error, Result};
|
||||
use futures_util::{stream::FuturesUnordered, StreamExt};
|
||||
use ruma::{
|
||||
api::{
|
||||
@@ -19,9 +18,11 @@ use ruma::{
|
||||
DeviceKeyAlgorithm, OwnedDeviceId, OwnedUserId, UserId,
|
||||
};
|
||||
use serde_json::json;
|
||||
use service::user_is_local;
|
||||
use tracing::debug;
|
||||
|
||||
use super::SESSION_ID_LENGTH;
|
||||
use crate::{service::Services, Ruma};
|
||||
use crate::{services, Ruma};
|
||||
|
||||
/// # `POST /_matrix/client/r0/keys/upload`
|
||||
///
|
||||
@@ -30,14 +31,12 @@ use crate::{service::Services, Ruma};
|
||||
/// - Adds one time keys
|
||||
/// - If there are no device keys yet: Adds device keys (TODO: merge with
|
||||
/// existing keys?)
|
||||
pub(crate) async fn upload_keys_route(
|
||||
State(services): State<crate::State>, body: Ruma<upload_keys::v3::Request>,
|
||||
) -> Result<upload_keys::v3::Response> {
|
||||
pub(crate) async fn upload_keys_route(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_device = body.sender_device.as_ref().expect("user is authenticated");
|
||||
|
||||
for (key_key, key_value) in &body.one_time_keys {
|
||||
services
|
||||
services()
|
||||
.users
|
||||
.add_one_time_key(sender_user, sender_device, key_key, key_value)?;
|
||||
}
|
||||
@@ -45,19 +44,19 @@ pub(crate) async fn upload_keys_route(
|
||||
if let Some(device_keys) = &body.device_keys {
|
||||
// TODO: merge this and the existing event?
|
||||
// This check is needed to assure that signatures are kept
|
||||
if services
|
||||
if services()
|
||||
.users
|
||||
.get_device_keys(sender_user, sender_device)?
|
||||
.is_none()
|
||||
{
|
||||
services
|
||||
services()
|
||||
.users
|
||||
.add_device_keys(sender_user, sender_device, device_keys)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(upload_keys::v3::Response {
|
||||
one_time_key_counts: services
|
||||
one_time_key_counts: services()
|
||||
.users
|
||||
.count_one_time_keys(sender_user, sender_device)?,
|
||||
})
|
||||
@@ -71,13 +70,10 @@ pub(crate) async fn upload_keys_route(
|
||||
/// - Gets master keys, self-signing keys, user signing keys and device keys.
|
||||
/// - The master and self-signing keys contain signatures that the user is
|
||||
/// allowed to see
|
||||
pub(crate) async fn get_keys_route(
|
||||
State(services): State<crate::State>, body: Ruma<get_keys::v3::Request>,
|
||||
) -> Result<get_keys::v3::Response> {
|
||||
pub(crate) async fn get_keys_route(body: Ruma<get_keys::v3::Request>) -> Result<get_keys::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
get_keys_helper(
|
||||
&services,
|
||||
Some(sender_user),
|
||||
&body.device_keys,
|
||||
|u| u == sender_user,
|
||||
@@ -89,10 +85,8 @@ pub(crate) async fn get_keys_route(
|
||||
/// # `POST /_matrix/client/r0/keys/claim`
|
||||
///
|
||||
/// Claims one-time keys
|
||||
pub(crate) async fn claim_keys_route(
|
||||
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
|
||||
pub(crate) async fn claim_keys_route(body: Ruma<claim_keys::v3::Request>) -> Result<claim_keys::v3::Response> {
|
||||
claim_keys_helper(&body.one_time_keys).await
|
||||
}
|
||||
|
||||
/// # `POST /_matrix/client/r0/keys/device_signing/upload`
|
||||
@@ -101,7 +95,7 @@ pub(crate) async fn claim_keys_route(
|
||||
///
|
||||
/// - Requires UIAA to verify password
|
||||
pub(crate) async fn upload_signing_keys_route(
|
||||
State(services): State<crate::State>, body: Ruma<upload_signing_keys::v3::Request>,
|
||||
body: Ruma<upload_signing_keys::v3::Request>,
|
||||
) -> Result<upload_signing_keys::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
||||
@@ -118,7 +112,7 @@ pub(crate) async fn upload_signing_keys_route(
|
||||
};
|
||||
|
||||
if let Some(auth) = &body.auth {
|
||||
let (worked, uiaainfo) = services
|
||||
let (worked, uiaainfo) = services()
|
||||
.uiaa
|
||||
.try_auth(sender_user, sender_device, auth, &uiaainfo)?;
|
||||
if !worked {
|
||||
@@ -127,7 +121,7 @@ pub(crate) async fn upload_signing_keys_route(
|
||||
// Success!
|
||||
} else if let Some(json) = body.json_body {
|
||||
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
||||
services
|
||||
services()
|
||||
.uiaa
|
||||
.create(sender_user, sender_device, &uiaainfo, &json)?;
|
||||
return Err(Error::Uiaa(uiaainfo));
|
||||
@@ -136,7 +130,7 @@ pub(crate) async fn upload_signing_keys_route(
|
||||
}
|
||||
|
||||
if let Some(master_key) = &body.master_key {
|
||||
services.users.add_cross_signing_keys(
|
||||
services().users.add_cross_signing_keys(
|
||||
sender_user,
|
||||
master_key,
|
||||
&body.self_signing_key,
|
||||
@@ -152,7 +146,7 @@ pub(crate) async fn upload_signing_keys_route(
|
||||
///
|
||||
/// Uploads end-to-end key signatures from the sender user.
|
||||
pub(crate) async fn upload_signatures_route(
|
||||
State(services): State<crate::State>, body: Ruma<upload_signatures::v3::Request>,
|
||||
body: Ruma<upload_signatures::v3::Request>,
|
||||
) -> Result<upload_signatures::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
@@ -179,7 +173,7 @@ pub(crate) async fn upload_signatures_route(
|
||||
.ok_or(Error::BadRequest(ErrorKind::InvalidParam, "Invalid signature value."))?
|
||||
.to_owned(),
|
||||
);
|
||||
services
|
||||
services()
|
||||
.users
|
||||
.sign_key(user_id, key_id, signature, sender_user)?;
|
||||
}
|
||||
@@ -198,14 +192,14 @@ pub(crate) async fn upload_signatures_route(
|
||||
///
|
||||
/// - TODO: left users
|
||||
pub(crate) async fn get_key_changes_route(
|
||||
State(services): State<crate::State>, body: Ruma<get_key_changes::v3::Request>,
|
||||
body: Ruma<get_key_changes::v3::Request>,
|
||||
) -> Result<get_key_changes::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
let mut device_list_updates = HashSet::new();
|
||||
|
||||
device_list_updates.extend(
|
||||
services
|
||||
services()
|
||||
.users
|
||||
.keys_changed(
|
||||
sender_user.as_str(),
|
||||
@@ -221,14 +215,14 @@ pub(crate) async fn get_key_changes_route(
|
||||
.filter_map(Result::ok),
|
||||
);
|
||||
|
||||
for room_id in services
|
||||
for room_id in services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.rooms_joined(sender_user)
|
||||
.filter_map(Result::ok)
|
||||
{
|
||||
device_list_updates.extend(
|
||||
services
|
||||
services()
|
||||
.users
|
||||
.keys_changed(
|
||||
room_id.as_ref(),
|
||||
@@ -251,8 +245,8 @@ pub(crate) async fn get_key_changes_route(
|
||||
}
|
||||
|
||||
pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool + Send>(
|
||||
services: &Services, sender_user: Option<&UserId>, device_keys_input: &BTreeMap<OwnedUserId, Vec<OwnedDeviceId>>,
|
||||
allowed_signatures: F, include_display_names: bool,
|
||||
sender_user: Option<&UserId>, device_keys_input: &BTreeMap<OwnedUserId, Vec<OwnedDeviceId>>, allowed_signatures: F,
|
||||
include_display_names: bool,
|
||||
) -> Result<get_keys::v3::Response> {
|
||||
let mut master_keys = BTreeMap::new();
|
||||
let mut self_signing_keys = BTreeMap::new();
|
||||
@@ -264,7 +258,7 @@ pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool + Send>(
|
||||
for (user_id, device_ids) in device_keys_input {
|
||||
let user_id: &UserId = user_id;
|
||||
|
||||
if !services.globals.user_is_local(user_id) {
|
||||
if !user_is_local(user_id) {
|
||||
get_over_federation
|
||||
.entry(user_id.server_name())
|
||||
.or_insert_with(Vec::new)
|
||||
@@ -274,10 +268,10 @@ pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool + Send>(
|
||||
|
||||
if device_ids.is_empty() {
|
||||
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?;
|
||||
if let Some(mut keys) = services.users.get_device_keys(user_id, &device_id)? {
|
||||
let metadata = services
|
||||
if let Some(mut keys) = services().users.get_device_keys(user_id, &device_id)? {
|
||||
let metadata = services()
|
||||
.users
|
||||
.get_device_metadata(user_id, &device_id)?
|
||||
.ok_or_else(|| Error::bad_database("all_device_keys contained nonexistent device."))?;
|
||||
@@ -292,8 +286,8 @@ pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool + Send>(
|
||||
} else {
|
||||
for device_id in device_ids {
|
||||
let mut container = BTreeMap::new();
|
||||
if let Some(mut keys) = services.users.get_device_keys(user_id, device_id)? {
|
||||
let metadata = services
|
||||
if let Some(mut keys) = services().users.get_device_keys(user_id, device_id)? {
|
||||
let metadata = services()
|
||||
.users
|
||||
.get_device_metadata(user_id, device_id)?
|
||||
.ok_or(Error::BadRequest(
|
||||
@@ -309,21 +303,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
|
||||
.get_master_key(sender_user, user_id, &allowed_signatures)?
|
||||
{
|
||||
master_keys.insert(user_id.to_owned(), master_key);
|
||||
}
|
||||
if let Some(self_signing_key) =
|
||||
services
|
||||
services()
|
||||
.users
|
||||
.get_self_signing_key(sender_user, user_id, &allowed_signatures)?
|
||||
{
|
||||
self_signing_keys.insert(user_id.to_owned(), self_signing_key);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -332,7 +326,7 @@ pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool + Send>(
|
||||
let mut failures = BTreeMap::new();
|
||||
|
||||
let back_off = |id| async {
|
||||
match services
|
||||
match services()
|
||||
.globals
|
||||
.bad_query_ratelimiter
|
||||
.write()
|
||||
@@ -351,7 +345,7 @@ pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool + Send>(
|
||||
let mut futures: FuturesUnordered<_> = get_over_federation
|
||||
.into_iter()
|
||||
.map(|(server, vec)| async move {
|
||||
if let Some((time, tries)) = services
|
||||
if let Some((time, tries)) = services()
|
||||
.globals
|
||||
.bad_query_ratelimiter
|
||||
.read()
|
||||
@@ -362,7 +356,8 @@ pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool + Send>(
|
||||
const MIN: u64 = 5 * 60;
|
||||
const MAX: u64 = 60 * 60 * 24;
|
||||
if continue_exponential_backoff_secs(MIN, MAX, time.elapsed(), *tries) {
|
||||
return (server, Err!(BadServerResponse("bad query from {server:?}, still backing off")));
|
||||
debug!("Backing off query from {server:?}");
|
||||
return (server, Err(Error::BadServerResponse("bad query, still backing off")));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -374,7 +369,7 @@ pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool + Send>(
|
||||
let request = federation::keys::get_keys::v1::Request {
|
||||
device_keys: device_keys_input_fed,
|
||||
};
|
||||
let response = services
|
||||
let response = services()
|
||||
.sending
|
||||
.send_federation_request(server, request)
|
||||
.await;
|
||||
@@ -386,19 +381,19 @@ pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool + Send>(
|
||||
while let Some((server, response)) = futures.next().await {
|
||||
if let Ok(Ok(response)) = response {
|
||||
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) =
|
||||
services
|
||||
services()
|
||||
.users
|
||||
.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);
|
||||
}
|
||||
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");
|
||||
services.users.add_cross_signing_keys(
|
||||
services().users.add_cross_signing_keys(
|
||||
&user, &raw, &None, &None,
|
||||
false, /* Dont notify. A notification would trigger another key request resulting in an
|
||||
* endless loop */
|
||||
@@ -449,14 +444,14 @@ fn add_unsigned_device_display_name(
|
||||
}
|
||||
|
||||
pub(crate) async fn claim_keys_helper(
|
||||
services: &Services, one_time_keys_input: &BTreeMap<OwnedUserId, BTreeMap<OwnedDeviceId, DeviceKeyAlgorithm>>,
|
||||
one_time_keys_input: &BTreeMap<OwnedUserId, BTreeMap<OwnedDeviceId, DeviceKeyAlgorithm>>,
|
||||
) -> Result<claim_keys::v3::Response> {
|
||||
let mut one_time_keys = BTreeMap::new();
|
||||
|
||||
let mut get_over_federation = BTreeMap::new();
|
||||
|
||||
for (user_id, map) in one_time_keys_input {
|
||||
if !services.globals.user_is_local(user_id) {
|
||||
if !user_is_local(user_id) {
|
||||
get_over_federation
|
||||
.entry(user_id.server_name())
|
||||
.or_insert_with(Vec::new)
|
||||
@@ -465,7 +460,7 @@ pub(crate) async fn claim_keys_helper(
|
||||
|
||||
let mut container = BTreeMap::new();
|
||||
for (device_id, key_algorithm) in map {
|
||||
if let Some(one_time_keys) = services
|
||||
if let Some(one_time_keys) = services()
|
||||
.users
|
||||
.take_one_time_key(user_id, device_id, key_algorithm)?
|
||||
{
|
||||
@@ -488,7 +483,7 @@ pub(crate) async fn claim_keys_helper(
|
||||
}
|
||||
(
|
||||
server,
|
||||
services
|
||||
services()
|
||||
.sending
|
||||
.send_federation_request(
|
||||
server,
|
||||
|
||||
+318
-85
@@ -1,18 +1,12 @@
|
||||
#![allow(deprecated)]
|
||||
|
||||
use std::time::Duration;
|
||||
use std::{io::Cursor, sync::Arc, time::Duration};
|
||||
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::InsecureClientIp;
|
||||
use conduit::{
|
||||
debug_warn, err, error,
|
||||
utils::{
|
||||
self,
|
||||
content_disposition::{content_disposition_type, make_content_disposition, sanitise_filename},
|
||||
math::ruma_from_usize,
|
||||
},
|
||||
warn, Err, Error, Result,
|
||||
};
|
||||
use conduit::{debug, error, utils::math::ruma_from_usize, warn};
|
||||
use image::io::Reader as ImgReader;
|
||||
use ipaddress::IPAddress;
|
||||
use reqwest::Url;
|
||||
use ruma::api::client::{
|
||||
error::{ErrorKind, RetryAfter},
|
||||
media::{
|
||||
@@ -20,12 +14,24 @@ use ruma::api::client::{
|
||||
get_media_preview,
|
||||
},
|
||||
};
|
||||
use service::{
|
||||
media::{FileMeta, MXC_LENGTH},
|
||||
Services,
|
||||
use webpage::HTML;
|
||||
|
||||
use crate::{
|
||||
debug_warn,
|
||||
service::{
|
||||
media::{FileMeta, UrlPreviewData},
|
||||
server_is_ours,
|
||||
},
|
||||
services,
|
||||
utils::{
|
||||
self,
|
||||
content_disposition::{content_disposition_type, make_content_disposition, sanitise_filename},
|
||||
},
|
||||
Error, Result, Ruma, RumaResponse,
|
||||
};
|
||||
|
||||
use crate::{Ruma, RumaResponse};
|
||||
/// generated MXC ID (`media-id`) length
|
||||
const MXC_LENGTH: usize = 32;
|
||||
|
||||
/// Cache control for immutable objects
|
||||
const CACHE_CONTROL_IMMUTABLE: &str = "public,max-age=31536000,immutable";
|
||||
@@ -36,10 +42,10 @@ const CORP_CROSS_ORIGIN: &str = "cross-origin";
|
||||
///
|
||||
/// Returns max upload size.
|
||||
pub(crate) async fn get_media_config_route(
|
||||
State(services): State<crate::State>, _body: Ruma<get_media_config::v3::Request>,
|
||||
_body: Ruma<get_media_config::v3::Request>,
|
||||
) -> Result<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),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -51,11 +57,9 @@ pub(crate) async fn get_media_config_route(
|
||||
///
|
||||
/// Returns max upload size.
|
||||
pub(crate) async fn get_media_config_v1_route(
|
||||
State(services): State<crate::State>, body: Ruma<get_media_config::v3::Request>,
|
||||
body: Ruma<get_media_config::v3::Request>,
|
||||
) -> Result<RumaResponse<get_media_config::v3::Response>> {
|
||||
get_media_config_route(State(services), body)
|
||||
.await
|
||||
.map(RumaResponse)
|
||||
get_media_config_route(body).await.map(RumaResponse)
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/media/v3/preview_url`
|
||||
@@ -63,19 +67,17 @@ pub(crate) async fn get_media_config_v1_route(
|
||||
/// Returns URL preview.
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "url_preview")]
|
||||
pub(crate) async fn get_media_preview_route(
|
||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||
body: Ruma<get_media_preview::v3::Request>,
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_media_preview::v3::Request>,
|
||||
) -> Result<get_media_preview::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
let url = &body.url;
|
||||
if !services.media.url_preview_allowed(url) {
|
||||
return Err!(Request(Forbidden(
|
||||
warn!(%sender_user, %url, "URL is not allowed to be previewed")
|
||||
)));
|
||||
if !url_preview_allowed(url) {
|
||||
warn!(%sender_user, "URL is not allowed to be previewed: {url}");
|
||||
return Err(Error::BadRequest(ErrorKind::forbidden(), "URL is not allowed to be previewed"));
|
||||
}
|
||||
|
||||
match services.media.get_url_preview(url).await {
|
||||
match get_url_preview(url).await {
|
||||
Ok(preview) => {
|
||||
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}");
|
||||
@@ -91,6 +93,7 @@ pub(crate) async fn get_media_preview_route(
|
||||
},
|
||||
Err(e) => {
|
||||
warn!(%sender_user, "Failed to generate a URL preview: {e}");
|
||||
|
||||
// there doesn't seem to be an agreed-upon error code in the spec.
|
||||
// the only response codes in the preview_url spec page are 200 and 429.
|
||||
Err(Error::BadRequest(
|
||||
@@ -112,10 +115,9 @@ pub(crate) async fn get_media_preview_route(
|
||||
/// Returns URL preview.
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "url_preview")]
|
||||
pub(crate) async fn get_media_preview_v1_route(
|
||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||
body: Ruma<get_media_preview::v3::Request>,
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_media_preview::v3::Request>,
|
||||
) -> Result<RumaResponse<get_media_preview::v3::Response>> {
|
||||
get_media_preview_route(State(services), InsecureClientIp(client), body)
|
||||
get_media_preview_route(InsecureClientIp(client), body)
|
||||
.await
|
||||
.map(RumaResponse)
|
||||
}
|
||||
@@ -128,14 +130,17 @@ pub(crate) async fn get_media_preview_v1_route(
|
||||
/// - Media will be saved in the media/ directory
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "media_upload")]
|
||||
pub(crate) async fn create_content_route(
|
||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||
body: Ruma<create_content::v3::Request>,
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<create_content::v3::Request>,
|
||||
) -> Result<create_content::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
let mxc = format!("mxc://{}/{}", services.globals.server_name(), utils::random_string(MXC_LENGTH));
|
||||
let mxc = format!(
|
||||
"mxc://{}/{}",
|
||||
services().globals.server_name(),
|
||||
utils::random_string(MXC_LENGTH)
|
||||
);
|
||||
|
||||
services
|
||||
services()
|
||||
.media
|
||||
.create(
|
||||
Some(sender_user.clone()),
|
||||
@@ -173,10 +178,9 @@ pub(crate) async fn create_content_route(
|
||||
/// - Media will be saved in the media/ directory
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "media_upload")]
|
||||
pub(crate) async fn create_content_v1_route(
|
||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||
body: Ruma<create_content::v3::Request>,
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<create_content::v3::Request>,
|
||||
) -> Result<RumaResponse<create_content::v3::Response>> {
|
||||
create_content_route(State(services), InsecureClientIp(client), body)
|
||||
create_content_route(InsecureClientIp(client), body)
|
||||
.await
|
||||
.map(RumaResponse)
|
||||
}
|
||||
@@ -191,19 +195,17 @@ pub(crate) async fn create_content_v1_route(
|
||||
/// seconds
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "media_get")]
|
||||
pub(crate) async fn get_content_route(
|
||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||
body: Ruma<get_content::v3::Request>,
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_content::v3::Request>,
|
||||
) -> Result<get_content::v3::Response> {
|
||||
let mxc = format!("mxc://{}/{}", body.server_name, body.media_id);
|
||||
|
||||
if let Some(FileMeta {
|
||||
content,
|
||||
content_type,
|
||||
file,
|
||||
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 file = content.expect("content");
|
||||
|
||||
Ok(get_content::v3::Response {
|
||||
file,
|
||||
@@ -212,9 +214,8 @@ pub(crate) async fn get_content_route(
|
||||
cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.to_owned()),
|
||||
cache_control: Some(CACHE_CONTROL_IMMUTABLE.into()),
|
||||
})
|
||||
} else if !services.globals.server_is_ours(&body.server_name) && body.allow_remote {
|
||||
} else if !server_is_ours(&body.server_name) && body.allow_remote {
|
||||
let response = get_remote_content(
|
||||
&services,
|
||||
&mxc,
|
||||
&body.server_name,
|
||||
body.media_id.clone(),
|
||||
@@ -222,7 +223,10 @@ pub(crate) async fn get_content_route(
|
||||
body.timeout_ms,
|
||||
)
|
||||
.await
|
||||
.map_err(|e| err!(Request(NotFound(debug_warn!("Fetching media `{mxc}` failed: {e:?}")))))?;
|
||||
.map_err(|e| {
|
||||
debug_warn!("Fetching media `{}` failed: {:?}", mxc, e);
|
||||
Error::BadRequest(ErrorKind::NotFound, "Remote media error.")
|
||||
})?;
|
||||
|
||||
let content_disposition = Some(make_content_disposition(
|
||||
&response.content_type,
|
||||
@@ -238,7 +242,7 @@ pub(crate) async fn get_content_route(
|
||||
cache_control: Some(CACHE_CONTROL_IMMUTABLE.to_owned()),
|
||||
})
|
||||
} else {
|
||||
Err!(Request(NotFound("Media not found.")))
|
||||
Err(Error::BadRequest(ErrorKind::NotFound, "Media not found."))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,10 +260,9 @@ pub(crate) async fn get_content_route(
|
||||
/// seconds
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "media_get")]
|
||||
pub(crate) async fn get_content_v1_route(
|
||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||
body: Ruma<get_content::v3::Request>,
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_content::v3::Request>,
|
||||
) -> Result<RumaResponse<get_content::v3::Response>> {
|
||||
get_content_route(State(services), InsecureClientIp(client), body)
|
||||
get_content_route(InsecureClientIp(client), body)
|
||||
.await
|
||||
.map(RumaResponse)
|
||||
}
|
||||
@@ -274,16 +277,15 @@ pub(crate) async fn get_content_v1_route(
|
||||
/// seconds
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "media_get")]
|
||||
pub(crate) async fn get_content_as_filename_route(
|
||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||
body: Ruma<get_content_as_filename::v3::Request>,
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_content_as_filename::v3::Request>,
|
||||
) -> Result<get_content_as_filename::v3::Response> {
|
||||
let mxc = format!("mxc://{}/{}", body.server_name, body.media_id);
|
||||
|
||||
if let Some(FileMeta {
|
||||
content,
|
||||
content_type,
|
||||
file,
|
||||
content_disposition,
|
||||
}) = services.media.get(&mxc).await?
|
||||
}) = services().media.get(&mxc).await?
|
||||
{
|
||||
let content_disposition = Some(make_content_disposition(
|
||||
&content_type,
|
||||
@@ -291,7 +293,6 @@ pub(crate) async fn get_content_as_filename_route(
|
||||
Some(body.filename.clone()),
|
||||
));
|
||||
|
||||
let file = content.expect("content");
|
||||
Ok(get_content_as_filename::v3::Response {
|
||||
file,
|
||||
content_type,
|
||||
@@ -299,9 +300,8 @@ pub(crate) async fn get_content_as_filename_route(
|
||||
cross_origin_resource_policy: Some(CORP_CROSS_ORIGIN.to_owned()),
|
||||
cache_control: Some(CACHE_CONTROL_IMMUTABLE.into()),
|
||||
})
|
||||
} else if !services.globals.server_is_ours(&body.server_name) && body.allow_remote {
|
||||
} else if !server_is_ours(&body.server_name) && body.allow_remote {
|
||||
match get_remote_content(
|
||||
&services,
|
||||
&mxc,
|
||||
&body.server_name,
|
||||
body.media_id.clone(),
|
||||
@@ -325,10 +325,13 @@ pub(crate) async fn get_content_as_filename_route(
|
||||
cache_control: Some(CACHE_CONTROL_IMMUTABLE.into()),
|
||||
})
|
||||
},
|
||||
Err(e) => Err!(Request(NotFound(debug_warn!("Fetching media `{mxc}` failed: {e:?}")))),
|
||||
Err(e) => {
|
||||
debug_warn!("Fetching media `{}` failed: {:?}", mxc, e);
|
||||
Err(Error::BadRequest(ErrorKind::NotFound, "Remote media error."))
|
||||
},
|
||||
}
|
||||
} else {
|
||||
Err!(Request(NotFound("Media not found.")))
|
||||
Err(Error::BadRequest(ErrorKind::NotFound, "Media not found."))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -346,10 +349,9 @@ pub(crate) async fn get_content_as_filename_route(
|
||||
/// seconds
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "media_get")]
|
||||
pub(crate) async fn get_content_as_filename_v1_route(
|
||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||
body: Ruma<get_content_as_filename::v3::Request>,
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_content_as_filename::v3::Request>,
|
||||
) -> Result<RumaResponse<get_content_as_filename::v3::Response>> {
|
||||
get_content_as_filename_route(State(services), InsecureClientIp(client), body)
|
||||
get_content_as_filename_route(InsecureClientIp(client), body)
|
||||
.await
|
||||
.map(RumaResponse)
|
||||
}
|
||||
@@ -364,30 +366,28 @@ pub(crate) async fn get_content_as_filename_v1_route(
|
||||
/// seconds
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "media_thumbnail_get")]
|
||||
pub(crate) async fn get_content_thumbnail_route(
|
||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||
body: Ruma<get_content_thumbnail::v3::Request>,
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_content_thumbnail::v3::Request>,
|
||||
) -> Result<get_content_thumbnail::v3::Response> {
|
||||
let mxc = format!("mxc://{}/{}", body.server_name, body.media_id);
|
||||
|
||||
if let Some(FileMeta {
|
||||
content,
|
||||
content_type,
|
||||
file,
|
||||
content_disposition,
|
||||
}) = services
|
||||
}) = services()
|
||||
.media
|
||||
.get_thumbnail(
|
||||
&mxc,
|
||||
body.width
|
||||
.try_into()
|
||||
.map_err(|e| err!(Request(InvalidParam("Width is invalid: {e:?}"))))?,
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Width is invalid."))?,
|
||||
body.height
|
||||
.try_into()
|
||||
.map_err(|e| err!(Request(InvalidParam("Height is invalid: {e:?}"))))?,
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Height is invalid."))?,
|
||||
)
|
||||
.await?
|
||||
{
|
||||
let content_disposition = Some(make_content_disposition(&content_type, content_disposition, None));
|
||||
let file = content.expect("content");
|
||||
|
||||
Ok(get_content_thumbnail::v3::Response {
|
||||
file,
|
||||
@@ -396,8 +396,8 @@ pub(crate) async fn get_content_thumbnail_route(
|
||||
cache_control: Some(CACHE_CONTROL_IMMUTABLE.into()),
|
||||
content_disposition,
|
||||
})
|
||||
} else if !services.globals.server_is_ours(&body.server_name) && body.allow_remote {
|
||||
if services
|
||||
} else if !server_is_ours(&body.server_name) && body.allow_remote {
|
||||
if services()
|
||||
.globals
|
||||
.prevent_media_downloads_from()
|
||||
.contains(&body.server_name)
|
||||
@@ -405,10 +405,10 @@ pub(crate) async fn get_content_thumbnail_route(
|
||||
// we'll lie to the client and say the blocked server's media was not found and
|
||||
// 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);
|
||||
return Err!(Request(NotFound("Media not found.")));
|
||||
return Err(Error::BadRequest(ErrorKind::NotFound, "Media not found."));
|
||||
}
|
||||
|
||||
match services
|
||||
match services()
|
||||
.sending
|
||||
.send_federation_request(
|
||||
&body.server_name,
|
||||
@@ -427,7 +427,7 @@ pub(crate) async fn get_content_thumbnail_route(
|
||||
.await
|
||||
{
|
||||
Ok(get_thumbnail_response) => {
|
||||
services
|
||||
services()
|
||||
.media
|
||||
.upload_thumbnail(
|
||||
None,
|
||||
@@ -454,10 +454,13 @@ pub(crate) async fn get_content_thumbnail_route(
|
||||
content_disposition,
|
||||
})
|
||||
},
|
||||
Err(e) => Err!(Request(NotFound(debug_warn!("Fetching media `{mxc}` failed: {e:?}")))),
|
||||
Err(e) => {
|
||||
debug_warn!("Fetching media `{}` failed: {:?}", mxc, e);
|
||||
Err(Error::BadRequest(ErrorKind::NotFound, "Remote media error."))
|
||||
},
|
||||
}
|
||||
} else {
|
||||
Err!(Request(NotFound("Media not found.")))
|
||||
Err(Error::BadRequest(ErrorKind::NotFound, "Media not found."))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -475,19 +478,17 @@ pub(crate) async fn get_content_thumbnail_route(
|
||||
/// seconds
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "media_thumbnail_get")]
|
||||
pub(crate) async fn get_content_thumbnail_v1_route(
|
||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||
body: Ruma<get_content_thumbnail::v3::Request>,
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_content_thumbnail::v3::Request>,
|
||||
) -> Result<RumaResponse<get_content_thumbnail::v3::Response>> {
|
||||
get_content_thumbnail_route(State(services), InsecureClientIp(client), body)
|
||||
get_content_thumbnail_route(InsecureClientIp(client), body)
|
||||
.await
|
||||
.map(RumaResponse)
|
||||
}
|
||||
|
||||
async fn get_remote_content(
|
||||
services: &Services, mxc: &str, server_name: &ruma::ServerName, media_id: String, allow_redirect: bool,
|
||||
timeout_ms: Duration,
|
||||
mxc: &str, server_name: &ruma::ServerName, media_id: String, allow_redirect: bool, timeout_ms: Duration,
|
||||
) -> Result<get_content::v3::Response, Error> {
|
||||
if services
|
||||
if services()
|
||||
.globals
|
||||
.prevent_media_downloads_from()
|
||||
.contains(&server_name.to_owned())
|
||||
@@ -495,10 +496,10 @@ async fn get_remote_content(
|
||||
// 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.
|
||||
debug_warn!("Received request for media `{mxc}` on blocklisted server");
|
||||
return Err!(Request(NotFound("Media not found.")));
|
||||
return Err(Error::BadRequest(ErrorKind::NotFound, "Media not found."));
|
||||
}
|
||||
|
||||
let content_response = services
|
||||
let content_response = services()
|
||||
.sending
|
||||
.send_federation_request(
|
||||
server_name,
|
||||
@@ -518,7 +519,7 @@ async fn get_remote_content(
|
||||
None,
|
||||
));
|
||||
|
||||
services
|
||||
services()
|
||||
.media
|
||||
.create(
|
||||
None,
|
||||
@@ -537,3 +538,235 @@ async fn get_remote_content(
|
||||
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
|
||||
}
|
||||
|
||||
+236
-243
File diff suppressed because it is too large
Load Diff
+27
-30
@@ -1,6 +1,5 @@
|
||||
use std::collections::{BTreeMap, HashSet};
|
||||
|
||||
use axum::extract::State;
|
||||
use conduit::PduCount;
|
||||
use ruma::{
|
||||
api::client::{
|
||||
@@ -13,10 +12,7 @@ use ruma::{
|
||||
};
|
||||
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}`
|
||||
///
|
||||
@@ -28,19 +24,21 @@ use crate::{
|
||||
/// - Tries to send the event into the room, auth rules will determine if it is
|
||||
/// allowed
|
||||
pub(crate) async fn send_message_event_route(
|
||||
State(services): State<crate::State>, body: Ruma<send_message_event::v3::Request>,
|
||||
body: Ruma<send_message_event::v3::Request>,
|
||||
) -> Result<send_message_event::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
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
|
||||
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"));
|
||||
}
|
||||
|
||||
if body.event_type == MessageLikeEventType::CallInvite && services.rooms.directory.is_public_room(&body.room_id)? {
|
||||
if body.event_type == MessageLikeEventType::CallInvite
|
||||
&& services().rooms.directory.is_public_room(&body.room_id)?
|
||||
{
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::forbidden(),
|
||||
"Room call invites are not allowed in public rooms",
|
||||
@@ -48,7 +46,7 @@ pub(crate) async fn send_message_event_route(
|
||||
}
|
||||
|
||||
// Check if this is a new transaction id
|
||||
if let Some(response) = services
|
||||
if let Some(response) = services()
|
||||
.transaction_ids
|
||||
.existing_txnid(sender_user, sender_device, &body.txn_id)?
|
||||
{
|
||||
@@ -73,7 +71,7 @@ pub(crate) async fn send_message_event_route(
|
||||
let mut unsigned = BTreeMap::new();
|
||||
unsigned.insert("transaction_id".to_owned(), body.txn_id.to_string().into());
|
||||
|
||||
let event_id = services
|
||||
let event_id = services()
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
@@ -91,7 +89,7 @@ pub(crate) async fn send_message_event_route(
|
||||
)
|
||||
.await?;
|
||||
|
||||
services
|
||||
services()
|
||||
.transaction_ids
|
||||
.add_txnid(sender_user, sender_device, &body.txn_id, event_id.as_bytes())?;
|
||||
|
||||
@@ -107,7 +105,7 @@ pub(crate) async fn send_message_event_route(
|
||||
/// - Only works if the user is joined (TODO: always allow, but only show events
|
||||
/// where the user was joined, depending on `history_visibility`)
|
||||
pub(crate) async fn get_message_events_route(
|
||||
State(services): State<crate::State>, body: Ruma<get_message_events::v3::Request>,
|
||||
body: Ruma<get_message_events::v3::Request>,
|
||||
) -> Result<get_message_events::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
||||
@@ -125,7 +123,7 @@ pub(crate) async fn get_message_events_route(
|
||||
.as_ref()
|
||||
.and_then(|t| PduCount::try_from_string(t).ok());
|
||||
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.lazy_loading
|
||||
.lazy_load_confirm_delivery(sender_user, sender_device, &body.room_id, from)
|
||||
@@ -141,12 +139,12 @@ pub(crate) async fn get_message_events_route(
|
||||
|
||||
match body.dir {
|
||||
ruma::api::Direction::Forward => {
|
||||
let events_after: Vec<_> = services
|
||||
let events_after: Vec<_> = services()
|
||||
.rooms
|
||||
.timeline
|
||||
.pdus_after(sender_user, &body.room_id, from)?
|
||||
.filter_map(Result::ok) // Filter out buggy events
|
||||
.filter(|(_, pdu)| { contains_url_filter(pdu, &body.filter) && visibility_filter(&services, pdu, sender_user, &body.room_id)
|
||||
.filter(|(_, pdu)| { contains_url_filter(pdu, &body.filter) && visibility_filter(pdu, sender_user, &body.room_id)
|
||||
|
||||
})
|
||||
.take_while(|&(k, _)| Some(k) != to) // Stop at `to`
|
||||
@@ -159,7 +157,7 @@ pub(crate) async fn get_message_events_route(
|
||||
* https://github.com/vector-im/element-web/issues/21034
|
||||
*/
|
||||
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_device,
|
||||
&body.room_id,
|
||||
@@ -183,17 +181,17 @@ pub(crate) async fn get_message_events_route(
|
||||
resp.chunk = events_after;
|
||||
},
|
||||
ruma::api::Direction::Backward => {
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.timeline
|
||||
.backfill_if_required(&body.room_id, from)
|
||||
.await?;
|
||||
let events_before: Vec<_> = services
|
||||
let events_before: Vec<_> = services()
|
||||
.rooms
|
||||
.timeline
|
||||
.pdus_until(sender_user, &body.room_id, from)?
|
||||
.filter_map(Result::ok) // Filter out buggy events
|
||||
.filter(|(_, pdu)| {contains_url_filter(pdu, &body.filter) && visibility_filter(&services, pdu, sender_user, &body.room_id)})
|
||||
.filter(|(_, pdu)| {contains_url_filter(pdu, &body.filter) && visibility_filter(pdu, sender_user, &body.room_id)})
|
||||
.take_while(|&(k, _)| Some(k) != to) // Stop at `to`
|
||||
.take(limit)
|
||||
.collect();
|
||||
@@ -204,7 +202,7 @@ pub(crate) async fn get_message_events_route(
|
||||
* https://github.com/vector-im/element-web/issues/21034
|
||||
*/
|
||||
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_device,
|
||||
&body.room_id,
|
||||
@@ -231,12 +229,11 @@ pub(crate) async fn get_message_events_route(
|
||||
|
||||
resp.state = Vec::new();
|
||||
for ll_id in &lazy_loaded {
|
||||
if let Some(member_event) =
|
||||
services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get(&body.room_id, &StateEventType::RoomMember, ll_id.as_str())?
|
||||
{
|
||||
if let Some(member_event) = services().rooms.state_accessor.room_state_get(
|
||||
&body.room_id,
|
||||
&StateEventType::RoomMember,
|
||||
ll_id.as_str(),
|
||||
)? {
|
||||
resp.state.push(member_event.to_state_event());
|
||||
}
|
||||
}
|
||||
@@ -244,7 +241,7 @@ pub(crate) async fn get_message_events_route(
|
||||
// remove the feature check when we are sure clients like element can handle it
|
||||
if !cfg!(feature = "element_hacks") {
|
||||
if let Some(next_token) = next_token {
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.lazy_loading
|
||||
.lazy_load_mark_sent(sender_user, sender_device, &body.room_id, lazy_loaded, next_token)
|
||||
@@ -255,8 +252,8 @@ pub(crate) async fn get_message_events_route(
|
||||
Ok(resp)
|
||||
}
|
||||
|
||||
fn visibility_filter(services: &Services, pdu: &PduEvent, user_id: &UserId, room_id: &RoomId) -> bool {
|
||||
services
|
||||
fn visibility_filter(pdu: &PduEvent, user_id: &UserId, room_id: &RoomId) -> bool {
|
||||
services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.user_can_see_event(user_id, room_id, &pdu.event_id)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use axum::extract::State;
|
||||
use conduit::utils;
|
||||
use ruma::{
|
||||
api::client::{account, error::ErrorKind},
|
||||
@@ -8,7 +7,7 @@ use ruma::{
|
||||
};
|
||||
|
||||
use super::TOKEN_LENGTH;
|
||||
use crate::{Error, Result, Ruma};
|
||||
use crate::{services, Error, Result, Ruma};
|
||||
|
||||
/// # `POST /_matrix/client/v3/user/{userId}/openid/request_token`
|
||||
///
|
||||
@@ -16,7 +15,7 @@ use crate::{Error, Result, Ruma};
|
||||
///
|
||||
/// - The token generated is only valid for the OpenID API
|
||||
pub(crate) async fn create_openid_token_route(
|
||||
State(services): State<crate::State>, body: Ruma<account::request_openid_token::v3::Request>,
|
||||
body: Ruma<account::request_openid_token::v3::Request>,
|
||||
) -> Result<account::request_openid_token::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
@@ -29,14 +28,14 @@ pub(crate) async fn create_openid_token_route(
|
||||
|
||||
let access_token = utils::random_string(TOKEN_LENGTH);
|
||||
|
||||
let expires_in = services
|
||||
let expires_in = services()
|
||||
.users
|
||||
.create_openid_token(&body.user_id, &access_token)?;
|
||||
|
||||
Ok(account::request_openid_token::v3::Response {
|
||||
access_token,
|
||||
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),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,32 +1,22 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use axum::extract::State;
|
||||
use ruma::api::client::{
|
||||
error::ErrorKind,
|
||||
presence::{get_presence, set_presence},
|
||||
};
|
||||
|
||||
use crate::{Error, Result, Ruma};
|
||||
use crate::{services, Error, Result, Ruma};
|
||||
|
||||
/// # `PUT /_matrix/client/r0/presence/{userId}/status`
|
||||
///
|
||||
/// Sets the presence state of the sender user.
|
||||
pub(crate) async fn set_presence_route(
|
||||
State(services): State<crate::State>, body: Ruma<set_presence::v3::Request>,
|
||||
) -> Result<set_presence::v3::Response> {
|
||||
if !services.globals.allow_local_presence() {
|
||||
pub(crate) async fn set_presence_route(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"));
|
||||
}
|
||||
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
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
|
||||
services()
|
||||
.presence
|
||||
.set_presence(sender_user, &body.presence, None, None, body.status_msg.clone())?;
|
||||
|
||||
@@ -38,10 +28,8 @@ pub(crate) async fn set_presence_route(
|
||||
/// Gets the presence state of the given user.
|
||||
///
|
||||
/// - Only works if you share a room with the user
|
||||
pub(crate) async fn get_presence_route(
|
||||
State(services): State<crate::State>, body: Ruma<get_presence::v3::Request>,
|
||||
) -> Result<get_presence::v3::Response> {
|
||||
if !services.globals.allow_local_presence() {
|
||||
pub(crate) async fn get_presence_route(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"));
|
||||
}
|
||||
|
||||
@@ -49,12 +37,12 @@ pub(crate) async fn get_presence_route(
|
||||
|
||||
let mut presence_event = None;
|
||||
|
||||
for _room_id in services
|
||||
for _room_id in services()
|
||||
.rooms
|
||||
.user
|
||||
.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);
|
||||
break;
|
||||
}
|
||||
|
||||
+59
-64
@@ -1,5 +1,3 @@
|
||||
use axum::extract::State;
|
||||
use conduit::{pdu::PduBuilder, warn, Error, Result};
|
||||
use ruma::{
|
||||
api::{
|
||||
client::{
|
||||
@@ -13,9 +11,12 @@ use ruma::{
|
||||
OwnedMxcUri, OwnedRoomId, OwnedUserId,
|
||||
};
|
||||
use serde_json::value::to_raw_value;
|
||||
use service::Services;
|
||||
use tracing::warn;
|
||||
|
||||
use crate::Ruma;
|
||||
use crate::{
|
||||
service::{pdu::PduBuilder, user_is_local},
|
||||
services, Error, Result, Ruma,
|
||||
};
|
||||
|
||||
/// # `PUT /_matrix/client/r0/profile/{userId}/displayname`
|
||||
///
|
||||
@@ -23,21 +24,21 @@ use crate::Ruma;
|
||||
///
|
||||
/// - Also makes sure other users receive the update using presence EDUs
|
||||
pub(crate) async fn set_displayname_route(
|
||||
State(services): State<crate::State>, body: Ruma<set_display_name::v3::Request>,
|
||||
body: Ruma<set_display_name::v3::Request>,
|
||||
) -> Result<set_display_name::v3::Response> {
|
||||
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
|
||||
.state_cache
|
||||
.rooms_joined(sender_user)
|
||||
.filter_map(Result::ok)
|
||||
.collect();
|
||||
|
||||
update_displayname(&services, sender_user.clone(), body.displayname.clone(), all_joined_rooms).await?;
|
||||
update_displayname(sender_user.clone(), body.displayname.clone(), all_joined_rooms).await?;
|
||||
|
||||
if services.globals.allow_local_presence() {
|
||||
if services().globals.allow_local_presence() {
|
||||
// Presence update
|
||||
services
|
||||
services()
|
||||
.presence
|
||||
.ping_presence(sender_user, &PresenceState::Online)?;
|
||||
}
|
||||
@@ -52,11 +53,11 @@ pub(crate) async fn set_displayname_route(
|
||||
/// - If user is on another server and we do not have a local copy already fetch
|
||||
/// displayname over federation
|
||||
pub(crate) async fn get_displayname_route(
|
||||
State(services): State<crate::State>, body: Ruma<get_display_name::v3::Request>,
|
||||
body: Ruma<get_display_name::v3::Request>,
|
||||
) -> Result<get_display_name::v3::Response> {
|
||||
if !services.globals.user_is_local(&body.user_id) {
|
||||
if !user_is_local(&body.user_id) {
|
||||
// Create and update our local copy of the user
|
||||
if let Ok(response) = services
|
||||
if let Ok(response) = services()
|
||||
.sending
|
||||
.send_federation_request(
|
||||
body.user_id.server_name(),
|
||||
@@ -67,19 +68,19 @@ pub(crate) async fn get_displayname_route(
|
||||
)
|
||||
.await
|
||||
{
|
||||
if !services.users.exists(&body.user_id)? {
|
||||
services.users.create(&body.user_id, None)?;
|
||||
if !services().users.exists(&body.user_id)? {
|
||||
services().users.create(&body.user_id, None)?;
|
||||
}
|
||||
|
||||
services
|
||||
services()
|
||||
.users
|
||||
.set_displayname(&body.user_id, response.displayname.clone())
|
||||
.await?;
|
||||
services
|
||||
services()
|
||||
.users
|
||||
.set_avatar_url(&body.user_id, response.avatar_url.clone())
|
||||
.await?;
|
||||
services
|
||||
services()
|
||||
.users
|
||||
.set_blurhash(&body.user_id, response.blurhash.clone())
|
||||
.await?;
|
||||
@@ -90,14 +91,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
|
||||
// federation
|
||||
return Err(Error::BadRequest(ErrorKind::NotFound, "Profile was not found."));
|
||||
}
|
||||
|
||||
Ok(get_display_name::v3::Response {
|
||||
displayname: services.users.displayname(&body.user_id)?,
|
||||
displayname: services().users.displayname(&body.user_id)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -107,10 +108,10 @@ pub(crate) async fn get_displayname_route(
|
||||
///
|
||||
/// - Also makes sure other users receive the update using presence EDUs
|
||||
pub(crate) async fn set_avatar_url_route(
|
||||
State(services): State<crate::State>, body: Ruma<set_avatar_url::v3::Request>,
|
||||
body: Ruma<set_avatar_url::v3::Request>,
|
||||
) -> Result<set_avatar_url::v3::Response> {
|
||||
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
|
||||
.state_cache
|
||||
.rooms_joined(sender_user)
|
||||
@@ -118,7 +119,6 @@ pub(crate) async fn set_avatar_url_route(
|
||||
.collect();
|
||||
|
||||
update_avatar_url(
|
||||
&services,
|
||||
sender_user.clone(),
|
||||
body.avatar_url.clone(),
|
||||
body.blurhash.clone(),
|
||||
@@ -126,9 +126,9 @@ pub(crate) async fn set_avatar_url_route(
|
||||
)
|
||||
.await?;
|
||||
|
||||
if services.globals.allow_local_presence() {
|
||||
if services().globals.allow_local_presence() {
|
||||
// Presence update
|
||||
services
|
||||
services()
|
||||
.presence
|
||||
.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
|
||||
/// `avatar_url` and blurhash over federation
|
||||
pub(crate) async fn get_avatar_url_route(
|
||||
State(services): State<crate::State>, body: Ruma<get_avatar_url::v3::Request>,
|
||||
body: Ruma<get_avatar_url::v3::Request>,
|
||||
) -> Result<get_avatar_url::v3::Response> {
|
||||
if !services.globals.user_is_local(&body.user_id) {
|
||||
if !user_is_local(&body.user_id) {
|
||||
// Create and update our local copy of the user
|
||||
if let Ok(response) = services
|
||||
if let Ok(response) = services()
|
||||
.sending
|
||||
.send_federation_request(
|
||||
body.user_id.server_name(),
|
||||
@@ -158,19 +158,19 @@ pub(crate) async fn get_avatar_url_route(
|
||||
)
|
||||
.await
|
||||
{
|
||||
if !services.users.exists(&body.user_id)? {
|
||||
services.users.create(&body.user_id, None)?;
|
||||
if !services().users.exists(&body.user_id)? {
|
||||
services().users.create(&body.user_id, None)?;
|
||||
}
|
||||
|
||||
services
|
||||
services()
|
||||
.users
|
||||
.set_displayname(&body.user_id, response.displayname.clone())
|
||||
.await?;
|
||||
services
|
||||
services()
|
||||
.users
|
||||
.set_avatar_url(&body.user_id, response.avatar_url.clone())
|
||||
.await?;
|
||||
services
|
||||
services()
|
||||
.users
|
||||
.set_blurhash(&body.user_id, response.blurhash.clone())
|
||||
.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
|
||||
// federation
|
||||
return Err(Error::BadRequest(ErrorKind::NotFound, "Profile was not found."));
|
||||
}
|
||||
|
||||
Ok(get_avatar_url::v3::Response {
|
||||
avatar_url: services.users.avatar_url(&body.user_id)?,
|
||||
blurhash: services.users.blurhash(&body.user_id)?,
|
||||
avatar_url: services().users.avatar_url(&body.user_id)?,
|
||||
blurhash: services().users.blurhash(&body.user_id)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -200,12 +200,10 @@ pub(crate) async fn get_avatar_url_route(
|
||||
///
|
||||
/// - If user is on another server and we do not have a local copy already,
|
||||
/// fetch profile over federation.
|
||||
pub(crate) async fn get_profile_route(
|
||||
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) {
|
||||
pub(crate) async fn get_profile_route(body: Ruma<get_profile::v3::Request>) -> Result<get_profile::v3::Response> {
|
||||
if !user_is_local(&body.user_id) {
|
||||
// Create and update our local copy of the user
|
||||
if let Ok(response) = services
|
||||
if let Ok(response) = services()
|
||||
.sending
|
||||
.send_federation_request(
|
||||
body.user_id.server_name(),
|
||||
@@ -216,19 +214,19 @@ pub(crate) async fn get_profile_route(
|
||||
)
|
||||
.await
|
||||
{
|
||||
if !services.users.exists(&body.user_id)? {
|
||||
services.users.create(&body.user_id, None)?;
|
||||
if !services().users.exists(&body.user_id)? {
|
||||
services().users.create(&body.user_id, None)?;
|
||||
}
|
||||
|
||||
services
|
||||
services()
|
||||
.users
|
||||
.set_displayname(&body.user_id, response.displayname.clone())
|
||||
.await?;
|
||||
services
|
||||
services()
|
||||
.users
|
||||
.set_avatar_url(&body.user_id, response.avatar_url.clone())
|
||||
.await?;
|
||||
services
|
||||
services()
|
||||
.users
|
||||
.set_blurhash(&body.user_id, response.blurhash.clone())
|
||||
.await?;
|
||||
@@ -241,23 +239,23 @@ pub(crate) async fn get_profile_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
|
||||
// federation
|
||||
return Err(Error::BadRequest(ErrorKind::NotFound, "Profile was not found."));
|
||||
}
|
||||
|
||||
Ok(get_profile::v3::Response {
|
||||
avatar_url: services.users.avatar_url(&body.user_id)?,
|
||||
blurhash: services.users.blurhash(&body.user_id)?,
|
||||
displayname: services.users.displayname(&body.user_id)?,
|
||||
avatar_url: services().users.avatar_url(&body.user_id)?,
|
||||
blurhash: services().users.blurhash(&body.user_id)?,
|
||||
displayname: services().users.displayname(&body.user_id)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn update_displayname(
|
||||
services: &Services, user_id: OwnedUserId, displayname: Option<String>, all_joined_rooms: Vec<OwnedRoomId>,
|
||||
user_id: OwnedUserId, displayname: Option<String>, all_joined_rooms: Vec<OwnedRoomId>,
|
||||
) -> Result<()> {
|
||||
services
|
||||
services()
|
||||
.users
|
||||
.set_displayname(&user_id, displayname.clone())
|
||||
.await?;
|
||||
@@ -273,7 +271,7 @@ pub async fn update_displayname(
|
||||
displayname: displayname.clone(),
|
||||
join_authorized_via_users_server: None,
|
||||
..serde_json::from_str(
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get(room_id, &StateEventType::RoomMember, user_id.as_str())?
|
||||
@@ -296,20 +294,19 @@ pub async fn update_displayname(
|
||||
.filter_map(Result::ok)
|
||||
.collect();
|
||||
|
||||
update_all_rooms(services, all_joined_rooms, user_id).await;
|
||||
update_all_rooms(all_joined_rooms, user_id).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn update_avatar_url(
|
||||
services: &Services, user_id: OwnedUserId, avatar_url: Option<OwnedMxcUri>, blurhash: Option<String>,
|
||||
all_joined_rooms: Vec<OwnedRoomId>,
|
||||
user_id: OwnedUserId, avatar_url: Option<OwnedMxcUri>, blurhash: Option<String>, all_joined_rooms: Vec<OwnedRoomId>,
|
||||
) -> Result<()> {
|
||||
services
|
||||
services()
|
||||
.users
|
||||
.set_avatar_url(&user_id, avatar_url.clone())
|
||||
.await?;
|
||||
services
|
||||
services()
|
||||
.users
|
||||
.set_blurhash(&user_id, blurhash.clone())
|
||||
.await?;
|
||||
@@ -326,7 +323,7 @@ pub async fn update_avatar_url(
|
||||
blurhash: blurhash.clone(),
|
||||
join_authorized_via_users_server: None,
|
||||
..serde_json::from_str(
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get(room_id, &StateEventType::RoomMember, user_id.as_str())?
|
||||
@@ -349,17 +346,15 @@ pub async fn update_avatar_url(
|
||||
.filter_map(Result::ok)
|
||||
.collect();
|
||||
|
||||
update_all_rooms(services, all_joined_rooms, user_id).await;
|
||||
update_all_rooms(all_joined_rooms, user_id).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn update_all_rooms(
|
||||
services: &Services, all_joined_rooms: Vec<(PduBuilder, &OwnedRoomId)>, user_id: OwnedUserId,
|
||||
) {
|
||||
pub async fn update_all_rooms(all_joined_rooms: Vec<(PduBuilder, &OwnedRoomId)>, user_id: OwnedUserId) {
|
||||
for (pdu_builder, room_id) in all_joined_rooms {
|
||||
let state_lock = services.rooms.state.mutex.lock(room_id).await;
|
||||
if let Err(e) = services
|
||||
let state_lock = services().rooms.state.mutex.lock(room_id).await;
|
||||
if let Err(e) = services()
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(pdu_builder, &user_id, room_id, &state_lock)
|
||||
|
||||
+26
-35
@@ -1,4 +1,3 @@
|
||||
use axum::extract::State;
|
||||
use ruma::{
|
||||
api::client::{
|
||||
error::ErrorKind,
|
||||
@@ -11,18 +10,18 @@ use ruma::{
|
||||
push::{InsertPushRuleError, RemovePushRuleError, Ruleset},
|
||||
};
|
||||
|
||||
use crate::{Error, Result, Ruma};
|
||||
use crate::{services, Error, Result, Ruma};
|
||||
|
||||
/// # `GET /_matrix/client/r0/pushrules/`
|
||||
///
|
||||
/// Retrieves the push rules event for this user.
|
||||
pub(crate) async fn get_pushrules_all_route(
|
||||
State(services): State<crate::State>, body: Ruma<get_pushrules_all::v3::Request>,
|
||||
body: Ruma<get_pushrules_all::v3::Request>,
|
||||
) -> Result<get_pushrules_all::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
let event =
|
||||
services
|
||||
services()
|
||||
.account_data
|
||||
.get(None, sender_user, GlobalAccountDataEventType::PushRules.to_string().into())?;
|
||||
|
||||
@@ -35,7 +34,7 @@ pub(crate) async fn get_pushrules_all_route(
|
||||
global: account_data.global,
|
||||
})
|
||||
} else {
|
||||
services.account_data.update(
|
||||
services().account_data.update(
|
||||
None,
|
||||
sender_user,
|
||||
GlobalAccountDataEventType::PushRules.to_string().into(),
|
||||
@@ -56,12 +55,10 @@ pub(crate) async fn get_pushrules_all_route(
|
||||
/// # `GET /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}`
|
||||
///
|
||||
/// Retrieves a single specified push rule for this user.
|
||||
pub(crate) async fn get_pushrule_route(
|
||||
State(services): State<crate::State>, body: Ruma<get_pushrule::v3::Request>,
|
||||
) -> Result<get_pushrule::v3::Response> {
|
||||
pub(crate) async fn get_pushrule_route(body: Ruma<get_pushrule::v3::Request>) -> Result<get_pushrule::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
let event = services
|
||||
let event = services()
|
||||
.account_data
|
||||
.get(None, sender_user, GlobalAccountDataEventType::PushRules.to_string().into())?
|
||||
.ok_or(Error::BadRequest(ErrorKind::NotFound, "PushRules event not found."))?;
|
||||
@@ -87,9 +84,7 @@ pub(crate) async fn get_pushrule_route(
|
||||
/// # `PUT /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}`
|
||||
///
|
||||
/// Creates a single specified push rule for this user.
|
||||
pub(crate) async fn set_pushrule_route(
|
||||
State(services): State<crate::State>, body: Ruma<set_pushrule::v3::Request>,
|
||||
) -> Result<set_pushrule::v3::Response> {
|
||||
pub(crate) async fn set_pushrule_route(body: Ruma<set_pushrule::v3::Request>) -> Result<set_pushrule::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let body = body.body;
|
||||
|
||||
@@ -100,7 +95,7 @@ pub(crate) async fn set_pushrule_route(
|
||||
));
|
||||
}
|
||||
|
||||
let event = services
|
||||
let event = services()
|
||||
.account_data
|
||||
.get(None, sender_user, GlobalAccountDataEventType::PushRules.to_string().into())?
|
||||
.ok_or(Error::BadRequest(ErrorKind::NotFound, "PushRules event not found."))?;
|
||||
@@ -139,7 +134,7 @@ pub(crate) async fn set_pushrule_route(
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
services.account_data.update(
|
||||
services().account_data.update(
|
||||
None,
|
||||
sender_user,
|
||||
GlobalAccountDataEventType::PushRules.to_string().into(),
|
||||
@@ -153,7 +148,7 @@ pub(crate) async fn set_pushrule_route(
|
||||
///
|
||||
/// Gets the actions of a single specified push rule for this user.
|
||||
pub(crate) async fn get_pushrule_actions_route(
|
||||
State(services): State<crate::State>, body: Ruma<get_pushrule_actions::v3::Request>,
|
||||
body: Ruma<get_pushrule_actions::v3::Request>,
|
||||
) -> Result<get_pushrule_actions::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
@@ -164,7 +159,7 @@ pub(crate) async fn get_pushrule_actions_route(
|
||||
));
|
||||
}
|
||||
|
||||
let event = services
|
||||
let event = services()
|
||||
.account_data
|
||||
.get(None, sender_user, GlobalAccountDataEventType::PushRules.to_string().into())?
|
||||
.ok_or(Error::BadRequest(ErrorKind::NotFound, "PushRules event not found."))?;
|
||||
@@ -188,7 +183,7 @@ pub(crate) async fn get_pushrule_actions_route(
|
||||
///
|
||||
/// Sets the actions of a single specified push rule for this user.
|
||||
pub(crate) async fn set_pushrule_actions_route(
|
||||
State(services): State<crate::State>, body: Ruma<set_pushrule_actions::v3::Request>,
|
||||
body: Ruma<set_pushrule_actions::v3::Request>,
|
||||
) -> Result<set_pushrule_actions::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
@@ -199,7 +194,7 @@ pub(crate) async fn set_pushrule_actions_route(
|
||||
));
|
||||
}
|
||||
|
||||
let event = services
|
||||
let event = services()
|
||||
.account_data
|
||||
.get(None, sender_user, GlobalAccountDataEventType::PushRules.to_string().into())?
|
||||
.ok_or(Error::BadRequest(ErrorKind::NotFound, "PushRules event not found."))?;
|
||||
@@ -216,7 +211,7 @@ pub(crate) async fn set_pushrule_actions_route(
|
||||
return Err(Error::BadRequest(ErrorKind::NotFound, "Push rule not found."));
|
||||
}
|
||||
|
||||
services.account_data.update(
|
||||
services().account_data.update(
|
||||
None,
|
||||
sender_user,
|
||||
GlobalAccountDataEventType::PushRules.to_string().into(),
|
||||
@@ -230,7 +225,7 @@ pub(crate) async fn set_pushrule_actions_route(
|
||||
///
|
||||
/// Gets the enabled status of a single specified push rule for this user.
|
||||
pub(crate) async fn get_pushrule_enabled_route(
|
||||
State(services): State<crate::State>, body: Ruma<get_pushrule_enabled::v3::Request>,
|
||||
body: Ruma<get_pushrule_enabled::v3::Request>,
|
||||
) -> Result<get_pushrule_enabled::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
@@ -241,7 +236,7 @@ pub(crate) async fn get_pushrule_enabled_route(
|
||||
));
|
||||
}
|
||||
|
||||
let event = services
|
||||
let event = services()
|
||||
.account_data
|
||||
.get(None, sender_user, GlobalAccountDataEventType::PushRules.to_string().into())?
|
||||
.ok_or(Error::BadRequest(ErrorKind::NotFound, "PushRules event not found."))?;
|
||||
@@ -264,7 +259,7 @@ pub(crate) async fn get_pushrule_enabled_route(
|
||||
///
|
||||
/// Sets the enabled status of a single specified push rule for this user.
|
||||
pub(crate) async fn set_pushrule_enabled_route(
|
||||
State(services): State<crate::State>, body: Ruma<set_pushrule_enabled::v3::Request>,
|
||||
body: Ruma<set_pushrule_enabled::v3::Request>,
|
||||
) -> Result<set_pushrule_enabled::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
@@ -275,7 +270,7 @@ pub(crate) async fn set_pushrule_enabled_route(
|
||||
));
|
||||
}
|
||||
|
||||
let event = services
|
||||
let event = services()
|
||||
.account_data
|
||||
.get(None, sender_user, GlobalAccountDataEventType::PushRules.to_string().into())?
|
||||
.ok_or(Error::BadRequest(ErrorKind::NotFound, "PushRules event not found."))?;
|
||||
@@ -292,7 +287,7 @@ pub(crate) async fn set_pushrule_enabled_route(
|
||||
return Err(Error::BadRequest(ErrorKind::NotFound, "Push rule not found."));
|
||||
}
|
||||
|
||||
services.account_data.update(
|
||||
services().account_data.update(
|
||||
None,
|
||||
sender_user,
|
||||
GlobalAccountDataEventType::PushRules.to_string().into(),
|
||||
@@ -306,7 +301,7 @@ pub(crate) async fn set_pushrule_enabled_route(
|
||||
///
|
||||
/// Deletes a single specified push rule for this user.
|
||||
pub(crate) async fn delete_pushrule_route(
|
||||
State(services): State<crate::State>, body: Ruma<delete_pushrule::v3::Request>,
|
||||
body: Ruma<delete_pushrule::v3::Request>,
|
||||
) -> Result<delete_pushrule::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
@@ -317,7 +312,7 @@ pub(crate) async fn delete_pushrule_route(
|
||||
));
|
||||
}
|
||||
|
||||
let event = services
|
||||
let event = services()
|
||||
.account_data
|
||||
.get(None, sender_user, GlobalAccountDataEventType::PushRules.to_string().into())?
|
||||
.ok_or(Error::BadRequest(ErrorKind::NotFound, "PushRules event not found."))?;
|
||||
@@ -341,7 +336,7 @@ pub(crate) async fn delete_pushrule_route(
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
services.account_data.update(
|
||||
services().account_data.update(
|
||||
None,
|
||||
sender_user,
|
||||
GlobalAccountDataEventType::PushRules.to_string().into(),
|
||||
@@ -354,13 +349,11 @@ pub(crate) async fn delete_pushrule_route(
|
||||
/// # `GET /_matrix/client/r0/pushers`
|
||||
///
|
||||
/// Gets all currently active pushers for the sender user.
|
||||
pub(crate) async fn get_pushers_route(
|
||||
State(services): State<crate::State>, body: Ruma<get_pushers::v3::Request>,
|
||||
) -> Result<get_pushers::v3::Response> {
|
||||
pub(crate) async fn get_pushers_route(body: Ruma<get_pushers::v3::Request>) -> Result<get_pushers::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
Ok(get_pushers::v3::Response {
|
||||
pushers: services.pusher.get_pushers(sender_user)?,
|
||||
pushers: services().pusher.get_pushers(sender_user)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -369,12 +362,10 @@ pub(crate) async fn get_pushers_route(
|
||||
/// Adds a pusher for the sender user.
|
||||
///
|
||||
/// - TODO: Handle `append`
|
||||
pub(crate) async fn set_pushers_route(
|
||||
State(services): State<crate::State>, body: Ruma<set_pusher::v3::Request>,
|
||||
) -> Result<set_pusher::v3::Response> {
|
||||
pub(crate) async fn set_pushers_route(body: Ruma<set_pusher::v3::Request>) -> Result<set_pusher::v3::Response> {
|
||||
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())
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use axum::extract::State;
|
||||
use conduit::PduCount;
|
||||
use ruma::{
|
||||
api::client::{error::ErrorKind, read_marker::set_read_marker, receipt::create_receipt},
|
||||
@@ -11,7 +10,7 @@ use ruma::{
|
||||
MilliSecondsSinceUnixEpoch,
|
||||
};
|
||||
|
||||
use crate::{Error, Result, Ruma};
|
||||
use crate::{services, Error, Result, Ruma};
|
||||
|
||||
/// # `POST /_matrix/client/r0/rooms/{roomId}/read_markers`
|
||||
///
|
||||
@@ -21,7 +20,7 @@ use crate::{Error, Result, Ruma};
|
||||
/// - If `read_receipt` is set: Update private marker and public read receipt
|
||||
/// EDU
|
||||
pub(crate) async fn set_read_marker_route(
|
||||
State(services): State<crate::State>, body: Ruma<set_read_marker::v3::Request>,
|
||||
body: Ruma<set_read_marker::v3::Request>,
|
||||
) -> Result<set_read_marker::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
@@ -31,7 +30,7 @@ pub(crate) async fn set_read_marker_route(
|
||||
event_id: fully_read.clone(),
|
||||
},
|
||||
};
|
||||
services.account_data.update(
|
||||
services().account_data.update(
|
||||
Some(&body.room_id),
|
||||
sender_user,
|
||||
RoomAccountDataEventType::FullyRead,
|
||||
@@ -40,14 +39,14 @@ pub(crate) async fn set_read_marker_route(
|
||||
}
|
||||
|
||||
if body.private_read_receipt.is_some() || body.read_receipt.is_some() {
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.user
|
||||
.reset_notification_counts(sender_user, &body.room_id)?;
|
||||
}
|
||||
|
||||
if let Some(event) = &body.private_read_receipt {
|
||||
let count = services
|
||||
let count = services()
|
||||
.rooms
|
||||
.timeline
|
||||
.get_pdu_count(event)?
|
||||
@@ -61,7 +60,7 @@ pub(crate) async fn set_read_marker_route(
|
||||
},
|
||||
PduCount::Normal(c) => c,
|
||||
};
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.read_receipt
|
||||
.private_read_set(&body.room_id, sender_user, count)?;
|
||||
@@ -83,7 +82,7 @@ pub(crate) async fn set_read_marker_route(
|
||||
let mut receipt_content = BTreeMap::new();
|
||||
receipt_content.insert(event.to_owned(), receipts);
|
||||
|
||||
services.rooms.read_receipt.readreceipt_update(
|
||||
services().rooms.read_receipt.readreceipt_update(
|
||||
sender_user,
|
||||
&body.room_id,
|
||||
&ruma::events::receipt::ReceiptEvent {
|
||||
@@ -100,7 +99,7 @@ pub(crate) async fn set_read_marker_route(
|
||||
///
|
||||
/// Sets private read marker and public read receipt EDU.
|
||||
pub(crate) async fn create_receipt_route(
|
||||
State(services): State<crate::State>, body: Ruma<create_receipt::v3::Request>,
|
||||
body: Ruma<create_receipt::v3::Request>,
|
||||
) -> Result<create_receipt::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
@@ -108,7 +107,7 @@ pub(crate) async fn create_receipt_route(
|
||||
&body.receipt_type,
|
||||
create_receipt::v3::ReceiptType::Read | create_receipt::v3::ReceiptType::ReadPrivate
|
||||
) {
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.user
|
||||
.reset_notification_counts(sender_user, &body.room_id)?;
|
||||
@@ -121,7 +120,7 @@ pub(crate) async fn create_receipt_route(
|
||||
event_id: body.event_id.clone(),
|
||||
},
|
||||
};
|
||||
services.account_data.update(
|
||||
services().account_data.update(
|
||||
Some(&body.room_id),
|
||||
sender_user,
|
||||
RoomAccountDataEventType::FullyRead,
|
||||
@@ -143,7 +142,7 @@ pub(crate) async fn create_receipt_route(
|
||||
let mut receipt_content = BTreeMap::new();
|
||||
receipt_content.insert(body.event_id.clone(), receipts);
|
||||
|
||||
services.rooms.read_receipt.readreceipt_update(
|
||||
services().rooms.read_receipt.readreceipt_update(
|
||||
sender_user,
|
||||
&body.room_id,
|
||||
&ruma::events::receipt::ReceiptEvent {
|
||||
@@ -153,7 +152,7 @@ pub(crate) async fn create_receipt_route(
|
||||
)?;
|
||||
},
|
||||
create_receipt::v3::ReceiptType::ReadPrivate => {
|
||||
let count = services
|
||||
let count = services()
|
||||
.rooms
|
||||
.timeline
|
||||
.get_pdu_count(&body.event_id)?
|
||||
@@ -167,7 +166,7 @@ pub(crate) async fn create_receipt_route(
|
||||
},
|
||||
PduCount::Normal(c) => c,
|
||||
};
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.read_receipt
|
||||
.private_read_set(&body.room_id, sender_user, count)?;
|
||||
|
||||
@@ -1,26 +1,23 @@
|
||||
use axum::extract::State;
|
||||
use ruma::{
|
||||
api::client::redact::redact_event,
|
||||
events::{room::redaction::RoomRedactionEventContent, TimelineEventType},
|
||||
};
|
||||
use serde_json::value::to_raw_value;
|
||||
|
||||
use crate::{service::pdu::PduBuilder, Result, Ruma};
|
||||
use crate::{service::pdu::PduBuilder, services, Result, Ruma};
|
||||
|
||||
/// # `PUT /_matrix/client/r0/rooms/{roomId}/redact/{eventId}/{txnId}`
|
||||
///
|
||||
/// Tries to send a redaction event into the room.
|
||||
///
|
||||
/// - TODO: Handle txn id
|
||||
pub(crate) async fn redact_event_route(
|
||||
State(services): State<crate::State>, body: Ruma<redact_event::v3::Request>,
|
||||
) -> Result<redact_event::v3::Response> {
|
||||
pub(crate) async fn redact_event_route(body: Ruma<redact_event::v3::Request>) -> Result<redact_event::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
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
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
use axum::extract::State;
|
||||
use ruma::api::client::relations::{
|
||||
get_relating_events, get_relating_events_with_rel_type, get_relating_events_with_rel_type_and_event_type,
|
||||
};
|
||||
|
||||
use crate::{Result, Ruma};
|
||||
use crate::{services, Result, Ruma};
|
||||
|
||||
/// # `GET /_matrix/client/r0/rooms/{roomId}/relations/{eventId}/{relType}/{eventType}`
|
||||
pub(crate) async fn get_relating_events_with_rel_type_and_event_type_route(
|
||||
State(services): State<crate::State>, body: Ruma<get_relating_events_with_rel_type_and_event_type::v1::Request>,
|
||||
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> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
let res = services.rooms.pdu_metadata.paginate_relations_with_filter(
|
||||
let res = services()
|
||||
.rooms
|
||||
.pdu_metadata
|
||||
.paginate_relations_with_filter(
|
||||
sender_user,
|
||||
&body.room_id,
|
||||
&body.event_id,
|
||||
@@ -34,11 +36,14 @@ pub(crate) async fn get_relating_events_with_rel_type_and_event_type_route(
|
||||
|
||||
/// # `GET /_matrix/client/r0/rooms/{roomId}/relations/{eventId}/{relType}`
|
||||
pub(crate) async fn get_relating_events_with_rel_type_route(
|
||||
State(services): State<crate::State>, body: Ruma<get_relating_events_with_rel_type::v1::Request>,
|
||||
body: Ruma<get_relating_events_with_rel_type::v1::Request>,
|
||||
) -> Result<get_relating_events_with_rel_type::v1::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
let res = services.rooms.pdu_metadata.paginate_relations_with_filter(
|
||||
let res = services()
|
||||
.rooms
|
||||
.pdu_metadata
|
||||
.paginate_relations_with_filter(
|
||||
sender_user,
|
||||
&body.room_id,
|
||||
&body.event_id,
|
||||
@@ -61,11 +66,14 @@ pub(crate) async fn get_relating_events_with_rel_type_route(
|
||||
|
||||
/// # `GET /_matrix/client/r0/rooms/{roomId}/relations/{eventId}`
|
||||
pub(crate) async fn get_relating_events_route(
|
||||
State(services): State<crate::State>, body: Ruma<get_relating_events::v1::Request>,
|
||||
body: Ruma<get_relating_events::v1::Request>,
|
||||
) -> Result<get_relating_events::v1::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
services.rooms.pdu_metadata.paginate_relations_with_filter(
|
||||
services()
|
||||
.rooms
|
||||
.pdu_metadata
|
||||
.paginate_relations_with_filter(
|
||||
sender_user,
|
||||
&body.room_id,
|
||||
&body.event_id,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use axum::extract::State;
|
||||
use rand::Rng;
|
||||
use ruma::{
|
||||
api::client::{error::ErrorKind, room::report_content},
|
||||
@@ -10,18 +9,13 @@ use ruma::{
|
||||
use tokio::time::sleep;
|
||||
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}`
|
||||
///
|
||||
/// Reports an inappropriate event to homeserver admins
|
||||
pub(crate) async fn report_event_route(
|
||||
State(services): State<crate::State>, body: Ruma<report_content::v3::Request>,
|
||||
body: Ruma<report_content::v3::Request>,
|
||||
) -> Result<report_content::v3::Response> {
|
||||
// user authentication
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -32,26 +26,18 @@ pub(crate) async fn report_event_route(
|
||||
);
|
||||
|
||||
// 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(
|
||||
ErrorKind::NotFound,
|
||||
"Event ID is not known to us or Event ID is invalid",
|
||||
));
|
||||
};
|
||||
|
||||
is_report_valid(
|
||||
&services,
|
||||
&pdu.event_id,
|
||||
&body.room_id,
|
||||
sender_user,
|
||||
&body.reason,
|
||||
body.score,
|
||||
&pdu,
|
||||
)?;
|
||||
is_report_valid(&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
|
||||
// urgency
|
||||
services
|
||||
services()
|
||||
.admin
|
||||
.send_message(message::RoomMessageEventContent::text_html(
|
||||
format!(
|
||||
@@ -93,8 +79,8 @@ pub(crate) async fn report_event_route(
|
||||
/// check if score is in valid range
|
||||
/// check if report reasoning is less than or equal to 750 characters
|
||||
fn is_report_valid(
|
||||
services: &Services, event_id: &EventId, room_id: &RoomId, sender_user: &UserId, reason: &Option<String>,
|
||||
score: Option<ruma::Int>, pdu: &std::sync::Arc<PduEvent>,
|
||||
event_id: &EventId, room_id: &RoomId, sender_user: &UserId, reason: &Option<String>, score: Option<ruma::Int>,
|
||||
pdu: &std::sync::Arc<PduEvent>,
|
||||
) -> Result<bool> {
|
||||
debug_info!("Checking if report from user {sender_user} for event {event_id} in room {room_id} is valid");
|
||||
|
||||
@@ -105,7 +91,7 @@ fn is_report_valid(
|
||||
));
|
||||
}
|
||||
|
||||
if !services
|
||||
if !services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_members(&pdu.room_id)
|
||||
|
||||
+63
-70
@@ -1,6 +1,5 @@
|
||||
use std::{cmp::max, collections::BTreeMap};
|
||||
|
||||
use axum::extract::State;
|
||||
use conduit::{debug_info, debug_warn};
|
||||
use ruma::{
|
||||
api::client::{
|
||||
@@ -31,8 +30,8 @@ use tracing::{error, info, warn};
|
||||
|
||||
use super::invite_helper;
|
||||
use crate::{
|
||||
service::{appservice::RegistrationInfo, pdu::PduBuilder, Services},
|
||||
Error, Result, Ruma,
|
||||
service::{appservice::RegistrationInfo, pdu::PduBuilder},
|
||||
services, Error, Result, Ruma,
|
||||
};
|
||||
|
||||
/// Recommended transferable state events list from the spec
|
||||
@@ -64,46 +63,44 @@ const TRANSFERABLE_STATE_EVENTS: &[StateEventType; 9] = &[
|
||||
/// - Send events listed in initial state
|
||||
/// - Send events implied by `name` and `topic`
|
||||
/// - Send invite events
|
||||
pub(crate) async fn create_room_route(
|
||||
State(services): State<crate::State>, body: Ruma<create_room::v3::Request>,
|
||||
) -> Result<create_room::v3::Response> {
|
||||
pub(crate) async fn create_room_route(body: Ruma<create_room::v3::Request>) -> Result<create_room::v3::Response> {
|
||||
use create_room::v3::RoomPreset;
|
||||
|
||||
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()
|
||||
&& !services.users.is_admin(sender_user)?
|
||||
&& !services().users.is_admin(sender_user)?
|
||||
{
|
||||
return Err(Error::BadRequest(ErrorKind::forbidden(), "Room creation has been disabled."));
|
||||
}
|
||||
|
||||
let room_id: OwnedRoomId = if let Some(custom_room_id) = &body.room_id {
|
||||
custom_room_id_check(&services, custom_room_id)?
|
||||
custom_room_id_check(custom_room_id)?
|
||||
} 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
|
||||
if services.rooms.short.get_shortroomid(&room_id)?.is_some() {
|
||||
if services().rooms.short.get_shortroomid(&room_id)?.is_some() {
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::RoomInUse,
|
||||
"Room with that custom room ID already exists",
|
||||
));
|
||||
}
|
||||
|
||||
let _short_id = services.rooms.short.get_or_create_shortroomid(&room_id)?;
|
||||
let state_lock = services.rooms.state.mutex.lock(&room_id).await;
|
||||
let _short_id = services().rooms.short.get_or_create_shortroomid(&room_id)?;
|
||||
let state_lock = services().rooms.state.mutex.lock(&room_id).await;
|
||||
|
||||
let alias: Option<OwnedRoomAliasId> = if let Some(alias) = &body.room_alias_name {
|
||||
Some(room_alias_check(&services, alias, &body.appservice_info).await?)
|
||||
Some(room_alias_check(alias, &body.appservice_info).await?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let room_version = match body.room_version.clone() {
|
||||
Some(room_version) => {
|
||||
if services
|
||||
if services()
|
||||
.globals
|
||||
.supported_room_versions()
|
||||
.contains(&room_version)
|
||||
@@ -116,7 +113,7 @@ pub(crate) async fn create_room_route(
|
||||
));
|
||||
}
|
||||
},
|
||||
None => services.globals.default_room_version(),
|
||||
None => services().globals.default_room_version(),
|
||||
};
|
||||
|
||||
let content = match &body.creation_content {
|
||||
@@ -187,7 +184,7 @@ pub(crate) async fn create_room_route(
|
||||
};
|
||||
|
||||
// 1. The room create event
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
@@ -205,7 +202,7 @@ pub(crate) async fn create_room_route(
|
||||
.await?;
|
||||
|
||||
// 2. Let the room creator join
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
@@ -213,11 +210,11 @@ pub(crate) async fn create_room_route(
|
||||
event_type: TimelineEventType::RoomMember,
|
||||
content: to_raw_value(&RoomMemberEventContent {
|
||||
membership: MembershipState::Join,
|
||||
displayname: services.users.displayname(sender_user)?,
|
||||
avatar_url: services.users.avatar_url(sender_user)?,
|
||||
displayname: services().users.displayname(sender_user)?,
|
||||
avatar_url: services().users.avatar_url(sender_user)?,
|
||||
is_direct: Some(body.is_direct),
|
||||
third_party_invite: None,
|
||||
blurhash: services.users.blurhash(sender_user)?,
|
||||
blurhash: services().users.blurhash(sender_user)?,
|
||||
reason: None,
|
||||
join_authorized_via_users_server: None,
|
||||
})
|
||||
@@ -252,7 +249,7 @@ pub(crate) async fn create_room_route(
|
||||
let power_levels_content =
|
||||
default_power_levels_content(&body.power_level_content_override, &body.visibility, users)?;
|
||||
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
@@ -271,7 +268,7 @@ pub(crate) async fn create_room_route(
|
||||
|
||||
// 4. Canonical room alias
|
||||
if let Some(room_alias_id) = &alias {
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
@@ -296,7 +293,7 @@ pub(crate) async fn create_room_route(
|
||||
// 5. Events set by preset
|
||||
|
||||
// 5.1 Join Rules
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
@@ -319,7 +316,7 @@ pub(crate) async fn create_room_route(
|
||||
.await?;
|
||||
|
||||
// 5.2 History Visibility
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
@@ -338,7 +335,7 @@ pub(crate) async fn create_room_route(
|
||||
.await?;
|
||||
|
||||
// 5.3 Guest Access
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
@@ -381,11 +378,11 @@ pub(crate) async fn create_room_route(
|
||||
pdu_builder.state_key.get_or_insert_with(String::new);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(pdu_builder, sender_user, &room_id, &state_lock)
|
||||
@@ -394,7 +391,7 @@ pub(crate) async fn create_room_route(
|
||||
|
||||
// 7. Events implied by name and topic
|
||||
if let Some(name) = &body.name {
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
@@ -414,7 +411,7 @@ pub(crate) async fn create_room_route(
|
||||
}
|
||||
|
||||
if let Some(topic) = &body.topic {
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
@@ -438,21 +435,21 @@ pub(crate) async fn create_room_route(
|
||||
// 8. Events implied by invite (and TODO: invite_3pid)
|
||||
drop(state_lock);
|
||||
for user_id in &body.invite {
|
||||
if let Err(e) = invite_helper(&services, sender_user, user_id, &room_id, None, body.is_direct).await {
|
||||
if let Err(e) = invite_helper(sender_user, user_id, &room_id, None, body.is_direct).await {
|
||||
warn!(%e, "Failed to send invite");
|
||||
}
|
||||
}
|
||||
|
||||
// Homeserver specific stuff
|
||||
if let Some(alias) = alias {
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.alias
|
||||
.set_alias(&alias, &room_id, sender_user)?;
|
||||
}
|
||||
|
||||
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}");
|
||||
@@ -467,11 +464,11 @@ pub(crate) async fn create_room_route(
|
||||
/// - You have to currently be joined to the room (TODO: Respect history
|
||||
/// visibility)
|
||||
pub(crate) async fn get_room_event_route(
|
||||
State(services): State<crate::State>, body: Ruma<get_room_event::v3::Request>,
|
||||
body: Ruma<get_room_event::v3::Request>,
|
||||
) -> Result<get_room_event::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
let event = services
|
||||
let event = services()
|
||||
.rooms
|
||||
.timeline
|
||||
.get_pdu(&body.event_id)?
|
||||
@@ -480,7 +477,7 @@ pub(crate) async fn get_room_event_route(
|
||||
Error::BadRequest(ErrorKind::NotFound, "Event not found.")
|
||||
})?;
|
||||
|
||||
if !services
|
||||
if !services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.user_can_see_event(sender_user, &event.room_id, &body.event_id)?
|
||||
@@ -505,12 +502,10 @@ pub(crate) async fn get_room_event_route(
|
||||
///
|
||||
/// - Only users joined to the room are allowed to call this, or if
|
||||
/// `history_visibility` is world readable in the room
|
||||
pub(crate) async fn get_room_aliases_route(
|
||||
State(services): State<crate::State>, body: Ruma<aliases::v3::Request>,
|
||||
) -> Result<aliases::v3::Response> {
|
||||
pub(crate) async fn get_room_aliases_route(body: Ruma<aliases::v3::Request>) -> Result<aliases::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
if !services
|
||||
if !services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.user_can_see_state_events(sender_user, &body.room_id)?
|
||||
@@ -522,7 +517,7 @@ pub(crate) async fn get_room_aliases_route(
|
||||
}
|
||||
|
||||
Ok(aliases::v3::Response {
|
||||
aliases: services
|
||||
aliases: services()
|
||||
.rooms
|
||||
.alias
|
||||
.local_aliases_for_room(&body.room_id)
|
||||
@@ -541,12 +536,10 @@ pub(crate) async fn get_room_aliases_route(
|
||||
/// - Transfers some state events
|
||||
/// - Moves local aliases
|
||||
/// - Modifies old room power levels to prevent users from speaking
|
||||
pub(crate) async fn upgrade_room_route(
|
||||
State(services): State<crate::State>, body: Ruma<upgrade_room::v3::Request>,
|
||||
) -> Result<upgrade_room::v3::Response> {
|
||||
pub(crate) async fn upgrade_room_route(body: Ruma<upgrade_room::v3::Request>) -> Result<upgrade_room::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
if !services
|
||||
if !services()
|
||||
.globals
|
||||
.supported_room_versions()
|
||||
.contains(&body.new_version)
|
||||
@@ -558,19 +551,19 @@ pub(crate) async fn upgrade_room_route(
|
||||
}
|
||||
|
||||
// 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
|
||||
.short
|
||||
.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
|
||||
// intended to be used any further Fail if the sender does not have the required
|
||||
// permissions
|
||||
let tombstone_event_id = services
|
||||
let tombstone_event_id = services()
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
@@ -593,11 +586,11 @@ pub(crate) async fn upgrade_room_route(
|
||||
|
||||
// Change lock to replacement room
|
||||
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
|
||||
let mut create_event_content = serde_json::from_str::<CanonicalJsonObject>(
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get(&body.room_id, &StateEventType::RoomCreate, "")?
|
||||
@@ -665,7 +658,7 @@ pub(crate) async fn upgrade_room_route(
|
||||
return Err(Error::BadRequest(ErrorKind::BadJson, "Error forming creation event"));
|
||||
}
|
||||
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
@@ -683,7 +676,7 @@ pub(crate) async fn upgrade_room_route(
|
||||
.await?;
|
||||
|
||||
// Join the new room
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
@@ -691,11 +684,11 @@ pub(crate) async fn upgrade_room_route(
|
||||
event_type: TimelineEventType::RoomMember,
|
||||
content: to_raw_value(&RoomMemberEventContent {
|
||||
membership: MembershipState::Join,
|
||||
displayname: services.users.displayname(sender_user)?,
|
||||
avatar_url: services.users.avatar_url(sender_user)?,
|
||||
displayname: services().users.displayname(sender_user)?,
|
||||
avatar_url: services().users.avatar_url(sender_user)?,
|
||||
is_direct: None,
|
||||
third_party_invite: None,
|
||||
blurhash: services.users.blurhash(sender_user)?,
|
||||
blurhash: services().users.blurhash(sender_user)?,
|
||||
reason: None,
|
||||
join_authorized_via_users_server: None,
|
||||
})
|
||||
@@ -712,7 +705,7 @@ pub(crate) async fn upgrade_room_route(
|
||||
|
||||
// Replicate transferable state events to the new room
|
||||
for event_type in TRANSFERABLE_STATE_EVENTS {
|
||||
let event_content = match services
|
||||
let event_content = match services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get(&body.room_id, event_type, "")?
|
||||
@@ -721,7 +714,7 @@ pub(crate) async fn upgrade_room_route(
|
||||
None => continue, // Skipping missing events.
|
||||
};
|
||||
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
@@ -740,13 +733,13 @@ pub(crate) async fn upgrade_room_route(
|
||||
}
|
||||
|
||||
// Moves any local aliases to the new room
|
||||
for alias in services
|
||||
for alias in services()
|
||||
.rooms
|
||||
.alias
|
||||
.local_aliases_for_room(&body.room_id)
|
||||
.filter_map(Result::ok)
|
||||
{
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.alias
|
||||
.set_alias(&alias, &replacement_room, sender_user)?;
|
||||
@@ -754,7 +747,7 @@ pub(crate) async fn upgrade_room_route(
|
||||
|
||||
// Get the old room power levels
|
||||
let mut power_levels_event_content: RoomPowerLevelsEventContent = serde_json::from_str(
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get(&body.room_id, &StateEventType::RoomPowerLevels, "")?
|
||||
@@ -779,7 +772,7 @@ pub(crate) async fn upgrade_room_route(
|
||||
|
||||
// Modify the power levels in the old room to prevent sending of events and
|
||||
// inviting new users
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
@@ -848,7 +841,7 @@ fn default_power_levels_content(
|
||||
|
||||
/// if a room is being created with a room alias, run our checks
|
||||
async fn room_alias_check(
|
||||
services: &Services, room_alias_name: &str, appservice_info: &Option<RegistrationInfo>,
|
||||
room_alias_name: &str, appservice_info: &Option<RegistrationInfo>,
|
||||
) -> Result<OwnedRoomAliasId> {
|
||||
// Basic checks on the room alias validity
|
||||
if room_alias_name.contains(':') {
|
||||
@@ -865,7 +858,7 @@ async fn room_alias_check(
|
||||
}
|
||||
|
||||
// check if room alias is forbidden
|
||||
if services
|
||||
if services()
|
||||
.globals
|
||||
.forbidden_alias_names()
|
||||
.is_match(room_alias_name)
|
||||
@@ -873,13 +866,13 @@ async fn room_alias_check(
|
||||
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| {
|
||||
info!("Failed to parse room alias {room_alias_name}: {e}");
|
||||
Error::BadRequest(ErrorKind::InvalidParam, "Invalid room alias specified.")
|
||||
})?;
|
||||
|
||||
if services
|
||||
if services()
|
||||
.rooms
|
||||
.alias
|
||||
.resolve_local_alias(&full_room_alias)?
|
||||
@@ -892,7 +885,7 @@ async fn room_alias_check(
|
||||
if !info.aliases.is_match(full_room_alias.as_str()) {
|
||||
return Err(Error::BadRequest(ErrorKind::Exclusive, "Room alias is not in namespace."));
|
||||
}
|
||||
} else if services
|
||||
} else if services()
|
||||
.appservice
|
||||
.is_exclusive_alias(&full_room_alias)
|
||||
.await
|
||||
@@ -906,9 +899,9 @@ async fn room_alias_check(
|
||||
}
|
||||
|
||||
/// if a room is being created with a custom room ID, run our checks against it
|
||||
fn custom_room_id_check(services: &Services, custom_room_id: &str) -> Result<OwnedRoomId> {
|
||||
fn custom_room_id_check(custom_room_id: &str) -> Result<OwnedRoomId> {
|
||||
// apply forbidden room alias checks to custom room IDs too
|
||||
if services
|
||||
if services()
|
||||
.globals
|
||||
.forbidden_alias_names()
|
||||
.is_match(custom_room_id)
|
||||
@@ -929,7 +922,7 @@ fn custom_room_id_check(services: &Services, custom_room_id: &str) -> Result<Own
|
||||
));
|
||||
}
|
||||
|
||||
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}");
|
||||
|
||||
|
||||
+18
-13
@@ -1,6 +1,5 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use axum::extract::State;
|
||||
use ruma::{
|
||||
api::client::{
|
||||
error::ErrorKind,
|
||||
@@ -15,7 +14,7 @@ use ruma::{
|
||||
};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::{Error, Result, Ruma};
|
||||
use crate::{services, Error, Result, Ruma};
|
||||
|
||||
/// # `POST /_matrix/client/r0/search`
|
||||
///
|
||||
@@ -23,9 +22,7 @@ use crate::{Error, Result, Ruma};
|
||||
///
|
||||
/// - Only works if the user is currently joined to the room (TODO: Respect
|
||||
/// history visibility)
|
||||
pub(crate) async fn search_events_route(
|
||||
State(services): State<crate::State>, body: Ruma<search_events::v3::Request>,
|
||||
) -> Result<search_events::v3::Response> {
|
||||
pub(crate) async fn search_events_route(body: Ruma<search_events::v3::Request>) -> Result<search_events::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
let search_criteria = body.search_categories.room_events.as_ref().unwrap();
|
||||
@@ -33,7 +30,7 @@ pub(crate) async fn search_events_route(
|
||||
let include_state = &search_criteria.include_state;
|
||||
|
||||
let room_ids = filter.rooms.clone().unwrap_or_else(|| {
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.rooms_joined(sender_user)
|
||||
@@ -53,7 +50,11 @@ pub(crate) async fn search_events_route(
|
||||
|
||||
if include_state.is_some_and(|include_state| include_state) {
|
||||
for room_id in &room_ids {
|
||||
if !services.rooms.state_cache.is_joined(sender_user, room_id)? {
|
||||
if !services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.is_joined(sender_user, room_id)?
|
||||
{
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::forbidden(),
|
||||
"You don't have permission to view this room.",
|
||||
@@ -61,12 +62,12 @@ pub(crate) async fn search_events_route(
|
||||
}
|
||||
|
||||
// check if sender_user can see state events
|
||||
if services
|
||||
if services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.user_can_see_state_events(sender_user, room_id)?
|
||||
{
|
||||
let room_state = services
|
||||
let room_state = services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_full(room_id)
|
||||
@@ -90,14 +91,18 @@ pub(crate) async fn search_events_route(
|
||||
let mut searches = Vec::new();
|
||||
|
||||
for room_id in &room_ids {
|
||||
if !services.rooms.state_cache.is_joined(sender_user, room_id)? {
|
||||
if !services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.is_joined(sender_user, room_id)?
|
||||
{
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::forbidden(),
|
||||
"You don't have permission to view this room.",
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(search) = services
|
||||
if let Some(search) = services()
|
||||
.rooms
|
||||
.search
|
||||
.search_pdus(room_id, &search_criteria.search_term)?
|
||||
@@ -130,14 +135,14 @@ pub(crate) async fn search_events_route(
|
||||
.iter()
|
||||
.skip(skip)
|
||||
.filter_map(|result| {
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.timeline
|
||||
.get_pdu_from_id(result)
|
||||
.ok()?
|
||||
.filter(|pdu| {
|
||||
!pdu.is_redacted()
|
||||
&& services
|
||||
&& services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.user_can_see_event(sender_user, &pdu.room_id, &pdu.event_id)
|
||||
|
||||
+19
-26
@@ -1,4 +1,3 @@
|
||||
use axum::extract::State;
|
||||
use ruma::{
|
||||
api::client::{
|
||||
error::ErrorKind,
|
||||
@@ -21,7 +20,7 @@ use serde::Deserialize;
|
||||
use tracing::{debug, info, warn};
|
||||
|
||||
use super::{DEVICE_ID_LENGTH, TOKEN_LENGTH};
|
||||
use crate::{utils, utils::hash, Error, Result, Ruma};
|
||||
use crate::{services, utils, utils::hash, Error, Result, Ruma};
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct Claims {
|
||||
@@ -56,9 +55,7 @@ pub(crate) async fn get_login_types_route(
|
||||
/// Note: You can use [`GET
|
||||
/// /_matrix/client/r0/login`](fn.get_supported_versions_route.html) to see
|
||||
/// supported login types.
|
||||
pub(crate) async fn login_route(
|
||||
State(services): State<crate::State>, body: Ruma<login::v3::Request>,
|
||||
) -> Result<login::v3::Response> {
|
||||
pub(crate) async fn login_route(body: Ruma<login::v3::Request>) -> Result<login::v3::Response> {
|
||||
// Validate login method
|
||||
// TODO: Other login methods
|
||||
let user_id = match &body.login_info {
|
||||
@@ -71,7 +68,7 @@ pub(crate) async fn login_route(
|
||||
}) => {
|
||||
debug!("Got password login type");
|
||||
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 {
|
||||
UserId::parse(user)
|
||||
} else {
|
||||
@@ -80,7 +77,7 @@ pub(crate) async fn login_route(
|
||||
}
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidUsername, "Username is invalid."))?;
|
||||
|
||||
let hash = services
|
||||
let hash = services()
|
||||
.users
|
||||
.password_hash(&user_id)?
|
||||
.ok_or(Error::BadRequest(ErrorKind::forbidden(), "Wrong username or password."))?;
|
||||
@@ -99,7 +96,7 @@ pub(crate) async fn login_route(
|
||||
token,
|
||||
}) => {
|
||||
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 =
|
||||
jsonwebtoken::decode::<Claims>(token, jwt_decoding_key, &jsonwebtoken::Validation::default())
|
||||
.map_err(|e| {
|
||||
@@ -109,7 +106,7 @@ pub(crate) async fn login_route(
|
||||
|
||||
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}");
|
||||
Error::BadRequest(ErrorKind::InvalidUsername, "Username is invalid.")
|
||||
})?
|
||||
@@ -127,7 +124,7 @@ pub(crate) async fn login_route(
|
||||
}) => {
|
||||
debug!("Got appservice login type");
|
||||
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 {
|
||||
UserId::parse(user)
|
||||
} else {
|
||||
@@ -167,22 +164,22 @@ pub(crate) async fn login_route(
|
||||
|
||||
// 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| {
|
||||
services
|
||||
services()
|
||||
.users
|
||||
.all_device_ids(&user_id)
|
||||
.any(|x| x.as_ref().map_or(false, |v| v == device_id))
|
||||
});
|
||||
|
||||
if device_exists {
|
||||
services.users.set_token(&user_id, &device_id, &token)?;
|
||||
services().users.set_token(&user_id, &device_id, &token)?;
|
||||
} else {
|
||||
services
|
||||
services()
|
||||
.users
|
||||
.create_device(&user_id, &device_id, &token, body.initial_device_display_name.clone())?;
|
||||
}
|
||||
|
||||
// send client well-known if specified so the client knows to reconfigure itself
|
||||
let client_discovery_info: Option<DiscoveryInfo> = services
|
||||
let client_discovery_info: Option<DiscoveryInfo> = services()
|
||||
.globals
|
||||
.well_known_client()
|
||||
.as_ref()
|
||||
@@ -200,7 +197,7 @@ pub(crate) async fn login_route(
|
||||
device_id,
|
||||
well_known: client_discovery_info,
|
||||
expires_in: None,
|
||||
home_server: Some(services.globals.server_name().to_owned()),
|
||||
home_server: Some(services().globals.server_name().to_owned()),
|
||||
refresh_token: None,
|
||||
})
|
||||
}
|
||||
@@ -214,16 +211,14 @@ pub(crate) async fn login_route(
|
||||
/// last seen ts)
|
||||
/// - Forgets to-device events
|
||||
/// - Triggers device list updates
|
||||
pub(crate) async fn logout_route(
|
||||
State(services): State<crate::State>, body: Ruma<logout::v3::Request>,
|
||||
) -> Result<logout::v3::Response> {
|
||||
pub(crate) async fn logout_route(body: Ruma<logout::v3::Request>) -> Result<logout::v3::Response> {
|
||||
let sender_user = body.sender_user.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
|
||||
services.users.mark_device_key_update(sender_user)?;
|
||||
services().users.mark_device_key_update(sender_user)?;
|
||||
|
||||
Ok(logout::v3::Response::new())
|
||||
}
|
||||
@@ -241,17 +236,15 @@ pub(crate) async fn logout_route(
|
||||
/// Note: This is equivalent to calling [`GET
|
||||
/// /_matrix/client/r0/logout`](fn.logout_route.html) from each device of this
|
||||
/// user.
|
||||
pub(crate) async fn logout_all_route(
|
||||
State(services): State<crate::State>, body: Ruma<logout_all::v3::Request>,
|
||||
) -> Result<logout_all::v3::Response> {
|
||||
pub(crate) async fn logout_all_route(body: Ruma<logout_all::v3::Request>) -> Result<logout_all::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
for device_id in services.users.all_device_ids(sender_user).flatten() {
|
||||
services.users.remove_device(sender_user, &device_id)?;
|
||||
for device_id in services().users.all_device_ids(sender_user).flatten() {
|
||||
services().users.remove_device(sender_user, &device_id)?;
|
||||
}
|
||||
|
||||
// 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())
|
||||
}
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use axum::extract::State;
|
||||
use ruma::{
|
||||
api::client::{error::ErrorKind, space::get_hierarchy},
|
||||
UInt,
|
||||
};
|
||||
|
||||
use crate::{service::rooms::spaces::PaginationToken, Error, Result, Ruma};
|
||||
use crate::{service::rooms::spaces::PaginationToken, services, Error, Result, Ruma};
|
||||
|
||||
/// # `GET /_matrix/client/v1/rooms/{room_id}/hierarchy`
|
||||
///
|
||||
/// Paginates over the space tree in a depth-first manner to locate child rooms
|
||||
/// of a given space.
|
||||
pub(crate) async fn get_hierarchy_route(
|
||||
State(services): State<crate::State>, body: Ruma<get_hierarchy::v1::Request>,
|
||||
) -> Result<get_hierarchy::v1::Response> {
|
||||
pub(crate) async fn get_hierarchy_route(body: Ruma<get_hierarchy::v1::Request>) -> Result<get_hierarchy::v1::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
let limit = body
|
||||
@@ -42,7 +39,7 @@ pub(crate) async fn get_hierarchy_route(
|
||||
}
|
||||
}
|
||||
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.spaces
|
||||
.get_client_hierarchy(
|
||||
|
||||
+26
-31
@@ -1,7 +1,6 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use axum::extract::State;
|
||||
use conduit::{debug_info, error, pdu::PduBuilder, Error, Result};
|
||||
use conduit::{debug_info, error};
|
||||
use ruma::{
|
||||
api::client::{
|
||||
error::ErrorKind,
|
||||
@@ -18,9 +17,11 @@ use ruma::{
|
||||
serde::Raw,
|
||||
EventId, RoomId, UserId,
|
||||
};
|
||||
use service::Services;
|
||||
|
||||
use crate::{Ruma, RumaResponse};
|
||||
use crate::{
|
||||
service::{pdu::PduBuilder, server_is_ours},
|
||||
services, Error, Result, Ruma, RumaResponse,
|
||||
};
|
||||
|
||||
/// # `PUT /_matrix/client/*/rooms/{roomId}/state/{eventType}/{stateKey}`
|
||||
///
|
||||
@@ -31,13 +32,12 @@ use crate::{Ruma, RumaResponse};
|
||||
/// allowed
|
||||
/// - If event is new `canonical_alias`: Rejects if alias is incorrect
|
||||
pub(crate) async fn send_state_event_for_key_route(
|
||||
State(services): State<crate::State>, body: Ruma<send_state_event::v3::Request>,
|
||||
body: Ruma<send_state_event::v3::Request>,
|
||||
) -> Result<send_state_event::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
Ok(send_state_event::v3::Response {
|
||||
event_id: send_state_event_for_key_helper(
|
||||
&services,
|
||||
sender_user,
|
||||
&body.room_id,
|
||||
&body.event_type,
|
||||
@@ -58,11 +58,9 @@ pub(crate) async fn send_state_event_for_key_route(
|
||||
/// allowed
|
||||
/// - If event is new `canonical_alias`: Rejects if alias is incorrect
|
||||
pub(crate) async fn send_state_event_for_empty_key_route(
|
||||
State(services): State<crate::State>, body: Ruma<send_state_event::v3::Request>,
|
||||
body: Ruma<send_state_event::v3::Request>,
|
||||
) -> Result<RumaResponse<send_state_event::v3::Response>> {
|
||||
send_state_event_for_key_route(State(services), body)
|
||||
.await
|
||||
.map(RumaResponse)
|
||||
send_state_event_for_key_route(body).await.map(RumaResponse)
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/client/v3/rooms/{roomid}/state`
|
||||
@@ -72,11 +70,11 @@ pub(crate) async fn send_state_event_for_empty_key_route(
|
||||
/// - If not joined: Only works if current room history visibility is world
|
||||
/// readable
|
||||
pub(crate) async fn get_state_events_route(
|
||||
State(services): State<crate::State>, body: Ruma<get_state_events::v3::Request>,
|
||||
body: Ruma<get_state_events::v3::Request>,
|
||||
) -> Result<get_state_events::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
if !services
|
||||
if !services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.user_can_see_state_events(sender_user, &body.room_id)?
|
||||
@@ -88,7 +86,7 @@ pub(crate) async fn get_state_events_route(
|
||||
}
|
||||
|
||||
Ok(get_state_events::v3::Response {
|
||||
room_state: services
|
||||
room_state: services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_full(&body.room_id)
|
||||
@@ -108,11 +106,11 @@ pub(crate) async fn get_state_events_route(
|
||||
/// - If not joined: Only works if current room history visibility is world
|
||||
/// readable
|
||||
pub(crate) async fn get_state_events_for_key_route(
|
||||
State(services): State<crate::State>, body: Ruma<get_state_events_for_key::v3::Request>,
|
||||
body: Ruma<get_state_events_for_key::v3::Request>,
|
||||
) -> Result<get_state_events_for_key::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
if !services
|
||||
if !services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.user_can_see_state_events(sender_user, &body.room_id)?
|
||||
@@ -123,7 +121,7 @@ pub(crate) async fn get_state_events_for_key_route(
|
||||
));
|
||||
}
|
||||
|
||||
let event = services
|
||||
let event = services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get(&body.room_id, &body.event_type, &body.state_key)?
|
||||
@@ -163,20 +161,17 @@ pub(crate) async fn get_state_events_for_key_route(
|
||||
/// - If not joined: Only works if current room history visibility is world
|
||||
/// readable
|
||||
pub(crate) async fn get_state_events_for_empty_key_route(
|
||||
State(services): State<crate::State>, body: Ruma<get_state_events_for_key::v3::Request>,
|
||||
body: Ruma<get_state_events_for_key::v3::Request>,
|
||||
) -> Result<RumaResponse<get_state_events_for_key::v3::Response>> {
|
||||
get_state_events_for_key_route(State(services), body)
|
||||
.await
|
||||
.map(RumaResponse)
|
||||
get_state_events_for_key_route(body).await.map(RumaResponse)
|
||||
}
|
||||
|
||||
async fn send_state_event_for_key_helper(
|
||||
services: &Services, sender: &UserId, room_id: &RoomId, event_type: &StateEventType,
|
||||
json: &Raw<AnyStateEventContent>, state_key: String,
|
||||
sender: &UserId, room_id: &RoomId, event_type: &StateEventType, json: &Raw<AnyStateEventContent>, state_key: String,
|
||||
) -> Result<Arc<EventId>> {
|
||||
allowed_to_send_state_event(services, room_id, event_type, json).await?;
|
||||
let state_lock = services.rooms.state.mutex.lock(room_id).await;
|
||||
let event_id = services
|
||||
allowed_to_send_state_event(room_id, event_type, json).await?;
|
||||
let state_lock = services().rooms.state.mutex.lock(room_id).await;
|
||||
let event_id = services()
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
@@ -197,18 +192,18 @@ async fn send_state_event_for_key_helper(
|
||||
}
|
||||
|
||||
async fn allowed_to_send_state_event(
|
||||
services: &Services, room_id: &RoomId, event_type: &StateEventType, json: &Raw<AnyStateEventContent>,
|
||||
room_id: &RoomId, event_type: &StateEventType, json: &Raw<AnyStateEventContent>,
|
||||
) -> Result<()> {
|
||||
match event_type {
|
||||
// Forbid m.room.encryption if encryption is disabled
|
||||
StateEventType::RoomEncryption => {
|
||||
if !services.globals.allow_encryption() {
|
||||
if !services().globals.allow_encryption() {
|
||||
return Err(Error::BadRequest(ErrorKind::forbidden(), "Encryption has been disabled"));
|
||||
}
|
||||
},
|
||||
// admin room is a sensitive room, it should not ever be made public
|
||||
StateEventType::RoomJoinRules => {
|
||||
if let Some(admin_room_id) = services.admin.get_admin_room()? {
|
||||
if let Some(admin_room_id) = service::admin::Service::get_admin_room()? {
|
||||
if admin_room_id == room_id {
|
||||
if let Ok(join_rule) = serde_json::from_str::<RoomJoinRulesEventContent>(json.json().get()) {
|
||||
if join_rule.join_rule == JoinRule::Public {
|
||||
@@ -223,7 +218,7 @@ async fn allowed_to_send_state_event(
|
||||
},
|
||||
// admin room is a sensitive room, it should not ever be made world readable
|
||||
StateEventType::RoomHistoryVisibility => {
|
||||
if let Some(admin_room_id) = services.admin.get_admin_room()? {
|
||||
if let Some(admin_room_id) = service::admin::Service::get_admin_room()? {
|
||||
if admin_room_id == room_id {
|
||||
if let Ok(visibility_content) =
|
||||
serde_json::from_str::<RoomHistoryVisibilityEventContent>(json.json().get())
|
||||
@@ -248,8 +243,8 @@ async fn allowed_to_send_state_event(
|
||||
}
|
||||
|
||||
for alias in aliases {
|
||||
if !services.globals.server_is_ours(alias.server_name())
|
||||
|| services
|
||||
if !server_is_ours(alias.server_name())
|
||||
|| services()
|
||||
.rooms
|
||||
.alias
|
||||
.resolve_local_alias(&alias)?
|
||||
|
||||
+139
-149
@@ -5,7 +5,6 @@ use std::{
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use axum::extract::State;
|
||||
use conduit::{
|
||||
error,
|
||||
utils::math::{ruma_from_u64, ruma_from_usize, usize_from_ruma, usize_from_u64_truncated},
|
||||
@@ -18,7 +17,7 @@ use ruma::{
|
||||
self,
|
||||
v3::{
|
||||
Ephemeral, Filter, GlobalAccountData, InviteState, InvitedRoom, JoinedRoom, LeftRoom, Presence,
|
||||
RoomAccountData, RoomSummary, Rooms, State as RoomState, Timeline, ToDevice,
|
||||
RoomAccountData, RoomSummary, Rooms, State, Timeline, ToDevice,
|
||||
},
|
||||
v4::SlidingOp,
|
||||
DeviceLists, UnreadNotificationsCount,
|
||||
@@ -35,10 +34,7 @@ use ruma::{
|
||||
};
|
||||
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`
|
||||
///
|
||||
@@ -76,23 +72,23 @@ use crate::{
|
||||
/// - If the user left after `since`: `prev_batch` token, empty state (TODO:
|
||||
/// subset of the state at the point of the leave)
|
||||
pub(crate) async fn sync_events_route(
|
||||
State(services): State<crate::State>, body: Ruma<sync_events::v3::Request>,
|
||||
body: Ruma<sync_events::v3::Request>,
|
||||
) -> Result<sync_events::v3::Response, RumaResponse<UiaaResponse>> {
|
||||
let sender_user = body.sender_user.expect("user is authenticated");
|
||||
let sender_device = body.sender_device.expect("user is authenticated");
|
||||
let body = body.body;
|
||||
|
||||
// Presence update
|
||||
if services.globals.allow_local_presence() {
|
||||
services
|
||||
if services().globals.allow_local_presence() {
|
||||
services()
|
||||
.presence
|
||||
.ping_presence(&sender_user, &body.set_presence)?;
|
||||
}
|
||||
|
||||
// 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_batch_string = next_batch.to_string();
|
||||
|
||||
@@ -100,7 +96,7 @@ pub(crate) async fn sync_events_route(
|
||||
let filter = match body.filter {
|
||||
None => FilterDefinition::default(),
|
||||
Some(Filter::FilterDefinition(filter)) => filter,
|
||||
Some(Filter::FilterId(filter_id)) => services
|
||||
Some(Filter::FilterId(filter_id)) => services()
|
||||
.users
|
||||
.get_filter(&sender_user, &filter_id)?
|
||||
.unwrap_or_default(),
|
||||
@@ -130,29 +126,28 @@ pub(crate) async fn sync_events_route(
|
||||
|
||||
// Look for device list updates of this account
|
||||
device_list_updates.extend(
|
||||
services
|
||||
services()
|
||||
.users
|
||||
.keys_changed(sender_user.as_ref(), since, None)
|
||||
.filter_map(Result::ok),
|
||||
);
|
||||
|
||||
if services.globals.allow_local_presence() {
|
||||
process_presence_updates(&services, &mut presence_updates, since, &sender_user).await?;
|
||||
if services().globals.allow_local_presence() {
|
||||
process_presence_updates(&mut presence_updates, since, &sender_user).await?;
|
||||
}
|
||||
|
||||
let all_joined_rooms = services
|
||||
let all_joined_rooms = services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.rooms_joined(&sender_user)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// 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 {
|
||||
let room_id = room_id?;
|
||||
if let Ok(joined_room) = load_joined_room(
|
||||
&services,
|
||||
&sender_user,
|
||||
&sender_device,
|
||||
&room_id,
|
||||
@@ -175,14 +170,13 @@ pub(crate) async fn sync_events_route(
|
||||
}
|
||||
|
||||
let mut left_rooms = BTreeMap::new();
|
||||
let all_left_rooms: Vec<_> = services
|
||||
let all_left_rooms: Vec<_> = services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.rooms_left(&sender_user)
|
||||
.collect();
|
||||
for result in all_left_rooms {
|
||||
handle_left_room(
|
||||
&services,
|
||||
since,
|
||||
&result?.0,
|
||||
&sender_user,
|
||||
@@ -196,7 +190,7 @@ pub(crate) async fn sync_events_route(
|
||||
}
|
||||
|
||||
let mut invited_rooms = BTreeMap::new();
|
||||
let all_invited_rooms: Vec<_> = services
|
||||
let all_invited_rooms: Vec<_> = services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.rooms_invited(&sender_user)
|
||||
@@ -205,10 +199,10 @@ pub(crate) async fn sync_events_route(
|
||||
let (room_id, invite_state_events) = result?;
|
||||
|
||||
// Get and drop the lock to wait for remaining operations to finish
|
||||
let insert_lock = services.rooms.timeline.mutex_insert.lock(&room_id).await;
|
||||
let insert_lock = services().rooms.timeline.mutex_insert.lock(&room_id).await;
|
||||
drop(insert_lock);
|
||||
|
||||
let invite_count = services
|
||||
let invite_count = services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.get_invite_count(&room_id, &sender_user)?;
|
||||
@@ -229,14 +223,14 @@ pub(crate) async fn sync_events_route(
|
||||
}
|
||||
|
||||
for user_id in left_encrypted_users {
|
||||
let dont_share_encrypted_room = services
|
||||
let dont_share_encrypted_room = services()
|
||||
.rooms
|
||||
.user
|
||||
.get_shared_rooms(vec![sender_user.clone(), user_id.clone()])?
|
||||
.filter_map(Result::ok)
|
||||
.filter_map(|other_room_id| {
|
||||
Some(
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get(&other_room_id, &StateEventType::RoomEncryption, "")
|
||||
@@ -253,7 +247,7 @@ pub(crate) async fn sync_events_route(
|
||||
}
|
||||
|
||||
// Remove all to-device events the device received *last time*
|
||||
services
|
||||
services()
|
||||
.users
|
||||
.remove_to_device_events(&sender_user, &sender_device, since)?;
|
||||
|
||||
@@ -272,7 +266,7 @@ pub(crate) async fn sync_events_route(
|
||||
.collect(),
|
||||
},
|
||||
account_data: GlobalAccountData {
|
||||
events: services
|
||||
events: services()
|
||||
.account_data
|
||||
.changes_since(None, &sender_user, since)?
|
||||
.into_iter()
|
||||
@@ -287,11 +281,11 @@ pub(crate) async fn sync_events_route(
|
||||
changed: device_list_updates.into_iter().collect(),
|
||||
left: device_list_left.into_iter().collect(),
|
||||
},
|
||||
device_one_time_keys_count: services
|
||||
device_one_time_keys_count: services()
|
||||
.users
|
||||
.count_one_time_keys(&sender_user, &sender_device)?,
|
||||
to_device: ToDevice {
|
||||
events: services
|
||||
events: services()
|
||||
.users
|
||||
.get_to_device_events(&sender_user, &sender_device)?,
|
||||
},
|
||||
@@ -317,18 +311,16 @@ pub(crate) async fn sync_events_route(
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
#[tracing::instrument(skip_all, fields(user_id = %sender_user, room_id = %room_id), name = "left_room")]
|
||||
async fn handle_left_room(
|
||||
services: &Services, since: u64, room_id: &RoomId, sender_user: &UserId,
|
||||
left_rooms: &mut BTreeMap<ruma::OwnedRoomId, LeftRoom>, next_batch_string: &str, full_state: bool,
|
||||
lazy_load_enabled: bool,
|
||||
since: u64, room_id: &RoomId, sender_user: &UserId, left_rooms: &mut BTreeMap<ruma::OwnedRoomId, LeftRoom>,
|
||||
next_batch_string: &str, full_state: bool, lazy_load_enabled: bool,
|
||||
) -> Result<()> {
|
||||
// Get and drop the lock to wait for remaining operations to finish
|
||||
let insert_lock = services.rooms.timeline.mutex_insert.lock(room_id).await;
|
||||
let insert_lock = services().rooms.timeline.mutex_insert.lock(room_id).await;
|
||||
drop(insert_lock);
|
||||
|
||||
let left_count = services
|
||||
let left_count = services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.get_left_count(room_id, sender_user)?;
|
||||
@@ -338,11 +330,11 @@ async fn handle_left_room(
|
||||
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
|
||||
// Insert a leave event anyways
|
||||
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(),
|
||||
origin: None,
|
||||
origin_server_ts: utils::millis_since_unix_epoch()
|
||||
@@ -375,7 +367,7 @@ async fn handle_left_room(
|
||||
prev_batch: Some(next_batch_string.to_owned()),
|
||||
events: Vec::new(),
|
||||
},
|
||||
state: RoomState {
|
||||
state: State {
|
||||
events: vec![event.to_sync_state_event()],
|
||||
},
|
||||
},
|
||||
@@ -385,27 +377,27 @@ async fn handle_left_room(
|
||||
|
||||
let mut left_state_events = Vec::new();
|
||||
|
||||
let since_shortstatehash = services
|
||||
let since_shortstatehash = services()
|
||||
.rooms
|
||||
.user
|
||||
.get_token_shortstatehash(room_id, since)?;
|
||||
|
||||
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(),
|
||||
};
|
||||
|
||||
let Some(left_event_id) =
|
||||
services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get_id(room_id, &StateEventType::RoomMember, sender_user.as_str())?
|
||||
let Some(left_event_id) = services().rooms.state_accessor.room_state_get_id(
|
||||
room_id,
|
||||
&StateEventType::RoomMember,
|
||||
sender_user.as_str(),
|
||||
)?
|
||||
else {
|
||||
error!("Left room but no left state event");
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let Some(left_shortstatehash) = services
|
||||
let Some(left_shortstatehash) = services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.pdu_shortstatehash(&left_event_id)?
|
||||
@@ -414,13 +406,13 @@ async fn handle_left_room(
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let mut left_state_ids = services
|
||||
let mut left_state_ids = services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.state_full_ids(left_shortstatehash)
|
||||
.await?;
|
||||
|
||||
let leave_shortstatekey = services
|
||||
let leave_shortstatekey = services()
|
||||
.rooms
|
||||
.short
|
||||
.get_or_create_shortstatekey(&StateEventType::RoomMember, sender_user.as_str())?;
|
||||
@@ -430,7 +422,7 @@ async fn handle_left_room(
|
||||
let mut i: u8 = 0;
|
||||
for (key, id) in left_state_ids {
|
||||
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
|
||||
|| event_type != StateEventType::RoomMember
|
||||
@@ -438,7 +430,7 @@ async fn handle_left_room(
|
||||
// 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)
|
||||
{
|
||||
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);
|
||||
continue;
|
||||
};
|
||||
@@ -464,7 +456,7 @@ async fn handle_left_room(
|
||||
prev_batch: Some(next_batch_string.to_owned()),
|
||||
events: Vec::new(),
|
||||
},
|
||||
state: RoomState {
|
||||
state: State {
|
||||
events: left_state_events,
|
||||
},
|
||||
},
|
||||
@@ -473,11 +465,13 @@ async fn handle_left_room(
|
||||
}
|
||||
|
||||
async fn process_presence_updates(
|
||||
services: &Services, presence_updates: &mut HashMap<OwnedUserId, PresenceEvent>, since: u64, syncing_user: &UserId,
|
||||
presence_updates: &mut HashMap<OwnedUserId, PresenceEvent>, since: u64, syncing_user: &UserId,
|
||||
) -> Result<()> {
|
||||
use crate::service::presence::Presence;
|
||||
|
||||
// Take presence updates
|
||||
for (user_id, _, presence_bytes) in services.presence.presence_since(since) {
|
||||
if !services
|
||||
for (user_id, _, presence_bytes) in services().presence.presence_since(since) {
|
||||
if !services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.user_sees_user(syncing_user, &user_id)?
|
||||
@@ -485,9 +479,7 @@ async fn process_presence_updates(
|
||||
continue;
|
||||
}
|
||||
|
||||
let presence_event = services
|
||||
.presence
|
||||
.from_json_bytes_to_event(&presence_bytes, &user_id)?;
|
||||
let presence_event = Presence::from_json_bytes_to_event(&presence_bytes, &user_id)?;
|
||||
match presence_updates.entry(user_id) {
|
||||
Entry::Vacant(slot) => {
|
||||
slot.insert(presence_event);
|
||||
@@ -521,20 +513,19 @@ async fn process_presence_updates(
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
async fn load_joined_room(
|
||||
services: &Services, sender_user: &UserId, sender_device: &DeviceId, room_id: &RoomId, since: u64,
|
||||
sincecount: PduCount, next_batch: u64, next_batchcount: PduCount, lazy_load_enabled: bool,
|
||||
lazy_load_send_redundant: bool, full_state: bool, device_list_updates: &mut HashSet<OwnedUserId>,
|
||||
left_encrypted_users: &mut HashSet<OwnedUserId>,
|
||||
sender_user: &UserId, sender_device: &DeviceId, room_id: &RoomId, since: u64, sincecount: PduCount,
|
||||
next_batch: u64, next_batchcount: PduCount, lazy_load_enabled: bool, lazy_load_send_redundant: bool,
|
||||
full_state: bool, device_list_updates: &mut HashSet<OwnedUserId>, left_encrypted_users: &mut HashSet<OwnedUserId>,
|
||||
) -> Result<JoinedRoom> {
|
||||
// Get and drop the lock to wait for remaining operations to finish
|
||||
// This will make sure the we have all events until next_batch
|
||||
let insert_lock = services.rooms.timeline.mutex_insert.lock(room_id).await;
|
||||
let insert_lock = services().rooms.timeline.mutex_insert.lock(room_id).await;
|
||||
drop(insert_lock);
|
||||
|
||||
let (timeline_pdus, limited) = load_timeline(services, sender_user, room_id, sincecount, 10)?;
|
||||
let (timeline_pdus, limited) = load_timeline(sender_user, room_id, sincecount, 10)?;
|
||||
|
||||
let send_notification_counts = !timeline_pdus.is_empty()
|
||||
|| services
|
||||
|| services()
|
||||
.rooms
|
||||
.user
|
||||
.last_notification_read(sender_user, room_id)?
|
||||
@@ -545,7 +536,7 @@ async fn load_joined_room(
|
||||
timeline_users.insert(event.sender.as_str().to_owned());
|
||||
}
|
||||
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.lazy_loading
|
||||
.lazy_load_confirm_delivery(sender_user, sender_device, room_id, sincecount)
|
||||
@@ -553,11 +544,11 @@ async fn load_joined_room(
|
||||
|
||||
// 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")));
|
||||
};
|
||||
|
||||
let since_shortstatehash = services
|
||||
let since_shortstatehash = services()
|
||||
.rooms
|
||||
.user
|
||||
.get_token_shortstatehash(room_id, since)?;
|
||||
@@ -569,12 +560,12 @@ async fn load_joined_room(
|
||||
} else {
|
||||
// Calculates joined_member_count, invited_member_count and heroes
|
||||
let calculate_counts = || {
|
||||
let joined_member_count = services
|
||||
let joined_member_count = services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_joined_count(room_id)?
|
||||
.unwrap_or(0);
|
||||
let invited_member_count = services
|
||||
let invited_member_count = services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_invited_count(room_id)?
|
||||
@@ -587,7 +578,7 @@ async fn load_joined_room(
|
||||
// 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
|
||||
|
||||
for hero in services
|
||||
for hero in services()
|
||||
.rooms
|
||||
.timeline
|
||||
.all_pdus(sender_user, room_id)?
|
||||
@@ -603,8 +594,8 @@ async fn load_joined_room(
|
||||
|
||||
// The membership was and still is invite or join
|
||||
if matches!(content.membership, MembershipState::Join | MembershipState::Invite)
|
||||
&& (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_joined(&user_id, room_id)?
|
||||
|| services().rooms.state_cache.is_invited(&user_id, room_id)?)
|
||||
{
|
||||
Ok::<_, Error>(Some(user_id))
|
||||
} else {
|
||||
@@ -631,7 +622,7 @@ async fn load_joined_room(
|
||||
|
||||
let since_sender_member: Option<RoomMemberEventContent> = since_shortstatehash
|
||||
.and_then(|shortstatehash| {
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.state_get(shortstatehash, &StateEventType::RoomMember, sender_user.as_str())
|
||||
@@ -652,7 +643,7 @@ async fn load_joined_room(
|
||||
|
||||
let (joined_member_count, invited_member_count, heroes) = calculate_counts()?;
|
||||
|
||||
let current_state_ids = services
|
||||
let current_state_ids = services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.state_full_ids(current_shortstatehash)
|
||||
@@ -663,13 +654,13 @@ async fn load_joined_room(
|
||||
|
||||
let mut i: u8 = 0;
|
||||
for (shortstatekey, id) in current_state_ids {
|
||||
let (event_type, state_key) = services
|
||||
let (event_type, state_key) = services()
|
||||
.rooms
|
||||
.short
|
||||
.get_statekey_from_short(shortstatekey)?;
|
||||
|
||||
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);
|
||||
continue;
|
||||
};
|
||||
@@ -685,7 +676,7 @@ async fn load_joined_room(
|
||||
// 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)
|
||||
{
|
||||
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);
|
||||
continue;
|
||||
};
|
||||
@@ -704,14 +695,14 @@ async fn load_joined_room(
|
||||
}
|
||||
|
||||
// Reset lazy loading because this is an initial sync
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.lazy_loading
|
||||
.lazy_load_reset(sender_user, sender_device, room_id)?;
|
||||
|
||||
// The state_events above should contain all timeline_users, let's mark them as
|
||||
// lazy loaded.
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.lazy_loading
|
||||
.lazy_load_mark_sent(sender_user, sender_device, room_id, lazy_loaded, next_batchcount)
|
||||
@@ -725,12 +716,12 @@ async fn load_joined_room(
|
||||
let mut delta_state_events = Vec::new();
|
||||
|
||||
if since_shortstatehash != current_shortstatehash {
|
||||
let current_state_ids = services
|
||||
let current_state_ids = services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.state_full_ids(current_shortstatehash)
|
||||
.await?;
|
||||
let since_state_ids = services
|
||||
let since_state_ids = services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.state_full_ids(since_shortstatehash)
|
||||
@@ -738,7 +729,7 @@ async fn load_joined_room(
|
||||
|
||||
for (key, id) in current_state_ids {
|
||||
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);
|
||||
continue;
|
||||
};
|
||||
@@ -749,13 +740,13 @@ async fn load_joined_room(
|
||||
}
|
||||
}
|
||||
|
||||
let encrypted_room = services
|
||||
let encrypted_room = services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.state_get(current_shortstatehash, &StateEventType::RoomEncryption, "")?
|
||||
.is_some();
|
||||
|
||||
let since_encryption = services.rooms.state_accessor.state_get(
|
||||
let since_encryption = services().rooms.state_accessor.state_get(
|
||||
since_shortstatehash,
|
||||
&StateEventType::RoomEncryption,
|
||||
"",
|
||||
@@ -790,7 +781,7 @@ async fn load_joined_room(
|
||||
match new_membership {
|
||||
MembershipState::Join => {
|
||||
// A new user joined an encrypted room
|
||||
if !share_encrypted_room(services, sender_user, &user_id, room_id)? {
|
||||
if !share_encrypted_room(sender_user, &user_id, room_id)? {
|
||||
device_list_updates.insert(user_id);
|
||||
}
|
||||
},
|
||||
@@ -807,7 +798,7 @@ async fn load_joined_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
|
||||
device_list_updates.extend(
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_members(room_id)
|
||||
@@ -819,7 +810,7 @@ async fn load_joined_room(
|
||||
.filter(|user_id| {
|
||||
// Only send keys if the sender doesn't share an encrypted room with the target
|
||||
// already
|
||||
!share_encrypted_room(services, sender_user, user_id, room_id).unwrap_or(false)
|
||||
!share_encrypted_room(sender_user, user_id, room_id).unwrap_or(false)
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -857,14 +848,14 @@ async fn load_joined_room(
|
||||
continue;
|
||||
}
|
||||
|
||||
if !services.rooms.lazy_loading.lazy_load_was_sent_before(
|
||||
if !services().rooms.lazy_loading.lazy_load_was_sent_before(
|
||||
sender_user,
|
||||
sender_device,
|
||||
room_id,
|
||||
&event.sender,
|
||||
)? || 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,
|
||||
&StateEventType::RoomMember,
|
||||
event.sender.as_str(),
|
||||
@@ -875,7 +866,7 @@ async fn load_joined_room(
|
||||
}
|
||||
}
|
||||
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.lazy_loading
|
||||
.lazy_load_mark_sent(sender_user, sender_device, room_id, lazy_loaded, next_batchcount)
|
||||
@@ -893,7 +884,7 @@ async fn load_joined_room(
|
||||
|
||||
// Look for device list updates in this room
|
||||
device_list_updates.extend(
|
||||
services
|
||||
services()
|
||||
.users
|
||||
.keys_changed(room_id.as_ref(), since, None)
|
||||
.filter_map(Result::ok),
|
||||
@@ -901,7 +892,7 @@ async fn load_joined_room(
|
||||
|
||||
let notification_count = if send_notification_counts {
|
||||
Some(
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.user
|
||||
.notification_count(sender_user, room_id)?
|
||||
@@ -914,7 +905,7 @@ async fn load_joined_room(
|
||||
|
||||
let highlight_count = if send_notification_counts {
|
||||
Some(
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.user
|
||||
.highlight_count(sender_user, room_id)?
|
||||
@@ -942,7 +933,7 @@ async fn load_joined_room(
|
||||
.map(|(_, pdu)| pdu.to_sync_room_event())
|
||||
.collect();
|
||||
|
||||
let mut edus: Vec<_> = services
|
||||
let mut edus: Vec<_> = services()
|
||||
.rooms
|
||||
.read_receipt
|
||||
.readreceipts_since(room_id, since)
|
||||
@@ -950,10 +941,10 @@ async fn load_joined_room(
|
||||
.map(|(_, _, v)| v)
|
||||
.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(
|
||||
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"),
|
||||
@@ -962,14 +953,14 @@ async fn load_joined_room(
|
||||
|
||||
// Save the state after this sync so we can send the correct state diff next
|
||||
// sync
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.user
|
||||
.associate_token_shortstatehash(room_id, next_batch, current_shortstatehash)?;
|
||||
|
||||
Ok(JoinedRoom {
|
||||
account_data: RoomAccountData {
|
||||
events: services
|
||||
events: services()
|
||||
.account_data
|
||||
.changes_since(Some(room_id), sender_user, since)?
|
||||
.into_iter()
|
||||
@@ -994,7 +985,7 @@ async fn load_joined_room(
|
||||
prev_batch,
|
||||
events: room_events,
|
||||
},
|
||||
state: RoomState {
|
||||
state: State {
|
||||
events: state_events
|
||||
.iter()
|
||||
.map(|pdu| pdu.to_sync_state_event())
|
||||
@@ -1008,16 +999,16 @@ async fn load_joined_room(
|
||||
}
|
||||
|
||||
fn load_timeline(
|
||||
services: &Services, sender_user: &UserId, room_id: &RoomId, roomsincecount: PduCount, limit: u64,
|
||||
sender_user: &UserId, room_id: &RoomId, roomsincecount: PduCount, limit: u64,
|
||||
) -> Result<(Vec<(PduCount, PduEvent)>, bool), Error> {
|
||||
let timeline_pdus;
|
||||
let limited = if services
|
||||
let limited = if services()
|
||||
.rooms
|
||||
.timeline
|
||||
.last_timeline_count(sender_user, room_id)?
|
||||
> roomsincecount
|
||||
{
|
||||
let mut non_timeline_pdus = services
|
||||
let mut non_timeline_pdus = services()
|
||||
.rooms
|
||||
.timeline
|
||||
.pdus_until(sender_user, room_id, PduCount::max())?
|
||||
@@ -1049,10 +1040,8 @@ fn load_timeline(
|
||||
Ok((timeline_pdus, limited))
|
||||
}
|
||||
|
||||
fn share_encrypted_room(
|
||||
services: &Services, sender_user: &UserId, user_id: &UserId, ignore_room: &RoomId,
|
||||
) -> Result<bool> {
|
||||
Ok(services
|
||||
fn share_encrypted_room(sender_user: &UserId, user_id: &UserId, ignore_room: &RoomId) -> Result<bool> {
|
||||
Ok(services()
|
||||
.rooms
|
||||
.user
|
||||
.get_shared_rooms(vec![sender_user.to_owned(), user_id.to_owned()])?
|
||||
@@ -1060,7 +1049,7 @@ fn share_encrypted_room(
|
||||
.filter(|room_id| room_id != ignore_room)
|
||||
.filter_map(|other_room_id| {
|
||||
Some(
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get(&other_room_id, &StateEventType::RoomEncryption, "")
|
||||
@@ -1075,15 +1064,15 @@ fn share_encrypted_room(
|
||||
///
|
||||
/// Sliding Sync endpoint (future endpoint: `/_matrix/client/v4/sync`)
|
||||
pub(crate) async fn sync_events_v4_route(
|
||||
State(services): State<crate::State>, body: Ruma<sync_events::v4::Request>,
|
||||
body: Ruma<sync_events::v4::Request>,
|
||||
) -> Result<sync_events::v4::Response, RumaResponse<UiaaResponse>> {
|
||||
let sender_user = body.sender_user.expect("user is authenticated");
|
||||
let sender_device = body.sender_device.expect("user is authenticated");
|
||||
let mut body = body.body;
|
||||
// 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
|
||||
.pos
|
||||
@@ -1093,19 +1082,21 @@ pub(crate) async fn sync_events_v4_route(
|
||||
|
||||
if globalsince == 0 {
|
||||
if let Some(conn_id) = &body.conn_id {
|
||||
services
|
||||
.users
|
||||
.forget_sync_request_connection(sender_user.clone(), sender_device.clone(), conn_id.clone());
|
||||
services().users.forget_sync_request_connection(
|
||||
sender_user.clone(),
|
||||
sender_device.clone(),
|
||||
conn_id.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Get sticky parameters from cache
|
||||
let known_rooms =
|
||||
services
|
||||
services()
|
||||
.users
|
||||
.update_sync_request_with_cache(sender_user.clone(), sender_device.clone(), &mut body);
|
||||
|
||||
let all_joined_rooms = services
|
||||
let all_joined_rooms = services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.rooms_joined(&sender_user)
|
||||
@@ -1113,7 +1104,7 @@ pub(crate) async fn sync_events_v4_route(
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if body.extensions.to_device.enabled.unwrap_or(false) {
|
||||
services
|
||||
services()
|
||||
.users
|
||||
.remove_to_device_events(&sender_user, &sender_device, globalsince)?;
|
||||
}
|
||||
@@ -1125,26 +1116,26 @@ pub(crate) async fn sync_events_v4_route(
|
||||
if body.extensions.e2ee.enabled.unwrap_or(false) {
|
||||
// Look for device list updates of this account
|
||||
device_list_changes.extend(
|
||||
services
|
||||
services()
|
||||
.users
|
||||
.keys_changed(sender_user.as_ref(), globalsince, None)
|
||||
.filter_map(Result::ok),
|
||||
);
|
||||
|
||||
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);
|
||||
continue;
|
||||
};
|
||||
|
||||
let since_shortstatehash = services
|
||||
let since_shortstatehash = services()
|
||||
.rooms
|
||||
.user
|
||||
.get_token_shortstatehash(room_id, globalsince)?;
|
||||
|
||||
let since_sender_member: Option<RoomMemberEventContent> = since_shortstatehash
|
||||
.and_then(|shortstatehash| {
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.state_get(shortstatehash, &StateEventType::RoomMember, sender_user.as_str())
|
||||
@@ -1157,7 +1148,7 @@ pub(crate) async fn sync_events_v4_route(
|
||||
.ok()
|
||||
});
|
||||
|
||||
let encrypted_room = services
|
||||
let encrypted_room = services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.state_get(current_shortstatehash, &StateEventType::RoomEncryption, "")?
|
||||
@@ -1169,7 +1160,7 @@ pub(crate) async fn sync_events_v4_route(
|
||||
continue;
|
||||
}
|
||||
|
||||
let since_encryption = services.rooms.state_accessor.state_get(
|
||||
let since_encryption = services().rooms.state_accessor.state_get(
|
||||
since_shortstatehash,
|
||||
&StateEventType::RoomEncryption,
|
||||
"",
|
||||
@@ -1180,12 +1171,12 @@ pub(crate) async fn sync_events_v4_route(
|
||||
|
||||
let new_encrypted_room = encrypted_room && since_encryption.is_none();
|
||||
if encrypted_room {
|
||||
let current_state_ids = services
|
||||
let current_state_ids = services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.state_full_ids(current_shortstatehash)
|
||||
.await?;
|
||||
let since_state_ids = services
|
||||
let since_state_ids = services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.state_full_ids(since_shortstatehash)
|
||||
@@ -1193,7 +1184,7 @@ pub(crate) async fn sync_events_v4_route(
|
||||
|
||||
for (key, id) in current_state_ids {
|
||||
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);
|
||||
continue;
|
||||
};
|
||||
@@ -1214,7 +1205,7 @@ pub(crate) async fn sync_events_v4_route(
|
||||
match new_membership {
|
||||
MembershipState::Join => {
|
||||
// A new user joined an encrypted room
|
||||
if !share_encrypted_room(&services, &sender_user, &user_id, room_id)? {
|
||||
if !share_encrypted_room(&sender_user, &user_id, room_id)? {
|
||||
device_list_changes.insert(user_id);
|
||||
}
|
||||
},
|
||||
@@ -1231,7 +1222,7 @@ pub(crate) async fn sync_events_v4_route(
|
||||
if joined_since_last_sync || new_encrypted_room {
|
||||
// If the user is in a new encrypted room, give them all joined users
|
||||
device_list_changes.extend(
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_members(room_id)
|
||||
@@ -1243,7 +1234,7 @@ pub(crate) async fn sync_events_v4_route(
|
||||
.filter(|user_id| {
|
||||
// Only send keys if the sender doesn't share an encrypted room with the target
|
||||
// already
|
||||
!share_encrypted_room(&services, &sender_user, user_id, room_id).unwrap_or(false)
|
||||
!share_encrypted_room(&sender_user, user_id, room_id).unwrap_or(false)
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -1251,21 +1242,21 @@ pub(crate) async fn sync_events_v4_route(
|
||||
}
|
||||
// Look for device list updates in this room
|
||||
device_list_changes.extend(
|
||||
services
|
||||
services()
|
||||
.users
|
||||
.keys_changed(room_id.as_ref(), globalsince, None)
|
||||
.filter_map(Result::ok),
|
||||
);
|
||||
}
|
||||
for user_id in left_encrypted_users {
|
||||
let dont_share_encrypted_room = services
|
||||
let dont_share_encrypted_room = services()
|
||||
.rooms
|
||||
.user
|
||||
.get_shared_rooms(vec![sender_user.clone(), user_id.clone()])?
|
||||
.filter_map(Result::ok)
|
||||
.filter_map(|other_room_id| {
|
||||
Some(
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get(&other_room_id, &StateEventType::RoomEncryption, "")
|
||||
@@ -1345,7 +1336,7 @@ pub(crate) async fn sync_events_v4_route(
|
||||
);
|
||||
|
||||
if let Some(conn_id) = &body.conn_id {
|
||||
services.users.update_sync_known_rooms(
|
||||
services().users.update_sync_known_rooms(
|
||||
sender_user.clone(),
|
||||
sender_device.clone(),
|
||||
conn_id.clone(),
|
||||
@@ -1358,7 +1349,7 @@ pub(crate) async fn sync_events_v4_route(
|
||||
|
||||
let mut known_subscription_rooms = BTreeSet::new();
|
||||
for (room_id, room) in &body.room_subscriptions {
|
||||
if !services.rooms.metadata.exists(room_id)? {
|
||||
if !services().rooms.metadata.exists(room_id)? {
|
||||
continue;
|
||||
}
|
||||
let todo_room = todo_rooms
|
||||
@@ -1384,7 +1375,7 @@ pub(crate) async fn sync_events_v4_route(
|
||||
}
|
||||
|
||||
if let Some(conn_id) = &body.conn_id {
|
||||
services.users.update_sync_known_rooms(
|
||||
services().users.update_sync_known_rooms(
|
||||
sender_user.clone(),
|
||||
sender_device.clone(),
|
||||
conn_id.clone(),
|
||||
@@ -1395,7 +1386,7 @@ pub(crate) async fn sync_events_v4_route(
|
||||
}
|
||||
|
||||
if let Some(conn_id) = &body.conn_id {
|
||||
services.users.update_sync_subscriptions(
|
||||
services().users.update_sync_subscriptions(
|
||||
sender_user.clone(),
|
||||
sender_device.clone(),
|
||||
conn_id.clone(),
|
||||
@@ -1407,8 +1398,7 @@ pub(crate) async fn sync_events_v4_route(
|
||||
for (room_id, (required_state_request, timeline_limit, roomsince)) in &todo_rooms {
|
||||
let roomsincecount = PduCount::Normal(*roomsince);
|
||||
|
||||
let (timeline_pdus, limited) =
|
||||
load_timeline(&services, &sender_user, room_id, roomsincecount, *timeline_limit)?;
|
||||
let (timeline_pdus, limited) = load_timeline(&sender_user, room_id, roomsincecount, *timeline_limit)?;
|
||||
|
||||
if roomsince != &0 && timeline_pdus.is_empty() {
|
||||
continue;
|
||||
@@ -1441,7 +1431,7 @@ pub(crate) async fn sync_events_v4_route(
|
||||
let required_state = required_state_request
|
||||
.iter()
|
||||
.map(|state| {
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get(room_id, &state.0, &state.1)
|
||||
@@ -1452,7 +1442,7 @@ pub(crate) async fn sync_events_v4_route(
|
||||
.collect();
|
||||
|
||||
// Heroes
|
||||
let heroes = services
|
||||
let heroes = services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_members(room_id)
|
||||
@@ -1460,7 +1450,7 @@ pub(crate) async fn sync_events_v4_route(
|
||||
.filter(|member| member != &sender_user)
|
||||
.map(|member| {
|
||||
Ok::<_, Error>(
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.get_member(room_id, &member)?
|
||||
@@ -1501,11 +1491,11 @@ pub(crate) async fn sync_events_v4_route(
|
||||
rooms.insert(
|
||||
room_id.clone(),
|
||||
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 {
|
||||
ruma::JsOption::Some(heroes_avatar)
|
||||
} 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::Null => ruma::JsOption::Null,
|
||||
ruma::JsOption::Undefined => ruma::JsOption::Undefined,
|
||||
@@ -1516,7 +1506,7 @@ pub(crate) async fn sync_events_v4_route(
|
||||
invite_state: None,
|
||||
unread_notifications: UnreadNotificationsCount {
|
||||
highlight_count: Some(
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.user
|
||||
.highlight_count(&sender_user, room_id)?
|
||||
@@ -1524,7 +1514,7 @@ pub(crate) async fn sync_events_v4_route(
|
||||
.expect("notification count can't go that high"),
|
||||
),
|
||||
notification_count: Some(
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.user
|
||||
.notification_count(&sender_user, room_id)?
|
||||
@@ -1537,7 +1527,7 @@ pub(crate) async fn sync_events_v4_route(
|
||||
prev_batch,
|
||||
limited,
|
||||
joined_count: Some(
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_joined_count(room_id)?
|
||||
@@ -1546,7 +1536,7 @@ pub(crate) async fn sync_events_v4_route(
|
||||
.unwrap_or_else(|_| uint!(0)),
|
||||
),
|
||||
invited_count: Some(
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_invited_count(room_id)?
|
||||
@@ -1581,7 +1571,7 @@ pub(crate) async fn sync_events_v4_route(
|
||||
extensions: sync_events::v4::Extensions {
|
||||
to_device: if body.extensions.to_device.enabled.unwrap_or(false) {
|
||||
Some(sync_events::v4::ToDevice {
|
||||
events: services
|
||||
events: services()
|
||||
.users
|
||||
.get_to_device_events(&sender_user, &sender_device)?,
|
||||
next_batch: next_batch.to_string(),
|
||||
@@ -1594,7 +1584,7 @@ pub(crate) async fn sync_events_v4_route(
|
||||
changed: device_list_changes.into_iter().collect(),
|
||||
left: device_list_left.into_iter().collect(),
|
||||
},
|
||||
device_one_time_keys_count: services
|
||||
device_one_time_keys_count: services()
|
||||
.users
|
||||
.count_one_time_keys(&sender_user, &sender_device)?,
|
||||
// Fallback keys are not yet supported
|
||||
@@ -1602,7 +1592,7 @@ pub(crate) async fn sync_events_v4_route(
|
||||
},
|
||||
account_data: sync_events::v4::AccountData {
|
||||
global: if body.extensions.account_data.enabled.unwrap_or(false) {
|
||||
services
|
||||
services()
|
||||
.account_data
|
||||
.changes_since(None, &sender_user, globalsince)?
|
||||
.into_iter()
|
||||
|
||||
+9
-16
@@ -1,6 +1,5 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use axum::extract::State;
|
||||
use ruma::{
|
||||
api::client::tag::{create_tag, delete_tag, get_tags},
|
||||
events::{
|
||||
@@ -9,19 +8,17 @@ use ruma::{
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{Error, Result, Ruma};
|
||||
use crate::{services, Error, Result, Ruma};
|
||||
|
||||
/// # `PUT /_matrix/client/r0/user/{userId}/rooms/{roomId}/tags/{tag}`
|
||||
///
|
||||
/// Adds a tag to the room.
|
||||
///
|
||||
/// - Inserts the tag into the tag event of the room account data.
|
||||
pub(crate) async fn update_tag_route(
|
||||
State(services): State<crate::State>, body: Ruma<create_tag::v3::Request>,
|
||||
) -> Result<create_tag::v3::Response> {
|
||||
pub(crate) async fn update_tag_route(body: Ruma<create_tag::v3::Request>) -> Result<create_tag::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
let event = services
|
||||
let event = services()
|
||||
.account_data
|
||||
.get(Some(&body.room_id), sender_user, RoomAccountDataEventType::Tag)?;
|
||||
|
||||
@@ -41,7 +38,7 @@ pub(crate) async fn update_tag_route(
|
||||
.tags
|
||||
.insert(body.tag.clone().into(), body.tag_info.clone());
|
||||
|
||||
services.account_data.update(
|
||||
services().account_data.update(
|
||||
Some(&body.room_id),
|
||||
sender_user,
|
||||
RoomAccountDataEventType::Tag,
|
||||
@@ -56,12 +53,10 @@ pub(crate) async fn update_tag_route(
|
||||
/// Deletes a tag from the room.
|
||||
///
|
||||
/// - Removes the tag from the tag event of the room account data.
|
||||
pub(crate) async fn delete_tag_route(
|
||||
State(services): State<crate::State>, body: Ruma<delete_tag::v3::Request>,
|
||||
) -> Result<delete_tag::v3::Response> {
|
||||
pub(crate) async fn delete_tag_route(body: Ruma<delete_tag::v3::Request>) -> Result<delete_tag::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
let event = services
|
||||
let event = services()
|
||||
.account_data
|
||||
.get(Some(&body.room_id), sender_user, RoomAccountDataEventType::Tag)?;
|
||||
|
||||
@@ -78,7 +73,7 @@ pub(crate) async fn delete_tag_route(
|
||||
|
||||
tags_event.content.tags.remove(&body.tag.clone().into());
|
||||
|
||||
services.account_data.update(
|
||||
services().account_data.update(
|
||||
Some(&body.room_id),
|
||||
sender_user,
|
||||
RoomAccountDataEventType::Tag,
|
||||
@@ -93,12 +88,10 @@ pub(crate) async fn delete_tag_route(
|
||||
/// Returns tags on the room.
|
||||
///
|
||||
/// - Gets the tag event of the room account data.
|
||||
pub(crate) async fn get_tags_route(
|
||||
State(services): State<crate::State>, body: Ruma<get_tags::v3::Request>,
|
||||
) -> Result<get_tags::v3::Response> {
|
||||
pub(crate) async fn get_tags_route(body: Ruma<get_tags::v3::Request>) -> Result<get_tags::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
let event = services
|
||||
let event = services()
|
||||
.account_data
|
||||
.get(Some(&body.room_id), sender_user, RoomAccountDataEventType::Tag)?;
|
||||
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
use axum::extract::State;
|
||||
use ruma::{
|
||||
api::client::{error::ErrorKind, threads::get_threads},
|
||||
uint,
|
||||
};
|
||||
|
||||
use crate::{Error, Result, Ruma};
|
||||
use crate::{services, Error, Result, Ruma};
|
||||
|
||||
/// # `GET /_matrix/client/r0/rooms/{roomId}/threads`
|
||||
pub(crate) async fn get_threads_route(
|
||||
State(services): State<crate::State>, body: Ruma<get_threads::v1::Request>,
|
||||
) -> Result<get_threads::v1::Response> {
|
||||
pub(crate) async fn get_threads_route(body: Ruma<get_threads::v1::Request>) -> Result<get_threads::v1::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
// Use limit or else 10, with maximum 100
|
||||
@@ -27,14 +24,14 @@ pub(crate) async fn get_threads_route(
|
||||
u64::MAX
|
||||
};
|
||||
|
||||
let threads = services
|
||||
let threads = services()
|
||||
.rooms
|
||||
.threads
|
||||
.threads_until(sender_user, &body.room_id, from, &body.include)?
|
||||
.take(limit)
|
||||
.filter_map(Result::ok)
|
||||
.filter(|(_, pdu)| {
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.user_can_see_event(sender_user, &body.room_id, &pdu.event_id)
|
||||
|
||||
+10
-12
@@ -1,7 +1,5 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use axum::extract::State;
|
||||
use conduit::{Error, Result};
|
||||
use ruma::{
|
||||
api::{
|
||||
client::{error::ErrorKind, to_device::send_event_to_device},
|
||||
@@ -10,19 +8,19 @@ use ruma::{
|
||||
to_device::DeviceIdOrAllDevices,
|
||||
};
|
||||
|
||||
use crate::Ruma;
|
||||
use crate::{services, user_is_local, Error, Result, Ruma};
|
||||
|
||||
/// # `PUT /_matrix/client/r0/sendToDevice/{eventType}/{txnId}`
|
||||
///
|
||||
/// Send a to-device event to a set of client devices.
|
||||
pub(crate) async fn send_event_to_device_route(
|
||||
State(services): State<crate::State>, body: Ruma<send_event_to_device::v3::Request>,
|
||||
body: Ruma<send_event_to_device::v3::Request>,
|
||||
) -> Result<send_event_to_device::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let sender_device = body.sender_device.as_deref();
|
||||
|
||||
// Check if this is a new transaction id
|
||||
if services
|
||||
if services()
|
||||
.transaction_ids
|
||||
.existing_txnid(sender_user, sender_device, &body.txn_id)?
|
||||
.is_some()
|
||||
@@ -32,14 +30,14 @@ pub(crate) async fn send_event_to_device_route(
|
||||
|
||||
for (target_user_id, map) in &body.messages {
|
||||
for (target_device_id_maybe, event) in map {
|
||||
if !services.globals.user_is_local(target_user_id) {
|
||||
if !user_is_local(target_user_id) {
|
||||
let mut map = BTreeMap::new();
|
||||
map.insert(target_device_id_maybe.clone(), event.clone());
|
||||
let mut messages = BTreeMap::new();
|
||||
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(),
|
||||
serde_json::to_vec(&federation::transactions::edu::Edu::DirectToDevice(DirectDeviceContent {
|
||||
sender: sender_user.clone(),
|
||||
@@ -55,7 +53,7 @@ pub(crate) async fn send_event_to_device_route(
|
||||
|
||||
match target_device_id_maybe {
|
||||
DeviceIdOrAllDevices::DeviceId(target_device_id) => {
|
||||
services.users.add_to_device_event(
|
||||
services().users.add_to_device_event(
|
||||
sender_user,
|
||||
target_user_id,
|
||||
target_device_id,
|
||||
@@ -67,8 +65,8 @@ pub(crate) async fn send_event_to_device_route(
|
||||
},
|
||||
|
||||
DeviceIdOrAllDevices::AllDevices => {
|
||||
for target_device_id in services.users.all_device_ids(target_user_id) {
|
||||
services.users.add_to_device_event(
|
||||
for target_device_id in services().users.all_device_ids(target_user_id) {
|
||||
services().users.add_to_device_event(
|
||||
sender_user,
|
||||
target_user_id,
|
||||
&target_device_id?,
|
||||
@@ -84,7 +82,7 @@ pub(crate) async fn send_event_to_device_route(
|
||||
}
|
||||
|
||||
// Save transaction id with empty data
|
||||
services
|
||||
services()
|
||||
.transaction_ids
|
||||
.add_txnid(sender_user, sender_device, &body.txn_id, &[])?;
|
||||
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
use axum::extract::State;
|
||||
use ruma::api::client::{error::ErrorKind, typing::create_typing_event};
|
||||
|
||||
use crate::{utils, Error, Result, Ruma};
|
||||
use crate::{services, utils, Error, Result, Ruma};
|
||||
|
||||
/// # `PUT /_matrix/client/r0/rooms/{roomId}/typing/{userId}`
|
||||
///
|
||||
/// Sets the typing state of the sender user.
|
||||
pub(crate) async fn create_typing_event_route(
|
||||
State(services): State<crate::State>, body: Ruma<create_typing_event::v3::Request>,
|
||||
body: Ruma<create_typing_event::v3::Request>,
|
||||
) -> Result<create_typing_event::v3::Response> {
|
||||
use create_typing_event::v3::Typing;
|
||||
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
if !services
|
||||
if !services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.is_joined(sender_user, &body.room_id)?
|
||||
@@ -24,20 +23,20 @@ pub(crate) async fn create_typing_event_route(
|
||||
if let Typing::Yes(duration) = body.state {
|
||||
let duration = utils::clamp(
|
||||
duration.as_millis().try_into().unwrap_or(u64::MAX),
|
||||
services
|
||||
services()
|
||||
.globals
|
||||
.config
|
||||
.typing_client_timeout_min_s
|
||||
.checked_mul(1000)
|
||||
.unwrap(),
|
||||
services
|
||||
services()
|
||||
.globals
|
||||
.config
|
||||
.typing_client_timeout_max_s
|
||||
.checked_mul(1000)
|
||||
.unwrap(),
|
||||
);
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.typing
|
||||
.typing_add(
|
||||
@@ -49,7 +48,7 @@ pub(crate) async fn create_typing_event_route(
|
||||
)
|
||||
.await?;
|
||||
} else {
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.typing
|
||||
.typing_remove(sender_user, &body.room_id)
|
||||
|
||||
+26
-26
@@ -1,4 +1,3 @@
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::InsecureClientIp;
|
||||
use conduit::warn;
|
||||
use ruma::{
|
||||
@@ -7,7 +6,7 @@ use ruma::{
|
||||
OwnedRoomId,
|
||||
};
|
||||
|
||||
use crate::{Error, Result, Ruma, RumaResponse};
|
||||
use crate::{services, Error, Result, Ruma, RumaResponse};
|
||||
|
||||
/// # `GET /_matrix/client/unstable/uk.half-shot.msc2666/user/mutual_rooms`
|
||||
///
|
||||
@@ -18,8 +17,7 @@ use crate::{Error, Result, Ruma, RumaResponse};
|
||||
/// An implementation of [MSC2666](https://github.com/matrix-org/matrix-spec-proposals/pull/2666)
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "mutual_rooms")]
|
||||
pub(crate) async fn get_mutual_rooms_route(
|
||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||
body: Ruma<mutual_rooms::unstable::Request>,
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<mutual_rooms::unstable::Request>,
|
||||
) -> Result<mutual_rooms::unstable::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
@@ -30,14 +28,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 {
|
||||
joined: vec![],
|
||||
next_batch_token: None,
|
||||
});
|
||||
}
|
||||
|
||||
let mutual_rooms: Vec<OwnedRoomId> = services
|
||||
let mutual_rooms: Vec<OwnedRoomId> = services()
|
||||
.rooms
|
||||
.user
|
||||
.get_shared_rooms(vec![sender_user.clone(), body.user_id.clone()])?
|
||||
@@ -60,10 +58,9 @@ pub(crate) async fn get_mutual_rooms_route(
|
||||
///
|
||||
/// An implementation of [MSC3266](https://github.com/matrix-org/matrix-spec-proposals/pull/3266)
|
||||
pub(crate) async fn get_room_summary_legacy(
|
||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||
body: Ruma<get_summary::msc3266::Request>,
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_summary::msc3266::Request>,
|
||||
) -> Result<RumaResponse<get_summary::msc3266::Response>> {
|
||||
get_room_summary(State(services), InsecureClientIp(client), body)
|
||||
get_room_summary(InsecureClientIp(client), body)
|
||||
.await
|
||||
.map(RumaResponse)
|
||||
}
|
||||
@@ -77,19 +74,22 @@ pub(crate) async fn get_room_summary_legacy(
|
||||
/// An implementation of [MSC3266](https://github.com/matrix-org/matrix-spec-proposals/pull/3266)
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "room_summary")]
|
||||
pub(crate) async fn get_room_summary(
|
||||
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
|
||||
body: Ruma<get_summary::msc3266::Request>,
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_summary::msc3266::Request>,
|
||||
) -> Result<get_summary::msc3266::Response> {
|
||||
let sender_user = body.sender_user.as_ref();
|
||||
|
||||
let room_id = services.rooms.alias.resolve(&body.room_id_or_alias).await?;
|
||||
let room_id = services()
|
||||
.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"));
|
||||
}
|
||||
|
||||
if sender_user.is_none()
|
||||
&& !services
|
||||
&& !services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.is_world_readable(&room_id)
|
||||
@@ -103,25 +103,25 @@ pub(crate) async fn get_room_summary(
|
||||
|
||||
Ok(get_summary::msc3266::Response {
|
||||
room_id: room_id.clone(),
|
||||
canonical_alias: services
|
||||
canonical_alias: services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.get_canonical_alias(&room_id)
|
||||
.unwrap_or(None),
|
||||
avatar_url: services
|
||||
avatar_url: services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.get_avatar(&room_id)?
|
||||
.into_option()
|
||||
.unwrap_or_default()
|
||||
.url,
|
||||
guest_can_join: services.rooms.state_accessor.guest_can_join(&room_id)?,
|
||||
name: services
|
||||
guest_can_join: services().rooms.state_accessor.guest_can_join(&room_id)?,
|
||||
name: services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.get_name(&room_id)
|
||||
.unwrap_or(None),
|
||||
num_joined_members: services
|
||||
num_joined_members: services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_joined_count(&room_id)
|
||||
@@ -132,21 +132,21 @@ pub(crate) async fn get_room_summary(
|
||||
})
|
||||
.try_into()
|
||||
.expect("user count should not be that big"),
|
||||
topic: services
|
||||
topic: services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.get_room_topic(&room_id)
|
||||
.unwrap_or(None),
|
||||
world_readable: services
|
||||
world_readable: services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.is_world_readable(&room_id)
|
||||
.unwrap_or(false),
|
||||
join_rule: services.rooms.state_accessor.get_join_rule(&room_id)?.0,
|
||||
room_type: services.rooms.state_accessor.get_room_type(&room_id)?,
|
||||
room_version: Some(services.rooms.state.get_room_version(&room_id)?),
|
||||
join_rule: services().rooms.state_accessor.get_join_rule(&room_id)?.0,
|
||||
room_type: services().rooms.state_accessor.get_room_type(&room_id)?,
|
||||
room_version: Some(services().rooms.state.get_room_version(&room_id)?),
|
||||
membership: if let Some(sender_user) = sender_user {
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.get_member(&room_id, sender_user)?
|
||||
@@ -154,7 +154,7 @@ pub(crate) async fn get_room_summary(
|
||||
} else {
|
||||
None
|
||||
},
|
||||
encryption: services
|
||||
encryption: services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.get_room_encryption(&room_id)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use axum::{extract::State, response::IntoResponse, Json};
|
||||
use axum::{response::IntoResponse, Json};
|
||||
use ruma::api::client::{
|
||||
discovery::{
|
||||
discover_homeserver::{self, HomeserverInfo, SlidingSyncProxyInfo},
|
||||
@@ -10,7 +10,7 @@ use ruma::api::client::{
|
||||
error::ErrorKind,
|
||||
};
|
||||
|
||||
use crate::{Error, Result, Ruma};
|
||||
use crate::{services, Error, Result, Ruma};
|
||||
|
||||
/// # `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.
|
||||
pub(crate) async fn well_known_client(
|
||||
State(services): State<crate::State>, _body: Ruma<discover_homeserver::Request>,
|
||||
_body: Ruma<discover_homeserver::Request>,
|
||||
) -> 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(),
|
||||
None => return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")),
|
||||
};
|
||||
@@ -84,24 +84,22 @@ pub(crate) async fn well_known_client(
|
||||
/// # `GET /.well-known/matrix/support`
|
||||
///
|
||||
/// Server support contact and support page of a homeserver's domain.
|
||||
pub(crate) async fn well_known_support(
|
||||
State(services): State<crate::State>, _body: Ruma<discover_support::Request>,
|
||||
) -> Result<discover_support::Response> {
|
||||
let support_page = services
|
||||
pub(crate) async fn well_known_support(_body: Ruma<discover_support::Request>) -> Result<discover_support::Response> {
|
||||
let support_page = services()
|
||||
.globals
|
||||
.well_known_support_page()
|
||||
.as_ref()
|
||||
.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
|
||||
if support_page.is_none() && role.is_none() {
|
||||
return Err(Error::BadRequest(ErrorKind::NotFound, "Not found."));
|
||||
}
|
||||
|
||||
let email_address = services.globals.well_known_support_email().clone();
|
||||
let matrix_id = services.globals.well_known_support_mxid().clone();
|
||||
let email_address = services().globals.well_known_support_email().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 role.is_some() && (email_address.is_none() && matrix_id.is_none()) {
|
||||
@@ -136,10 +134,10 @@ pub(crate) async fn well_known_support(
|
||||
///
|
||||
/// Endpoint provided by sliding sync proxy used by some clients such as Element
|
||||
/// Web as a non-standard health check.
|
||||
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() {
|
||||
pub(crate) async fn syncv3_client_server_json() -> Result<impl IntoResponse> {
|
||||
let server_url = match services().globals.well_known_client() {
|
||||
Some(url) => url.to_string(),
|
||||
None => match services.globals.well_known_server() {
|
||||
None => match services().globals.well_known_server() {
|
||||
Some(url) => url.to_string(),
|
||||
None => return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")),
|
||||
},
|
||||
@@ -167,8 +165,8 @@ pub(crate) async fn conduwuit_server_version() -> Result<impl IntoResponse> {
|
||||
/// conduwuit-specific API to return the amount of users registered on this
|
||||
/// homeserver. Endpoint is disabled if federation is disabled for privacy. This
|
||||
/// only includes active users (not deactivated, no guests, etc)
|
||||
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();
|
||||
pub(crate) async fn conduwuit_local_user_count() -> Result<impl IntoResponse> {
|
||||
let user_count = services().users.list_local_users()?.len();
|
||||
|
||||
Ok(Json(serde_json::json!({
|
||||
"count": user_count
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use axum::extract::State;
|
||||
use ruma::{
|
||||
api::client::user_directory::search_users,
|
||||
events::{
|
||||
@@ -7,7 +6,7 @@ use ruma::{
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{Result, Ruma};
|
||||
use crate::{services, Result, Ruma};
|
||||
|
||||
/// # `POST /_matrix/client/r0/user_directory/search`
|
||||
///
|
||||
@@ -15,20 +14,18 @@ use crate::{Result, Ruma};
|
||||
///
|
||||
/// - 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
|
||||
pub(crate) async fn search_users_route(
|
||||
State(services): State<crate::State>, body: Ruma<search_users::v3::Request>,
|
||||
) -> Result<search_users::v3::Response> {
|
||||
pub(crate) async fn search_users_route(body: Ruma<search_users::v3::Request>) -> Result<search_users::v3::Response> {
|
||||
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 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...)
|
||||
let user_id = user_id.ok()?;
|
||||
|
||||
let user = search_users::v3::User {
|
||||
user_id: user_id.clone(),
|
||||
display_name: services.users.displayname(&user_id).ok()?,
|
||||
avatar_url: services.users.avatar_url(&user_id).ok()?,
|
||||
display_name: services().users.displayname(&user_id).ok()?,
|
||||
avatar_url: services().users.avatar_url(&user_id).ok()?,
|
||||
};
|
||||
|
||||
let user_id_matches = user
|
||||
@@ -53,13 +50,13 @@ pub(crate) async fn search_users_route(
|
||||
// It's a matching user, but is the sender allowed to see them?
|
||||
let mut user_visible = false;
|
||||
|
||||
let user_is_in_public_rooms = services
|
||||
let user_is_in_public_rooms = services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.rooms_joined(&user_id)
|
||||
.filter_map(Result::ok)
|
||||
.any(|room| {
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get(&room, &StateEventType::RoomJoinRules, "")
|
||||
@@ -74,7 +71,7 @@ pub(crate) async fn search_users_route(
|
||||
if user_is_in_public_rooms {
|
||||
user_visible = true;
|
||||
} else {
|
||||
let user_is_in_shared_rooms = services
|
||||
let user_is_in_shared_rooms = services()
|
||||
.rooms
|
||||
.user
|
||||
.get_shared_rooms(vec![sender_user.clone(), user_id])
|
||||
|
||||
+9
-10
@@ -1,13 +1,12 @@
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
use axum::extract::State;
|
||||
use base64::{engine::general_purpose, Engine as _};
|
||||
use conduit::utils;
|
||||
use hmac::{Hmac, Mac};
|
||||
use ruma::{api::client::voip::get_turn_server_info, SecondsSinceUnixEpoch, UserId};
|
||||
use sha1::Sha1;
|
||||
|
||||
use crate::{Result, Ruma};
|
||||
use crate::{services, Result, Ruma};
|
||||
|
||||
const RANDOM_USER_ID_LENGTH: usize = 10;
|
||||
|
||||
@@ -17,14 +16,14 @@ type HmacSha1 = Hmac<Sha1>;
|
||||
///
|
||||
/// TODO: Returns information about the recommended turn server.
|
||||
pub(crate) async fn turn_server_route(
|
||||
State(services): State<crate::State>, body: Ruma<get_turn_server_info::v3::Request>,
|
||||
body: Ruma<get_turn_server_info::v3::Request>,
|
||||
) -> Result<get_turn_server_info::v3::Response> {
|
||||
let turn_secret = services.globals.turn_secret().clone();
|
||||
let turn_secret = services().globals.turn_secret().clone();
|
||||
|
||||
let (username, password) = if !turn_secret.is_empty() {
|
||||
let expiry = SecondsSinceUnixEpoch::from_system_time(
|
||||
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("time is valid");
|
||||
@@ -32,7 +31,7 @@ pub(crate) async fn turn_server_route(
|
||||
let user = body.sender_user.unwrap_or_else(|| {
|
||||
UserId::parse_with_server_name(
|
||||
utils::random_string(RANDOM_USER_ID_LENGTH).to_lowercase(),
|
||||
&services.globals.config.server_name,
|
||||
&services().globals.config.server_name,
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
@@ -47,15 +46,15 @@ pub(crate) async fn turn_server_route(
|
||||
(username, password)
|
||||
} else {
|
||||
(
|
||||
services.globals.turn_username().clone(),
|
||||
services.globals.turn_password().clone(),
|
||||
services().globals.turn_username().clone(),
|
||||
services().globals.turn_password().clone(),
|
||||
)
|
||||
};
|
||||
|
||||
Ok(get_turn_server_info::v3::Response {
|
||||
username,
|
||||
password,
|
||||
uris: services.globals.turn_uris().to_vec(),
|
||||
ttl: Duration::from_secs(services.globals.turn_ttl()),
|
||||
uris: services().globals.turn_uris().to_vec(),
|
||||
ttl: Duration::from_secs(services().globals.turn_ttl()),
|
||||
})
|
||||
}
|
||||
|
||||
+5
-6
@@ -1,16 +1,15 @@
|
||||
#![recursion_limit = "192"]
|
||||
|
||||
pub mod client;
|
||||
pub mod router;
|
||||
mod router;
|
||||
pub mod routes;
|
||||
pub mod server;
|
||||
|
||||
extern crate conduit_core as conduit;
|
||||
extern crate conduit_service as service;
|
||||
|
||||
pub(crate) use conduit::{debug_info, pdu::PduEvent, utils, Error, Result};
|
||||
pub(crate) use service::services;
|
||||
pub(crate) use conduit::{debug_info, debug_warn, utils, Error, Result};
|
||||
pub(crate) use service::{pdu::PduEvent, services, user_is_local};
|
||||
|
||||
pub(crate) use self::router::{Ruma, RumaResponse, State};
|
||||
pub(crate) use self::router::{Ruma, RumaResponse};
|
||||
|
||||
conduit::mod_ctor! {}
|
||||
conduit::mod_dtor! {}
|
||||
|
||||
+19
-20
@@ -6,17 +6,17 @@ use axum_extra::{
|
||||
typed_header::TypedHeaderRejectionReason,
|
||||
TypedHeader,
|
||||
};
|
||||
use conduit::{warn, Err, Error, Result};
|
||||
use conduit::Err;
|
||||
use http::uri::PathAndQuery;
|
||||
use ruma::{
|
||||
api::{client::error::ErrorKind, AuthScheme, Metadata},
|
||||
server_util::authorization::XMatrix,
|
||||
CanonicalJsonValue, OwnedDeviceId, OwnedServerName, OwnedUserId, UserId,
|
||||
};
|
||||
use service::Services;
|
||||
use tracing::warn;
|
||||
|
||||
use super::request::Request;
|
||||
use crate::service::appservice::RegistrationInfo;
|
||||
use crate::{service::appservice::RegistrationInfo, services, Error, Result};
|
||||
|
||||
enum Token {
|
||||
Appservice(Box<RegistrationInfo>),
|
||||
@@ -33,7 +33,7 @@ pub(super) struct Auth {
|
||||
}
|
||||
|
||||
pub(super) async fn auth(
|
||||
services: &Services, request: &mut Request, json_body: &Option<CanonicalJsonValue>, metadata: &Metadata,
|
||||
request: &mut Request, json_body: &Option<CanonicalJsonValue>, metadata: &Metadata,
|
||||
) -> Result<Auth> {
|
||||
let bearer: Option<TypedHeader<Authorization<Bearer>>> = request.parts.extract().await?;
|
||||
let token = match &bearer {
|
||||
@@ -42,9 +42,9 @@ pub(super) async fn auth(
|
||||
};
|
||||
|
||||
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))
|
||||
} 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)))
|
||||
} else {
|
||||
Token::Invalid
|
||||
@@ -57,7 +57,7 @@ pub(super) async fn auth(
|
||||
match request.parts.uri.path() {
|
||||
// TODO: can we check this better?
|
||||
"/_matrix/client/v3/publicRooms" | "/_matrix/client/r0/publicRooms" => {
|
||||
if !services
|
||||
if !services()
|
||||
.globals
|
||||
.config
|
||||
.allow_public_room_directory_without_auth
|
||||
@@ -98,7 +98,7 @@ pub(super) async fn auth(
|
||||
))
|
||||
}
|
||||
},
|
||||
(AuthScheme::AccessToken, Token::Appservice(info)) => Ok(auth_appservice(services, request, info)?),
|
||||
(AuthScheme::AccessToken, Token::Appservice(info)) => Ok(auth_appservice(request, info)?),
|
||||
(AuthScheme::None | AuthScheme::AccessTokenOptional | AuthScheme::AppserviceToken, Token::Appservice(info)) => {
|
||||
Ok(Auth {
|
||||
origin: None,
|
||||
@@ -110,7 +110,7 @@ pub(super) async fn auth(
|
||||
(AuthScheme::AccessToken, Token::None) => match request.parts.uri.path() {
|
||||
// TODO: can we check this better?
|
||||
"/_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 {
|
||||
origin: None,
|
||||
sender_user: None,
|
||||
@@ -132,7 +132,7 @@ pub(super) async fn auth(
|
||||
sender_device: Some(device_id),
|
||||
appservice_info: None,
|
||||
}),
|
||||
(AuthScheme::ServerSignatures, Token::None) => Ok(auth_server(services, request, json_body).await?),
|
||||
(AuthScheme::ServerSignatures, Token::None) => Ok(auth_server(request, json_body).await?),
|
||||
(AuthScheme::None | AuthScheme::AppserviceToken | AuthScheme::AccessTokenOptional, Token::None) => Ok(Auth {
|
||||
sender_user: None,
|
||||
sender_device: None,
|
||||
@@ -150,7 +150,7 @@ pub(super) async fn auth(
|
||||
}
|
||||
}
|
||||
|
||||
fn auth_appservice(services: &Services, request: &Request, info: Box<RegistrationInfo>) -> Result<Auth> {
|
||||
fn auth_appservice(request: &Request, info: Box<RegistrationInfo>) -> Result<Auth> {
|
||||
let user_id = request
|
||||
.query
|
||||
.user_id
|
||||
@@ -159,7 +159,7 @@ fn auth_appservice(services: &Services, request: &Request, info: Box<Registratio
|
||||
|| {
|
||||
UserId::parse_with_server_name(
|
||||
info.registration.sender_localpart.as_str(),
|
||||
services.globals.server_name(),
|
||||
services().globals.server_name(),
|
||||
)
|
||||
},
|
||||
UserId::parse,
|
||||
@@ -170,7 +170,7 @@ fn auth_appservice(services: &Services, request: &Request, info: Box<Registratio
|
||||
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."));
|
||||
}
|
||||
|
||||
@@ -182,10 +182,8 @@ fn auth_appservice(services: &Services, request: &Request, info: Box<Registratio
|
||||
})
|
||||
}
|
||||
|
||||
async fn auth_server(
|
||||
services: &Services, request: &mut Request, json_body: &Option<CanonicalJsonValue>,
|
||||
) -> Result<Auth> {
|
||||
if !services.globals.allow_federation() {
|
||||
async fn auth_server(request: &mut Request, json_body: &Option<CanonicalJsonValue>) -> Result<Auth> {
|
||||
if !services().globals.allow_federation() {
|
||||
return Err!(Config("allow_federation", "Federation is disabled."));
|
||||
}
|
||||
|
||||
@@ -218,7 +216,7 @@ async fn auth_server(
|
||||
),
|
||||
)]);
|
||||
|
||||
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 destination != &server_destination {
|
||||
return Err(Error::BadRequest(ErrorKind::forbidden(), "Invalid authorization."));
|
||||
@@ -249,8 +247,9 @@ async fn auth_server(
|
||||
request_map.insert("content".to_owned(), json_body.clone());
|
||||
};
|
||||
|
||||
let keys_result = services
|
||||
.server_keys
|
||||
let keys_result = services()
|
||||
.rooms
|
||||
.event_handler
|
||||
.fetch_signing_keys_for_server(origin, vec![x_matrix.key.to_string()])
|
||||
.await;
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ use conduit::Result;
|
||||
use http::Method;
|
||||
use ruma::api::IncomingRequest;
|
||||
|
||||
use super::{Ruma, RumaResponse, State};
|
||||
use super::{Ruma, RumaResponse};
|
||||
|
||||
pub(in super::super) trait RouterExt {
|
||||
fn ruma_route<H, T>(self, handler: H) -> Self
|
||||
@@ -18,7 +18,7 @@ pub(in super::super) trait RouterExt {
|
||||
H: RumaHandler<T>;
|
||||
}
|
||||
|
||||
impl RouterExt for Router<State> {
|
||||
impl RouterExt for Router {
|
||||
fn ruma_route<H, T>(self, handler: H) -> Self
|
||||
where
|
||||
H: RumaHandler<T>,
|
||||
@@ -28,9 +28,9 @@ impl RouterExt for Router<State> {
|
||||
}
|
||||
|
||||
pub(in super::super) trait RumaHandler<T> {
|
||||
fn add_routes(&self, router: Router<State>) -> Router<State>;
|
||||
fn add_routes(&self, router: Router) -> Router;
|
||||
|
||||
fn add_route(&self, router: Router<State>, path: &str) -> Router<State>;
|
||||
fn add_route(&self, router: Router, path: &str) -> Router;
|
||||
}
|
||||
|
||||
macro_rules! ruma_handler {
|
||||
@@ -41,17 +41,17 @@ macro_rules! ruma_handler {
|
||||
Req: IncomingRequest + Send + 'static,
|
||||
Ret: IntoResponse,
|
||||
Fut: Future<Output = Result<Req::OutgoingResponse, Ret>> + Send,
|
||||
Fun: FnOnce($($tx,)* Ruma<Req>,) -> Fut + Clone + Send + Sync + 'static,
|
||||
$( $tx: FromRequestParts<State> + Send + 'static, )*
|
||||
Fun: FnOnce($($tx,)* Ruma<Req>) -> Fut + Clone + Send + Sync + 'static,
|
||||
$( $tx: FromRequestParts<()> + Send + 'static, )*
|
||||
{
|
||||
fn add_routes(&self, router: Router<State>) -> Router<State> {
|
||||
fn add_routes(&self, router: Router) -> Router {
|
||||
Req::METADATA
|
||||
.history
|
||||
.all_paths()
|
||||
.fold(router, |router, path| self.add_route(router, path))
|
||||
}
|
||||
|
||||
fn add_route(&self, router: Router<State>, path: &str) -> Router<State> {
|
||||
fn add_route(&self, router: Router, path: &str) -> Router {
|
||||
let handle = self.clone();
|
||||
let method = method_to_filter(&Req::METADATA.method);
|
||||
let action = |$($tx,)* req| async { handle($($tx,)* req).await.map(RumaResponse) };
|
||||
|
||||
@@ -1,16 +1,24 @@
|
||||
mod auth;
|
||||
mod handler;
|
||||
mod request;
|
||||
mod response;
|
||||
|
||||
use std::{mem, ops::Deref};
|
||||
|
||||
use axum::{async_trait, body::Body, extract::FromRequest};
|
||||
use bytes::{BufMut, BytesMut};
|
||||
use conduit::{debug, err, trace, utils::string::EMPTY, Error, Result};
|
||||
use ruma::{api::IncomingRequest, CanonicalJsonValue, OwnedDeviceId, OwnedServerName, OwnedUserId, UserId};
|
||||
use service::Services;
|
||||
use conduit::{debug, debug_warn, trace, warn};
|
||||
use ruma::{
|
||||
api::{client::error::ErrorKind, IncomingRequest},
|
||||
CanonicalJsonValue, OwnedDeviceId, OwnedServerName, OwnedUserId, UserId,
|
||||
};
|
||||
|
||||
use super::{auth, auth::Auth, request, request::Request};
|
||||
use crate::{service::appservice::RegistrationInfo, State};
|
||||
use self::{auth::Auth, request::Request};
|
||||
pub(super) use self::{handler::RouterExt, response::RumaResponse};
|
||||
use crate::{service::appservice::RegistrationInfo, services, Error, Result};
|
||||
|
||||
/// Extractor for Ruma request structs
|
||||
pub(crate) struct Args<T> {
|
||||
pub(crate) struct Ruma<T> {
|
||||
/// Request struct body
|
||||
pub(crate) body: T,
|
||||
|
||||
@@ -36,18 +44,18 @@ pub(crate) struct Args<T> {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<T> FromRequest<State, Body> for Args<T>
|
||||
impl<T, S> FromRequest<S, Body> for Ruma<T>
|
||||
where
|
||||
T: IncomingRequest,
|
||||
{
|
||||
type Rejection = Error;
|
||||
|
||||
async fn from_request(request: hyper::Request<Body>, services: &State) -> Result<Self, Self::Rejection> {
|
||||
let mut request = request::from(services, request).await?;
|
||||
async fn from_request(request: hyper::Request<Body>, _: &S) -> Result<Self, Self::Rejection> {
|
||||
let mut request = request::from(request).await?;
|
||||
let mut json_body = serde_json::from_slice::<CanonicalJsonValue>(&request.body).ok();
|
||||
let auth = auth::auth(services, &mut request, &json_body, &T::METADATA).await?;
|
||||
let auth = auth::auth(&mut request, &json_body, &T::METADATA).await?;
|
||||
Ok(Self {
|
||||
body: make_body::<T>(services, &mut request, &mut json_body, &auth)?,
|
||||
body: make_body::<T>(&mut request, &mut json_body, &auth)?,
|
||||
origin: auth.origin,
|
||||
sender_user: auth.sender_user,
|
||||
sender_device: auth.sender_device,
|
||||
@@ -57,22 +65,19 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for Args<T> {
|
||||
impl<T> Deref for Ruma<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.body }
|
||||
}
|
||||
|
||||
fn make_body<T>(
|
||||
services: &Services, request: &mut Request, json_body: &mut Option<CanonicalJsonValue>, auth: &Auth,
|
||||
) -> Result<T>
|
||||
fn make_body<T>(request: &mut Request, json_body: &mut Option<CanonicalJsonValue>, auth: &Auth) -> Result<T>
|
||||
where
|
||||
T: IncomingRequest,
|
||||
{
|
||||
let body = if let Some(CanonicalJsonValue::Object(json_body)) = json_body {
|
||||
let user_id = auth.sender_user.clone().unwrap_or_else(|| {
|
||||
let server_name = services.globals.server_name();
|
||||
UserId::parse_with_server_name(EMPTY, server_name).expect("valid user_id")
|
||||
UserId::parse_with_server_name("", services().globals.server_name()).expect("we know this is valid")
|
||||
});
|
||||
|
||||
let uiaa_request = json_body
|
||||
@@ -81,9 +86,9 @@ where
|
||||
.and_then(|auth| auth.get("session"))
|
||||
.and_then(|session| session.as_str())
|
||||
.and_then(|session| {
|
||||
services.uiaa.get_uiaa_request(
|
||||
services().uiaa.get_uiaa_request(
|
||||
&user_id,
|
||||
&auth.sender_device.clone().unwrap_or_else(|| EMPTY.into()),
|
||||
&auth.sender_device.clone().unwrap_or_else(|| "".into()),
|
||||
session,
|
||||
)
|
||||
});
|
||||
@@ -104,14 +109,21 @@ where
|
||||
let mut http_request = hyper::Request::builder()
|
||||
.uri(request.parts.uri.clone())
|
||||
.method(request.parts.method.clone());
|
||||
*http_request.headers_mut().expect("mutable http headers") = request.parts.headers.clone();
|
||||
let http_request = http_request.body(body).expect("http request body");
|
||||
*http_request.headers_mut().unwrap() = request.parts.headers.clone();
|
||||
let http_request = http_request.body(body).unwrap();
|
||||
debug!(
|
||||
"{:?} {:?} {:?}",
|
||||
http_request.method(),
|
||||
http_request.uri(),
|
||||
http_request.headers()
|
||||
);
|
||||
|
||||
let headers = http_request.headers();
|
||||
let method = http_request.method();
|
||||
let uri = http_request.uri();
|
||||
debug!("{method:?} {uri:?} {headers:?}");
|
||||
trace!("{method:?} {uri:?} {json_body:?}");
|
||||
trace!("{:?} {:?} {:?}", http_request.method(), http_request.uri(), json_body);
|
||||
let body = T::try_from_http_request(http_request, &request.path).map_err(|e| {
|
||||
warn!("try_from_http_request failed: {e:?}",);
|
||||
debug_warn!("JSON body: {:?}", json_body);
|
||||
Error::BadRequest(ErrorKind::BadJson, "Failed to deserialize request.")
|
||||
})?;
|
||||
|
||||
T::try_from_http_request(http_request, &request.path).map_err(|e| err!(Request(BadJson(debug_warn!("{e}")))))
|
||||
Ok(body)
|
||||
}
|
||||
@@ -2,10 +2,11 @@ use std::str;
|
||||
|
||||
use axum::{extract::Path, RequestExt, RequestPartsExt};
|
||||
use bytes::Bytes;
|
||||
use conduit::{err, Result};
|
||||
use http::request::Parts;
|
||||
use ruma::api::client::error::ErrorKind;
|
||||
use serde::Deserialize;
|
||||
use service::Services;
|
||||
|
||||
use crate::{services, Error, Result};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub(super) struct QueryParams {
|
||||
@@ -20,20 +21,19 @@ pub(super) struct Request {
|
||||
pub(super) parts: Parts,
|
||||
}
|
||||
|
||||
pub(super) async fn from(services: &Services, request: hyper::Request<axum::body::Body>) -> Result<Request> {
|
||||
pub(super) async fn from(request: hyper::Request<axum::body::Body>) -> Result<Request> {
|
||||
let limited = request.with_limited_body();
|
||||
let (mut parts, body) = limited.into_parts();
|
||||
|
||||
let path: Path<Vec<String>> = parts.extract().await?;
|
||||
let query = parts.uri.query().unwrap_or_default();
|
||||
let query =
|
||||
serde_html_form::from_str(query).map_err(|e| err!(Request(Unknown("Failed to read query parameters: {e}"))))?;
|
||||
let query = serde_html_form::from_str(parts.uri.query().unwrap_or_default())
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::Unknown, "Failed to read query parameters"))?;
|
||||
|
||||
let max_body_size = services.globals.config.max_request_size;
|
||||
let max_body_size = services().globals.config.max_request_size;
|
||||
|
||||
let body = axum::body::to_bytes(body, max_body_size)
|
||||
.await
|
||||
.map_err(|e| err!(Request(TooLarge("Request body too large: {e}"))))?;
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::TooLarge, "Request body too large"))?;
|
||||
|
||||
Ok(Request {
|
||||
path,
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
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() }
|
||||
}
|
||||
@@ -1,23 +1,15 @@
|
||||
mod args;
|
||||
mod auth;
|
||||
mod handler;
|
||||
mod request;
|
||||
mod response;
|
||||
pub mod state;
|
||||
|
||||
use axum::{
|
||||
response::IntoResponse,
|
||||
routing::{any, get, post},
|
||||
Router,
|
||||
};
|
||||
use conduit::{err, Server};
|
||||
use conduit::{err, Error, Server};
|
||||
use http::Uri;
|
||||
use ruma::api::client::error::ErrorKind;
|
||||
|
||||
use self::handler::RouterExt;
|
||||
pub(super) use self::{args::Args as Ruma, response::RumaResponse, state::State};
|
||||
use crate::{client, server};
|
||||
use crate::{client, router::RouterExt, server};
|
||||
|
||||
pub fn build(router: Router<State>, server: &Server) -> Router<State> {
|
||||
pub fn build(router: Router, server: &Server) -> Router {
|
||||
let config = &server.config;
|
||||
let router = router
|
||||
.ruma_route(client::get_supported_versions_route)
|
||||
@@ -241,7 +233,7 @@ pub fn build(router: Router<State>, server: &Server) -> Router<State> {
|
||||
}
|
||||
|
||||
async fn initial_sync(_uri: Uri) -> impl IntoResponse {
|
||||
err!(Request(GuestAccessForbidden("Guest access not implemented")))
|
||||
Error::BadRequest(ErrorKind::GuestAccessForbidden, "Guest access not implemented")
|
||||
}
|
||||
|
||||
async fn federation_disabled() -> impl IntoResponse { err!(Config("allow_federation", "Federation is disabled.")) }
|
||||
+11
-13
@@ -1,9 +1,9 @@
|
||||
use axum::extract::State;
|
||||
use conduit::{Error, Result};
|
||||
use ruma::{
|
||||
api::{client::error::ErrorKind, federation::backfill::get_backfill},
|
||||
uint, user_id, MilliSecondsSinceUnixEpoch,
|
||||
};
|
||||
use service::{sending::convert_to_outgoing_federation_event, services};
|
||||
|
||||
use crate::Ruma;
|
||||
|
||||
@@ -11,21 +11,19 @@ use crate::Ruma;
|
||||
///
|
||||
/// Retrieves events from before the sender joined the room, if the room's
|
||||
/// history visibility allows.
|
||||
pub(crate) async fn get_backfill_route(
|
||||
State(services): State<crate::State>, body: Ruma<get_backfill::v1::Request>,
|
||||
) -> Result<get_backfill::v1::Response> {
|
||||
pub(crate) async fn get_backfill_route(body: Ruma<get_backfill::v1::Request>) -> Result<get_backfill::v1::Response> {
|
||||
let origin = body.origin.as_ref().expect("server is authenticated");
|
||||
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.event_handler
|
||||
.acl_check(origin, &body.room_id)?;
|
||||
|
||||
if !services
|
||||
if !services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.is_world_readable(&body.room_id)?
|
||||
&& !services
|
||||
&& !services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.server_in_room(origin, &body.room_id)?
|
||||
@@ -36,7 +34,7 @@ pub(crate) async fn get_backfill_route(
|
||||
let until = body
|
||||
.v
|
||||
.iter()
|
||||
.map(|event_id| services.rooms.timeline.get_pdu_count(event_id))
|
||||
.map(|event_id| services().rooms.timeline.get_pdu_count(event_id))
|
||||
.filter_map(|r| r.ok().flatten())
|
||||
.max()
|
||||
.ok_or_else(|| Error::BadRequest(ErrorKind::InvalidParam, "Event not found."))?;
|
||||
@@ -47,7 +45,7 @@ pub(crate) async fn get_backfill_route(
|
||||
.try_into()
|
||||
.expect("UInt could not be converted to usize");
|
||||
|
||||
let all_events = services
|
||||
let all_events = services()
|
||||
.rooms
|
||||
.timeline
|
||||
.pdus_until(user_id!("@doesntmatter:conduit.rs"), &body.room_id, until)?
|
||||
@@ -57,20 +55,20 @@ pub(crate) async fn get_backfill_route(
|
||||
.filter_map(Result::ok)
|
||||
.filter(|(_, e)| {
|
||||
matches!(
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.server_can_see_event(origin, &e.room_id, &e.event_id,),
|
||||
Ok(true),
|
||||
)
|
||||
})
|
||||
.map(|(_, pdu)| services.rooms.timeline.get_pdu_json(&pdu.event_id))
|
||||
.map(|(_, pdu)| services().rooms.timeline.get_pdu_json(&pdu.event_id))
|
||||
.filter_map(|r| r.ok().flatten())
|
||||
.map(|pdu| services.sending.convert_to_outgoing_federation_event(pdu))
|
||||
.map(convert_to_outgoing_federation_event)
|
||||
.collect();
|
||||
|
||||
Ok(get_backfill::v1::Response {
|
||||
origin: services.globals.server_name().to_owned(),
|
||||
origin: services().globals.server_name().to_owned(),
|
||||
origin_server_ts: MilliSecondsSinceUnixEpoch::now(),
|
||||
pdus: events,
|
||||
})
|
||||
|
||||
+11
-10
@@ -1,9 +1,9 @@
|
||||
use axum::extract::State;
|
||||
use conduit::{Error, Result};
|
||||
use ruma::{
|
||||
api::{client::error::ErrorKind, federation::event::get_event},
|
||||
MilliSecondsSinceUnixEpoch, RoomId,
|
||||
};
|
||||
use service::{sending::convert_to_outgoing_federation_event, services};
|
||||
|
||||
use crate::Ruma;
|
||||
|
||||
@@ -13,12 +13,10 @@ use crate::Ruma;
|
||||
///
|
||||
/// - Only works if a user of this server is currently invited or joined the
|
||||
/// room
|
||||
pub(crate) async fn get_event_route(
|
||||
State(services): State<crate::State>, body: Ruma<get_event::v1::Request>,
|
||||
) -> Result<get_event::v1::Response> {
|
||||
pub(crate) async fn get_event_route(body: Ruma<get_event::v1::Request>) -> Result<get_event::v1::Response> {
|
||||
let origin = body.origin.as_ref().expect("server is authenticated");
|
||||
|
||||
let event = services
|
||||
let event = services()
|
||||
.rooms
|
||||
.timeline
|
||||
.get_pdu_json(&body.event_id)?
|
||||
@@ -32,13 +30,16 @@ pub(crate) async fn get_event_route(
|
||||
let room_id =
|
||||
<&RoomId>::try_from(room_id_str).map_err(|_| Error::bad_database("Invalid room_id in event in database."))?;
|
||||
|
||||
if !services.rooms.state_accessor.is_world_readable(room_id)?
|
||||
&& !services.rooms.state_cache.server_in_room(origin, room_id)?
|
||||
if !services().rooms.state_accessor.is_world_readable(room_id)?
|
||||
&& !services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.server_in_room(origin, room_id)?
|
||||
{
|
||||
return Err(Error::BadRequest(ErrorKind::forbidden(), "Server is not in room."));
|
||||
}
|
||||
|
||||
if !services
|
||||
if !services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.server_can_see_event(origin, room_id, &body.event_id)?
|
||||
@@ -47,8 +48,8 @@ pub(crate) async fn get_event_route(
|
||||
}
|
||||
|
||||
Ok(get_event::v1::Response {
|
||||
origin: services.globals.server_name().to_owned(),
|
||||
origin: services().globals.server_name().to_owned(),
|
||||
origin_server_ts: MilliSecondsSinceUnixEpoch::now(),
|
||||
pdu: services.sending.convert_to_outgoing_federation_event(event),
|
||||
pdu: convert_to_outgoing_federation_event(event),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use axum::extract::State;
|
||||
use conduit::{Error, Result};
|
||||
use ruma::{
|
||||
api::{client::error::ErrorKind, federation::authorization::get_event_authorization},
|
||||
RoomId,
|
||||
};
|
||||
use service::{sending::convert_to_outgoing_federation_event, services};
|
||||
|
||||
use crate::Ruma;
|
||||
|
||||
@@ -15,20 +15,20 @@ use crate::Ruma;
|
||||
///
|
||||
/// - This does not include the event itself
|
||||
pub(crate) async fn get_event_authorization_route(
|
||||
State(services): State<crate::State>, body: Ruma<get_event_authorization::v1::Request>,
|
||||
body: Ruma<get_event_authorization::v1::Request>,
|
||||
) -> Result<get_event_authorization::v1::Response> {
|
||||
let origin = body.origin.as_ref().expect("server is authenticated");
|
||||
|
||||
services
|
||||
services()
|
||||
.rooms
|
||||
.event_handler
|
||||
.acl_check(origin, &body.room_id)?;
|
||||
|
||||
if !services
|
||||
if !services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.is_world_readable(&body.room_id)?
|
||||
&& !services
|
||||
&& !services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.server_in_room(origin, &body.room_id)?
|
||||
@@ -36,7 +36,7 @@ pub(crate) async fn get_event_authorization_route(
|
||||
return Err(Error::BadRequest(ErrorKind::forbidden(), "Server is not in room."));
|
||||
}
|
||||
|
||||
let event = services
|
||||
let event = services()
|
||||
.rooms
|
||||
.timeline
|
||||
.get_pdu_json(&body.event_id)?
|
||||
@@ -50,7 +50,7 @@ pub(crate) async fn get_event_authorization_route(
|
||||
let room_id =
|
||||
<&RoomId>::try_from(room_id_str).map_err(|_| Error::bad_database("Invalid room_id in event in database."))?;
|
||||
|
||||
let auth_chain_ids = services
|
||||
let auth_chain_ids = services()
|
||||
.rooms
|
||||
.auth_chain
|
||||
.event_ids_iter(room_id, vec![Arc::from(&*body.event_id)])
|
||||
@@ -58,8 +58,8 @@ pub(crate) async fn get_event_authorization_route(
|
||||
|
||||
Ok(get_event_authorization::v1::Response {
|
||||
auth_chain: auth_chain_ids
|
||||
.filter_map(|id| services.rooms.timeline.get_pdu_json(&id).ok()?)
|
||||
.map(|pdu| services.sending.convert_to_outgoing_federation_event(pdu))
|
||||
.filter_map(|id| services().rooms.timeline.get_pdu_json(&id).ok()?)
|
||||
.map(convert_to_outgoing_federation_event)
|
||||
.collect(),
|
||||
})
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user