mirror of
https://forgejo.ellis.link/continuwuation/continuwuity.git
synced 2026-05-26 20:49:55 +00:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d5a9c98657 | |||
| 395b466b4a | |||
| 0376b58006 | |||
| 78c1e2f427 | |||
| 6614b8f6bf | |||
| c2fa8e6f8d | |||
| b8108f5897 | |||
| cf8358cbe6 | |||
| 7ecc570bb8 | |||
| 002799177d |
@@ -4,6 +4,7 @@ tests
|
||||
|
||||
# Docker files
|
||||
Dockerfile*
|
||||
docker-compose*
|
||||
|
||||
# IDE files
|
||||
.vscode
|
||||
|
||||
+227
-216
@@ -1,255 +1,266 @@
|
||||
name: CI and Artifacts
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
# documentation workflow deals with this or is not relevant for this workflow
|
||||
paths-ignore:
|
||||
- '*.md'
|
||||
- 'conduwuit-example.toml'
|
||||
- 'book.toml'
|
||||
- '.gitlab-ci.yml'
|
||||
- '.gitignore'
|
||||
- 'renovate.json'
|
||||
- 'docs/**'
|
||||
- 'debian/**'
|
||||
- 'docker/**'
|
||||
- 'test_results/**'
|
||||
branches:
|
||||
- main
|
||||
- dev
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- dev
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.head_ref || github.ref_name }}
|
||||
cancel-in-progress: true
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
# Required to make some things output color
|
||||
TERM: ansi
|
||||
# Publishing to my nix binary cache
|
||||
ATTIC_TOKEN: ${{ secrets.ATTIC_TOKEN }}
|
||||
# Just in case incremental is still being set to true, speeds up CI
|
||||
CARGO_INCREMENTAL: 0
|
||||
# Custom nix binary cache if fork is being used
|
||||
ATTIC_ENDPOINT: ${{ vars.ATTIC_ENDPOINT }}
|
||||
ATTIC_PUBLIC_KEY: ${{ vars.ATTIC_PUBLIC_KEY }}
|
||||
# Required to make some things output color
|
||||
TERM: ansi
|
||||
# Publishing to my nix binary cache
|
||||
ATTIC_TOKEN: ${{ secrets.ATTIC_TOKEN }}
|
||||
# Just in case incremental is still being set to true, speeds up CI
|
||||
CARGO_INCREMENTAL: 0
|
||||
# Custom nix binary cache if fork is being used
|
||||
ATTIC_ENDPOINT: ${{ vars.ATTIC_ENDPOINT }}
|
||||
ATTIC_PUBLIC_KEY: ${{ vars.ATTIC_PUBLIC_KEY }}
|
||||
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
packages: write
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
name: Test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Sync repository
|
||||
uses: actions/checkout@v4
|
||||
setup:
|
||||
name: CI Setup
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Sync repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Nix
|
||||
uses: DeterminateSystems/nix-installer-action@main
|
||||
- name: Install Nix (with flakes and nix-command enabled)
|
||||
uses: cachix/install-nix-action@v26
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
|
||||
- name: Enable Cachix binary cache
|
||||
run: |
|
||||
nix-env -iA cachix -f https://cachix.org/api/v1/install
|
||||
cachix use crane
|
||||
cachix use nix-community
|
||||
# Add `nix-community`, Crane, upstream Conduit, and conduwuit binary caches
|
||||
extra_nix_config: |
|
||||
experimental-features = nix-command flakes
|
||||
extra-substituters = https://nix-community.cachix.org
|
||||
extra-trusted-public-keys = nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=
|
||||
extra-substituters = https://crane.cachix.org
|
||||
extra-trusted-public-keys = crane.cachix.org-1:8Scfpmn9w+hGdXH/Q9tTLiYAE/2dnJYRJP7kl80GuRk=
|
||||
extra-substituters = https://nix.computer.surgery/conduit
|
||||
extra-trusted-public-keys = conduit:ZGAf6P6LhNvnoJJ3Me3PRg7tlLSrPxcQ2RiE5LIppjo=
|
||||
extra-substituters = https://attic.kennel.juneis.dog/conduit
|
||||
extra-trusted-public-keys = conduit:Isq8FGyEC6FOXH6nD+BOeAA+bKp6X6UIbupSlGEPuOg=
|
||||
extra-substituters = https://attic.kennel.juneis.dog/conduwuit
|
||||
extra-trusted-public-keys = conduwuit:lYPVh7o1hLu1idH4Xt2QHaRa49WRGSAqzcfFd94aOTw=
|
||||
|
||||
- name: Configure Magic Nix Cache
|
||||
uses: DeterminateSystems/magic-nix-cache-action@main
|
||||
- name: Add alternative Nix binary caches if specified
|
||||
if: ${{ (env.ATTIC_ENDPOINT != '') && (env.ATTIC_PUBLIC_KEY != '') }}
|
||||
run: |
|
||||
echo "extra-substituters = ${{ env.ATTIC_ENDPOINT }}" >> /etc/nix/nix.conf
|
||||
echo "extra-trusted-public-keys = ${{ env.ATTIC_PUBLIC_KEY }}" >> /etc/nix/nix.conf
|
||||
|
||||
- name: Apply Nix binary cache configuration
|
||||
run: |
|
||||
sudo tee -a /etc/nix/nix.conf > /dev/null <<EOF
|
||||
extra-substituters = https://nix.computer.surgery/conduit https://attic.kennel.juneis.dog/conduit https://attic.kennel.juneis.dog/conduwuit
|
||||
extra-trusted-public-keys = conduit:ZGAf6P6LhNvnoJJ3Me3PRg7tlLSrPxcQ2RiE5LIppjo= conduit:Isq8FGyEC6FOXH6nD+BOeAA+bKp6X6UIbupSlGEPuOg= conduwuit:lYPVh7o1hLu1idH4Xt2QHaRa49WRGSAqzcfFd94aOTw=
|
||||
EOF
|
||||
- name: Pop/push Magic Nix Cache
|
||||
uses: DeterminateSystems/magic-nix-cache-action@main
|
||||
|
||||
- name: Use alternative Nix binary caches if specified
|
||||
if: ${{ (env.ATTIC_ENDPOINT != '') && (env.ATTIC_PUBLIC_KEY != '') }}
|
||||
run: |
|
||||
sudo tee -a /etc/nix/nix.conf > /dev/null <<EOF
|
||||
extra-substituters = ${{ env.ATTIC_ENDPOINT }}
|
||||
extra-trusted-public-keys = ${{ env.ATTIC_PUBLIC_KEY }}
|
||||
EOF
|
||||
- name: Configure `nix-direnv`
|
||||
run: |
|
||||
echo 'source $HOME/.nix-profile/share/nix-direnv/direnvrc' > "$HOME/.direnvrc"
|
||||
|
||||
- name: Prepare build environment
|
||||
run: |
|
||||
echo 'source $HOME/.nix-profile/share/nix-direnv/direnvrc' > "$HOME/.direnvrc"
|
||||
nix-env -f "<nixpkgs>" -iA direnv -iA nix-direnv
|
||||
direnv allow
|
||||
nix develop --command true
|
||||
- name: Install `direnv` and `nix-direnv`
|
||||
run: nix-env -f "<nixpkgs>" -iA direnv -iA nix-direnv
|
||||
|
||||
- name: Run CI tests
|
||||
run: |
|
||||
direnv exec . engage > >(tee -a test_output.log)
|
||||
- name: Pop/push downloaded crate cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
key: downloaded-crates
|
||||
path: ~/.cargo
|
||||
|
||||
- name: Update Job Summary
|
||||
if: success() || failure()
|
||||
run: |
|
||||
if [ ${{ job.status }} == 'success' ]; then
|
||||
echo '# ✅ completed suwuccessfully' >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
tail -n 20 test_output.log | sed 's/\x1b\[[0-9;]*m//g' >> $GITHUB_STEP_SUMMARY
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
- name: Pop/push compiled crate cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
key: compiled-crates-${{runner.os}}
|
||||
path: target
|
||||
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
needs: tests
|
||||
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev'
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- target: aarch64-unknown-linux-musl
|
||||
- target: aarch64-unknown-linux-musl-jemalloc
|
||||
- target: x86_64-unknown-linux-musl
|
||||
- target: x86_64-unknown-linux-musl-jemalloc
|
||||
steps:
|
||||
- name: Sync repository
|
||||
uses: actions/checkout@v4
|
||||
# Do this to shorten the logs for the real CI step
|
||||
- name: Populate `/nix/store`
|
||||
run: nix develop --command true
|
||||
|
||||
- name: Install Nix
|
||||
uses: DeterminateSystems/nix-installer-action@main
|
||||
- name: Allow direnv
|
||||
run: direnv allow
|
||||
|
||||
- name: Enable Cachix binary cache
|
||||
run: |
|
||||
nix-env -iA cachix -f https://cachix.org/api/v1/install
|
||||
cachix use crane
|
||||
cachix use nix-community
|
||||
- name: Cache x86_64 inputs for devShell
|
||||
run: |
|
||||
./bin/nix-build-and-cache .#devShells.x86_64-linux.default.inputDerivation
|
||||
|
||||
- name: Configure Magic Nix Cache
|
||||
uses: DeterminateSystems/magic-nix-cache-action@main
|
||||
|
||||
- name: Apply Nix binary cache configuration
|
||||
run: |
|
||||
sudo tee -a /etc/nix/nix.conf > /dev/null <<EOF
|
||||
extra-substituters = https://nix.computer.surgery/conduit https://attic.kennel.juneis.dog/conduit https://attic.kennel.juneis.dog/conduwuit
|
||||
extra-trusted-public-keys = conduit:ZGAf6P6LhNvnoJJ3Me3PRg7tlLSrPxcQ2RiE5LIppjo= conduit:Isq8FGyEC6FOXH6nD+BOeAA+bKp6X6UIbupSlGEPuOg= conduwuit:lYPVh7o1hLu1idH4Xt2QHaRa49WRGSAqzcfFd94aOTw=
|
||||
EOF
|
||||
build-and-test:
|
||||
name: CI and Artifacts
|
||||
needs: setup
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
target: [
|
||||
"static-x86_64-unknown-linux-musl",
|
||||
"static-x86_64-unknown-linux-musl-jemalloc",
|
||||
"static-x86_64-unknown-linux-musl-hmalloc",
|
||||
"static-aarch64-unknown-linux-musl",
|
||||
"static-aarch64-unknown-linux-musl-jemalloc",
|
||||
"static-aarch64-unknown-linux-musl-hmalloc",
|
||||
]
|
||||
oci-target: [
|
||||
"x86_64-unknown-linux-gnu",
|
||||
"x86_64-unknown-linux-musl",
|
||||
"x86_64-unknown-linux-musl-jemalloc",
|
||||
"x86_64-unknown-linux-musl-hmalloc",
|
||||
"aarch64-unknown-linux-musl",
|
||||
"aarch64-unknown-linux-musl-jemalloc",
|
||||
"aarch64-unknown-linux-musl-hmalloc",
|
||||
]
|
||||
|
||||
- name: Use alternative Nix binary caches if specified
|
||||
if: ${{ (env.ATTIC_ENDPOINT != '') && (env.ATTIC_PUBLIC_KEY != '') }}
|
||||
run: |
|
||||
sudo tee -a /etc/nix/nix.conf > /dev/null <<EOF
|
||||
extra-substituters = ${{ env.ATTIC_ENDPOINT }}
|
||||
extra-trusted-public-keys = ${{ env.ATTIC_PUBLIC_KEY }}
|
||||
EOF
|
||||
steps:
|
||||
- name: Perform continuous integration
|
||||
run: direnv exec . engage
|
||||
|
||||
- name: Prepare build environment
|
||||
run: |
|
||||
echo 'source $HOME/.nix-profile/share/nix-direnv/direnvrc' > "$HOME/.direnvrc"
|
||||
nix-env -f "<nixpkgs>" -iA direnv -iA nix-direnv
|
||||
direnv allow
|
||||
nix develop --command true
|
||||
|
||||
- name: Build static ${{ matrix.target }}
|
||||
run: |
|
||||
bin/nix-build-and-cache .#static-${{ matrix.target }}
|
||||
mkdir -p target/release
|
||||
cp -v -f result/bin/conduit target/release/
|
||||
direnv exec . cargo deb --no-build --no-strip --output target/debian/${{ matrix.target }}.deb
|
||||
mv target/release/conduit static-${{ matrix.target }}
|
||||
- name: Build static artifacts
|
||||
run: |
|
||||
./bin/nix-build-and-cache .#${{ matrix.target }}
|
||||
mkdir -p target/release
|
||||
cp -v -f result/bin/conduit target/release
|
||||
direnv exec . cargo deb --no-build --output target/debian/${{ matrix.target }}.deb
|
||||
|
||||
- name: Upload static-${{ matrix.target }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: static-${{ matrix.target }}
|
||||
path: static-${{ matrix.target }}
|
||||
if-no-files-found: error
|
||||
- name: Upload static artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.target }}
|
||||
path: result/bin/conduit
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload deb ${{ matrix.target }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: deb-${{ matrix.target }}
|
||||
path: target/debian/${{ matrix.target }}.deb
|
||||
if-no-files-found: error
|
||||
- name: Upload static deb artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.target }}.deb
|
||||
path: target/debian/${{ matrix.target }}.deb
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Build OCI image ${{ matrix.target }}
|
||||
run: |
|
||||
bin/nix-build-and-cache .#oci-image-${{ matrix.target }}
|
||||
cp -v -f result oci-image-${{ matrix.target }}.tar.gz
|
||||
|
||||
- name: Upload OCI image ${{ matrix.target }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: oci-image-${{ matrix.target }}
|
||||
path: oci-image-${{ matrix.target }}.tar.gz
|
||||
if-no-files-found: error
|
||||
compression-level: 0
|
||||
- name: Build OCI images
|
||||
run: |
|
||||
./bin/nix-build-and-cache .#oci-image-${{ matrix.oci-target }}
|
||||
cp -v -f result oci-image-${{ matrix.oci-target }}.tar.gz
|
||||
|
||||
docker:
|
||||
name: Docker publish
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
if: (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev') && github.event_name != 'pull_request'
|
||||
env:
|
||||
DOCKER_ARM64: docker.io/${{ github.repository }}:${{ github.ref_name }}-${{ github.sha }}-arm64v8
|
||||
DOCKER_AMD64: docker.io/${{ github.repository }}:${{ github.ref_name }}-${{ github.sha }}-amd64
|
||||
DOCKER_TAG: docker.io/${{ github.repository }}:${{ github.ref_name }}-${{ github.sha }}
|
||||
DOCKER_BRANCH: docker.io/${{ github.repository }}:${{ (github.ref == 'refs/heads/main' && 'latest') || github.ref_name }}
|
||||
GHCR_ARM64: ghcr.io/${{ github.repository }}:${{ github.ref_name }}-${{ github.sha }}-arm64v8
|
||||
GHCR_AMD64: ghcr.io/${{ github.repository }}:${{ github.ref_name }}-${{ github.sha }}-amd64
|
||||
GHCR_TAG: ghcr.io/${{ github.repository }}:${{ github.ref_name }}-${{ github.sha }}
|
||||
GHCR_BRANCH: ghcr.io/${{ github.repository }}:${{ (github.ref == 'refs/heads/main' && 'latest') || github.ref_name }}
|
||||
steps:
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Upload OCI image artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: oci-image-${{ matrix.oci-target }}
|
||||
path: oci-image-${{ matrix.oci-target }}.tar.gz
|
||||
if-no-files-found: error
|
||||
# don't compress again
|
||||
compression-level: 0
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: docker.io
|
||||
username: ${{ vars.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
|
||||
- name: Move OCI images into position
|
||||
run: |
|
||||
mv oci-image-x86_64-*-jemalloc/*.tar.gz oci-image-amd64.tar.gz
|
||||
mv oci-image-aarch64-*-jemalloc/*.tar.gz oci-image-arm64v8.tar.gz
|
||||
|
||||
- name: Load and push amd64 image
|
||||
run: |
|
||||
docker load -i oci-image-amd64.tar.gz
|
||||
docker tag $(docker images -q conduit:main) ${{ env.DOCKER_AMD64 }}
|
||||
docker tag $(docker images -q conduit:main) ${{ env.GHCR_AMD64 }}
|
||||
docker push ${{ env.DOCKER_AMD64 }}
|
||||
docker push ${{ env.GHCR_AMD64 }}
|
||||
publish:
|
||||
needs: build-and-test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Extract metadata for Dockerhub
|
||||
env:
|
||||
REGISTRY: registry.hub.docker.com
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
id: meta-dockerhub
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
|
||||
- name: Load and push arm64 image
|
||||
run: |
|
||||
docker load -i oci-image-arm64v8.tar.gz
|
||||
docker tag $(docker images -q conduit:main) ${{ env.DOCKER_ARM64 }}
|
||||
docker tag $(docker images -q conduit:main) ${{ env.GHCR_ARM64 }}
|
||||
docker push ${{ env.DOCKER_ARM64 }}
|
||||
docker push ${{ env.GHCR_ARM64 }}
|
||||
- name: Extract metadata for GitHub Container Registry
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
id: meta-ghcr
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
|
||||
- name: Create Docker combined manifests
|
||||
run: |
|
||||
docker manifest create ${{ env.DOCKER_TAG }} --amend ${{ env.DOCKER_ARM64 }} --amend ${{ env.DOCKER_AMD64 }}
|
||||
docker manifest create ${{ env.DOCKER_BRANCH }} --amend ${{ env.DOCKER_ARM64 }} --amend ${{ env.DOCKER_AMD64 }}
|
||||
docker manifest create ${{ env.GHCR_TAG }} --amend ${{ env.GHCR_ARM64 }} --amend ${{ env.GHCR_AMD64 }}
|
||||
docker manifest create ${{ env.GHCR_BRANCH }} --amend ${{ env.GHCR_ARM64 }} --amend ${{ env.GHCR_AMD64 }}
|
||||
|
||||
- name: Push manifests to Docker registries
|
||||
run: |
|
||||
docker manifest push ${{ env.DOCKER_TAG }}
|
||||
docker manifest push ${{ env.DOCKER_BRANCH }}
|
||||
docker manifest push ${{ env.GHCR_TAG }}
|
||||
docker manifest push ${{ env.GHCR_BRANCH }}
|
||||
- name: Login to Dockerhub
|
||||
env:
|
||||
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
DOCKER_USERNAME: ${{ vars.DOCKER_USERNAME }}
|
||||
if: ${{ (github.event_name != 'pull_request') && (env.DOCKER_USERNAME != '') && (env.DOCKERHUB_TOKEN != '') }}
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
# username is not really a secret
|
||||
username: ${{ vars.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Add Image Links to Job Summary
|
||||
run: |
|
||||
echo "- \`docker pull ${{ env.DOCKER_TAG }}\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- \`docker pull ${{ env.GHCR_TAG }}\`" >> $GITHUB_STEP_SUMMARY
|
||||
- name: Login to GitHub Container Registry
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@v3
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
|
||||
- name: Publish to Dockerhub
|
||||
env:
|
||||
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
DOCKER_USERNAME: ${{ vars.DOCKER_USERNAME }}
|
||||
IMAGE_NAME: docker.io/${{ github.repository }}
|
||||
IMAGE_SUFFIX_AMD64: amd64
|
||||
IMAGE_SUFFIX_ARM64V8: arm64v8
|
||||
if: ${{ (github.event_name != 'pull_request') && (env.DOCKER_USERNAME != '') && (env.DOCKERHUB_TOKEN != '') }}
|
||||
run: |
|
||||
docker load -i oci-image-amd64.tar.gz
|
||||
IMAGE_ID_AMD64=$(docker images -q conduit:main)
|
||||
docker load -i oci-image-arm64v8.tar.gz
|
||||
IMAGE_ID_ARM64V8=$(docker images -q conduit:main)
|
||||
|
||||
# Tag and push the architecture specific images
|
||||
docker tag $IMAGE_ID_AMD64 $IMAGE_NAME:$GITHUB_SHA-$IMAGE_SUFFIX_AMD64
|
||||
docker tag $IMAGE_ID_ARM64V8 $IMAGE_NAME:$GITHUB_SHA-$IMAGE_SUFFIX_ARM64V8
|
||||
docker push $IMAGE_NAME:$GITHUB_SHA-$IMAGE_SUFFIX_AMD64
|
||||
docker push $IMAGE_NAME:$GITHUB_SHA-$IMAGE_SUFFIX_ARM64V8
|
||||
# Tag the multi-arch image
|
||||
docker manifest create $IMAGE_NAME:$GITHUB_SHA --amend $IMAGE_NAME:$GITHUB_SHA-$IMAGE_SUFFIX_AMD64 --amend $IMAGE_NAME:$GITHUB_SHA-$IMAGE_SUFFIX_ARM64V8
|
||||
docker manifest push $IMAGE_NAME:$GITHUB_SHA
|
||||
# Tag and push the git ref
|
||||
docker manifest create $IMAGE_NAME:$GITHUB_REF_NAME --amend $IMAGE_NAME:$GITHUB_SHA-$IMAGE_SUFFIX_AMD64 --amend $IMAGE_NAME:$GITHUB_SHA-$IMAGE_SUFFIX_ARM64V8
|
||||
docker manifest push $IMAGE_NAME:$GITHUB_REF_NAME
|
||||
# Tag "main" as latest (stable branch)
|
||||
if [[ "$GITHUB_REF_NAME" = "main" ]]; then
|
||||
docker manifest create $IMAGE_NAME:latest --amend $IMAGE_NAME:$GITHUB_SHA-$IMAGE_SUFFIX_AMD64 --amend $IMAGE_NAME:$GITHUB_SHA-$IMAGE_SUFFIX_ARM64V8
|
||||
docker manifest push $IMAGE_NAME:latest
|
||||
fi
|
||||
|
||||
- name: Publish to GitHub Container Registry
|
||||
if: github.event_name != 'pull_request'
|
||||
env:
|
||||
IMAGE_NAME: ghcr.io/${{ github.repository }}
|
||||
IMAGE_SUFFIX_AMD64: amd64
|
||||
IMAGE_SUFFIX_ARM64V8: arm64v8
|
||||
run: |
|
||||
docker load -i oci-image-amd64.tar.gz
|
||||
IMAGE_ID_AMD64=$(docker images -q conduit:main)
|
||||
docker load -i oci-image-arm64v8.tar.gz
|
||||
IMAGE_ID_ARM64V8=$(docker images -q conduit:main)
|
||||
|
||||
# Tag and push the architecture specific images
|
||||
docker tag $IMAGE_ID_AMD64 $IMAGE_NAME:$GITHUB_SHA-$IMAGE_SUFFIX_AMD64
|
||||
docker tag $IMAGE_ID_ARM64V8 $IMAGE_NAME:$GITHUB_SHA-$IMAGE_SUFFIX_ARM64V8
|
||||
docker push $IMAGE_NAME:$GITHUB_SHA-$IMAGE_SUFFIX_AMD64
|
||||
docker push $IMAGE_NAME:$GITHUB_SHA-$IMAGE_SUFFIX_ARM64V8
|
||||
# Tag the multi-arch image
|
||||
docker manifest create $IMAGE_NAME:$GITHUB_SHA --amend $IMAGE_NAME:$GITHUB_SHA-$IMAGE_SUFFIX_AMD64 --amend $IMAGE_NAME:$GITHUB_SHA-$IMAGE_SUFFIX_ARM64V8
|
||||
docker manifest push $IMAGE_NAME:$GITHUB_SHA
|
||||
# Tag and push the git ref
|
||||
docker manifest create $IMAGE_NAME:$GITHUB_REF_NAME --amend $IMAGE_NAME:$GITHUB_SHA-$IMAGE_SUFFIX_AMD64 --amend $IMAGE_NAME:$GITHUB_SHA-$IMAGE_SUFFIX_ARM64V8
|
||||
docker manifest push $IMAGE_NAME:$GITHUB_REF_NAME
|
||||
# Tag "main" as latest (stable branch)
|
||||
if [[ "$GITHUB_REF_NAME" = "main" ]]; then
|
||||
docker manifest create $IMAGE_NAME:latest --amend $IMAGE_NAME:$GITHUB_SHA-$IMAGE_SUFFIX_AMD64 --amend $IMAGE_NAME:$GITHUB_SHA-$IMAGE_SUFFIX_ARM64V8
|
||||
docker manifest push $IMAGE_NAME:latest
|
||||
fi
|
||||
|
||||
@@ -83,6 +83,3 @@ public/
|
||||
|
||||
# Zed
|
||||
.zed/
|
||||
|
||||
# idk where you're coming from, but i'm tired of you
|
||||
rustc-ice-*
|
||||
|
||||
+4
-4
@@ -54,7 +54,7 @@ before_script:
|
||||
|
||||
ci:
|
||||
stage: ci
|
||||
image: nixos/nix:2.22.0
|
||||
image: nixos/nix:2.21.2
|
||||
script:
|
||||
# Cache the inputs required for the devShell
|
||||
- ./bin/nix-build-and-cache .#devShells.x86_64-linux.default.inputDerivation
|
||||
@@ -79,7 +79,7 @@ ci:
|
||||
|
||||
artifacts:
|
||||
stage: artifacts
|
||||
image: nixos/nix:2.22.0
|
||||
image: nixos/nix:2.21.2
|
||||
script:
|
||||
- ./bin/nix-build-and-cache .#static-x86_64-unknown-linux-musl
|
||||
- cp result/bin/conduit x86_64-unknown-linux-musl
|
||||
@@ -129,9 +129,9 @@ artifacts:
|
||||
|
||||
.push-oci-image:
|
||||
stage: publish
|
||||
image: docker:26.0.2
|
||||
image: docker:26.0.1
|
||||
services:
|
||||
- docker:26.0.2-dind
|
||||
- docker:26.0.1-dind
|
||||
variables:
|
||||
IMAGE_SUFFIX_AMD64: amd64
|
||||
IMAGE_SUFFIX_ARM64V8: arm64v8
|
||||
|
||||
Generated
+327
-471
File diff suppressed because it is too large
Load Diff
+61
-89
@@ -1,16 +1,15 @@
|
||||
[package]
|
||||
# TODO: when can we rename to conduwuit?
|
||||
name = "conduit"
|
||||
description = "a very cool fork of Conduit, a Matrix homeserver written in Rust"
|
||||
description = "a cool fork of Conduit, a Matrix homeserver written in Rust"
|
||||
license = "Apache-2.0"
|
||||
authors = [
|
||||
"strawberry <strawberry@puppygock.gay>",
|
||||
"timokoesters <timo@koesters.xyz>",
|
||||
]
|
||||
homepage = "https://conduwuit.puppyirl.gay/"
|
||||
homepage = "https://puppygock.gay/conduwuit"
|
||||
repository = "https://github.com/girlbossceo/conduwuit"
|
||||
readme = "README.md"
|
||||
version = "0.3.0"
|
||||
version = "0.7.0+conduwuit-0.2.0"
|
||||
edition = "2021"
|
||||
|
||||
# See also `rust-toolchain.toml`
|
||||
@@ -18,13 +17,11 @@ rust-version = "1.75.0"
|
||||
|
||||
|
||||
[dependencies]
|
||||
hot-lib-reloader = { version = "^0.6", optional = true }
|
||||
|
||||
# Used for secure identifiers
|
||||
rand = "0.8.5"
|
||||
|
||||
# Used for conduit::Error type
|
||||
thiserror = "1.0.59"
|
||||
thiserror = "1.0.58"
|
||||
|
||||
# Used to encode server public key
|
||||
base64 = "0.22.0"
|
||||
@@ -44,6 +41,8 @@ itertools = "0.12.1"
|
||||
# jwt jsonwebtokens
|
||||
jsonwebtoken = "9.3.0"
|
||||
|
||||
lru-cache = "0.1.2"
|
||||
|
||||
# Used for ruma wrapper
|
||||
serde_html_form = "0.2.6"
|
||||
|
||||
@@ -51,6 +50,8 @@ serde_html_form = "0.2.6"
|
||||
hmac = "0.12.1"
|
||||
sha-1 = "0.10.1"
|
||||
|
||||
async-trait = "0.1.80"
|
||||
|
||||
# used for checking if an IP is in specific subnets / CIDR ranges easier
|
||||
ipaddress = "0.1.3"
|
||||
|
||||
@@ -66,8 +67,7 @@ cyborgtime = "2.1.1"
|
||||
# all the web/HTTP dependencies
|
||||
# Used for the http request / response body type for Ruma endpoints used with reqwest
|
||||
bytes = "1.6.0"
|
||||
http = "1.1.0"
|
||||
http-body-util = "0.1.1"
|
||||
http = "0.2.12"
|
||||
|
||||
# used to replace the channels of the tokio runtime
|
||||
loole = "0.3.0"
|
||||
@@ -75,12 +75,6 @@ loole = "0.3.0"
|
||||
# Validating urls in config, was already a transitive dependency
|
||||
url = { version = "2", features = ["serde"] }
|
||||
|
||||
async-trait = "0.1.80"
|
||||
|
||||
lru-cache = "0.1.2"
|
||||
|
||||
proc-macro2 = "=1.0.79"
|
||||
|
||||
# standard date and time tools
|
||||
[dependencies.chrono]
|
||||
version = "0.4.38"
|
||||
@@ -89,17 +83,12 @@ default-features = false
|
||||
|
||||
# Web framework
|
||||
[dependencies.axum]
|
||||
version = "0.7.5"
|
||||
version = "0.6.20"
|
||||
default-features = false
|
||||
features = ["form", "http1", "http2", "json", "matched-path"]
|
||||
|
||||
[dependencies.axum-extra]
|
||||
version = "0.9.3"
|
||||
default-features = false
|
||||
features = ["typed-header"]
|
||||
features = ["form", "headers", "http1", "http2", "json", "matched-path"]
|
||||
|
||||
[dependencies.axum-server]
|
||||
version = "0.6.0"
|
||||
version = "0.5.1"
|
||||
features = ["tls-rustls"]
|
||||
|
||||
[dependencies.tower]
|
||||
@@ -107,32 +96,24 @@ version = "0.4.13"
|
||||
features = ["util"]
|
||||
|
||||
[dependencies.tower-http]
|
||||
version = "0.5.2"
|
||||
features = [
|
||||
"add-extension",
|
||||
"cors",
|
||||
"sensitive-headers",
|
||||
"trace",
|
||||
"util",
|
||||
"catch-panic",
|
||||
]
|
||||
version = "0.4.4"
|
||||
features = ["add-extension", "cors", "sensitive-headers", "trace", "util"]
|
||||
|
||||
[dependencies.hyper]
|
||||
version = "1.3.1"
|
||||
version = "0.14"
|
||||
features = ["server", "http1", "http2"]
|
||||
|
||||
[dependencies.hyper-util]
|
||||
version = "0.1.3"
|
||||
|
||||
[dependencies.reqwest]
|
||||
version = "0.12.4"
|
||||
#version = "0.11.27"
|
||||
git = "https://github.com/girlbossceo/reqwest"
|
||||
rev = "319335e000fdea2e3d01f44245c8a21864d0c1c3"
|
||||
default-features = false
|
||||
features = ["rustls-tls-native-roots", "socks", "hickory-dns"]
|
||||
|
||||
# all the serde stuff
|
||||
# Used for pdu definition
|
||||
[dependencies.serde]
|
||||
version = "1.0.198"
|
||||
version = "1.0.197"
|
||||
features = ["rc"]
|
||||
# Used for appservice registration files
|
||||
[dependencies.serde_yaml]
|
||||
@@ -159,9 +140,11 @@ features = ["jpeg", "png", "gif", "webp"]
|
||||
[dependencies.log]
|
||||
version = "0.4.21"
|
||||
default-features = false
|
||||
features = ["max_level_trace", "release_max_level_info"]
|
||||
[dependencies.tracing]
|
||||
version = "0.1.40"
|
||||
default-features = false
|
||||
features = ["max_level_trace", "release_max_level_info"]
|
||||
[dependencies.tracing-subscriber]
|
||||
version = "0.3.18"
|
||||
features = ["env-filter"]
|
||||
@@ -238,7 +221,7 @@ features = ["serde"]
|
||||
|
||||
# to listen on both HTTP and HTTPS if listening on TLS dierctly from conduwuit for complement or sytest
|
||||
[dependencies.axum-server-dual-protocol]
|
||||
version = "0.6"
|
||||
version = "0.5.2"
|
||||
optional = true
|
||||
|
||||
# used for conduit's CLI and admin room command parsing
|
||||
@@ -251,15 +234,20 @@ features = ["std", "derive", "help", "usage", "error-context", "string"]
|
||||
version = "0.3.30"
|
||||
default-features = false
|
||||
|
||||
# Used for reading the configuration from conduwuit.toml & environment variables
|
||||
# Used for reading the configuration from conduit.toml & environment variables
|
||||
[dependencies.figment]
|
||||
version = "0.10.18"
|
||||
version = "0.10.17"
|
||||
features = ["env", "toml"]
|
||||
|
||||
# Used for matrix spec type definitions and helpers
|
||||
#ruma = { version = "0.4.0", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "state-res", "unstable-pre-spec", "unstable-exhaustive-types"] }
|
||||
#ruma = { git = "https://github.com/ruma/ruma", rev = "4d9f754657a099df8e61533787b8eebd12946435", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "state-res", "unstable-msc2448", "unstable-msc3575", "unstable-exhaustive-types", "ring-compat", "unstable-unspecified", "unstable-msc2870", "unstable-msc3061", "unstable-msc2867", "unstable-extensible-events"] }
|
||||
#ruma = { path = "../ruma/crates/ruma", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "state-res", "unstable-msc2448", "unstable-msc3575", "unstable-exhaustive-types", "ring-compat", "unstable-unspecified" ] }
|
||||
[dependencies.ruma]
|
||||
git = "https://github.com/girlbossceo/ruma"
|
||||
#rev = "c988b5ff158ede9c10aeffc76ad5e31604f19ddb"
|
||||
branch = "conduwuit-changes"
|
||||
#path = "../ruma/crates/ruma"
|
||||
features = [
|
||||
"compat",
|
||||
"rand",
|
||||
@@ -283,17 +271,14 @@ features = [
|
||||
"unstable-extensible-events",
|
||||
]
|
||||
|
||||
[dependencies.ruma-identifiers-validation]
|
||||
git = "https://github.com/girlbossceo/ruma"
|
||||
branch = "conduwuit-changes"
|
||||
|
||||
[dependencies.hickory-resolver]
|
||||
version = "0.24.1"
|
||||
default-features = false
|
||||
git = "https://github.com/hickory-dns/hickory-dns"
|
||||
rev = "94ac564c3f677e038f7255ddb762e9301d0f2c5d"
|
||||
|
||||
[dependencies.rust-rocksdb]
|
||||
git = "https://github.com/zaidoon1/rust-rocksdb"
|
||||
branch = "master"
|
||||
#rev = "60f783b06b49d2f6fcf1d3dda66c7194e49095d4"
|
||||
optional = true
|
||||
default-features = true
|
||||
features = ["multi-threaded-cf", "zstd"]
|
||||
@@ -307,7 +292,7 @@ features = ["bundled"]
|
||||
|
||||
# used only by rusqlite
|
||||
[dependencies.parking_lot]
|
||||
version = "0.12.2"
|
||||
version = "0.12.1"
|
||||
optional = true
|
||||
|
||||
# used only by rusqlite
|
||||
@@ -318,6 +303,7 @@ optional = true
|
||||
# used only by rusqlite and rust-rocksdb
|
||||
[dependencies.num_cpus]
|
||||
version = "1.16.0"
|
||||
optional = true
|
||||
|
||||
[dependencies.tokio]
|
||||
version = "1.37.0"
|
||||
@@ -326,13 +312,15 @@ features = ["fs", "macros", "sync", "signal"]
|
||||
# *nix-specific dependencies
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
nix = { version = "0.28.0", features = ["resource"] }
|
||||
sd-notify = { version = "0.4.1", optional = true } # systemd is only available/relevant on *nix platforms
|
||||
sd-notify = { version = "0.4.1", optional = true } # systemd is only available/relevant on *nix platforms
|
||||
hyperlocal = { git = "https://github.com/softprops/hyperlocal", rev = "2ee4d149644600d326559af0d2b235c945b05c04", features = [
|
||||
"server",
|
||||
] } # unix socket support
|
||||
|
||||
|
||||
[target.'cfg(all(not(target_env = "msvc"), target_os = "linux"))'.dependencies]
|
||||
hardened_malloc-rs = { version = "0.1.2", optional = true, features = [
|
||||
[target.'cfg(all(not(target_env = "msvc"), not(target_os = "macos"), target_os = "linux"))'.dependencies]
|
||||
hardened_malloc-rs = { version = "0.1", optional = true, features = [
|
||||
"static",
|
||||
"gcc",
|
||||
"clang",
|
||||
"light",
|
||||
], default-features = false }
|
||||
#hardened_malloc-rs = { optional = true, features = ["static","clang","light"], path = "../hardened_malloc-rs", default-features = false }
|
||||
@@ -347,13 +335,12 @@ default = [
|
||||
"gzip_compression",
|
||||
"brotli_compression",
|
||||
"zstd_compression",
|
||||
"release_max_log_level",
|
||||
]
|
||||
backend_sqlite = ["sqlite"]
|
||||
backend_rocksdb = ["rocksdb"]
|
||||
rocksdb = ["rust-rocksdb"]
|
||||
rocksdb = ["rust-rocksdb", "num_cpus"]
|
||||
jemalloc = ["tikv-jemalloc-ctl", "tikv-jemallocator", "rust-rocksdb/jemalloc"]
|
||||
sqlite = ["rusqlite", "parking_lot", "thread_local"]
|
||||
sqlite = ["rusqlite", "parking_lot", "thread_local", "num_cpus"]
|
||||
systemd = ["sd-notify"]
|
||||
sentry_telemetry = ["sentry", "sentry-tracing", "sentry-tower"]
|
||||
|
||||
@@ -373,65 +360,57 @@ perf_measurements = [
|
||||
"opentelemetry-jaeger",
|
||||
]
|
||||
|
||||
hot_reload = ["dep:hot-lib-reloader"]
|
||||
|
||||
hardened_malloc = ["hardened_malloc-rs"]
|
||||
|
||||
# increases performance, reduces build times, and reduces binary size by not compiling or
|
||||
# genreating code for log level filters that users will generally not use (debug and trace) only in release builds
|
||||
#
|
||||
# the expense is obviously losing those log level filters for usage at runtime. debug builds will still have all log levels
|
||||
release_max_log_level = [
|
||||
"tracing/max_level_trace",
|
||||
"tracing/release_max_level_info",
|
||||
"log/max_level_trace",
|
||||
"log/release_max_level_info",
|
||||
]
|
||||
|
||||
# developer feature useful only in debug builds.
|
||||
dev_release_log_level = []
|
||||
|
||||
# client/server interopability hacks
|
||||
#
|
||||
## element has various non-spec compliant behaviour
|
||||
element_hacks = []
|
||||
|
||||
|
||||
[[bin]]
|
||||
name = "conduit"
|
||||
path = "src/main.rs"
|
||||
|
||||
[lib]
|
||||
name = "conduit"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[package.metadata.deb]
|
||||
name = "conduwuit"
|
||||
name = "matrix-conduit"
|
||||
maintainer = "strawberry <strawberry@puppygock.gay>"
|
||||
copyright = "2024, strawberry <strawberry@puppygock.gay>"
|
||||
copyright = "2024, Timo Kösters <timo@koesters.xyz>"
|
||||
license-file = ["LICENSE", "3"]
|
||||
depends = "$auto, ca-certificates"
|
||||
extended-description = """\
|
||||
a cool hard fork of Conduit, a Matrix homeserver written in Rust"""
|
||||
a cool fork of Conduit, a Matrix homeserver written in Rust"""
|
||||
section = "net"
|
||||
priority = "optional"
|
||||
assets = [
|
||||
[
|
||||
"debian/README.md",
|
||||
"usr/share/doc/conduwuit/README.Debian",
|
||||
"usr/share/doc/matrix-conduit/README.Debian",
|
||||
"644",
|
||||
],
|
||||
[
|
||||
"README.md",
|
||||
"usr/share/doc/conduwuit/",
|
||||
"usr/share/doc/matrix-conduit/",
|
||||
"644",
|
||||
],
|
||||
[
|
||||
"target/release/conduit",
|
||||
"usr/sbin/conduwuit",
|
||||
"usr/sbin/matrix-conduit",
|
||||
"755",
|
||||
],
|
||||
[
|
||||
"conduwuit-example.toml",
|
||||
"etc/conduwuit/conduwuit.toml",
|
||||
"etc/matrix-conduit/conduit.toml",
|
||||
"640",
|
||||
],
|
||||
]
|
||||
conf-files = ["/etc/conduwuit/conduwuit.toml"]
|
||||
conf-files = ["/etc/matrix-conduit/conduit.toml"]
|
||||
maintainer-scripts = "debian/"
|
||||
systemd-units = { unit-name = "conduwuit" }
|
||||
systemd-units = { unit-name = "matrix-conduit" }
|
||||
|
||||
|
||||
[profile.dev]
|
||||
@@ -457,13 +436,6 @@ strip = "symbols"
|
||||
control-flow-guard = true # Windows only
|
||||
debug = 0
|
||||
|
||||
# release profile with debug symbols
|
||||
[profile.release-debuginfo]
|
||||
inherits = "release"
|
||||
strip = "none"
|
||||
debug = "full"
|
||||
|
||||
|
||||
# high performance release profile which uses fat LTO across all crates, 1 codegen unit, max opt-level, and optimises across all crates
|
||||
[profile.release-high-perf]
|
||||
inherits = "release"
|
||||
@@ -501,7 +473,6 @@ elided_lifetimes_in_paths = "warn"
|
||||
macro_use_extern_crate = "warn"
|
||||
single_use_lifetimes = "warn"
|
||||
unsafe_op_in_unsafe_fn = "warn"
|
||||
unreachable_pub = "warn"
|
||||
|
||||
# not in rust 1.75.0 (doesn't break CI but won't check for it)
|
||||
unit_bindings = "warn"
|
||||
@@ -510,6 +481,7 @@ unit_bindings = "warn"
|
||||
unused_braces = "allow"
|
||||
|
||||
# some sadness
|
||||
unreachable_pub = "allow"
|
||||
missing_docs = "allow"
|
||||
|
||||
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
# conduwuit
|
||||
|
||||
`main` / stable: [](https://github.com/girlbossceo/conduwuit/actions/workflows/ci.yml)
|
||||
|
||||
`dev`: [](https://github.com/girlbossceo/conduwuit/actions/workflows/ci.yml)
|
||||
[](https://github.com/girlbossceo/conduwuit/actions/workflows/ci.yml)
|
||||
|
||||
<!-- ANCHOR: catchphrase -->
|
||||
### a very cool, featureful fork of [Conduit](https://conduit.rs/)
|
||||
### a well maintained fork of [Conduit](https://conduit.rs/)
|
||||
<!-- ANCHOR_END: catchphrase -->
|
||||
|
||||
Visit the [Conduwuit documentation](https://conduwuit.puppyirl.gay/) for more information.
|
||||
Alternatively you can open [docs/introduction.md](docs/introduction.md) in this repository.
|
||||
|
||||
<!-- ANCHOR: body -->
|
||||
#### What is Matrix?
|
||||
@@ -30,10 +29,22 @@ An official conduwuit server ran by me is available at transfem.dev ([element.tr
|
||||
|
||||
#### What is the current status?
|
||||
|
||||
conduwuit is a hard fork of Conduit which is in beta, meaning you can join and participate in most
|
||||
conduwuit is a fork of Conduit which is in beta, meaning you can join and participate in most
|
||||
Matrix rooms, but not all features are supported and you might run into bugs
|
||||
from time to time.
|
||||
|
||||
#### Why does this fork exist? Why don't you contribute back upstream?
|
||||
|
||||
I now intend on contributing back as time and mental energy sees fit, but my fork still exists as a way to:
|
||||
- avoid unnecessary Matrix and general developer politics
|
||||
- avoid bikeshedding unnecessary or irrelevant things in upstream MRs
|
||||
- Fast tracked bug fixes, performance improvements, security improvements, and new features
|
||||
- Have early access to MRs that may not be suitable/acceptable for Conduit (e.g. too niche, too advanced for general users, only being blocked due to pending on contributor actions that we can fix ourselves downstream, pending Matrix spec stuff, etc)
|
||||
- Support unspecced or WIP features
|
||||
- Have official support for other OS's like Windows, macOS, and BSD.
|
||||
- Have a **stable** testing ground for some MRs or new features and bug fixes
|
||||
|
||||
And various other reasons that may not be listed here.
|
||||
<!-- ANCHOR_END: body -->
|
||||
|
||||
<!-- ANCHOR: footer -->
|
||||
@@ -60,11 +71,11 @@ If you run into any question, feel free to
|
||||
|
||||
#### Logo
|
||||
|
||||
Original repo and Matrix room picture was from bran (<3). Current banner image and logo is directly from [this cohost post](https://cohost.org/RatBaby/post/1028290-finally-a-flag-for).
|
||||
No official conduwuit logo exists. Repo and Matrix room picture is from bran (<3). Banner image is directly from [this cohost post](https://cohost.org/RatBaby/post/1028290-finally-a-flag-for).
|
||||
|
||||
#### Is it conduwuit or Conduwuit?
|
||||
|
||||
Both, but I prefer conduwuit.
|
||||
Both.
|
||||
|
||||
#### Mirrors of conduwuit
|
||||
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
[advisories]
|
||||
ignore = ["RUSTSEC-2020-0016"]
|
||||
+7
-9
@@ -3,10 +3,6 @@
|
||||
set -euo pipefail
|
||||
|
||||
# Path to Complement's source code
|
||||
#
|
||||
# The `COMPLEMENT_SRC` environment variable is set in the Nix dev shell, which
|
||||
# points to a store path containing the Complement source code. It's likely you
|
||||
# want to just pass that as the first argument to use it here.
|
||||
COMPLEMENT_SRC="$1"
|
||||
|
||||
# A `.jsonl` file to write test logs to
|
||||
@@ -17,17 +13,19 @@ RESULTS_FILE="$3"
|
||||
|
||||
OCI_IMAGE="complement-conduit:dev"
|
||||
|
||||
pushd "$(git rev-parse --show-toplevel)" > /dev/null
|
||||
nix build .#complement
|
||||
docker load < result
|
||||
popd > /dev/null
|
||||
env \
|
||||
-C "$(git rev-parse --show-toplevel)" \
|
||||
docker build \
|
||||
--tag "$OCI_IMAGE" \
|
||||
--file tests/complement/Dockerfile \
|
||||
.
|
||||
|
||||
# It's okay (likely, even) that `go test` exits nonzero
|
||||
set +o pipefail
|
||||
env \
|
||||
-C "$COMPLEMENT_SRC" \
|
||||
COMPLEMENT_BASE_IMAGE="$OCI_IMAGE" \
|
||||
go test -timeout 1h -json ./tests | tee "$LOG_FILE"
|
||||
go test -vet=all -timeout 30m -json ./tests | tee "$LOG_FILE"
|
||||
set -o pipefail
|
||||
|
||||
# Post-process the results into an easy-to-compare format
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[book]
|
||||
title = "conduwuit"
|
||||
description = "conduwuit, which is a well-maintained fork of Conduit, is a simple, fast and reliable chat server for the Matrix protocol"
|
||||
description = "conduwuit, which is a fork of Conduit, is a simple, fast and reliable chat server for the Matrix protocol"
|
||||
language = "en"
|
||||
multilingual = false
|
||||
src = "docs"
|
||||
|
||||
+36
-62
@@ -34,17 +34,12 @@
|
||||
# Defaults to `matrix.org`
|
||||
# trusted_servers = ["matrix.org"]
|
||||
|
||||
# Sentry.io crash/panic reporting, performance monitoring/metrics, etc. This is NOT enabled by default.
|
||||
# conduwuit's default Sentry reporting endpoint is o4506996327251968.ingest.us.sentry.io
|
||||
# Sentry.io crash/panic reporting, performance monitoring/metrics, etc.
|
||||
# Conduwuit's Sentry reporting endpoint is o4506996327251968.ingest.us.sentry.io
|
||||
#
|
||||
# Defaults to *false*
|
||||
# Defaults to false
|
||||
#sentry = false
|
||||
|
||||
# Sentry reporting URL if a custom one is desired
|
||||
#
|
||||
# Defaults to conduwuit's default Sentry endpoint: "https://fe2eb4536aa04949e28eff3128d64757@o4506996327251968.ingest.us.sentry.io/4506996334657536"
|
||||
#sentry_endpoint = ""
|
||||
|
||||
# Report your Conduwuit server_name in Sentry.io crash reports and metrics
|
||||
#
|
||||
# Defaults to false
|
||||
@@ -83,6 +78,21 @@ port = 6167
|
||||
# likely need this to be 0.0.0.0.
|
||||
address = "127.0.0.1"
|
||||
|
||||
# How many requests conduwuit sends to other servers at the same time concurrently. Default is 500
|
||||
# Note that because conduwuit is very fast unlike other homeserver implementations, setting this too
|
||||
# high could inadvertently result in ratelimits kicking in, or overloading lower-end homeservers out there.
|
||||
#
|
||||
# A valid use-case for enabling this is if you have a significant amount of overall federation activity
|
||||
# such as many rooms joined/tracked, and many servers in the true destination cache caused by that. Upon
|
||||
# rebooting conduwuit, depending on how fast your resources are, client and incoming federation requests
|
||||
# may timeout or be "stalled" for a period of time due to hitting the max concurrent requests limit from
|
||||
# refreshing federation/destination caches and such.
|
||||
#
|
||||
# If you have a lot of active users on your homeserver, you will definitely need to raise this.
|
||||
#
|
||||
# No this will not speed up room joins.
|
||||
#max_concurrent_requests = 500
|
||||
|
||||
# Max request size for file uploads
|
||||
max_request_size = 20_000_000 # in bytes
|
||||
|
||||
@@ -278,8 +288,8 @@ allow_profile_lookup_federation_requests = true
|
||||
# For release builds, the tracing crate is configured to only implement levels higher than error to avoid unnecessary overhead in the compiled binary from trace macros.
|
||||
# For debug builds, this restriction is not applied.
|
||||
#
|
||||
# Defaults to "info"
|
||||
#log = "info"
|
||||
# Defaults to "warn"
|
||||
#log = "warn"
|
||||
|
||||
# controls whether encrypted rooms and events are allowed (default true)
|
||||
#allow_encryption = false
|
||||
@@ -382,19 +392,11 @@ allow_profile_lookup_federation_requests = true
|
||||
# Time in seconds before RocksDB will forcibly rotate logs. Defaults to 0.
|
||||
#rocksdb_log_time_to_roll = 0
|
||||
|
||||
# Amount of threads that RocksDB will use for parallelism on database operatons such as cleanup, sync, flush, compaction, etc. Set to 0 to use all your logical threads.
|
||||
# Amount of threads that RocksDB will use for parallelism on database operatons such as cleanup, sync, flush, compaction, etc. Set to 0 to use all your physical cores.
|
||||
#
|
||||
# Defaults to your CPU logical thread count.
|
||||
# Defaults to your CPU physical core count (not logical threads).
|
||||
#rocksdb_parallelism_threads = 0
|
||||
|
||||
# Enables idle IO priority for compaction thread. This prevents any unexpected lag in the server's operation and
|
||||
# is usually a good idea. Enabled by default.
|
||||
#rocksdb_compaction_ioprio_idle = true
|
||||
|
||||
# Enables idle CPU priority for compaction thread. This is not enabled by default to prevent compaction from
|
||||
# falling too far behind on busy systems.
|
||||
#rocksdb_compaction_prio_idle = false
|
||||
|
||||
# Maximum number of LOG files RocksDB will keep. This must *not* be set to 0. It must be at least 1.
|
||||
# Defaults to 3 as these are not very useful.
|
||||
#rocksdb_max_log_files = 3
|
||||
@@ -510,37 +512,30 @@ allow_profile_lookup_federation_requests = true
|
||||
##
|
||||
## Generally these defaults are the best, but if you find a reason to need to change these they are here.
|
||||
|
||||
# Default/base connection timeout.
|
||||
# Default/base connection timeout
|
||||
# This is used only by URL previews and update/news endpoint checks
|
||||
#
|
||||
# Defaults to 10 seconds
|
||||
#request_conn_timeout = 10
|
||||
|
||||
# Default/base request timeout. The time waiting to receive more data from another server.
|
||||
# This is used only by URL previews, update/news, and misc endpoint checks
|
||||
# Default/base request timeout
|
||||
# This is used only by URL previews and update/news endpoint checks
|
||||
#
|
||||
# Defaults to 35 seconds
|
||||
#request_timeout = 35
|
||||
|
||||
# Default/base request total timeout. The time limit for a whole request. This is set very high to not
|
||||
# cancel healthy requests while serving as a backstop.
|
||||
# This is used only by URL previews and update/news endpoint checks
|
||||
#
|
||||
# Defaults to 320 seconds
|
||||
#request_total_timeout = 320
|
||||
|
||||
# Default/base idle connection pool timeout
|
||||
# This is used only by URL previews and update/news endpoint checks
|
||||
#
|
||||
# Defaults to 5 seconds
|
||||
#request_idle_timeout = 5
|
||||
|
||||
# Default/base max idle connections per host
|
||||
# This is used only by URL previews and update/news endpoint checks
|
||||
#
|
||||
# Defaults to 1 as generally the same open connection can be re-used
|
||||
#request_idle_per_host = 1
|
||||
|
||||
# Default/base idle connection pool timeout
|
||||
# This is used only by URL previews and update/news endpoint checks
|
||||
#
|
||||
# Defaults to 5 seconds
|
||||
#request_idle_timeout = 5
|
||||
|
||||
# Federation well-known resolution connection timeout
|
||||
#
|
||||
# Defaults to 6 seconds
|
||||
@@ -551,37 +546,21 @@ allow_profile_lookup_federation_requests = true
|
||||
# Defaults to 10 seconds
|
||||
#well_known_timeout = 10
|
||||
|
||||
# Federation client request timeout
|
||||
# Federation client/server request timeout
|
||||
# You most definitely want this to be high to account for extremely large room joins, slow homeservers, your own resources etc.
|
||||
#
|
||||
# Defaults to 300 seconds
|
||||
#federation_timeout = 300
|
||||
|
||||
# Federation client idle connection pool timeout
|
||||
#
|
||||
# Defaults to 25 seconds
|
||||
#federation_idle_timeout = 25
|
||||
|
||||
# Federation client max idle connections per host
|
||||
# Federation client/sender max idle connections per host
|
||||
#
|
||||
# Defaults to 1 as generally the same open connection can be re-used
|
||||
#federation_idle_per_host = 1
|
||||
|
||||
# Federation sender request timeout
|
||||
# The time it takes for the remote server to process sent transactions can take a while.
|
||||
# Federation client/sender idle connection pool timeout
|
||||
#
|
||||
# Defaults to 180 seconds
|
||||
#sender_timeout = 180
|
||||
|
||||
# Federation sender idle connection pool timeout
|
||||
#
|
||||
# Defaults to 180 seconds
|
||||
#sender_idle_timeout = 180
|
||||
|
||||
# Federation sender transaction retry backoff limit
|
||||
#
|
||||
# Defaults to 86400 seconds
|
||||
#sender_retry_backoff_limit = 86400
|
||||
# Defaults to 25 seconds
|
||||
#federation_idle_timeout = 25
|
||||
|
||||
# Appservice URL request connection timeout
|
||||
#
|
||||
@@ -620,11 +599,6 @@ allow_profile_lookup_federation_requests = true
|
||||
#
|
||||
#allow_outgoing_presence = true
|
||||
|
||||
# Config option to enable the presence idle timer for remote users. Disabling is offered as an optimization for
|
||||
# servers participating in many large rooms or when resources are limited. Disabling it may cause incorrect
|
||||
# presence states (i.e. stuck online) to be seen for some remote users. Defaults to true.
|
||||
#presence_timeout_remote_users = true
|
||||
|
||||
# Config option to control how many seconds before presence updates that you are idle. Defaults to 5 minutes.
|
||||
#presence_idle_timeout_s = 300
|
||||
|
||||
|
||||
Vendored
+12
-9
@@ -5,7 +5,7 @@ Installation
|
||||
------------
|
||||
|
||||
Information about downloading, building and deploying the Debian package, see
|
||||
the "Installing conduwuit" section in the Deploying docs.
|
||||
the "Installing Conduit" section in the Deploying docs.
|
||||
All following sections until "Setting up the Reverse Proxy" be ignored because
|
||||
this is handled automatically by the packaging.
|
||||
|
||||
@@ -14,21 +14,24 @@ Configuration
|
||||
|
||||
When installed, Debconf generates the configuration of the homeserver
|
||||
(host)name, the address and port it listens on. This configuration ends up in
|
||||
`/etc/conduwuit/conduwuit.toml`.
|
||||
`/etc/matrix-conduit/conduit.toml`.
|
||||
|
||||
You can tweak more detailed settings by uncommenting and setting the variables
|
||||
in `/etc/conduwuit/conduwuit.toml`. This involves settings such as the maximum
|
||||
in `/etc/matrix-conduit/conduit.toml`. This involves settings such as the maximum
|
||||
file size for download/upload, enabling federation, etc.
|
||||
|
||||
Running
|
||||
-------
|
||||
|
||||
The package uses the `conduwuit.service` systemd unit file to start and
|
||||
stop conduwuit. It loads the configuration file mentioned above to set up the
|
||||
The package uses the `matrix-conduit.service` systemd unit file to start and
|
||||
stop Conduit. It loads the configuration file mentioned above to set up the
|
||||
environment before running the server.
|
||||
|
||||
This package assumes by default that conduwuit will be placed behind a reverse
|
||||
proxy. This default deployment entails just listening
|
||||
This package assumes by default that Conduit will be placed behind a reverse
|
||||
proxy such as Apache or nginx. This default deployment entails just listening
|
||||
on `127.0.0.1` and the free port `6167` and is reachable via a client using the URL
|
||||
<http://localhost:6167>. Matrix federation requires TLS, so you will need to set up
|
||||
some certificates and renewal, for it to work properly.
|
||||
<http://localhost:6167>.
|
||||
|
||||
At a later stage this packaging may support also setting up TLS and running
|
||||
stand-alone. In this case, however, you need to set up some certificates and
|
||||
renewal, for it to work properly.
|
||||
|
||||
Vendored
+3
-3
@@ -5,13 +5,13 @@ set -e
|
||||
. /usr/share/debconf/confmodule
|
||||
|
||||
# Ask for the Matrix homeserver name, address and port.
|
||||
db_input high conduwuit/hostname || true
|
||||
db_input high matrix-conduit/hostname || true
|
||||
db_go
|
||||
|
||||
db_input low conduwuit/address || true
|
||||
db_input low matrix-conduit/address || true
|
||||
db_go
|
||||
|
||||
db_input medium conduwuit/port || true
|
||||
db_input medium matrix-conduit/port || true
|
||||
db_go
|
||||
|
||||
exit 0
|
||||
|
||||
@@ -4,8 +4,8 @@ After=network-online.target
|
||||
|
||||
[Service]
|
||||
DynamicUser=yes
|
||||
User=_conduwuit
|
||||
Group=_conduwuit
|
||||
User=_matrix-conduit
|
||||
Group=_matrix-conduit
|
||||
Type=notify
|
||||
|
||||
AmbientCapabilities=
|
||||
@@ -36,17 +36,17 @@ RestrictNamespaces=yes
|
||||
RestrictRealtime=yes
|
||||
RestrictSUIDSGID=yes
|
||||
SystemCallArchitectures=native
|
||||
SystemCallFilter=@system-service @resources
|
||||
SystemCallFilter=~@clock @debug @module @mount @reboot @swap @cpu-emulation @obsolete @timer @chown @setuid @privileged @keyring @ipc
|
||||
SystemCallFilter=@system-service
|
||||
SystemCallFilter=~@clock @debug @module @mount @reboot @swap @cpu-emulation @obsolete @timer @chown @setuid @resources @privileged @keyring @ipc
|
||||
SystemCallErrorNumber=EPERM
|
||||
StateDirectory=matrix-conduit
|
||||
|
||||
RuntimeDirectory=conduit
|
||||
RuntimeDirectoryMode=0750
|
||||
|
||||
Environment="CONDUIT_CONFIG=/etc/conduwuit/conduwuit.toml"
|
||||
Environment="CONDUIT_CONFIG=/etc/matrix-conduit/conduit.toml"
|
||||
|
||||
ExecStart=/usr/sbin/conduwuit
|
||||
ExecStart=/usr/sbin/matrix-conduit
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
|
||||
Vendored
+9
-9
@@ -3,26 +3,26 @@ set -e
|
||||
|
||||
. /usr/share/debconf/confmodule
|
||||
|
||||
CONDUWUIT_DATABASE_PATH=/var/lib/conduwuit/
|
||||
CONDUIT_DATABASE_PATH=/var/lib/matrix-conduit/
|
||||
|
||||
case "$1" in
|
||||
configure)
|
||||
# Create the `_conduwuit` user if it does not exist yet.
|
||||
if ! getent passwd _conduwuit > /dev/null ; then
|
||||
echo 'Adding system user for the conduwuit Matrix homeserver' 1>&2
|
||||
# Create the `_matrix-conduit` user if it does not exist yet.
|
||||
if ! getent passwd _matrix-conduit > /dev/null ; then
|
||||
echo 'Adding system user for the Conduwuit Matrix homeserver' 1>&2
|
||||
adduser --system --group --quiet \
|
||||
--home "$CONDUWUIT_DATABASE_PATH" \
|
||||
--home "$CONDUIT_DATABASE_PATH" \
|
||||
--disabled-login \
|
||||
--shell "/usr/sbin/nologin" \
|
||||
--force-badname \
|
||||
_conduwuit
|
||||
_matrix-conduit
|
||||
fi
|
||||
|
||||
# Create the database path if it does not exist yet and fix up ownership
|
||||
# and permissions.
|
||||
mkdir -p "$CONDUWUIT_DATABASE_PATH"
|
||||
chown _conduwuit:_conduwuit -R "$CONDUWUIT_DATABASE_PATH"
|
||||
chmod 700 "$CONDUWUIT_DATABASE_PATH"
|
||||
mkdir -p "$CONDUIT_DATABASE_PATH"
|
||||
chown _matrix-conduit:_matrix-conduit -R "$CONDUIT_DATABASE_PATH"
|
||||
chmod 700 "$CONDUIT_DATABASE_PATH"
|
||||
;;
|
||||
esac
|
||||
|
||||
|
||||
Vendored
+6
-6
@@ -3,8 +3,8 @@ set -e
|
||||
|
||||
. /usr/share/debconf/confmodule
|
||||
|
||||
CONDUWUIT_CONFIG_PATH=/etc/conduwuit
|
||||
CONDUWUIT_DATABASE_PATH=/var/lib/conduwuit
|
||||
CONDUIT_CONFIG_PATH=/etc/matrix-conduit
|
||||
CONDUIT_DATABASE_PATH=/var/lib/matrix-conduit
|
||||
|
||||
case $1 in
|
||||
purge)
|
||||
@@ -14,12 +14,12 @@ case $1 in
|
||||
# Per https://www.debian.org/doc/debian-policy/ch-files.html#behavior
|
||||
# "configuration files must be preserved when the package is removed, and
|
||||
# only deleted when the package is purged."
|
||||
if [ -d "$CONDUWUIT_CONFIG_PATH" ]; then
|
||||
rm -r "$CONDUWUIT_CONFIG_PATH"
|
||||
if [ -d "$CONDUIT_CONFIG_PATH" ]; then
|
||||
rm -r "$CONDUIT_CONFIG_PATH"
|
||||
fi
|
||||
|
||||
if [ -d "$CONDUWUIT_DATABASE_PATH" ]; then
|
||||
rm -r "$CONDUWUIT_DATABASE_PATH"
|
||||
if [ -d "$CONDUIT_DATABASE_PATH" ]; then
|
||||
rm -r "$CONDUIT_DATABASE_PATH"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
Vendored
+3
-3
@@ -1,4 +1,4 @@
|
||||
Template: conduwuit/hostname
|
||||
Template: matrix-conduit/hostname
|
||||
Type: string
|
||||
Default: localhost
|
||||
Description: The server (host)name of the Matrix homeserver
|
||||
@@ -7,14 +7,14 @@ Description: The server (host)name of the Matrix homeserver
|
||||
If set to "localhost", you can connect with a client locally and clients
|
||||
from other hosts and also other homeservers will not be able to reach you!
|
||||
|
||||
Template: conduwuit/address
|
||||
Template: matrix-conduit/address
|
||||
Type: string
|
||||
Default: 127.0.0.1
|
||||
Description: The listen address of the Matrix homeserver
|
||||
This is the address the homeserver will listen on. Leave it set to 127.0.0.1
|
||||
when using a reverse proxy.
|
||||
|
||||
Template: conduwuit/port
|
||||
Template: matrix-conduit/port
|
||||
Type: string
|
||||
Default: 6167
|
||||
Description: The port of the Matrix homeserver
|
||||
|
||||
@@ -11,5 +11,3 @@
|
||||
- [NixOS](deploying/nixos.md)
|
||||
- [TURN](turn.md)
|
||||
- [Appservices](appservices.md)
|
||||
- [Development](development.md)
|
||||
- [Testing](development/testing.md)
|
||||
|
||||
@@ -7,8 +7,8 @@ services:
|
||||
### then you are ready to go.
|
||||
image: girlbossceo/conduwuit:latest
|
||||
### If you want to build a fresh image from the sources, then comment the image line and uncomment the
|
||||
### build lines. If you want meaningful labels in your built Conduit image, you should run docker compose like this:
|
||||
### CREATED=$(date -u +'%Y-%m-%dT%H:%M:%SZ') VERSION=$(grep -m1 -o '[0-9].[0-9].[0-9]' Cargo.toml) docker compose up -d
|
||||
### build lines. If you want meaningful labels in your built Conduit image, you should run docker-compose like this:
|
||||
### CREATED=$(date -u +'%Y-%m-%dT%H:%M:%SZ') VERSION=$(grep -m1 -o '[0-9].[0-9].[0-9]' Cargo.toml) docker-compose up -d
|
||||
# build:
|
||||
# context: .
|
||||
# args:
|
||||
@@ -32,6 +32,7 @@ services:
|
||||
CONDUIT_ALLOW_FEDERATION: 'true'
|
||||
CONDUIT_ALLOW_CHECK_FOR_UPDATES: 'true'
|
||||
CONDUIT_TRUSTED_SERVERS: '["matrix.org"]'
|
||||
#CONDUIT_MAX_CONCURRENT_REQUESTS: 100
|
||||
#CONDUIT_LOG: warn,state_res=warn
|
||||
CONDUIT_ADDRESS: 0.0.0.0
|
||||
#CONDUIT_CONFIG: './conduwuit.toml' # Uncomment if you mapped config toml above
|
||||
@@ -39,7 +40,7 @@ services:
|
||||
|
||||
# We need some way to server the client and server .well-known json. The simplest way is to use a nginx container
|
||||
# to serve those two as static files. If you want to use a different way, delete or comment the below service, here
|
||||
# and in the docker compose override file.
|
||||
# and in the docker-compose override file.
|
||||
well-known:
|
||||
image: nginx:latest
|
||||
restart: unless-stopped
|
||||
|
||||
@@ -18,7 +18,7 @@ services:
|
||||
|
||||
# We need some way to server the client and server .well-known json. The simplest way is to use a nginx container
|
||||
# to serve those two as static files. If you want to use a different way, delete or comment the below service, here
|
||||
# and in the docker compose file.
|
||||
# and in the docker-compose file.
|
||||
well-known:
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
|
||||
@@ -7,8 +7,8 @@ services:
|
||||
### then you are ready to go.
|
||||
image: girlbossceo/conduwuit:latest
|
||||
### If you want to build a fresh image from the sources, then comment the image line and uncomment the
|
||||
### build lines. If you want meaningful labels in your built Conduit image, you should run docker compose like this:
|
||||
### CREATED=$(date -u +'%Y-%m-%dT%H:%M:%SZ') VERSION=$(grep -m1 -o '[0-9].[0-9].[0-9]' Cargo.toml) docker compose up -d
|
||||
### build lines. If you want meaningful labels in your built Conduit image, you should run docker-compose like this:
|
||||
### CREATED=$(date -u +'%Y-%m-%dT%H:%M:%SZ') VERSION=$(grep -m1 -o '[0-9].[0-9].[0-9]' Cargo.toml) docker-compose up -d
|
||||
# build:
|
||||
# context: .
|
||||
# args:
|
||||
@@ -43,7 +43,7 @@ services:
|
||||
|
||||
# We need some way to server the client and server .well-known json. The simplest way is to use a nginx container
|
||||
# to serve those two as static files. If you want to use a different way, delete or comment the below service, here
|
||||
# and in the docker compose override file.
|
||||
# and in the docker-compose override file.
|
||||
well-known:
|
||||
image: nginx:latest
|
||||
restart: unless-stopped
|
||||
@@ -94,4 +94,4 @@ volumes:
|
||||
acme:
|
||||
|
||||
networks:
|
||||
proxy:
|
||||
proxy:
|
||||
@@ -7,8 +7,8 @@ services:
|
||||
### then you are ready to go.
|
||||
image: girlbossceo/conduwuit:latest
|
||||
### If you want to build a fresh image from the sources, then comment the image line and uncomment the
|
||||
### build lines. If you want meaningful labels in your built Conduit image, you should run docker compose like this:
|
||||
### CREATED=$(date -u +'%Y-%m-%dT%H:%M:%SZ') VERSION=$(grep -m1 -o '[0-9].[0-9].[0-9]' Cargo.toml) docker compose up -d
|
||||
### build lines. If you want meaningful labels in your built Conduit image, you should run docker-compose like this:
|
||||
### CREATED=$(date -u +'%Y-%m-%dT%H:%M:%SZ') VERSION=$(grep -m1 -o '[0-9].[0-9].[0-9]' Cargo.toml) docker-compose up -d
|
||||
# build:
|
||||
# context: .
|
||||
# args:
|
||||
@@ -32,6 +32,7 @@ services:
|
||||
CONDUIT_ALLOW_FEDERATION: 'true'
|
||||
CONDUIT_ALLOW_CHECK_FOR_UPDATES: 'true'
|
||||
CONDUIT_TRUSTED_SERVERS: '["matrix.org"]'
|
||||
#CONDUIT_MAX_CONCURRENT_REQUESTS: 400
|
||||
#CONDUIT_LOG: warn,state_res=warn
|
||||
CONDUIT_ADDRESS: 0.0.0.0
|
||||
#CONDUIT_CONFIG: './conduwuit.toml' # Uncomment if you mapped config toml above
|
||||
|
||||
+28
-29
@@ -1,4 +1,4 @@
|
||||
# conduwuit for Docker
|
||||
# Conduwuit for Docker
|
||||
|
||||
## Docker
|
||||
|
||||
@@ -11,22 +11,19 @@ OCI images for conduwuit are available in the registries listed below.
|
||||
|
||||
| Registry | Image | Size | Notes |
|
||||
| --------------- | --------------------------------------------------------------- | ----------------------------- | ---------------------- |
|
||||
| GitHub Registry | [ghcr.io/girlbossceo/conduwuit:latest][gh] | ![Image Size][shield-latest] | Stable tagged image. |
|
||||
| Docker Hub | [docker.io/girlbossceo/conduwuit:latest][dh] | ![Image Size][shield-latest] | Stable tagged image. |
|
||||
| GitHub Registry | [ghcr.io/girlbossceo/conduwuit:main][gh] | ![Image Size][shield-main] | Stable branch. |
|
||||
| Docker Hub | [docker.io/girlbossceo/conduwuit:main][dh] | ![Image Size][shield-main] | Stable branch. |
|
||||
| GitHub Registry | [ghcr.io/girlbossceo/conduwuit:dev][gh] | ![Image Size][shield-main] | Development version. |
|
||||
| Docker Hub | [docker.io/girlbossceo/conduwuit:dev][dh] | ![Image Size][shield-dev] | Development version. |
|
||||
| GitHub Registry | [ghcr.io/girlbossceo/conduwuit:latest][gh] | ![Image Size][shield-latest] | Stable image. |
|
||||
| Docker Hub | [docker.io/girlbossceo/conduwuit:latest][dh] | ![Image Size][shield-latest] | Stable image. |
|
||||
| GitHub Registry | [ghcr.io/girlbossceo/conduwuit:main][gh] | ![Image Size][shield-main] | Development version. |
|
||||
| Docker Hub | [docker.io/girlbossceo/conduwuit:main][dh] | ![Image Size][shield-main] | Development version. |
|
||||
|
||||
|
||||
[dh]: https://hub.docker.com/repository/docker/girlbossceo/conduwuit
|
||||
[gh]: https://github.com/girlbossceo/conduwuit/pkgs/container/conduwuit
|
||||
[shield-latest]: https://img.shields.io/docker/image-size/girlbossceo/conduwuit/latest
|
||||
[shield-main]: https://img.shields.io/docker/image-size/girlbossceo/conduwuit/main
|
||||
[shield-dev]: https://img.shields.io/docker/image-size/girlbossceo/conduwuit/dev
|
||||
|
||||
|
||||
Use
|
||||
Use
|
||||
```bash
|
||||
docker image pull <link>
|
||||
```
|
||||
@@ -36,7 +33,7 @@ to pull it to your machine.
|
||||
|
||||
### Build using a Dockerfile
|
||||
|
||||
The Dockerfile provided by conduwuit has two stages, each of which creates an image.
|
||||
The Dockerfile provided by Conduit has two stages, each of which creates an image.
|
||||
|
||||
1. **Builder:** Builds the binary from local context or by cloning a git revision from the official repository.
|
||||
2. **Runner:** Copies the built binary from **Builder** and sets up the runtime environment, like creating a volume to persist the database and applying the correct permissions.
|
||||
@@ -57,24 +54,25 @@ When you have the image you can simply run it with
|
||||
|
||||
```bash
|
||||
docker run -d -p 8448:6167 \
|
||||
-v db:/var/lib/conduwuit/ \
|
||||
-v db:/var/lib/matrix-conduit/ \
|
||||
-e CONDUIT_SERVER_NAME="your.server.name" \
|
||||
-e CONDUIT_DATABASE_BACKEND="rocksdb" \
|
||||
-e CONDUIT_ALLOW_REGISTRATION=false \
|
||||
-e CONDUIT_ALLOW_REGISTRATION=true \
|
||||
-e CONDUIT_ALLOW_FEDERATION=true \
|
||||
-e CONDUIT_MAX_REQUEST_SIZE="40000000" \
|
||||
-e CONDUIT_MAX_REQUEST_SIZE="20000000" \
|
||||
-e CONDUIT_TRUSTED_SERVERS="[\"matrix.org\"]" \
|
||||
-e CONDUIT_MAX_CONCURRENT_REQUESTS="500" \
|
||||
-e CONDUIT_LOG="warn,ruma_state_res=warn" \
|
||||
--name conduit <link>
|
||||
```
|
||||
|
||||
or you can use [docker compose](#docker-compose).
|
||||
or you can use [docker-compose](#docker-compose).
|
||||
|
||||
The `-d` flag lets the container run in detached mode. You now need to supply a `conduwuit.toml` config file, an example can be found [here](../configuration.md).
|
||||
You can pass in different env vars to change config values on the fly. You can even configure conduwuit completely by using env vars, but for that you need
|
||||
The `-d` flag lets the container run in detached mode. You now need to supply a `conduit.toml` config file, an example can be found [here](../configuration.md).
|
||||
You can pass in different env vars to change config values on the fly. You can even configure Conduit completely by using env vars, but for that you need
|
||||
to pass `-e CONDUIT_CONFIG=""` into your container. For an overview of possible values, please take a look at the `docker-compose.yml` file.
|
||||
|
||||
If you just want to test conduwuit for a short time, you can use the `--rm` flag, which will clean up everything related to your container after you stop it.
|
||||
If you just want to test Conduit for a short time, you can use the `--rm` flag, which will clean up everything related to your container after you stop it.
|
||||
|
||||
### Docker-compose
|
||||
|
||||
@@ -89,14 +87,14 @@ When picking the traefik-related compose file, rename it so it matches `docker-c
|
||||
rename the override file to `docker-compose.override.yml`. Edit the latter with the values you want
|
||||
for your server.
|
||||
|
||||
Additional info about deploying conduwuit can be found [here](generic.md).
|
||||
Additional info about deploying Conduit can be found [here](generic.md).
|
||||
|
||||
### Build
|
||||
|
||||
To build the conduwuit image with docker-compose, you first need to open and modify the `docker-compose.yml` file. There you need to comment the `image:` option and uncomment the `build:` option. Then call docker compose with:
|
||||
To build the Conduit image with docker-compose, you first need to open and modify the `docker-compose.yml` file. There you need to comment the `image:` option and uncomment the `build:` option. Then call docker-compose with:
|
||||
|
||||
```bash
|
||||
docker compose up
|
||||
docker-compose up
|
||||
```
|
||||
|
||||
This will also start the container right afterwards, so if want it to run in detached mode, you also should use the `-d` flag.
|
||||
@@ -106,7 +104,7 @@ This will also start the container right afterwards, so if want it to run in det
|
||||
If you already have built the image or want to use one from the registries, you can just start the container and everything else in the compose file in detached mode with:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
> **Note:** Don't forget to modify and adjust the compose file to your needs.
|
||||
@@ -118,9 +116,9 @@ containerized app and services available through the web. With the two provided
|
||||
[`docker-compose.for-traefik.yml`](docker-compose.for-traefik.yml) (or
|
||||
[`docker-compose.with-traefik.yml`](docker-compose.with-traefik.yml)) and
|
||||
[`docker-compose.override.yml`](docker-compose.override.yml), it is equally easy to deploy
|
||||
and use conduwuit, with a little caveat. If you already took a look at the files, then you should have
|
||||
and use Conduit, with a little caveat. If you already took a look at the files, then you should have
|
||||
seen the `well-known` service, and that is the little caveat. Traefik is simply a proxy and
|
||||
loadbalancer and is not able to serve any kind of content, but for conduwuit to federate, we need to
|
||||
loadbalancer and is not able to serve any kind of content, but for Conduit to federate, we need to
|
||||
either expose ports `443` and `8448` or serve two endpoints `.well-known/matrix/client` and
|
||||
`.well-known/matrix/server`.
|
||||
|
||||
@@ -131,7 +129,7 @@ So...step by step:
|
||||
1. Copy [`docker-compose.for-traefik.yml`](docker-compose.for-traefik.yml) (or
|
||||
[`docker-compose.with-traefik.yml`](docker-compose.with-traefik.yml)) and [`docker-compose.override.yml`](docker-compose.override.yml) from the repository and remove `.for-traefik` (or `.with-traefik`) from the filename.
|
||||
2. Open both files and modify/adjust them to your needs. Meaning, change the `CONDUIT_SERVER_NAME` and the volume host mappings according to your needs.
|
||||
3. Create the `conduwuit.toml` config file, an example can be found [here](../configuration.md), or set `CONDUIT_CONFIG=""` and configure conduwuit per env vars.
|
||||
3. Create the `conduit.toml` config file, an example can be found [here](../configuration.md), or set `CONDUIT_CONFIG=""` and configure Conduit per env vars.
|
||||
4. Uncomment the `element-web` service if you want to host your own Element Web Client and create a `element_config.json`.
|
||||
5. Create the files needed by the `well-known` service.
|
||||
|
||||
@@ -159,7 +157,7 @@ So...step by step:
|
||||
}
|
||||
```
|
||||
|
||||
6. Run `docker compose up -d`
|
||||
6. Run `docker-compose up -d`
|
||||
7. Connect to your homeserver with your preferred client and create a user. You should do this immediately after starting Conduit, because the first created user is the admin.
|
||||
|
||||
|
||||
@@ -167,7 +165,7 @@ So...step by step:
|
||||
|
||||
## Voice communication
|
||||
|
||||
In order to make or receive calls, a TURN server is required. conduwuit suggests using [Coturn](https://github.com/coturn/coturn) for this purpose, which is also available as a Docker image. Before proceeding with the software installation, it is essential to have the necessary configurations in place.
|
||||
In order to make or receive calls, a TURN server is required. Conduit suggests using [Coturn](https://github.com/coturn/coturn) for this purpose, which is also available as a Docker image. Before proceeding with the software installation, it is essential to have the necessary configurations in place.
|
||||
|
||||
### Configuration
|
||||
|
||||
@@ -180,7 +178,7 @@ realm=<your server domain>
|
||||
```
|
||||
A common way to generate a suitable alphanumeric secret key is by using `pwgen -s 64 1`.
|
||||
|
||||
These same values need to be set in conduwuit. You can either modify conduwuit.toml to include these lines:
|
||||
These same values need to be set in conduit. You can either modify conduit.toml to include these lines:
|
||||
```
|
||||
turn_uris = ["turn:<your server domain>?transport=udp", "turn:<your server domain>?transport=tcp"]
|
||||
turn_secret = "<secret key from coturn configuration>"
|
||||
@@ -193,13 +191,13 @@ CONDUIT_TURN_SECRET: "<secret key from coturn configuration>"
|
||||
Restart Conduit to apply these changes.
|
||||
|
||||
### Run
|
||||
Run the [Coturn](https://hub.docker.com/r/coturn/coturn) image using
|
||||
Run the [Coturn](https://hub.docker.com/r/coturn/coturn) image using
|
||||
```bash
|
||||
docker run -d --network=host -v $(pwd)/coturn.conf:/etc/coturn/turnserver.conf coturn/coturn
|
||||
```
|
||||
|
||||
or docker-compose. For the latter, paste the following section into a file called `docker-compose.yml`
|
||||
and run `docker compose up -d` in the same directory.
|
||||
and run `docker-compose up -d` in the same directory.
|
||||
|
||||
```yml
|
||||
version: 3
|
||||
@@ -215,3 +213,4 @@ services:
|
||||
|
||||
To understand why the host networking mode is used and explore alternative configuration options, please visit the following link: https://github.com/coturn/coturn/blob/master/docker/coturn/README.md.
|
||||
For security recommendations see Synapse's [Coturn documentation](https://github.com/matrix-org/synapse/blob/develop/docs/setup/turn/coturn.md#configuration).
|
||||
|
||||
|
||||
+28
-28
@@ -22,47 +22,47 @@ $ sudo apt install libclang-dev build-essential
|
||||
# RHEL
|
||||
$ sudo dnf install clang
|
||||
```
|
||||
Then, `cd` into the source tree of conduwuit and run:
|
||||
Then, `cd` into the source tree of conduit-next and run:
|
||||
```bash
|
||||
$ cargo build --release
|
||||
```
|
||||
|
||||
## Adding a conduwuit user
|
||||
## Adding a Conduit user
|
||||
|
||||
While conduwuit can run as any user it is usually better to use dedicated users for different services. This also allows
|
||||
While Conduit can run as any user it is usually 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 RHEL, you can use this command to create a conduwuit user:
|
||||
In Debian or RHEL, you can use this command to create a Conduit user:
|
||||
|
||||
```bash
|
||||
sudo adduser --system conduwuit --group --disabled-login --no-create-home
|
||||
sudo adduser --system conduit --group --disabled-login --no-create-home
|
||||
```
|
||||
|
||||
## Forwarding ports in the firewall or the router
|
||||
|
||||
conduwuit uses the ports 443 and 8448 both of which need to be open in the firewall.
|
||||
Conduit uses the ports 443 and 8448 both of which need to be open in the firewall.
|
||||
|
||||
If conduwuit runs behind a router or in a container and has a different public IP address than the host system these public ports need to be forwarded directly or indirectly to the port mentioned in the config.
|
||||
If Conduit runs behind a router or in a container and has a different public IP address than the host system these public ports need to be forwarded directly or indirectly to the port mentioned in the config.
|
||||
|
||||
## Setting up a systemd service
|
||||
|
||||
Now we'll set up a systemd service for conduwuit, so it's easy to start/stop conduwuit and set it to autostart when your
|
||||
Now we'll set up a systemd service for Conduit, so it's easy to start/stop Conduit and set it to autostart when your
|
||||
server reboots. Simply paste the default systemd service you can find below into
|
||||
`/etc/systemd/system/conduwuit.service`.
|
||||
`/etc/systemd/system/conduit.service`.
|
||||
|
||||
```systemd
|
||||
[Unit]
|
||||
Description=conduwuit Matrix Server
|
||||
Description=Conduwuit Matrix Server
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Environment="CONDUWUIT_CONFIG=/etc/conduwuit/conduwuit.toml"
|
||||
User=conduwuit
|
||||
Group=conduwuit
|
||||
RuntimeDirectory=conduwuit
|
||||
Environment="CONDUIT_CONFIG=/etc/matrix-conduit/conduit.toml"
|
||||
User=conduit
|
||||
Group=conduit
|
||||
RuntimeDirectory=conduit
|
||||
RuntimeDirectoryMode=0750
|
||||
Restart=always
|
||||
ExecStart=/usr/local/bin/conduwuit
|
||||
ExecStart=/usr/local/bin/matrix-conduit
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -74,30 +74,30 @@ Finally, run
|
||||
$ sudo systemctl daemon-reload
|
||||
```
|
||||
|
||||
## Creating the conduwuit configuration file
|
||||
## Creating the Conduit configuration file
|
||||
|
||||
Now we need to create the conduwuit's config file in `/etc/conduwuit/conduwuit.toml`. Paste this in **and take a moment
|
||||
to read it. You need to change at least the server name.**
|
||||
Now we need to create the Conduit's config file in `/etc/conduwuit/conduwuit.toml`. Paste this in **and take a moment
|
||||
to read it. You need to change at least the server name.**
|
||||
RocksDB (`rocksdb`) is the only supported database backend. SQLite only exists for historical reasons and is not recommended. Any performance issues, storage issues, database issues, etc will not be assisted if using SQLite and you will be asked to migrate to RocksDB first.
|
||||
|
||||
See the following example config at [conduwuit-example.toml](../configuration.md)
|
||||
|
||||
## Setting the correct file permissions
|
||||
|
||||
As we are using a conduwuit specific user we need to allow it to read the config. To do that you can run this command on
|
||||
As we are using a Conduit specific user we 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 755 /etc/conduwuit
|
||||
sudo chown -R root:root /etc/matrix-conduit
|
||||
sudo chmod 755 /etc/matrix-conduit
|
||||
```
|
||||
|
||||
If you use the default database path you also need to run this:
|
||||
|
||||
```bash
|
||||
sudo mkdir -p /var/lib/conduwuit/
|
||||
sudo chown -R conduwuit:conduwuit /var/lib/conduwuit/
|
||||
sudo chmod 700 /var/lib/conduwuit/
|
||||
sudo mkdir -p /var/lib/matrix-conduit/
|
||||
sudo chown -R conduit:conduit /var/lib/matrix-conduit/
|
||||
sudo chmod 700 /var/lib/matrix-conduit/
|
||||
```
|
||||
|
||||
## Setting up the Reverse Proxy
|
||||
@@ -114,7 +114,7 @@ your.server.name, your.server.name:8448 {
|
||||
reverse_proxy 127.0.0.1:6167
|
||||
|
||||
# UNIX socket
|
||||
#reverse_proxy unix//run/conduwuit/conduwuit.sock
|
||||
#reverse_proxy unix//run/conduit/conduit.sock
|
||||
}
|
||||
```
|
||||
|
||||
@@ -126,16 +126,16 @@ $ sudo systemctl enable caddy
|
||||
|
||||
## You're done!
|
||||
|
||||
Now you can start conduwuit with:
|
||||
Now you can start Conduit with:
|
||||
|
||||
```bash
|
||||
$ sudo systemctl start conduwuit
|
||||
$ sudo systemctl start conduit
|
||||
```
|
||||
|
||||
Set it to start automatically when your system boots with:
|
||||
|
||||
```bash
|
||||
$ sudo systemctl enable conduwuit
|
||||
$ sudo systemctl enable conduit
|
||||
```
|
||||
|
||||
## How do I know it works?
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# conduwuit for NixOS
|
||||
# Conduwuit for NixOS
|
||||
|
||||
conduwuit can be acquired by Nix from various places:
|
||||
Conduwuit can be acquired by Nix from various places:
|
||||
|
||||
* The `flake.nix` at the root of the repo
|
||||
* The `default.nix` at the root of the repo
|
||||
* From conduwuit's binary cache
|
||||
* From Conduwuit's binary cache
|
||||
|
||||
A binary cache for conduwuit that the CI/CD publishes to is available at the
|
||||
following places (both are the same just different names):
|
||||
@@ -20,7 +20,7 @@ If specifying a URL in your flake, please use the GitHub remote: `github:girlbos
|
||||
|
||||
The `flake.nix` and `default.nix` do not (currently) provide a NixOS module, so
|
||||
(for now) [`services.matrix-conduit`][module] from Nixpkgs should be used to
|
||||
configure conduwuit.
|
||||
configure Conduit.
|
||||
|
||||
If you want to run the latest code, you should get Conduwuit from the `flake.nix`
|
||||
or `default.nix` and set [`services.matrix-conduit.package`][package]
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
# Development
|
||||
|
||||
Information about developing the project. If you are only interested in using
|
||||
it, you can safely ignore this section.
|
||||
@@ -1,17 +0,0 @@
|
||||
# Testing
|
||||
|
||||
## Complement
|
||||
|
||||
Have a look at [Complement's repository][complement] for an explanation of what
|
||||
it is.
|
||||
|
||||
To test against Complement, with Nix and direnv installed and set up, you can
|
||||
either:
|
||||
|
||||
* Run `./bin/complement "$COMPLEMENT_SRC" ./path/to/logs.jsonl ./path/to/results.jsonl`
|
||||
to build a Complement image, run the tests, and output the logs and results
|
||||
to the specified paths
|
||||
* Run `nix build .#complement` from the root of the repository to just build a
|
||||
Complement image
|
||||
|
||||
[complement]: https://github.com/matrix-org/complement
|
||||
+96
-129
@@ -1,142 +1,109 @@
|
||||
#### **Note: This list may not up to date. There are rapidly more and more improvements, fixes, changes, etc being made that it is becoming more difficult to maintain this list. I recommend that you give conduwuit a try and see the differences for yourself. If you have any concerns, feel free to join the conduwuit Matrix room and ask any pre-usage questions.**
|
||||
#### **Note: This list is not up to date. There are rapidly more and more improvements, fixes, changes, etc being made that it is becoming more difficult to maintain this list. I recommend that you give Conduwuit a try and see the differences for yourself. If you have any concerns, feel free to join the Conduwuit Matrix room and ask any pre-usage questions.**
|
||||
|
||||
### list of features, bug fixes, etc that conduwuit does that Conduit does not:
|
||||
### list of features, bug fixes, etc that conduwuit does that upstream does not:
|
||||
|
||||
Outgoing typing indicators, outgoing read receipts, **and** outgoing presence!
|
||||
|
||||
## Performance:
|
||||
- Concurrency support for key fetching for faster remote room joins and room joins that will error less frequently
|
||||
- Send `Cache-Control` response header with `immutable` and 1 year cache length for all media requests to instruct clients to cache media, and reduce server load from media requests that could be otherwise cached
|
||||
- GitLab CI ported to GitHub Actions
|
||||
- Fixed every single clippy (default lints) and rustc warnings, including some that were performance related or potential safety issues / unsoundness
|
||||
- Add a **lot** of other clippy and rustc lints and a rustfmt.toml file
|
||||
- Has Renovate and significantly updates all dependencies possible
|
||||
- Uses proper argon2 crate instead of questionable rust-argon2 crate
|
||||
- Improved and cleaned up logging (less noisy dead server logging, registration attempts, more useful troubleshooting logging, etc)
|
||||
- Attempts and interest in removing extreme and unnecessary panics/unwraps/expects that can lead to denial of service or such (upstream and upstream contributors want this unusual behaviour for some reason)
|
||||
- Merged and cleaned up upstream MRs that have been sitting for 6-12 months
|
||||
- Configurable RocksDB logging (`LOG` files) with proper defaults (rotate, max size, verbosity, etc) to stop LOG files from accumulating so much
|
||||
- Concurrency support for key fetching for faster remote room joins and room joins that will error less frequently (via upstream MR)
|
||||
- Room version 11 support (via upstream MR)
|
||||
- Explicit startup error/warning if your configuration allows open registration without a token or such like Synapse
|
||||
- Improved RocksDB defaults to use new features that help with performance significantly, uses settings tailored to SSDs, various ways to tweak RocksDB, and a conduwuit setting to tell RocksDB to use settings that are tailored to HDDs or slow spinning rust storage.
|
||||
- Revamped admin room infrastructure and commands (via upstream MR)
|
||||
- Admin room commands to delete room aliases and unpublish rooms from our room directory (via upstream MR)
|
||||
- Make spaces/hierarchy cache use cache_capacity_modifier instead of hardcoded small value
|
||||
- Add *optional* feature flag to use SHA256 key names for media instead of base64 to overcome filesystem file name length limitations (OS error file name too long) (via upstream MR)
|
||||
- Add feature flags and config options to enable/build with zstd, brotli, and/or gzip HTTP body compression (response and request)
|
||||
- Eliminate all usage of the thread-blocking `getaddrinfo(3)` call upon DNS queries, significantly improving federation latency/ping and cache DNS results (NXDOMAINs, successful queries, etc) using hickory-dns / hickory-resolver
|
||||
- Vastly improve RocksDB default settings to use new features that help with performance significantly, uses settings tailored to SSDs, various ways to tweak RocksDB, and a conduwuit setting to tell RocksDB to use settings that are tailored to HDDs or slow spinning rust storage or buggy filesystems.
|
||||
- Add a Cargo build profile for aggressive build-time performance optimisations for release builds (1 codegen unit, no debug, fat LTO, etc, and optimise all crates with same)
|
||||
- Implement database flush and cleanup conduwuit operations when using RocksDB
|
||||
- Implement RocksDB write buffer corking and coalescing in database write-heavy areas
|
||||
- Perform connection pooling and keepalives where necessary to significantly improve federation performance and latency
|
||||
- Various config options to tweak connection pooling, request timeouts, connection timeouts, DNS timeouts and settings, etc with good defaults which also help huge with performance via reusing connections and retrying where needed
|
||||
- Implement building conduwuit with jemalloc (which extends to the RocksDB jemalloc feature for maximum gains) or hardened_malloc light variant, and produce CI builds with jemalloc for performance (Nix doesn't seem to build [hardened_malloc-rs](https://github.com/girlbossceo/hardened_malloc-rs) properly)
|
||||
- Add support for caching DNS results with hickory-dns / hickory-resolver in conduwuit (not a replacement for a proper resolver cache, but still far better than nothing)
|
||||
- Overall significant database, Client-Server, and federation performance and latency improvements (check out the ping room leaderboards if you don't believe me :>)
|
||||
- Add config options for RocksDB compression and bottommost compression, including choosing the algorithm and compression level
|
||||
- Use [loole](https://github.com/mahdi-shojaee/loole) MPSC channels instead of tokio MPSC channels for huge performance boosts in sending channels (mainly relevant for federation) and presence channels
|
||||
|
||||
|
||||
## General Fixes:
|
||||
- Raise and improve all the various request timeouts making some things like room joins and client bugs error less or none at all than they should, and make them all user configurable
|
||||
- Add missing `reason` field to user ban events (`/ban`)
|
||||
- Fixed spec compliance issue with room version 8 - 11 joins (https://github.com/matrix-org/synapse/issues/16717 / https://github.com/matrix-org/matrix-spec/issues/1708)
|
||||
- Safer and cleaner shutdowns on both database side as we run cleanup on shutdown and exits database loop better (no potential hanging issues in database loop), overall cleaner shutdown logic
|
||||
- Stop sending `make_join` requests on room joins if 15 servers respond with `M_UNSUPPORTED_ROOM_VERSION` or `M_INVALID_ROOM_VERSION`
|
||||
- Stop sending `make_join` requests if 50 servers cannot provide `make_join` for us
|
||||
- Respect *most* client parameters for `/media/` requests (`allow_redirect` still needs work)
|
||||
- Increased graceful shutdown timeout from a low 60 seconds to 180 seconds to avoid killing connections and let the remaining ones finish processing
|
||||
- Return joined member count of rooms for push rules/conditions instead of a hardcoded value of 10
|
||||
- Make `CONDUIT_CONFIG` optional, relevant for container users that configure only by environment variables and no longer need to set `CONDUIT_CONFIG` to an empty string.
|
||||
- Allow HEAD HTTP requests in CORS for clients (despite not being explicity mentioned in Matrix spec, HTTP spec says all HEAD requests need to behave the same as GET requests, Synapse supports HEAD requests)
|
||||
- Add missing `destination` key to all `X-Matrix` `Authorization` requests (spec compliance issue)
|
||||
- Resolve and remove some "features" from upstream that result in concurrency hazards, exponential backoff issues, or arbitrary performance limiters
|
||||
- Find more servers for outbound federation `/hierarchy` requests instead of just the room ID server name
|
||||
- Support for suggesting servers to join through at `/_matrix/client/v3/directory/room/{roomAlias}`
|
||||
- Support for suggesting servers to join through us at `/_matrix/federation/v1/query/directory`
|
||||
|
||||
|
||||
## Moderation:
|
||||
- (Also see [Admin Room](#admin-room) for all the admin commands pertaining to moderation, there's a lot!)
|
||||
- Add support for room banning/blocking by ID using admin command
|
||||
- Add support for serving `support` well-known from `[well_known.support]` (MSC1929)
|
||||
- Config option to forbid publishing rooms to the room directory (`lockdown_public_room_directory`) except for admins
|
||||
- Admin commands to delete room aliases and unpublish rooms from our room directory
|
||||
- For all [`/report`](https://spec.matrix.org/v1.9/client-server-api/#post_matrixclientv3roomsroomidreporteventid) requests: check if the reported event ID belongs to the reported room ID, raise report reasoning character limit to 750, fix broken formatting, make a small delayed random response per spec suggestion on privacy, and check if the sender user is in the reported room.
|
||||
- Support blocking servers from downloading remote media from, returning a 404
|
||||
- Don't allow `m.call.invite` events to be sent in public rooms (prevents calling the entire room)
|
||||
- On new public room creations, only allow moderators to send `m.call.invite`, `org.matrix.msc3401.call`, and `org.matrix.msc3401.call.member` events
|
||||
- Add support for a "global ACLs" feature (`forbidden_remote_server_names`) that blocks inbound remote room invites, room joins by room ID on server name, room joins by room alias on server name, incoming federated joins, and incoming federated room directory requests. This is very helpful for blocking servers that are purely toxic/bad and serve no value in allowing our users to suffer from things like room invite spam or such. Please note that this is not a substitute for room ACLs.
|
||||
- Add support for a config option to forbid our local users from sending federated room directory requests for (`forbidden_remote_room_directory_server_names`). Similar to above, useful for blocking servers that help prevent our users from wandering into bad areas of Matrix via room directories of those malicious servers.
|
||||
|
||||
|
||||
## Privacy/Security:
|
||||
- Add support for querying both Matrix SRV records, the deprecated `_matrix` record and `_matrix-fed` record if necessary
|
||||
- Add config option for device name federation with a privacy-friendly default (disabled)
|
||||
- Add config option for requiring authentication to the `/publicRooms` endpoint (room directory) with a default enabled for privacy
|
||||
- Add config option for federating `/publicRooms` endpoint (room directory) to other servers with a default disabled for privacy
|
||||
- Uses proper `argon2` crate by RustCrypto instead of questionable `rust-argon2` crate
|
||||
- Generate passwords with 25 characters instead of 15
|
||||
- Config option `ip_range_denylist` to support refusing to send requests (typically federation) to specific IP ranges, typically RFC 1918, non-routable, testnet, etc addresses like Synapse for security (note: this is not a guaranteed protection, and you should be using a firewall with zones if you want guaranteed protection as doing this on the application level is prone to bypasses).
|
||||
- Config option to block non-admin users from sending room invites or receiving remote room invites. Admin users are still allowed.
|
||||
- Config option to disable incoming and/or outgoing remote read receipts
|
||||
- Config option to disable incoming and/or outgoing remote typing indicators
|
||||
- Config option to disable incoming, outgoing, and/or local presence
|
||||
|
||||
|
||||
## Administration/Logging:
|
||||
- Commandline argument to specify the path to a config file instead of relying on `CONDUIT_CONFIG`
|
||||
- Revamped admin room infrastructure and commands
|
||||
- Substantially clean up, improve, and fix logging (less noisy dead server logging, registration attempts, more useful troubleshooting logging, proper error propagation, etc)
|
||||
- Configurable RocksDB logging (`LOG` files) with proper defaults (rotate, max size, verbosity, etc) to stop LOG files from accumulating so much
|
||||
- Explicit startup error if your configuration allows open registration without a token or such like Synapse with a way to bypass it if needed
|
||||
- Replace the lightning bolt emoji option with support for setting any arbitrary text (e.g. another emoji) to suffix to all new user registrations, with a conduwuit default of 🏳️⚧️
|
||||
- Implement config option to auto join rooms upon registration
|
||||
- Warn on unknown config options specified
|
||||
- Add `/_conduwuit/server_version` route to return the version of conduwuit without relying on the federation API `/_matrix/federation/v1/version`
|
||||
- Add configurable RocksDB recovery modes to aid in recovering corrupted RocksDB databases
|
||||
- Support config options via `CONDUWUIT_` prefix
|
||||
- Add support for listening on multiple TCP ports
|
||||
- Disable update check by default as it's not useful for conduwuit
|
||||
- **Opt-in** Sentry.io telemetry and metrics, mainly used for crash reporting
|
||||
|
||||
|
||||
## Maintenance/Stability:
|
||||
- GitLab CI ported to GitHub Actions
|
||||
- Repo is mirrored to GitHub, GitLab, git.gay, sourcehut, and Codeberg (see README.md for their links)
|
||||
- Extensively revamp the example config to be extremely helpful and useful to both new users and power users
|
||||
- Fixed every single clippy (default lints) and rustc warnings, including some that were performance related or potential safety issues / unsoundness
|
||||
- Add a **lot** of other clippy and rustc lints and a rustfmt.toml file
|
||||
- Has [Renovate](https://docs.renovatebot.com/), [Trivy](https://github.com/aquasecurity/trivy-action), and keeps ALL dependencies as up to date as possible
|
||||
- Attempts and interest in removing extreme and unnecessary panics/unwraps/expects that can lead to denial of service or such (upstream and upstream contributors want this unusual behaviour for some reason)
|
||||
- Purge unmaintained/irrelevant/broken database backends (heed, sled, persy) and other unnecessary code or overhead
|
||||
- Add support for listening on a UNIX socket for performance and host security with proper default permissions (660)
|
||||
- Add missing `destination` key to all `X-Matrix` `Authorization` requests (spec compliance issue)
|
||||
- Use aggressive build-time performance optimisations for release builds (1 codegen unit, no debug, fat LTO, etc, and optimise all crates with same)
|
||||
- Raise various hardcoded timeouts in codebase that were way too short, making some things like room joins and client bugs error less or none at all than they should
|
||||
- Add debug admin command to force update user device lists (could potentially resolve some E2EE flukes) (`ForceDeviceListUpdates`)
|
||||
- Declare various missing Matrix versions and features at `/_matrix/client/versions`
|
||||
- Add support for serving server and client well-known files from conduwuit using `well_known_client` and `well_known_server` options
|
||||
- Send a User-Agent on all of our requests (`conduwuit/0.7.0-alpha+conduwuit-0.1.1`) which strangely was not done upstream since forever. Some providers consider no User-Agent suspicious and block said requests.
|
||||
- Safer and cleaner shutdowns on both database side as we run cleanup on shutdown and exits database loop better (no potential hanging issues in database loop), overall cleaner shutdown logic
|
||||
- Allow HEAD HTTP requests in CORS for clients (despite not being explicity mentioned in Matrix spec, HTTP spec says all HEAD requests need to behave the same as GET requests, Synapse supports HEAD requests)
|
||||
- Purge unmaintained/irrelevant/broken database backends (heed, sled, persy)
|
||||
- webp support for images
|
||||
- Add cargo audit support to CI
|
||||
- CI tests with all features
|
||||
- Add timestamp by commit date support to building OCI images for keeping image build reproducibility and still have a meaningful "last modified date" for OCI image metadata
|
||||
- Update rusqlite/sqlite (not that you should be using it)
|
||||
- Startup check if conduwuit running in a container and is listening on 127.0.0.1 (generally containers are using NAT networking and 0.0.0.0 is the intended listening address)
|
||||
|
||||
|
||||
## Admin Room:
|
||||
- Fix admin room handler to not panic/crash if the admin room command response fails (e.g. too large message)
|
||||
- Add command to dynamically change conduwuit's tracing log level filter on the fly
|
||||
- Add admin command to fetch a server's `/.well-known/matrix/support` file
|
||||
- Add debug admin command to force update user device lists (could potentially resolve some E2EE flukes)
|
||||
- Implement **RocksDB online backups**, listing RocksDB backups, and listing database file counts all via admin commands
|
||||
- Add various database visibility commands such as being able to query the getters and iterators used in conduwuit, a very helpful online debugging utility
|
||||
- Forbid the admin room from being made public or world readable history
|
||||
- Add `!admin` as a way to call the admin bot
|
||||
- Extend clear cache admin command to support clearing more caches such as DNS and TLS name overrides
|
||||
- Admin debug command to send a federation request/ping to a server's `/_matrix/federation/v1/version` endpoint and measures the latency it took
|
||||
- Support for suggesting servers to join at `/_matrix/client/v3/directory/room/{roomAlias}`
|
||||
- Prevent admin credential commands like reset password and deactivate user from modifying non-local users (https://gitlab.com/famedly/conduit/-/issues/377)
|
||||
- Fixed spec compliance issue with room version 8 - 11 joins (https://github.com/matrix-org/synapse/issues/16717 / https://github.com/matrix-org/matrix-spec/issues/1708)
|
||||
- Add basic cache eviction for true destinations when requests fail if we use a cached destination (e.g. a server has modified their well-known and we're still connecting to the old destination)
|
||||
- Generate passwords with 25 characters instead of 15
|
||||
- Add missing `reason` field to user ban events (`/ban`)
|
||||
- For all [`/report`](https://spec.matrix.org/v1.9/client-server-api/#post_matrixclientv3roomsroomidreporteventid) requests: check if the reported event ID belongs to the reported room ID, raise report reasoning character limit to 750, fix broken formatting, make a small delayed random response per spec suggestion on privacy, and check if the sender user is in the reported room.
|
||||
- Support blocking servers from downloading remote media from
|
||||
- Support sending `well_known` response to client logins if using config option `well_known_client`
|
||||
- Send `avatar_url` on invite room membership events/changes
|
||||
- Revamp example config, adding a lot of config options available (still some missing)
|
||||
- Return joined member count of rooms for push rules/conditions instead of a hardcoded value of 10
|
||||
- Respect *most* client parameters for `/media/` requests (`allow_redirect` still needs work)
|
||||
- Config option `ip_range_denylist` to support refusing to send requests (typically federation) to specific IP ranges, typically RFC 1918, non-routable, testnet, etc addresses like Synapse for security (note: this is not a guaranteed protection, and you should be using a firewall with zones if you want guaranteed protection as doing this on the application level is prone to bypasses).
|
||||
- Support for creating rooms with custom room IDs like Maunium Synapse (`room_id` request body field to `/createRoom`)
|
||||
- Assume well-knowns are broken if they exceed past 10000 characters.
|
||||
- Basic validation/checks on user-specified room aliases and custom room ID creations
|
||||
- Warn on unknown config options specified
|
||||
- URL preview support (via upstream MR) with various improvements
|
||||
- Increased graceful shutdown timeout from a low 60 seconds to 180 seconds to avoid killing connections and let the remaining ones finish processing
|
||||
- Query parameter `?format=event|content` for returning either the room state event's content (default) for the full room state event on ` /_matrix/client/v3/rooms/{roomId}/state/{eventType}[/{stateKey}]` requests (see https://github.com/matrix-org/matrix-spec/issues/1047)
|
||||
- Add admin commands for banning (blocking) room IDs from our local users joining (admins are always allowed) and evicts all our local users from that room, in addition to bulk room banning support, and blocks room invites (remote and local) to the banned room, as a moderation feature
|
||||
- Add admin command to delete media via a specific MXC. This deletes the MXC from our database, and the file locally.
|
||||
- Replace the lightning bolt emoji option with support for setting any arbitrary text (e.g. another emoji) to suffix to all new user registrations
|
||||
- Add admin command to bulk delete media via a codeblock list of MXC URLs.
|
||||
- Add admin command to delete both the thumbnail and media MXC URLs from an event ID (e.g. from an abuse report)
|
||||
- Add `!admin` as a way to call the Conduit admin bot
|
||||
- Add support for listening on multiple TCP ports
|
||||
- Add admin command to list all the rooms a local user is joined in
|
||||
- Add admin command to delete all remote media in the past X minutes as a form of deleting media that you don't want on your server that a remote user posted in a room
|
||||
- Add admin command to return a room's state
|
||||
- Admin debug command to fetch a PDU from a remote server and inserts it into our database/timeline as backfill
|
||||
- Add admin command to delete media via a specific MXC. This deletes the MXC from our database, and the file locally.
|
||||
- Add admin commands for banning (blocking) room IDs from our local users joining (admins are always allowed) and evicts all our local users from that room, in addition to bulk room banning support, and blocks room invites (remote and local) to the banned room, as a moderation feature
|
||||
|
||||
|
||||
## Misc:
|
||||
- Support for creating rooms with custom room IDs like Maunium Synapse (`room_id` request body field to `/createRoom`)
|
||||
- Query parameter `?format=event|content` for returning either the room state event's content (default) for the full room state event on ` /_matrix/client/v3/rooms/{roomId}/state/{eventType}[/{stateKey}]` requests (see https://github.com/matrix-org/matrix-spec/issues/1047)
|
||||
- Add **optional** feature flag to use SHA256 key names for media instead of base64 to overcome filesystem file name length limitations (OS error file name too long)
|
||||
- Send a User-Agent on all of our requests
|
||||
- Send `avatar_url` on invite room membership events/changes
|
||||
- Support sending [`well_known` response to client login responses](https://spec.matrix.org/v1.10/client-server-api/#post_matrixclientv3login) if using config option `[well_known.client]`
|
||||
- Implement `include_state` search criteria support for `/search` requests (response now can include room states)
|
||||
- Declare various missing Matrix versions and features at `/_matrix/client/versions`
|
||||
- Config option to block non-admin users from sending room invites or receiving remote room invites. Admin users are still allowed.
|
||||
- Startup check if conduwuit running in a container and is listening on 127.0.0.1
|
||||
- Make `CONDUIT_CONFIG` optional, relevant for container users that configure only by environment variables and no longer need to set `CONDUIT_CONFIG` to an empty string.
|
||||
- Config option to change Conduit's behaviour of homeserver key fetching (`query_trusted_key_servers_first`). This option sets whether conduwuit will query trusted notary key servers first before the individual homeserver(s), or vice versa.
|
||||
- Implement database flush and cleanup Conduit operations when using RocksDB
|
||||
- Implement legacy Matrix `/v1/` media endpoints that some clients and servers may still call
|
||||
- Config option to change Conduit's behaviour of homeserver key fetching (`query_trusted_key_servers_first`). This option sets whether conduwuit will query trusted notary key servers first before the individual homeserver(s), or vice versa which may help in joining certain rooms.
|
||||
- Commandline argument to specify the path to a config file
|
||||
- Admin debug command to fetch a PDU from a remote server and inserts it into our database/timeline
|
||||
- Update rusqlite/sqlite (not that you should be using it)
|
||||
- Disable update check by default as it's not useful for conduwuit
|
||||
- Config option to disable incoming remote read receipts if desired
|
||||
- Extend clear cache admin command to support clearing DNS and TLS name override caches
|
||||
- Responsive outgoing read receipt EDU support
|
||||
- Eliminate all usage of the thread-blocking `getaddrinfo(3)` call upon DNS queries, significantly improving federation latency/ping and cache DNS results using hickory-dns / hickory-resolver
|
||||
- Store the sender user with the MXC URL for all media uploads (`/upload`) (not for thumbnails or media requests which are unauthenticated)
|
||||
- Perform connection pooling and keepalives where necessary to significantly improve federation performance and latency
|
||||
- Implement RocksDB online backups via admin command
|
||||
- Implement RocksDB write buffer corking and coalescing in database write-heavy areas
|
||||
- Various config options to tweak connection pooling, request timeouts, connection timeouts, DNS timeouts and settings, etc with good defaults
|
||||
- Implement config option to auto join rooms upon registration
|
||||
- Overall significant database, Client-Server, and federation performance and latency improvements
|
||||
- Outgoing read receipt and private read receipt support (EDU)
|
||||
- Outgoing typing indicator support (EDU)
|
||||
- Outgoing and local presence support (EDU)
|
||||
- **Opt-in** Sentry.io telemetry and metrics, mainly used for crash reporting
|
||||
- Add `/_conduwuit/server_version` route to return the version of Conduwuit without relying on the federation API `/_matrix/federation/v1/version`
|
||||
- Add configurable RocksDB recovery modes to aid in recovering corrupte RocksDB database
|
||||
- Config option to forbid publishing rooms to the room directory (`lockdown_public_room_directory`) except for admins
|
||||
- Don't allow `m.call.invite` events to be sent in public rooms (prevents calling the entire room)
|
||||
- On new public room creations, only allow moderators to send `m.call.invite`, `org.matrix.msc3401.call`, and `org.matrix.msc3401.call.member` events
|
||||
- Stop sending `make_join` requests on room joins if 15 servers respond with `M_UNSUPPORTED_ROOM_VERSION` or `M_INVALID_ROOM_VERSION`
|
||||
- Stop sending `make_join` requests if 50 servers cannot provide `make_join` for us
|
||||
- Admin debug command to send a federation request/ping to a server's `/_matrix/federation/v1/version` endpoint and measures the latency it took
|
||||
- Implement building Conduwuit with jemalloc or hardened_malloc light variant, and produce CI builds with jemalloc or hardened_malloc, for performance and/or security
|
||||
- Significant RocksDB tuning and improvements tailored to maximising Conduwuit performance with RocksDB
|
||||
- Implement unstable MSC2666 support for querying mutual rooms with a user
|
||||
- Assume well-knowns are broken if they exceed past 10000 characters.
|
||||
- Add support for the Matrix spec compliance test suite [Complement](https://github.com/matrix-org/complement/) via the Nix flake and various other fixes for it
|
||||
- Add support for listening on both HTTP and HTTPS if using direct TLS with conduwuit for usecases such as Complement
|
||||
- Interest in supporting other operating systems such as macOS, BSDs, and Windows, and getting them added into CI and doing builds for them
|
||||
- No cryptocurrency donations allowed, conduwuit is fully maintained by independent queer maintainers, and with a strong priority on inclusitivity and comfort for protected groups 🏳️⚧️
|
||||
- Add admin command to fetch a server's `/.well-known/matrix/support` file
|
||||
- Send `Cache-Control` response header with immutable and 1 year cache length for all media requests to instruct clients to cache media, and reduce server load from media requests that could be otherwise cached
|
||||
- Forbid the admin room from being made public
|
||||
- Fix admin room handler to not panic/crash if the admin room command response fails (e.g. too large message)
|
||||
- Implement `include_state` search criteria support for `/search` requests (response now can include room states)
|
||||
|
||||
+1
-1
@@ -58,7 +58,7 @@ script = "lychee --version"
|
||||
[[task]]
|
||||
name = "cargo-audit"
|
||||
group = "security"
|
||||
script = "cargo audit -D warnings -D unmaintained -D unsound -D yanked --ignore RUSTSEC-2020-0016"
|
||||
script = "cargo audit -D warnings -D unmaintained -D unsound -D yanked"
|
||||
|
||||
[[task]]
|
||||
name = "cargo-fmt"
|
||||
|
||||
Generated
+23
-58
@@ -9,11 +9,11 @@
|
||||
"nixpkgs-stable": "nixpkgs-stable"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1711742460,
|
||||
"narHash": "sha256-0O4v6e4a1toxXZ2gf5INhg4WPE5C5T+SVvsBt+45Mcc=",
|
||||
"lastModified": 1707922053,
|
||||
"narHash": "sha256-wSZjK+rOXn+UQiP1NbdNn5/UW6UcBxjvlqr2wh++MbM=",
|
||||
"owner": "zhaofengli",
|
||||
"repo": "attic",
|
||||
"rev": "4dbdbee45728d8ce5788db6461aaaa89d98081f0",
|
||||
"rev": "6eabc3f02fae3683bffab483e614bebfcd476b21",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -23,22 +23,6 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"complement": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1713458251,
|
||||
"narHash": "sha256-hom/Lt0gZzLWqFhUJG0X2i88CAMIILInO5w0tPj6G3s=",
|
||||
"owner": "matrix-org",
|
||||
"repo": "complement",
|
||||
"rev": "d73c81a091604b0fc5b6b0617dcac58c25763f57",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "matrix-org",
|
||||
"repo": "complement",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"crane": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
@@ -67,17 +51,17 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1713738183,
|
||||
"narHash": "sha256-qd/MuLm7OfKQKyd4FAMqV4H6zYyOfef5lLzRrmXwKJM=",
|
||||
"lastModified": 1707685877,
|
||||
"narHash": "sha256-XoXRS+5whotelr1rHiZle5t5hDg9kpguS5yk8c8qzOc=",
|
||||
"owner": "ipetkov",
|
||||
"repo": "crane",
|
||||
"rev": "f6c6a2fb1b8bd9b65d65ca9342dd0eb180a63f11",
|
||||
"rev": "2c653e4478476a52c6aa3ac0495e4dea7449ea0e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "ipetkov",
|
||||
"ref": "master",
|
||||
"repo": "crane",
|
||||
"rev": "2c653e4478476a52c6aa3ac0495e4dea7449ea0e",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
@@ -89,11 +73,11 @@
|
||||
"rust-analyzer-src": "rust-analyzer-src"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1713680591,
|
||||
"narHash": "sha256-3pbv7UgAgetwz9YdjzIT/lZ6Rgj6wj6MR4mphBLyDjU=",
|
||||
"lastModified": 1711606966,
|
||||
"narHash": "sha256-nTaO7ZDL4D02dVC5ktqnXNiNuODBUHyE4qEcFjAUCQY=",
|
||||
"owner": "nix-community",
|
||||
"repo": "fenix",
|
||||
"rev": "19aaa94a73cc670a4d87e84f0909966cd8f8cd79",
|
||||
"rev": "aa45c3e901ea42d6633af083c0c555efaf948b17",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -184,11 +168,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1711401922,
|
||||
"narHash": "sha256-QoQqXoj8ClGo0sqD/qWKFWezgEwUL0SUh37/vY2jNhc=",
|
||||
"lastModified": 1702539185,
|
||||
"narHash": "sha256-KnIRG5NMdLIpEkZTnN5zovNYc0hhXjAgv6pfd5Z4c7U=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "07262b18b97000d16a4bdb003418bd2fb067a932",
|
||||
"rev": "aa9d4729cbc99dabacb50e3994dcefb3ea0f7447",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -200,11 +184,11 @@
|
||||
},
|
||||
"nixpkgs-stable": {
|
||||
"locked": {
|
||||
"lastModified": 1711460390,
|
||||
"narHash": "sha256-akSgjDZL6pVHEfSE6sz1DNSXuYX6hq+P/1Z5IoYWs7E=",
|
||||
"lastModified": 1702780907,
|
||||
"narHash": "sha256-blbrBBXjjZt6OKTcYX1jpe9SRof2P9ZYWPzq22tzXAA=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "44733514b72e732bd49f5511bd0203dea9b9a434",
|
||||
"rev": "1e2e384c5b7c50dbf8e9c441a9e58d85f408b01f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -216,11 +200,11 @@
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1713537308,
|
||||
"narHash": "sha256-XtTSSIB2DA6tOv+l0FhvfDMiyCmhoRbNB+0SeInZkbk=",
|
||||
"lastModified": 1711523803,
|
||||
"narHash": "sha256-UKcYiHWHQynzj6CN/vTcix4yd1eCu1uFdsuarupdCQQ=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "5c24cf2f0a12ad855f444c30b2421d044120c66f",
|
||||
"rev": "2726f127c15a4cc9810843b96cad73c7eb39e443",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -230,44 +214,25 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"rocksdb": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1713810944,
|
||||
"narHash": "sha256-/Xf0bzNJPclH9IP80QNaABfhj4IAR5LycYET18VFCXc=",
|
||||
"owner": "facebook",
|
||||
"repo": "rocksdb",
|
||||
"rev": "6f7cabeac80a3a6150be2c8a8369fcecb107bf43",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "facebook",
|
||||
"ref": "v9.1.1",
|
||||
"repo": "rocksdb",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"attic": "attic",
|
||||
"complement": "complement",
|
||||
"crane": "crane_2",
|
||||
"fenix": "fenix",
|
||||
"flake-compat": "flake-compat_2",
|
||||
"flake-utils": "flake-utils_2",
|
||||
"nix-filter": "nix-filter",
|
||||
"nixpkgs": "nixpkgs_2",
|
||||
"rocksdb": "rocksdb"
|
||||
"nixpkgs": "nixpkgs_2"
|
||||
}
|
||||
},
|
||||
"rust-analyzer-src": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1713628977,
|
||||
"narHash": "sha256-iN5QUlUq527lswmBC+RopfXdu6Xx7mmTaBSH2l59FtM=",
|
||||
"lastModified": 1711562745,
|
||||
"narHash": "sha256-s/YOyBM0vumhkqCFi8CnV5imFlC5JJrGia8CmEXyQkM=",
|
||||
"owner": "rust-lang",
|
||||
"repo": "rust-analyzer",
|
||||
"rev": "55d9a533b309119c8acd13061581b43ae8840823",
|
||||
"rev": "ad51a17c627b4ca57f83f0dc1f3bb5f3f17e6d0b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -1,68 +1,257 @@
|
||||
{
|
||||
inputs = {
|
||||
attic.url = "github:zhaofengli/attic?ref=main";
|
||||
complement = { url = "github:matrix-org/complement"; flake = false; };
|
||||
crane = { url = "github:ipetkov/crane?ref=master"; inputs.nixpkgs.follows = "nixpkgs"; };
|
||||
fenix = { url = "github:nix-community/fenix"; inputs.nixpkgs.follows = "nixpkgs"; };
|
||||
flake-compat = { url = "github:edolstra/flake-compat"; flake = false; };
|
||||
nixpkgs.url = "github:NixOS/nixpkgs?ref=nixos-unstable";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
nix-filter.url = "github:numtide/nix-filter";
|
||||
nixpkgs.url = "github:NixOS/nixpkgs?ref=nixos-unstable";
|
||||
rocksdb = { url = "github:facebook/rocksdb?ref=v9.1.1"; flake = false; };
|
||||
flake-compat = {
|
||||
url = "github:edolstra/flake-compat";
|
||||
flake = false;
|
||||
};
|
||||
|
||||
fenix = {
|
||||
url = "github:nix-community/fenix";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
crane = {
|
||||
# Pin latest crane that's not affected by the following bugs:
|
||||
#
|
||||
# * <https://github.com/ipetkov/crane/issues/527#issuecomment-1978079140>
|
||||
# * <https://github.com/toml-rs/toml/issues/691>
|
||||
# * <https://github.com/toml-rs/toml/issues/267>
|
||||
url = "github:ipetkov/crane?rev=2c653e4478476a52c6aa3ac0495e4dea7449ea0e";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
attic.url = "github:zhaofengli/attic?ref=main";
|
||||
};
|
||||
|
||||
outputs = inputs:
|
||||
inputs.flake-utils.lib.eachDefaultSystem (system:
|
||||
outputs =
|
||||
{ self
|
||||
, nixpkgs
|
||||
, flake-utils
|
||||
, nix-filter
|
||||
|
||||
, fenix
|
||||
, crane
|
||||
, ...
|
||||
}: flake-utils.lib.eachDefaultSystem (system:
|
||||
let
|
||||
pkgsHost = inputs.nixpkgs.legacyPackages.${system};
|
||||
pkgsHost = nixpkgs.legacyPackages.${system};
|
||||
allocator = null;
|
||||
|
||||
rocksdb' = pkgs:
|
||||
let
|
||||
version = "9.0.0";
|
||||
in
|
||||
(pkgs.rocksdb.overrideAttrs (old: {
|
||||
inherit version;
|
||||
src = pkgs.fetchFromGitHub {
|
||||
owner = "girlbossceo";
|
||||
repo = "rocksdb";
|
||||
rev = "449768a833b79c267c584b5ab1d50e73db6faf9d";
|
||||
hash = "sha256-MjmGfAlZ5WC2+hFH6nEUprqBjO8xiTQh2HJIqQ5mIg8=";
|
||||
};
|
||||
}));
|
||||
|
||||
# Nix-accessible `Cargo.toml`
|
||||
cargoToml = builtins.fromTOML (builtins.readFile ./Cargo.toml);
|
||||
|
||||
# The Rust toolchain to use
|
||||
toolchain = inputs.fenix.packages.${system}.fromToolchainFile {
|
||||
toolchain = fenix.packages.${system}.fromToolchainFile {
|
||||
file = ./rust-toolchain.toml;
|
||||
|
||||
# See also `rust-toolchain.toml`
|
||||
sha256 = "sha256-SXRtAuO4IqNOQq+nLbrsDFbVk+3aVA8NNpSZsKlVH/8=";
|
||||
};
|
||||
|
||||
scope = pkgs: pkgs.lib.makeScope pkgs.newScope (self: {
|
||||
book = self.callPackage ./nix/pkgs/book {};
|
||||
complement = self.callPackage ./nix/pkgs/complement {};
|
||||
craneLib = ((inputs.crane.mkLib pkgs).overrideToolchain toolchain);
|
||||
inherit inputs;
|
||||
main = self.callPackage ./nix/pkgs/main {};
|
||||
oci-image = self.callPackage ./nix/pkgs/oci-image {};
|
||||
rocksdb = pkgs.rocksdb.overrideAttrs (old: {
|
||||
src = inputs.rocksdb;
|
||||
version = pkgs.lib.removePrefix
|
||||
"v"
|
||||
(builtins.fromJSON (builtins.readFile ./flake.lock))
|
||||
.nodes.rocksdb.original.ref;
|
||||
});
|
||||
});
|
||||
builder = pkgs:
|
||||
((crane.mkLib pkgs).overrideToolchain toolchain).buildPackage;
|
||||
|
||||
scopeHost = (scope pkgsHost);
|
||||
nativeBuildInputs = pkgs: [
|
||||
# bindgen needs the build platform's libclang. Apparently due to
|
||||
# "splicing weirdness", pkgs.rustPlatform.bindgenHook on its own doesn't
|
||||
# quite do the right thing here.
|
||||
pkgs.pkgsBuildHost.rustPlatform.bindgenHook
|
||||
];
|
||||
|
||||
env = pkgs: {
|
||||
CONDUIT_VERSION_EXTRA = self.shortRev or self.dirtyShortRev;
|
||||
ROCKSDB_INCLUDE_DIR = "${rocksdb' pkgs}/include";
|
||||
ROCKSDB_LIB_DIR = "${rocksdb' pkgs}/lib";
|
||||
}
|
||||
// pkgs.lib.optionalAttrs pkgs.stdenv.hostPlatform.isStatic {
|
||||
ROCKSDB_STATIC = "";
|
||||
}
|
||||
// {
|
||||
CARGO_BUILD_RUSTFLAGS = let inherit (pkgs) lib stdenv; in
|
||||
lib.concatStringsSep " " ([ ]
|
||||
++ lib.optionals
|
||||
# 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
|
||||
# leaving PIE enabled.
|
||||
stdenv.hostPlatform.isStatic
|
||||
[ "-C" "relocation-model=static" ]
|
||||
++ lib.optionals
|
||||
(stdenv.buildPlatform.config != stdenv.hostPlatform.config)
|
||||
[ "-l" "c" ]
|
||||
++ lib.optionals
|
||||
# This check has to match the one [here][0]. We only need to set
|
||||
# these flags when using a different linker. Don't ask me why,
|
||||
# though, because I don't know. All I know is it breaks otherwise.
|
||||
#
|
||||
# [0]: https://github.com/NixOS/nixpkgs/blob/5cdb38bb16c6d0a38779db14fcc766bc1b2394d6/pkgs/build-support/rust/lib/default.nix#L37-L40
|
||||
(
|
||||
# Nixpkgs doesn't check for x86_64 here but we do, because I
|
||||
# observed a failure building statically for x86_64 without
|
||||
# including it here. Linkers are weird.
|
||||
(stdenv.hostPlatform.isAarch64 || stdenv.hostPlatform.isx86_64)
|
||||
&& stdenv.hostPlatform.isStatic
|
||||
&& !stdenv.isDarwin
|
||||
&& !stdenv.cc.bintools.isLLVM
|
||||
)
|
||||
[
|
||||
"-l"
|
||||
"stdc++"
|
||||
"-L"
|
||||
"${stdenv.cc.cc.lib}/${stdenv.hostPlatform.config}/lib"
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
# What follows is stolen from [here][0]. Its purpose is to properly
|
||||
# configure compilers and linkers for various stages of the build, and
|
||||
# even covers the case of build scripts that need native code compiled and
|
||||
# run on the build platform (I think).
|
||||
#
|
||||
# [0]: https://github.com/NixOS/nixpkgs/blob/5cdb38bb16c6d0a38779db14fcc766bc1b2394d6/pkgs/build-support/rust/lib/default.nix#L57-L80
|
||||
// (
|
||||
let
|
||||
inherit (pkgs.rust.lib) envVars;
|
||||
in
|
||||
pkgs.lib.optionalAttrs
|
||||
(pkgs.stdenv.targetPlatform.rust.rustcTarget
|
||||
!= pkgs.stdenv.hostPlatform.rust.rustcTarget)
|
||||
(
|
||||
let
|
||||
inherit (pkgs.stdenv.targetPlatform.rust) cargoEnvVarTarget;
|
||||
in
|
||||
{
|
||||
"CC_${cargoEnvVarTarget}" = envVars.ccForTarget;
|
||||
"CXX_${cargoEnvVarTarget}" = envVars.cxxForTarget;
|
||||
"CARGO_TARGET_${cargoEnvVarTarget}_LINKER" =
|
||||
envVars.linkerForTarget;
|
||||
}
|
||||
)
|
||||
// (
|
||||
let
|
||||
inherit (pkgs.stdenv.hostPlatform.rust) cargoEnvVarTarget rustcTarget;
|
||||
in
|
||||
{
|
||||
"CC_${cargoEnvVarTarget}" = envVars.ccForHost;
|
||||
"CXX_${cargoEnvVarTarget}" = envVars.cxxForHost;
|
||||
"CARGO_TARGET_${cargoEnvVarTarget}_LINKER" = envVars.linkerForHost;
|
||||
CARGO_BUILD_TARGET = rustcTarget;
|
||||
}
|
||||
)
|
||||
// (
|
||||
let
|
||||
inherit (pkgs.stdenv.buildPlatform.rust) cargoEnvVarTarget;
|
||||
in
|
||||
{
|
||||
"CC_${cargoEnvVarTarget}" = envVars.ccForBuild;
|
||||
"CXX_${cargoEnvVarTarget}" = envVars.cxxForBuild;
|
||||
"CARGO_TARGET_${cargoEnvVarTarget}_LINKER" = envVars.linkerForBuild;
|
||||
HOST_CC = "${pkgs.pkgsBuildHost.stdenv.cc}/bin/cc";
|
||||
HOST_CXX = "${pkgs.pkgsBuildHost.stdenv.cc}/bin/c++";
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
mkPackage = pkgs: allocator: builder pkgs {
|
||||
src = nix-filter {
|
||||
root = ./.;
|
||||
include = [
|
||||
"src"
|
||||
"Cargo.toml"
|
||||
"Cargo.lock"
|
||||
];
|
||||
};
|
||||
|
||||
buildFeatures = [ ]
|
||||
++ (if allocator == "jemalloc" then [ "jemalloc" ] else [ ])
|
||||
++ (if allocator == "hmalloc" then [ "hardened_malloc" ] else [ ])
|
||||
;
|
||||
|
||||
rocksdb' = (if allocator == "jemalloc" then (pkgs.rocksdb.override { enableJemalloc = true; }) else (rocksdb' pkgs));
|
||||
|
||||
# This is redundant with CI
|
||||
doCheck = false;
|
||||
|
||||
env = env pkgs;
|
||||
nativeBuildInputs = nativeBuildInputs pkgs;
|
||||
|
||||
meta.mainProgram = cargoToml.package.name;
|
||||
};
|
||||
|
||||
mkOciImage = pkgs: package: allocator:
|
||||
pkgs.dockerTools.buildLayeredImage {
|
||||
name = package.pname;
|
||||
tag = "main";
|
||||
# Debian makes builds reproducible through using the HEAD commit's date
|
||||
created = "@${toString self.lastModified}";
|
||||
contents = [
|
||||
pkgs.dockerTools.caCertificates
|
||||
];
|
||||
config = {
|
||||
# Use the `tini` init system so that signals (e.g. ctrl+c/SIGINT)
|
||||
# are handled as expected
|
||||
Entrypoint = [
|
||||
"${pkgs.lib.getExe' pkgs.tini "tini"}"
|
||||
"--"
|
||||
];
|
||||
Cmd = [
|
||||
"${pkgs.lib.getExe package}"
|
||||
];
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
packages = {
|
||||
default = scopeHost.main;
|
||||
jemalloc = scopeHost.main.override { features = ["jemalloc"]; };
|
||||
hmalloc = scopeHost.main.override { features = ["hardened_malloc"]; };
|
||||
default = mkPackage pkgsHost null;
|
||||
jemalloc = mkPackage pkgsHost "jemalloc";
|
||||
hmalloc = mkPackage pkgsHost "hmalloc";
|
||||
oci-image = mkOciImage pkgsHost self.packages.${system}.default null;
|
||||
oci-image-jemalloc = mkOciImage pkgsHost self.packages.${system}.default "jemalloc";
|
||||
oci-image-hmalloc = mkOciImage pkgsHost self.packages.${system}.default "hmalloc";
|
||||
|
||||
oci-image = scopeHost.oci-image;
|
||||
oci-image-jemalloc = scopeHost.oci-image.override {
|
||||
main = scopeHost.main.override {
|
||||
features = ["jemalloc"];
|
||||
book =
|
||||
let
|
||||
package = self.packages.${system}.default;
|
||||
in
|
||||
pkgsHost.stdenv.mkDerivation {
|
||||
pname = "${package.pname}-book";
|
||||
version = package.version;
|
||||
|
||||
src = nix-filter {
|
||||
root = ./.;
|
||||
include = [
|
||||
"book.toml"
|
||||
"conduwuit-example.toml"
|
||||
"README.md"
|
||||
"debian/README.md"
|
||||
"docs"
|
||||
];
|
||||
};
|
||||
|
||||
nativeBuildInputs = (with pkgsHost; [
|
||||
mdbook
|
||||
]);
|
||||
|
||||
buildPhase = ''
|
||||
mdbook build
|
||||
mv public $out
|
||||
'';
|
||||
};
|
||||
};
|
||||
oci-image-hmalloc = scopeHost.oci-image.override {
|
||||
main = scopeHost.main.override {
|
||||
features = ["hardened_malloc"];
|
||||
};
|
||||
};
|
||||
|
||||
book = scopeHost.book;
|
||||
|
||||
complement = scopeHost.complement;
|
||||
}
|
||||
//
|
||||
builtins.listToAttrs
|
||||
@@ -72,96 +261,90 @@
|
||||
let
|
||||
binaryName = "static-${crossSystem}";
|
||||
pkgsCrossStatic =
|
||||
(import inputs.nixpkgs {
|
||||
(import nixpkgs {
|
||||
inherit system;
|
||||
crossSystem = {
|
||||
config = crossSystem;
|
||||
};
|
||||
}).pkgsStatic;
|
||||
scopeCrossStatic = scope pkgsCrossStatic;
|
||||
in
|
||||
[
|
||||
# An output for a statically-linked binary
|
||||
{
|
||||
name = binaryName;
|
||||
value = scopeCrossStatic.main;
|
||||
value = mkPackage pkgsCrossStatic null;
|
||||
}
|
||||
|
||||
# An output for a statically-linked binary with jemalloc
|
||||
{
|
||||
name = "${binaryName}-jemalloc";
|
||||
value = scopeCrossStatic.main.override {
|
||||
features = ["jemalloc"];
|
||||
};
|
||||
value = mkPackage pkgsCrossStatic "jemalloc";
|
||||
}
|
||||
|
||||
# An output for a statically-linked binary with hardened_malloc
|
||||
{
|
||||
name = "${binaryName}-hmalloc";
|
||||
value = scopeCrossStatic.main.override {
|
||||
features = ["hardened_malloc"];
|
||||
};
|
||||
value = mkPackage pkgsCrossStatic "hmalloc";
|
||||
}
|
||||
|
||||
# An output for an OCI image based on that binary
|
||||
{
|
||||
name = "oci-image-${crossSystem}";
|
||||
value = scopeCrossStatic.oci-image;
|
||||
value = mkOciImage
|
||||
pkgsCrossStatic
|
||||
self.packages.${system}.${binaryName}
|
||||
null;
|
||||
}
|
||||
|
||||
# An output for an OCI image based on that binary with jemalloc
|
||||
{
|
||||
name = "oci-image-${crossSystem}-jemalloc";
|
||||
value = scopeCrossStatic.oci-image.override {
|
||||
main = scopeCrossStatic.main.override {
|
||||
features = ["jemalloc"];
|
||||
};
|
||||
};
|
||||
value = mkOciImage
|
||||
pkgsCrossStatic
|
||||
self.packages.${system}.${binaryName}
|
||||
"jemalloc";
|
||||
}
|
||||
|
||||
# An output for an OCI image based on that binary with hardened_malloc
|
||||
{
|
||||
name = "oci-image-${crossSystem}-hmalloc";
|
||||
value = scopeCrossStatic.oci-image.override {
|
||||
main = scopeCrossStatic.main.override {
|
||||
features = ["hardened_malloc"];
|
||||
};
|
||||
};
|
||||
value = mkOciImage
|
||||
pkgsCrossStatic
|
||||
self.packages.${system}.${binaryName}
|
||||
"hmalloc";
|
||||
}
|
||||
]
|
||||
)
|
||||
[
|
||||
"x86_64-unknown-linux-musl"
|
||||
"x86_64-unknown-linux-musl-jemalloc"
|
||||
"x86_64-unknown-linux-musl-hmalloc"
|
||||
"aarch64-unknown-linux-musl"
|
||||
"aarch64-unknown-linux-musl-jemalloc"
|
||||
"aarch64-unknown-linux-musl-hmalloc"
|
||||
]
|
||||
)
|
||||
);
|
||||
|
||||
devShells.default = pkgsHost.mkShell {
|
||||
env = scopeHost.main.env // {
|
||||
env = env pkgsHost // {
|
||||
# Rust Analyzer needs to be able to find the path to default crate
|
||||
# sources, and it can read this environment variable to do so. The
|
||||
# `rust-src` component is required in order for this to work.
|
||||
RUST_SRC_PATH = "${toolchain}/lib/rustlib/src/rust/library";
|
||||
|
||||
# Convenient way to access a pinned version of Complement's source
|
||||
# code.
|
||||
COMPLEMENT_SRC = inputs.complement.outPath;
|
||||
};
|
||||
|
||||
# Development tools
|
||||
packages = [
|
||||
nativeBuildInputs = nativeBuildInputs pkgsHost ++ [
|
||||
# Always use nightly rustfmt because most of its options are unstable
|
||||
#
|
||||
# This needs to come before `toolchain` in this list, otherwise
|
||||
# `$PATH` will have stable rustfmt instead.
|
||||
inputs.fenix.packages.${system}.latest.rustfmt
|
||||
fenix.packages.${system}.latest.rustfmt
|
||||
|
||||
toolchain
|
||||
]
|
||||
++ (with pkgsHost; [
|
||||
] ++ (with pkgsHost; [
|
||||
engage
|
||||
cargo-audit
|
||||
|
||||
# Needed for producing Debian packages
|
||||
cargo-deb
|
||||
@@ -178,9 +361,7 @@
|
||||
|
||||
# Useful for editing the book locally
|
||||
mdbook
|
||||
])
|
||||
++
|
||||
scopeHost.main.nativeBuildInputs;
|
||||
]);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
{ inputs
|
||||
|
||||
# Dependencies
|
||||
, main
|
||||
, mdbook
|
||||
, stdenv
|
||||
}:
|
||||
|
||||
stdenv.mkDerivation {
|
||||
inherit (main) pname version;
|
||||
|
||||
src = inputs.nix-filter {
|
||||
root = inputs.self;
|
||||
include = [
|
||||
"book.toml"
|
||||
"conduwuit-example.toml"
|
||||
"README.md"
|
||||
"debian/README.md"
|
||||
"docs"
|
||||
];
|
||||
};
|
||||
|
||||
nativeBuildInputs = [
|
||||
mdbook
|
||||
];
|
||||
|
||||
buildPhase = ''
|
||||
mdbook build
|
||||
mv public $out
|
||||
'';
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
[global]
|
||||
address = "0.0.0.0"
|
||||
allow_device_name_federation = true
|
||||
allow_guest_registration = true
|
||||
allow_public_room_directory_over_federation = true
|
||||
allow_public_room_directory_without_auth = true
|
||||
allow_registration = true
|
||||
allow_unstable_room_versions = true
|
||||
database_backend = "rocksdb"
|
||||
database_path = "/database"
|
||||
log = "trace"
|
||||
port = [8008, 8448]
|
||||
trusted_servers = []
|
||||
yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse = true
|
||||
|
||||
[global.tls]
|
||||
certs = "/certificate.crt"
|
||||
dual_protocol = true
|
||||
key = "/private_key.key"
|
||||
@@ -1,92 +0,0 @@
|
||||
# Dependencies
|
||||
{ bashInteractive
|
||||
, buildEnv
|
||||
, coreutils
|
||||
, dockerTools
|
||||
, gawk
|
||||
, lib
|
||||
, main
|
||||
, openssl
|
||||
, stdenv
|
||||
, tini
|
||||
, writeShellScriptBin
|
||||
}:
|
||||
|
||||
let
|
||||
main' = main.override {
|
||||
profile = "dev";
|
||||
features = ["axum_dual_protocol"];
|
||||
};
|
||||
|
||||
start = writeShellScriptBin "start" ''
|
||||
set -euxo pipefail
|
||||
|
||||
${lib.getExe openssl} genrsa -out private_key.key 2048
|
||||
${lib.getExe openssl} req \
|
||||
-new \
|
||||
-sha256 \
|
||||
-key private_key.key \
|
||||
-subj "/C=US/ST=CA/O=MyOrg, Inc./CN=$SERVER_NAME" \
|
||||
-out signing_request.csr
|
||||
cp ${./v3.ext} v3.ext
|
||||
echo "DNS.1 = $SERVER_NAME" >> v3.ext
|
||||
echo "IP.1 = $(${lib.getExe gawk} 'END{print $1}' /etc/hosts)" \
|
||||
>> v3.ext
|
||||
${lib.getExe openssl} x509 \
|
||||
-req \
|
||||
-extfile v3.ext \
|
||||
-in signing_request.csr \
|
||||
-CA /complement/ca/ca.crt \
|
||||
-CAkey /complement/ca/ca.key \
|
||||
-CAcreateserial \
|
||||
-out certificate.crt \
|
||||
-days 1 \
|
||||
-sha256
|
||||
|
||||
${lib.getExe' coreutils "env"} \
|
||||
CONDUIT_SERVER_NAME="$SERVER_NAME" \
|
||||
CONDUIT_WELL_KNOWN_SERVER="$SERVER_NAME:8448" \
|
||||
CONDUIT_WELL_KNOWN_SERVER="$SERVER_NAME:8008" \
|
||||
${lib.getExe main'}
|
||||
'';
|
||||
in
|
||||
|
||||
dockerTools.buildImage {
|
||||
name = "complement-${main.pname}";
|
||||
tag = "dev";
|
||||
|
||||
copyToRoot = buildEnv {
|
||||
name = "root";
|
||||
pathsToLink = [
|
||||
"/bin"
|
||||
];
|
||||
paths = [
|
||||
bashInteractive
|
||||
coreutils
|
||||
main'
|
||||
start
|
||||
];
|
||||
};
|
||||
|
||||
config = {
|
||||
Cmd = [
|
||||
"${lib.getExe start}"
|
||||
];
|
||||
|
||||
Entrypoint = if !stdenv.isDarwin
|
||||
# Use the `tini` init system so that signals (e.g. ctrl+c/SIGINT)
|
||||
# are handled as expected
|
||||
then [ "${lib.getExe' tini "tini"}" "--" ]
|
||||
else [];
|
||||
|
||||
Env = [
|
||||
"SSL_CERT_FILE=/complement/ca/ca.crt"
|
||||
"CONDUIT_CONFIG=${./config.toml}"
|
||||
];
|
||||
|
||||
ExposedPorts = {
|
||||
"8008/tcp" = {};
|
||||
"8448/tcp" = {};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
authorityKeyIdentifier=keyid,issuer
|
||||
basicConstraints=CA:FALSE
|
||||
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
|
||||
subjectAltName = @alt_names
|
||||
|
||||
[alt_names]
|
||||
@@ -1,100 +0,0 @@
|
||||
{ lib
|
||||
, pkgsBuildHost
|
||||
, rust
|
||||
, stdenv
|
||||
}:
|
||||
|
||||
lib.optionalAttrs stdenv.hostPlatform.isStatic {
|
||||
ROCKSDB_STATIC = "";
|
||||
}
|
||||
//
|
||||
{
|
||||
CARGO_BUILD_RUSTFLAGS =
|
||||
lib.concatStringsSep
|
||||
" "
|
||||
([]
|
||||
# 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
|
||||
# leaving PIE enabled.
|
||||
++ lib.optionals
|
||||
stdenv.hostPlatform.isStatic
|
||||
[ "-C" "relocation-model=static" ]
|
||||
++ lib.optionals
|
||||
(stdenv.buildPlatform.config != stdenv.hostPlatform.config)
|
||||
[ "-l" "c" ]
|
||||
++ lib.optionals
|
||||
# This check has to match the one [here][0]. We only need to set
|
||||
# these flags when using a different linker. Don't ask me why,
|
||||
# though, because I don't know. All I know is it breaks otherwise.
|
||||
#
|
||||
# [0]: https://github.com/NixOS/nixpkgs/blob/5cdb38bb16c6d0a38779db14fcc766bc1b2394d6/pkgs/build-support/rust/lib/default.nix#L37-L40
|
||||
(
|
||||
# Nixpkgs doesn't check for x86_64 here but we do, because I
|
||||
# observed a failure building statically for x86_64 without
|
||||
# including it here. Linkers are weird.
|
||||
(stdenv.hostPlatform.isAarch64 || stdenv.hostPlatform.isx86_64)
|
||||
&& stdenv.hostPlatform.isStatic
|
||||
&& !stdenv.isDarwin
|
||||
&& !stdenv.cc.bintools.isLLVM
|
||||
)
|
||||
[
|
||||
"-l"
|
||||
"stdc++"
|
||||
"-L"
|
||||
"${stdenv.cc.cc.lib}/${stdenv.hostPlatform.config}/lib"
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
# What follows is stolen from [here][0]. Its purpose is to properly
|
||||
# configure compilers and linkers for various stages of the build, and
|
||||
# even covers the case of build scripts that need native code compiled and
|
||||
# run on the build platform (I think).
|
||||
#
|
||||
# [0]: https://github.com/NixOS/nixpkgs/blob/5cdb38bb16c6d0a38779db14fcc766bc1b2394d6/pkgs/build-support/rust/lib/default.nix#L57-L80
|
||||
//
|
||||
(
|
||||
let
|
||||
inherit (rust.lib) envVars;
|
||||
in
|
||||
lib.optionalAttrs
|
||||
(stdenv.targetPlatform.rust.rustcTarget
|
||||
!= stdenv.hostPlatform.rust.rustcTarget)
|
||||
(
|
||||
let
|
||||
inherit (stdenv.targetPlatform.rust) cargoEnvVarTarget;
|
||||
in
|
||||
{
|
||||
"CC_${cargoEnvVarTarget}" = envVars.ccForTarget;
|
||||
"CXX_${cargoEnvVarTarget}" = envVars.cxxForTarget;
|
||||
"CARGO_TARGET_${cargoEnvVarTarget}_LINKER" =
|
||||
envVars.linkerForTarget;
|
||||
}
|
||||
)
|
||||
//
|
||||
(
|
||||
let
|
||||
inherit (stdenv.hostPlatform.rust) cargoEnvVarTarget rustcTarget;
|
||||
in
|
||||
{
|
||||
"CC_${cargoEnvVarTarget}" = envVars.ccForHost;
|
||||
"CXX_${cargoEnvVarTarget}" = envVars.cxxForHost;
|
||||
"CARGO_TARGET_${cargoEnvVarTarget}_LINKER" = envVars.linkerForHost;
|
||||
CARGO_BUILD_TARGET = rustcTarget;
|
||||
}
|
||||
)
|
||||
//
|
||||
(
|
||||
let
|
||||
inherit (stdenv.buildPlatform.rust) cargoEnvVarTarget;
|
||||
in
|
||||
{
|
||||
"CC_${cargoEnvVarTarget}" = envVars.ccForBuild;
|
||||
"CXX_${cargoEnvVarTarget}" = envVars.cxxForBuild;
|
||||
"CARGO_TARGET_${cargoEnvVarTarget}_LINKER" = envVars.linkerForBuild;
|
||||
HOST_CC = "${pkgsBuildHost.stdenv.cc}/bin/cc";
|
||||
HOST_CXX = "${pkgsBuildHost.stdenv.cc}/bin/c++";
|
||||
}
|
||||
)
|
||||
)
|
||||
@@ -1,75 +0,0 @@
|
||||
{ inputs
|
||||
|
||||
# Dependencies
|
||||
, craneLib
|
||||
, lib
|
||||
, libiconv
|
||||
, pkgsBuildHost
|
||||
, rocksdb
|
||||
, rust
|
||||
, stdenv
|
||||
|
||||
# Options
|
||||
, default_features ? true
|
||||
, features ? []
|
||||
, profile ? "release"
|
||||
}:
|
||||
|
||||
craneLib.buildPackage rec {
|
||||
src = inputs.nix-filter {
|
||||
root = inputs.self;
|
||||
include = [
|
||||
"src"
|
||||
"Cargo.toml"
|
||||
"Cargo.lock"
|
||||
];
|
||||
};
|
||||
|
||||
# This is redundant with CI
|
||||
doCheck = false;
|
||||
|
||||
env =
|
||||
let
|
||||
rocksdb' = rocksdb.override {
|
||||
enableJemalloc = builtins.elem "jemalloc" features;
|
||||
};
|
||||
in
|
||||
{
|
||||
CARGO_PROFILE = profile;
|
||||
CONDUIT_VERSION_EXTRA = inputs.self.shortRev or inputs.self.dirtyShortRev;
|
||||
ROCKSDB_INCLUDE_DIR = "${rocksdb'}/include";
|
||||
ROCKSDB_LIB_DIR = "${rocksdb'}/lib";
|
||||
}
|
||||
//
|
||||
(import ./cross-compilation-env.nix {
|
||||
inherit
|
||||
lib
|
||||
pkgsBuildHost
|
||||
rust
|
||||
stdenv;
|
||||
});
|
||||
|
||||
nativeBuildInputs = [
|
||||
# bindgen needs the build platform's libclang. Apparently due to "splicing
|
||||
# weirdness", pkgs.rustPlatform.bindgenHook on its own doesn't quite do the
|
||||
# right thing here.
|
||||
pkgsBuildHost.rustPlatform.bindgenHook
|
||||
]
|
||||
++ lib.optionals stdenv.isDarwin [ libiconv ];
|
||||
|
||||
cargoExtraArgs = ""
|
||||
+ lib.optionalString
|
||||
(!default_features)
|
||||
"--no-default-features "
|
||||
+ lib.optionalString
|
||||
(features != [])
|
||||
"--features " + (builtins.concatStringsSep "," features);
|
||||
|
||||
meta.mainProgram = (craneLib.crateNameFromCargoToml {
|
||||
cargoToml = "${inputs.self}/Cargo.toml";
|
||||
}).pname;
|
||||
|
||||
passthru = {
|
||||
inherit env;
|
||||
};
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
{ inputs
|
||||
|
||||
# Dependencies
|
||||
, dockerTools
|
||||
, lib
|
||||
, main
|
||||
, stdenv
|
||||
, tini
|
||||
}:
|
||||
|
||||
dockerTools.buildLayeredImage {
|
||||
name = main.pname;
|
||||
tag = "main";
|
||||
created = "@${toString inputs.self.lastModified}";
|
||||
contents = [
|
||||
dockerTools.caCertificates
|
||||
];
|
||||
config = {
|
||||
Entrypoint = if !stdenv.isDarwin
|
||||
# Use the `tini` init system so that signals (e.g. ctrl+c/SIGINT)
|
||||
# are handled as expected
|
||||
then [ "${lib.getExe' tini "tini"}" "--" ]
|
||||
else [];
|
||||
Cmd = [
|
||||
"${lib.getExe main}"
|
||||
];
|
||||
};
|
||||
}
|
||||
+2
-1
@@ -11,5 +11,6 @@
|
||||
},
|
||||
"nix": {
|
||||
"enabled": true
|
||||
}
|
||||
},
|
||||
"ignoreDeps": ["tower-http", "axum-server", "hyper", "axum", "http"]
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ const RANDOM_USER_ID_LENGTH: usize = 10;
|
||||
///
|
||||
/// Note: This will not reserve the username, so the username might become
|
||||
/// invalid when trying to register
|
||||
pub(crate) async fn get_register_available_route(
|
||||
pub async fn get_register_available_route(
|
||||
body: Ruma<get_username_availability::v3::Request>,
|
||||
) -> Result<get_username_availability::v3::Response> {
|
||||
// Validate user id
|
||||
@@ -82,7 +82,7 @@ pub(crate) async fn get_register_available_route(
|
||||
/// - If `inhibit_login` is false: Creates a device and returns device id and
|
||||
/// access_token
|
||||
#[allow(clippy::doc_markdown)]
|
||||
pub(crate) async fn register_route(body: Ruma<register::v3::Request>) -> Result<register::v3::Response> {
|
||||
pub async fn register_route(body: Ruma<register::v3::Request>) -> Result<register::v3::Response> {
|
||||
if !services().globals.allow_registration() && body.appservice_info.is_none() {
|
||||
info!(
|
||||
"Registration disabled and request not from known appservice, rejecting registration attempt for username \
|
||||
@@ -406,9 +406,7 @@ pub(crate) async fn register_route(body: Ruma<register::v3::Request>) -> Result<
|
||||
/// last seen ts)
|
||||
/// - Forgets to-device events
|
||||
/// - Triggers device list updates
|
||||
pub(crate) async fn change_password_route(
|
||||
body: Ruma<change_password::v3::Request>,
|
||||
) -> Result<change_password::v3::Response> {
|
||||
pub async fn change_password_route(body: Ruma<change_password::v3::Request>) -> Result<change_password::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");
|
||||
|
||||
@@ -471,7 +469,7 @@ 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(body: Ruma<whoami::v3::Request>) -> Result<whoami::v3::Response> {
|
||||
pub 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();
|
||||
|
||||
@@ -493,7 +491,7 @@ pub(crate) async fn whoami_route(body: Ruma<whoami::v3::Request>) -> Result<whoa
|
||||
/// - Forgets all to-device events
|
||||
/// - Triggers device list updates
|
||||
/// - Removes ability to log in again
|
||||
pub(crate) async fn deactivate_route(body: Ruma<deactivate::v3::Request>) -> Result<deactivate::v3::Response> {
|
||||
pub async fn deactivate_route(body: Ruma<deactivate::v3::Request>) -> Result<deactivate::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");
|
||||
|
||||
@@ -548,7 +546,7 @@ pub(crate) async fn deactivate_route(body: Ruma<deactivate::v3::Request>) -> Res
|
||||
/// Get a list of third party identifiers associated with this account.
|
||||
///
|
||||
/// - Currently always returns empty list
|
||||
pub(crate) async fn third_party_route(body: Ruma<get_3pids::v3::Request>) -> Result<get_3pids::v3::Response> {
|
||||
pub async fn third_party_route(body: Ruma<get_3pids::v3::Request>) -> Result<get_3pids::v3::Response> {
|
||||
let _sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
Ok(get_3pids::v3::Response::new(Vec::new()))
|
||||
@@ -561,7 +559,7 @@ pub(crate) async fn third_party_route(body: Ruma<get_3pids::v3::Request>) -> Res
|
||||
///
|
||||
/// - 403 signals that The homeserver does not allow the third party identifier
|
||||
/// as a contact option.
|
||||
pub(crate) async fn request_3pid_management_token_via_email_route(
|
||||
pub async fn request_3pid_management_token_via_email_route(
|
||||
_body: Ruma<request_3pid_management_token_via_email::v3::Request>,
|
||||
) -> Result<request_3pid_management_token_via_email::v3::Response> {
|
||||
Err(Error::BadRequest(
|
||||
@@ -577,7 +575,7 @@ pub(crate) async fn request_3pid_management_token_via_email_route(
|
||||
///
|
||||
/// - 403 signals that The homeserver does not allow the third party identifier
|
||||
/// as a contact option.
|
||||
pub(crate) async fn request_3pid_management_token_via_msisdn_route(
|
||||
pub async fn request_3pid_management_token_via_msisdn_route(
|
||||
_body: Ruma<request_3pid_management_token_via_msisdn::v3::Request>,
|
||||
) -> Result<request_3pid_management_token_via_msisdn::v3::Response> {
|
||||
Err(Error::BadRequest(
|
||||
|
||||
@@ -10,14 +10,13 @@ use ruma::{
|
||||
},
|
||||
OwnedRoomAliasId, OwnedServerName,
|
||||
};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::{debug_info, debug_warn, services, Error, Result, Ruma};
|
||||
use crate::{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(body: Ruma<create_alias::v3::Request>) -> Result<create_alias::v3::Response> {
|
||||
pub async fn create_alias_route(body: Ruma<create_alias::v3::Request>) -> Result<create_alias::v3::Response> {
|
||||
if body.room_alias.server_name() != services().globals.server_name() {
|
||||
return Err(Error::BadRequest(ErrorKind::InvalidParam, "Alias is from another server."));
|
||||
}
|
||||
@@ -72,7 +71,7 @@ pub(crate) async fn create_alias_route(body: Ruma<create_alias::v3::Request>) ->
|
||||
///
|
||||
/// - TODO: additional access control checks
|
||||
/// - TODO: Update canonical alias event
|
||||
pub(crate) async fn delete_alias_route(body: Ruma<delete_alias::v3::Request>) -> Result<delete_alias::v3::Response> {
|
||||
pub async fn delete_alias_route(body: Ruma<delete_alias::v3::Request>) -> Result<delete_alias::v3::Response> {
|
||||
if body.room_alias.server_name() != services().globals.server_name() {
|
||||
return Err(Error::BadRequest(ErrorKind::InvalidParam, "Alias is from another server."));
|
||||
}
|
||||
@@ -118,21 +117,13 @@ pub(crate) async fn delete_alias_route(body: Ruma<delete_alias::v3::Request>) ->
|
||||
/// # `GET /_matrix/client/v3/directory/room/{roomAlias}`
|
||||
///
|
||||
/// Resolve an alias locally or over federation.
|
||||
pub(crate) async fn get_alias_route(body: Ruma<get_alias::v3::Request>) -> Result<get_alias::v3::Response> {
|
||||
get_alias_helper(body.body.room_alias, None).await
|
||||
pub async fn get_alias_route(body: Ruma<get_alias::v3::Request>) -> Result<get_alias::v3::Response> {
|
||||
get_alias_helper(body.body.room_alias).await
|
||||
}
|
||||
|
||||
pub(crate) async fn get_alias_helper(
|
||||
room_alias: OwnedRoomAliasId, servers: Option<Vec<OwnedServerName>>,
|
||||
) -> Result<get_alias::v3::Response> {
|
||||
debug!("get_alias_helper servers: {servers:?}");
|
||||
if room_alias.server_name() != services().globals.server_name()
|
||||
&& (!servers
|
||||
.as_ref()
|
||||
.is_some_and(|servers| servers.contains(&services().globals.server_name().to_owned()))
|
||||
|| servers.as_ref().is_none())
|
||||
{
|
||||
let mut response = services()
|
||||
pub(crate) async fn get_alias_helper(room_alias: OwnedRoomAliasId) -> Result<get_alias::v3::Response> {
|
||||
if room_alias.server_name() != services().globals.server_name() {
|
||||
let response = services()
|
||||
.sending
|
||||
.send_federation_request(
|
||||
room_alias.server_name(),
|
||||
@@ -140,89 +131,47 @@ pub(crate) async fn get_alias_helper(
|
||||
room_alias: room_alias.clone(),
|
||||
},
|
||||
)
|
||||
.await;
|
||||
.await?;
|
||||
|
||||
debug_info!("room alias server_name get_alias_helper response: {response:?}");
|
||||
let room_id = response.room_id;
|
||||
|
||||
if let Err(ref e) = response {
|
||||
debug_info!(
|
||||
"Server {} of the original room alias failed to assist in resolving room alias: {e}",
|
||||
room_alias.server_name()
|
||||
);
|
||||
let mut servers = response.servers;
|
||||
|
||||
// since the room alias server_name responded, insert it into the list
|
||||
servers.push(room_alias.server_name().into());
|
||||
|
||||
// find active servers in room state cache to suggest
|
||||
servers.extend(
|
||||
services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_servers(&room_id)
|
||||
.filter_map(Result::ok),
|
||||
);
|
||||
|
||||
servers.sort_unstable();
|
||||
servers.dedup();
|
||||
|
||||
// shuffle list of servers randomly after sort and dedupe
|
||||
servers.shuffle(&mut rand::thread_rng());
|
||||
|
||||
// prefer the very first server to be ourselves if available, else prefer the
|
||||
// room alias server first
|
||||
if let Some(server_index) = servers
|
||||
.iter()
|
||||
.position(|server| server == services().globals.server_name())
|
||||
{
|
||||
servers.remove(server_index);
|
||||
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())
|
||||
{
|
||||
servers.remove(alias_server_index);
|
||||
servers.insert(0, room_alias.server_name().into());
|
||||
}
|
||||
|
||||
if response.as_ref().is_ok_and(|resp| resp.servers.is_empty()) || response.as_ref().is_err() {
|
||||
if let Some(servers) = servers {
|
||||
for server in servers {
|
||||
response = services()
|
||||
.sending
|
||||
.send_federation_request(
|
||||
&server,
|
||||
federation::query::get_room_information::v1::Request {
|
||||
room_alias: room_alias.clone(),
|
||||
},
|
||||
)
|
||||
.await;
|
||||
debug_info!("Got response from server {server} for room aliases: {response:?}");
|
||||
|
||||
if let Ok(ref response) = response {
|
||||
if !response.servers.is_empty() {
|
||||
break;
|
||||
}
|
||||
debug_warn!(
|
||||
"Server {server} responded with room aliases, but was empty? Response: {response:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(response) = response {
|
||||
let room_id = response.room_id;
|
||||
|
||||
let mut servers = response.servers;
|
||||
|
||||
// since the room alias server_name responded, insert it into the list
|
||||
servers.push(room_alias.server_name().into());
|
||||
|
||||
// find active servers in room state cache to suggest
|
||||
servers.extend(
|
||||
services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_servers(&room_id)
|
||||
.filter_map(Result::ok),
|
||||
);
|
||||
|
||||
servers.sort_unstable();
|
||||
servers.dedup();
|
||||
|
||||
// shuffle list of servers randomly after sort and dedupe
|
||||
servers.shuffle(&mut rand::thread_rng());
|
||||
|
||||
// prefer the very first server to be ourselves if available, else prefer the
|
||||
// room alias server first
|
||||
if let Some(server_index) = servers
|
||||
.iter()
|
||||
.position(|server| server == services().globals.server_name())
|
||||
{
|
||||
servers.remove(server_index);
|
||||
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())
|
||||
{
|
||||
servers.remove(alias_server_index);
|
||||
servers.insert(0, room_alias.server_name().into());
|
||||
}
|
||||
|
||||
return Ok(get_alias::v3::Response::new(room_id, servers));
|
||||
}
|
||||
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::Unknown,
|
||||
"No servers could assist in resolving the room alias",
|
||||
));
|
||||
return Ok(get_alias::v3::Response::new(room_id, servers));
|
||||
}
|
||||
|
||||
let mut room_id = None;
|
||||
|
||||
@@ -13,7 +13,7 @@ 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(
|
||||
pub async fn create_backup_version_route(
|
||||
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");
|
||||
@@ -30,7 +30,7 @@ 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(
|
||||
pub async fn update_backup_version_route(
|
||||
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");
|
||||
@@ -44,7 +44,7 @@ pub(crate) async fn update_backup_version_route(
|
||||
/// # `GET /_matrix/client/r0/room_keys/version`
|
||||
///
|
||||
/// Get information about the latest backup version.
|
||||
pub(crate) async fn get_latest_backup_info_route(
|
||||
pub async fn get_latest_backup_info_route(
|
||||
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");
|
||||
@@ -65,9 +65,7 @@ pub(crate) async fn get_latest_backup_info_route(
|
||||
/// # `GET /_matrix/client/r0/room_keys/version`
|
||||
///
|
||||
/// Get information about an existing backup.
|
||||
pub(crate) async fn get_backup_info_route(
|
||||
body: Ruma<get_backup_info::v3::Request>,
|
||||
) -> Result<get_backup_info::v3::Response> {
|
||||
pub async fn get_backup_info_route(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()
|
||||
.key_backups
|
||||
@@ -93,7 +91,7 @@ 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(
|
||||
pub async fn delete_backup_version_route(
|
||||
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");
|
||||
@@ -113,9 +111,7 @@ pub(crate) async fn delete_backup_version_route(
|
||||
/// allowed
|
||||
/// - 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(
|
||||
body: Ruma<add_backup_keys::v3::Request>,
|
||||
) -> Result<add_backup_keys::v3::Response> {
|
||||
pub async fn add_backup_keys_route(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)
|
||||
@@ -157,7 +153,7 @@ pub(crate) async fn add_backup_keys_route(
|
||||
/// allowed
|
||||
/// - 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(
|
||||
pub async fn add_backup_keys_for_room_route(
|
||||
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");
|
||||
@@ -199,7 +195,7 @@ pub(crate) async fn add_backup_keys_for_room_route(
|
||||
/// allowed
|
||||
/// - 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(
|
||||
pub async fn add_backup_keys_for_session_route(
|
||||
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");
|
||||
@@ -234,9 +230,7 @@ pub(crate) async fn add_backup_keys_for_session_route(
|
||||
/// # `GET /_matrix/client/r0/room_keys/keys`
|
||||
///
|
||||
/// Retrieves all keys from the backup.
|
||||
pub(crate) async fn get_backup_keys_route(
|
||||
body: Ruma<get_backup_keys::v3::Request>,
|
||||
) -> Result<get_backup_keys::v3::Response> {
|
||||
pub async fn get_backup_keys_route(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)?;
|
||||
@@ -249,7 +243,7 @@ pub(crate) async fn get_backup_keys_route(
|
||||
/// # `GET /_matrix/client/r0/room_keys/keys/{roomId}`
|
||||
///
|
||||
/// Retrieves all keys from the backup for a given room.
|
||||
pub(crate) async fn get_backup_keys_for_room_route(
|
||||
pub async fn get_backup_keys_for_room_route(
|
||||
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");
|
||||
@@ -266,7 +260,7 @@ pub(crate) async fn get_backup_keys_for_room_route(
|
||||
/// # `GET /_matrix/client/r0/room_keys/keys/{roomId}/{sessionId}`
|
||||
///
|
||||
/// Retrieves a key from the backup.
|
||||
pub(crate) async fn get_backup_keys_for_session_route(
|
||||
pub async fn get_backup_keys_for_session_route(
|
||||
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");
|
||||
@@ -287,7 +281,7 @@ pub(crate) async fn get_backup_keys_for_session_route(
|
||||
/// # `DELETE /_matrix/client/r0/room_keys/keys`
|
||||
///
|
||||
/// Delete the keys from the backup.
|
||||
pub(crate) async fn delete_backup_keys_route(
|
||||
pub async fn delete_backup_keys_route(
|
||||
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");
|
||||
@@ -310,7 +304,7 @@ pub(crate) async fn delete_backup_keys_route(
|
||||
/// # `DELETE /_matrix/client/r0/room_keys/keys/{roomId}`
|
||||
///
|
||||
/// Delete the keys from the backup for a given room.
|
||||
pub(crate) async fn delete_backup_keys_for_room_route(
|
||||
pub async fn delete_backup_keys_for_room_route(
|
||||
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");
|
||||
@@ -333,7 +327,7 @@ pub(crate) async fn delete_backup_keys_for_room_route(
|
||||
/// # `DELETE /_matrix/client/r0/room_keys/keys/{roomId}/{sessionId}`
|
||||
///
|
||||
/// Delete a key from the backup.
|
||||
pub(crate) async fn delete_backup_keys_for_session_route(
|
||||
pub async fn delete_backup_keys_for_session_route(
|
||||
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");
|
||||
|
||||
@@ -11,7 +11,7 @@ use crate::{services, Result, Ruma};
|
||||
///
|
||||
/// Get information on the supported feature set and other relevent capabilities
|
||||
/// of this server.
|
||||
pub(crate) async fn get_capabilities_route(
|
||||
pub async fn get_capabilities_route(
|
||||
_body: Ruma<get_capabilities::v3::Request>,
|
||||
) -> Result<get_capabilities::v3::Response> {
|
||||
let mut available = BTreeMap::new();
|
||||
|
||||
@@ -14,7 +14,7 @@ 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(
|
||||
pub async fn set_global_account_data_route(
|
||||
body: Ruma<set_global_account_data::v3::Request>,
|
||||
) -> Result<set_global_account_data::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -40,7 +40,7 @@ pub(crate) async fn set_global_account_data_route(
|
||||
/// # `PUT /_matrix/client/r0/user/{userId}/rooms/{roomId}/account_data/{type}`
|
||||
///
|
||||
/// Sets some room account data for the sender user.
|
||||
pub(crate) async fn set_room_account_data_route(
|
||||
pub async fn set_room_account_data_route(
|
||||
body: Ruma<set_room_account_data::v3::Request>,
|
||||
) -> Result<set_room_account_data::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -66,7 +66,7 @@ pub(crate) async fn set_room_account_data_route(
|
||||
/// # `GET /_matrix/client/r0/user/{userId}/account_data/{type}`
|
||||
///
|
||||
/// Gets some account data for the sender user.
|
||||
pub(crate) async fn get_global_account_data_route(
|
||||
pub async fn get_global_account_data_route(
|
||||
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");
|
||||
@@ -88,7 +88,7 @@ pub(crate) async fn get_global_account_data_route(
|
||||
/// # `GET /_matrix/client/r0/user/{userId}/rooms/{roomId}/account_data/{type}`
|
||||
///
|
||||
/// Gets some room account data for the sender user.
|
||||
pub(crate) async fn get_room_account_data_route(
|
||||
pub async fn get_room_account_data_route(
|
||||
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");
|
||||
|
||||
@@ -15,7 +15,7 @@ use crate::{services, 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(body: Ruma<get_context::v3::Request>) -> Result<get_context::v3::Response> {
|
||||
pub 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");
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ 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(body: Ruma<get_devices::v3::Request>) -> Result<get_devices::v3::Response> {
|
||||
pub 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()
|
||||
@@ -27,7 +27,7 @@ pub(crate) async fn get_devices_route(body: Ruma<get_devices::v3::Request>) -> R
|
||||
/// # `GET /_matrix/client/r0/devices/{deviceId}`
|
||||
///
|
||||
/// Get metadata on a single device of the sender user.
|
||||
pub(crate) async fn get_device_route(body: Ruma<get_device::v3::Request>) -> Result<get_device::v3::Response> {
|
||||
pub 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()
|
||||
@@ -43,7 +43,7 @@ pub(crate) async fn get_device_route(body: Ruma<get_device::v3::Request>) -> Res
|
||||
/// # `PUT /_matrix/client/r0/devices/{deviceId}`
|
||||
///
|
||||
/// Updates the metadata on a given device of the sender user.
|
||||
pub(crate) async fn update_device_route(body: Ruma<update_device::v3::Request>) -> Result<update_device::v3::Response> {
|
||||
pub 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()
|
||||
@@ -70,7 +70,7 @@ pub(crate) async fn update_device_route(body: Ruma<update_device::v3::Request>)
|
||||
/// last seen ts)
|
||||
/// - Forgets to-device events
|
||||
/// - Triggers device list updates
|
||||
pub(crate) async fn delete_device_route(body: Ruma<delete_device::v3::Request>) -> Result<delete_device::v3::Response> {
|
||||
pub 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");
|
||||
|
||||
@@ -122,9 +122,7 @@ pub(crate) async fn delete_device_route(body: Ruma<delete_device::v3::Request>)
|
||||
/// last seen ts)
|
||||
/// - Forgets to-device events
|
||||
/// - Triggers device list updates
|
||||
pub(crate) async fn delete_devices_route(
|
||||
body: Ruma<delete_devices::v3::Request>,
|
||||
) -> Result<delete_devices::v3::Response> {
|
||||
pub async fn delete_devices_route(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");
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ use crate::{services, Error, Result, Ruma};
|
||||
/// Lists the public rooms on this server.
|
||||
///
|
||||
/// - Rooms are ordered by the number of joined members
|
||||
pub(crate) async fn get_public_rooms_filtered_route(
|
||||
pub async fn get_public_rooms_filtered_route(
|
||||
body: Ruma<get_public_rooms_filtered::v3::Request>,
|
||||
) -> Result<get_public_rooms_filtered::v3::Response> {
|
||||
if let Some(server) = &body.server {
|
||||
@@ -68,7 +68,7 @@ pub(crate) async fn get_public_rooms_filtered_route(
|
||||
/// Lists the public rooms on this server.
|
||||
///
|
||||
/// - Rooms are ordered by the number of joined members
|
||||
pub(crate) async fn get_public_rooms_route(
|
||||
pub async fn get_public_rooms_route(
|
||||
body: Ruma<get_public_rooms::v3::Request>,
|
||||
) -> Result<get_public_rooms::v3::Response> {
|
||||
if let Some(server) = &body.server {
|
||||
@@ -110,7 +110,7 @@ pub(crate) async fn get_public_rooms_route(
|
||||
/// Sets the visibility of a given room in the room directory.
|
||||
///
|
||||
/// - TODO: Access control checks
|
||||
pub(crate) async fn set_room_visibility_route(
|
||||
pub async fn set_room_visibility_route(
|
||||
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");
|
||||
@@ -153,7 +153,7 @@ pub(crate) async fn set_room_visibility_route(
|
||||
/// # `GET /_matrix/client/r0/directory/list/room/{roomId}`
|
||||
///
|
||||
/// Gets the visibility of a given room in the room directory.
|
||||
pub(crate) async fn get_room_visibility_route(
|
||||
pub async fn get_room_visibility_route(
|
||||
body: Ruma<get_room_visibility::v3::Request>,
|
||||
) -> Result<get_room_visibility::v3::Response> {
|
||||
if !services().rooms.metadata.exists(&body.room_id)? {
|
||||
|
||||
@@ -10,7 +10,7 @@ use crate::{services, Error, Result, Ruma};
|
||||
/// Loads a filter that was previously created.
|
||||
///
|
||||
/// - A user can only access their own filters
|
||||
pub(crate) async fn get_filter_route(body: Ruma<get_filter::v3::Request>) -> Result<get_filter::v3::Response> {
|
||||
pub 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 {
|
||||
return Err(Error::BadRequest(ErrorKind::NotFound, "Filter not found."));
|
||||
@@ -22,7 +22,7 @@ pub(crate) async fn get_filter_route(body: Ruma<get_filter::v3::Request>) -> Res
|
||||
/// # `PUT /_matrix/client/r0/user/{userId}/filter`
|
||||
///
|
||||
/// Creates a new filter to be used by other endpoints.
|
||||
pub(crate) async fn create_filter_route(body: Ruma<create_filter::v3::Request>) -> Result<create_filter::v3::Response> {
|
||||
pub 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)?,
|
||||
|
||||
@@ -17,7 +17,7 @@ use ruma::{
|
||||
DeviceKeyAlgorithm, OwnedDeviceId, OwnedUserId, UserId,
|
||||
};
|
||||
use serde_json::json;
|
||||
use tracing::debug;
|
||||
use tracing::{debug, error};
|
||||
|
||||
use super::SESSION_ID_LENGTH;
|
||||
use crate::{services, utils, Error, Result, Ruma};
|
||||
@@ -29,7 +29,7 @@ use crate::{services, utils, Error, Result, 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(body: Ruma<upload_keys::v3::Request>) -> Result<upload_keys::v3::Response> {
|
||||
pub 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");
|
||||
|
||||
@@ -68,7 +68,7 @@ pub(crate) async fn upload_keys_route(body: Ruma<upload_keys::v3::Request>) -> R
|
||||
/// - Gets master keys, self-signing keys, user signing keys and device keys.
|
||||
/// - The master and self-signing keys contain signatures that the user is
|
||||
/// allowed to see
|
||||
pub(crate) async fn get_keys_route(body: Ruma<get_keys::v3::Request>) -> Result<get_keys::v3::Response> {
|
||||
pub 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");
|
||||
|
||||
let response = get_keys_helper(
|
||||
@@ -85,7 +85,7 @@ pub(crate) async fn get_keys_route(body: Ruma<get_keys::v3::Request>) -> Result<
|
||||
/// # `POST /_matrix/client/r0/keys/claim`
|
||||
///
|
||||
/// Claims one-time keys
|
||||
pub(crate) async fn claim_keys_route(body: Ruma<claim_keys::v3::Request>) -> Result<claim_keys::v3::Response> {
|
||||
pub async fn claim_keys_route(body: Ruma<claim_keys::v3::Request>) -> Result<claim_keys::v3::Response> {
|
||||
let response = claim_keys_helper(&body.one_time_keys).await?;
|
||||
|
||||
Ok(response)
|
||||
@@ -96,7 +96,7 @@ pub(crate) async fn claim_keys_route(body: Ruma<claim_keys::v3::Request>) -> Res
|
||||
/// Uploads end-to-end key information for the sender user.
|
||||
///
|
||||
/// - Requires UIAA to verify password
|
||||
pub(crate) async fn upload_signing_keys_route(
|
||||
pub async fn upload_signing_keys_route(
|
||||
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");
|
||||
@@ -147,7 +147,7 @@ pub(crate) async fn upload_signing_keys_route(
|
||||
/// # `POST /_matrix/client/r0/keys/signatures/upload`
|
||||
///
|
||||
/// Uploads end-to-end key signatures from the sender user.
|
||||
pub(crate) async fn upload_signatures_route(
|
||||
pub async fn upload_signatures_route(
|
||||
body: Ruma<upload_signatures::v3::Request>,
|
||||
) -> Result<upload_signatures::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -193,9 +193,7 @@ pub(crate) async fn upload_signatures_route(
|
||||
/// previous sync token.
|
||||
///
|
||||
/// - TODO: left users
|
||||
pub(crate) async fn get_key_changes_route(
|
||||
body: Ruma<get_key_changes::v3::Request>,
|
||||
) -> Result<get_key_changes::v3::Response> {
|
||||
pub async fn get_key_changes_route(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();
|
||||
@@ -368,16 +366,23 @@ pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool>(
|
||||
for (user_id, keys) in vec {
|
||||
device_keys_input_fed.insert(user_id.to_owned(), keys.clone());
|
||||
}
|
||||
|
||||
let request = federation::keys::get_keys::v1::Request {
|
||||
device_keys: device_keys_input_fed,
|
||||
};
|
||||
let response = services()
|
||||
.sending
|
||||
.send_federation_request(server, request)
|
||||
.await;
|
||||
|
||||
(server, Ok(response))
|
||||
(
|
||||
server,
|
||||
tokio::time::timeout(
|
||||
Duration::from_secs(90),
|
||||
services().sending.send_federation_request(
|
||||
server,
|
||||
federation::keys::get_keys::v1::Request {
|
||||
device_keys: device_keys_input_fed,
|
||||
},
|
||||
),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!("get_keys_helper query took too long: {e}");
|
||||
Error::BadServerResponse("get_keys_helper query took too long")
|
||||
}),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
@@ -401,7 +406,7 @@ pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool>(
|
||||
false, /* Dont notify. A notification would trigger another key request resulting in an
|
||||
* endless loop */
|
||||
)?;
|
||||
master_keys.insert(user.clone(), raw);
|
||||
master_keys.insert(user, raw);
|
||||
}
|
||||
|
||||
self_signing_keys.extend(response.self_signing_keys);
|
||||
|
||||
+467
-257
@@ -10,11 +10,10 @@ use ruma::api::client::{
|
||||
get_media_preview,
|
||||
},
|
||||
};
|
||||
use tracing::{debug, error, warn};
|
||||
use tracing::{debug, error, info, warn};
|
||||
use webpage::HTML;
|
||||
|
||||
use crate::{
|
||||
debug_warn,
|
||||
service::media::{FileMeta, UrlPreviewData},
|
||||
services, utils, Error, Result, Ruma, RumaResponse,
|
||||
};
|
||||
@@ -22,13 +21,10 @@ use crate::{
|
||||
/// 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";
|
||||
|
||||
/// # `GET /_matrix/media/v3/config`
|
||||
///
|
||||
/// Returns max upload size.
|
||||
pub(crate) async fn get_media_config_route(
|
||||
pub async fn get_media_config_route(
|
||||
_body: Ruma<get_media_config::v3::Request>,
|
||||
) -> Result<get_media_config::v3::Response> {
|
||||
Ok(get_media_config::v3::Response {
|
||||
@@ -43,16 +39,19 @@ pub(crate) async fn get_media_config_route(
|
||||
/// See <https://spec.matrix.org/legacy/legacy/#id27>
|
||||
///
|
||||
/// Returns max upload size.
|
||||
pub(crate) async fn get_media_config_v1_route(
|
||||
body: Ruma<get_media_config::v3::Request>,
|
||||
pub async fn get_media_config_v1_route(
|
||||
_body: Ruma<get_media_config::v3::Request>,
|
||||
) -> Result<RumaResponse<get_media_config::v3::Response>> {
|
||||
get_media_config_route(body).await.map(RumaResponse)
|
||||
Ok(get_media_config::v3::Response {
|
||||
upload_size: services().globals.max_request_size().into(),
|
||||
}
|
||||
.into())
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/media/v3/preview_url`
|
||||
///
|
||||
/// Returns URL preview.
|
||||
pub(crate) async fn get_media_preview_route(
|
||||
pub async fn get_media_preview_route(
|
||||
body: Ruma<get_media_preview::v3::Request>,
|
||||
) -> Result<get_media_preview::v3::Response> {
|
||||
let url = &body.url;
|
||||
@@ -96,10 +95,41 @@ pub(crate) async fn get_media_preview_route(
|
||||
/// See <https://spec.matrix.org/legacy/legacy/#id27>
|
||||
///
|
||||
/// Returns URL preview.
|
||||
pub(crate) async fn get_media_preview_v1_route(
|
||||
pub async fn get_media_preview_v1_route(
|
||||
body: Ruma<get_media_preview::v3::Request>,
|
||||
) -> Result<RumaResponse<get_media_preview::v3::Response>> {
|
||||
get_media_preview_route(body).await.map(RumaResponse)
|
||||
let url = &body.url;
|
||||
if !url_preview_allowed(url) {
|
||||
return Err(Error::BadRequest(ErrorKind::forbidden(), "URL is not allowed to be previewed"));
|
||||
}
|
||||
|
||||
match get_url_preview(url).await {
|
||||
Ok(preview) => {
|
||||
let res = serde_json::value::to_raw_value(&preview).map_err(|e| {
|
||||
error!("Failed to convert UrlPreviewData into a serde json value: {}", e);
|
||||
Error::BadRequest(
|
||||
ErrorKind::LimitExceeded {
|
||||
retry_after: Some(RetryAfter::Delay(Duration::from_secs(5))),
|
||||
},
|
||||
"Failed to generate a URL preview, try again later.",
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(get_media_preview::v3::Response::from_raw_value(res).into())
|
||||
},
|
||||
Err(e) => {
|
||||
warn!("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(
|
||||
ErrorKind::LimitExceeded {
|
||||
retry_after: Some(RetryAfter::Delay(Duration::from_secs(5))),
|
||||
},
|
||||
"Failed to generate a URL preview, try again later.",
|
||||
))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// # `POST /_matrix/media/v3/upload`
|
||||
@@ -108,9 +138,7 @@ pub(crate) async fn get_media_preview_v1_route(
|
||||
///
|
||||
/// - Some metadata will be saved in the database
|
||||
/// - Media will be saved in the media/ directory
|
||||
pub(crate) async fn create_content_route(
|
||||
body: Ruma<create_content::v3::Request>,
|
||||
) -> Result<create_content::v3::Response> {
|
||||
pub async fn create_content_route(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!(
|
||||
@@ -151,263 +179,55 @@ pub(crate) async fn create_content_route(
|
||||
///
|
||||
/// - Some metadata will be saved in the database
|
||||
/// - Media will be saved in the media/ directory
|
||||
pub(crate) async fn create_content_v1_route(
|
||||
pub async fn create_content_v1_route(
|
||||
body: Ruma<create_content::v3::Request>,
|
||||
) -> Result<RumaResponse<create_content::v3::Response>> {
|
||||
create_content_route(body).await.map(RumaResponse)
|
||||
}
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
/// # `GET /_matrix/media/v3/download/{serverName}/{mediaId}`
|
||||
///
|
||||
/// Load media from our server or over federation.
|
||||
///
|
||||
/// - Only allows federation if `allow_remote` is true
|
||||
/// - Only redirects if `allow_redirect` is true
|
||||
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
||||
/// seconds
|
||||
pub(crate) async fn get_content_route(body: Ruma<get_content::v3::Request>) -> Result<get_content::v3::Response> {
|
||||
let mxc = format!("mxc://{}/{}", body.server_name, body.media_id);
|
||||
let mxc = format!(
|
||||
"mxc://{}/{}",
|
||||
services().globals.server_name(),
|
||||
utils::random_string(MXC_LENGTH)
|
||||
);
|
||||
|
||||
if let Some(FileMeta {
|
||||
content_disposition,
|
||||
content_type,
|
||||
file,
|
||||
}) = services().media.get(mxc.clone()).await?
|
||||
{
|
||||
Ok(get_content::v3::Response {
|
||||
file,
|
||||
content_type,
|
||||
content_disposition,
|
||||
cross_origin_resource_policy: Some("cross-origin".to_owned()),
|
||||
cache_control: Some(CACHE_CONTROL_IMMUTABLE.into()),
|
||||
})
|
||||
} else if &*body.server_name != services().globals.server_name() && body.allow_remote {
|
||||
get_remote_content(
|
||||
&mxc,
|
||||
&body.server_name,
|
||||
body.media_id.clone(),
|
||||
body.allow_redirect,
|
||||
body.timeout_ms,
|
||||
)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
debug_warn!("Fetching media `{}` failed: {:?}", mxc, e);
|
||||
Error::BadRequest(ErrorKind::NotFound, "Remote media error.")
|
||||
})
|
||||
} else {
|
||||
Err(Error::BadRequest(ErrorKind::NotFound, "Media not found."))
|
||||
}
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/media/v1/download/{serverName}/{mediaId}`
|
||||
///
|
||||
/// Load media from our server or over federation.
|
||||
///
|
||||
/// This is a legacy endpoint ("/v1/") that some very old homeservers and/or
|
||||
/// clients may call. conduwuit adds these for compatibility purposes.
|
||||
/// See <https://spec.matrix.org/legacy/legacy/#id27>
|
||||
///
|
||||
/// - Only allows federation if `allow_remote` is true
|
||||
/// - Only redirects if `allow_redirect` is true
|
||||
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
||||
/// seconds
|
||||
pub(crate) async fn get_content_v1_route(
|
||||
body: Ruma<get_content::v3::Request>,
|
||||
) -> Result<RumaResponse<get_content::v3::Response>> {
|
||||
get_content_route(body).await.map(RumaResponse)
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/media/v3/download/{serverName}/{mediaId}/{fileName}`
|
||||
///
|
||||
/// Load media from our server or over federation, permitting desired filename.
|
||||
///
|
||||
/// - Only allows federation if `allow_remote` is true
|
||||
/// - Only redirects if `allow_redirect` is true
|
||||
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
||||
/// seconds
|
||||
pub(crate) async fn get_content_as_filename_route(
|
||||
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_type,
|
||||
file,
|
||||
..
|
||||
}) = services().media.get(mxc.clone()).await?
|
||||
{
|
||||
Ok(get_content_as_filename::v3::Response {
|
||||
file,
|
||||
content_type,
|
||||
content_disposition: Some(format!("inline; filename={}", body.filename)),
|
||||
cross_origin_resource_policy: Some("cross-origin".to_owned()),
|
||||
cache_control: Some(CACHE_CONTROL_IMMUTABLE.into()),
|
||||
})
|
||||
} else if &*body.server_name != services().globals.server_name() && body.allow_remote {
|
||||
match get_remote_content(
|
||||
&mxc,
|
||||
&body.server_name,
|
||||
body.media_id.clone(),
|
||||
body.allow_redirect,
|
||||
body.timeout_ms,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(remote_content_response) => Ok(get_content_as_filename::v3::Response {
|
||||
content_disposition: Some(format!("inline: filename={}", body.filename)),
|
||||
content_type: remote_content_response.content_type,
|
||||
file: remote_content_response.file,
|
||||
cross_origin_resource_policy: Some("cross-origin".to_owned()),
|
||||
cache_control: Some(CACHE_CONTROL_IMMUTABLE.into()),
|
||||
}),
|
||||
Err(e) => {
|
||||
debug_warn!("Fetching media `{}` failed: {:?}", mxc, e);
|
||||
Err(Error::BadRequest(ErrorKind::NotFound, "Remote media error."))
|
||||
},
|
||||
}
|
||||
} else {
|
||||
Err(Error::BadRequest(ErrorKind::NotFound, "Media not found."))
|
||||
}
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/media/v1/download/{serverName}/{mediaId}/{fileName}`
|
||||
///
|
||||
/// Load media from our server or over federation, permitting desired filename.
|
||||
///
|
||||
/// This is a legacy endpoint ("/v1/") that some very old homeservers and/or
|
||||
/// clients may call. conduwuit adds these for compatibility purposes.
|
||||
/// See <https://spec.matrix.org/legacy/legacy/#id27>
|
||||
///
|
||||
/// - Only allows federation if `allow_remote` is true
|
||||
/// - Only redirects if `allow_redirect` is true
|
||||
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
||||
/// seconds
|
||||
pub(crate) async fn get_content_as_filename_v1_route(
|
||||
body: Ruma<get_content_as_filename::v3::Request>,
|
||||
) -> Result<RumaResponse<get_content_as_filename::v3::Response>> {
|
||||
get_content_as_filename_route(body).await.map(RumaResponse)
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/media/v3/thumbnail/{serverName}/{mediaId}`
|
||||
///
|
||||
/// Load media thumbnail from our server or over federation.
|
||||
///
|
||||
/// - Only allows federation if `allow_remote` is true
|
||||
/// - Only redirects if `allow_redirect` is true
|
||||
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
||||
/// seconds
|
||||
pub(crate) async fn get_content_thumbnail_route(
|
||||
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_type,
|
||||
file,
|
||||
..
|
||||
}) = services()
|
||||
services()
|
||||
.media
|
||||
.get_thumbnail(
|
||||
.create(
|
||||
Some(sender_user.clone()),
|
||||
mxc.clone(),
|
||||
body.width
|
||||
.try_into()
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Width is invalid."))?,
|
||||
body.height
|
||||
.try_into()
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Height is invalid."))?,
|
||||
body.filename
|
||||
.as_ref()
|
||||
.map(|filename| "inline; filename=".to_owned() + filename)
|
||||
.as_deref(),
|
||||
body.content_type.as_deref(),
|
||||
&body.file,
|
||||
)
|
||||
.await?
|
||||
{
|
||||
Ok(get_content_thumbnail::v3::Response {
|
||||
file,
|
||||
content_type,
|
||||
cross_origin_resource_policy: Some("cross-origin".to_owned()),
|
||||
cache_control: Some(CACHE_CONTROL_IMMUTABLE.into()),
|
||||
})
|
||||
} else if &*body.server_name != services().globals.server_name() && body.allow_remote {
|
||||
if services()
|
||||
.globals
|
||||
.prevent_media_downloads_from()
|
||||
.contains(&body.server_name.clone())
|
||||
{
|
||||
// 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(Error::BadRequest(ErrorKind::NotFound, "Media not found."));
|
||||
}
|
||||
.await?;
|
||||
|
||||
match services()
|
||||
.sending
|
||||
.send_federation_request(
|
||||
&body.server_name,
|
||||
get_content_thumbnail::v3::Request {
|
||||
allow_remote: body.allow_remote,
|
||||
height: body.height,
|
||||
width: body.width,
|
||||
method: body.method.clone(),
|
||||
server_name: body.server_name.clone(),
|
||||
media_id: body.media_id.clone(),
|
||||
timeout_ms: body.timeout_ms,
|
||||
allow_redirect: body.allow_redirect,
|
||||
},
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(get_thumbnail_response) => {
|
||||
services()
|
||||
.media
|
||||
.upload_thumbnail(
|
||||
None,
|
||||
mxc,
|
||||
None,
|
||||
get_thumbnail_response.content_type.as_deref(),
|
||||
body.width.try_into().expect("all UInts are valid u32s"),
|
||||
body.height.try_into().expect("all UInts are valid u32s"),
|
||||
&get_thumbnail_response.file,
|
||||
)
|
||||
.await?;
|
||||
let content_uri = mxc.into();
|
||||
|
||||
Ok(get_thumbnail_response)
|
||||
},
|
||||
Err(e) => {
|
||||
debug_warn!("Fetching media `{}` failed: {:?}", mxc, e);
|
||||
Err(Error::BadRequest(ErrorKind::NotFound, "Remote media error."))
|
||||
},
|
||||
}
|
||||
} else {
|
||||
Err(Error::BadRequest(ErrorKind::NotFound, "Media not found."))
|
||||
Ok(create_content::v3::Response {
|
||||
content_uri,
|
||||
blurhash: None,
|
||||
}
|
||||
.into())
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/media/v1/thumbnail/{serverName}/{mediaId}`
|
||||
///
|
||||
/// Load media thumbnail from our server or over federation.
|
||||
///
|
||||
/// This is a legacy endpoint ("/v1/") that some very old homeservers and/or
|
||||
/// clients may call. conduwuit adds these for compatibility purposes.
|
||||
/// See <https://spec.matrix.org/legacy/legacy/#id27>
|
||||
///
|
||||
/// - Only allows federation if `allow_remote` is true
|
||||
/// - Only redirects if `allow_redirect` is true
|
||||
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
||||
/// seconds
|
||||
pub(crate) async fn get_content_thumbnail_v1_route(
|
||||
body: Ruma<get_content_thumbnail::v3::Request>,
|
||||
) -> Result<RumaResponse<get_content_thumbnail::v3::Response>> {
|
||||
get_content_thumbnail_route(body).await.map(RumaResponse)
|
||||
}
|
||||
|
||||
async fn get_remote_content(
|
||||
/// helper method to fetch remote media from other servers over federation
|
||||
pub async fn get_remote_content(
|
||||
mxc: &str, server_name: &ruma::ServerName, media_id: String, allow_redirect: bool, timeout_ms: Duration,
|
||||
) -> Result<get_content::v3::Response, Error> {
|
||||
// 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.
|
||||
if services()
|
||||
.globals
|
||||
.prevent_media_downloads_from()
|
||||
.contains(&server_name.to_owned())
|
||||
{
|
||||
// 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);
|
||||
info!(
|
||||
"Received request for remote media `{}` but server is in our media server blocklist. Returning 404.",
|
||||
mxc
|
||||
);
|
||||
return Err(Error::BadRequest(ErrorKind::NotFound, "Media not found."));
|
||||
}
|
||||
|
||||
@@ -439,6 +259,372 @@ async fn get_remote_content(
|
||||
Ok(content_response)
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/media/v3/download/{serverName}/{mediaId}`
|
||||
///
|
||||
/// Load media from our server or over federation.
|
||||
///
|
||||
/// - Only allows federation if `allow_remote` is true
|
||||
/// - Only redirects if `allow_redirect` is true
|
||||
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
||||
/// seconds
|
||||
pub async fn get_content_route(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_disposition,
|
||||
content_type,
|
||||
file,
|
||||
}) = services().media.get(mxc.clone()).await?
|
||||
{
|
||||
Ok(get_content::v3::Response {
|
||||
file,
|
||||
content_type,
|
||||
content_disposition,
|
||||
cross_origin_resource_policy: Some("cross-origin".to_owned()),
|
||||
cache_control: Some("public, max-age=31536000, immutable".to_owned()),
|
||||
})
|
||||
} else if &*body.server_name != services().globals.server_name() && body.allow_remote {
|
||||
let remote_content_response = get_remote_content(
|
||||
&mxc,
|
||||
&body.server_name,
|
||||
body.media_id.clone(),
|
||||
body.allow_redirect,
|
||||
body.timeout_ms,
|
||||
)
|
||||
.await?;
|
||||
Ok(remote_content_response)
|
||||
} else {
|
||||
Err(Error::BadRequest(ErrorKind::NotFound, "Media not found."))
|
||||
}
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/media/v1/download/{serverName}/{mediaId}`
|
||||
///
|
||||
/// Load media from our server or over federation.
|
||||
///
|
||||
/// This is a legacy endpoint ("/v1/") that some very old homeservers and/or
|
||||
/// clients may call. conduwuit adds these for compatibility purposes.
|
||||
/// See <https://spec.matrix.org/legacy/legacy/#id27>
|
||||
///
|
||||
/// - Only allows federation if `allow_remote` is true
|
||||
/// - Only redirects if `allow_redirect` is true
|
||||
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
||||
/// seconds
|
||||
pub async fn get_content_v1_route(
|
||||
body: Ruma<get_content::v3::Request>,
|
||||
) -> Result<RumaResponse<get_content::v3::Response>> {
|
||||
let mxc = format!("mxc://{}/{}", body.server_name, body.media_id);
|
||||
|
||||
if let Some(FileMeta {
|
||||
content_disposition,
|
||||
content_type,
|
||||
file,
|
||||
}) = services().media.get(mxc.clone()).await?
|
||||
{
|
||||
Ok(get_content::v3::Response {
|
||||
file,
|
||||
content_type,
|
||||
content_disposition,
|
||||
cross_origin_resource_policy: Some("cross-origin".to_owned()),
|
||||
cache_control: Some("public, max-age=31536000, immutable".to_owned()),
|
||||
}
|
||||
.into())
|
||||
} else if &*body.server_name != services().globals.server_name() && body.allow_remote {
|
||||
let remote_content_response = get_remote_content(
|
||||
&mxc,
|
||||
&body.server_name,
|
||||
body.media_id.clone(),
|
||||
body.allow_redirect,
|
||||
body.timeout_ms,
|
||||
)
|
||||
.await?;
|
||||
Ok(remote_content_response.into())
|
||||
} else {
|
||||
Err(Error::BadRequest(ErrorKind::NotFound, "Media not found."))
|
||||
}
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/media/v3/download/{serverName}/{mediaId}/{fileName}`
|
||||
///
|
||||
/// Load media from our server or over federation, permitting desired filename.
|
||||
///
|
||||
/// - Only allows federation if `allow_remote` is true
|
||||
/// - Only redirects if `allow_redirect` is true
|
||||
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
||||
/// seconds
|
||||
pub async fn get_content_as_filename_route(
|
||||
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_type,
|
||||
file,
|
||||
..
|
||||
}) = services().media.get(mxc.clone()).await?
|
||||
{
|
||||
Ok(get_content_as_filename::v3::Response {
|
||||
file,
|
||||
content_type,
|
||||
content_disposition: Some(format!("inline; filename={}", body.filename)),
|
||||
cross_origin_resource_policy: Some("cross-origin".to_owned()),
|
||||
cache_control: Some("public, max-age=31536000, immutable".to_owned()),
|
||||
})
|
||||
} else if &*body.server_name != services().globals.server_name() && body.allow_remote {
|
||||
let remote_content_response = get_remote_content(
|
||||
&mxc,
|
||||
&body.server_name,
|
||||
body.media_id.clone(),
|
||||
body.allow_redirect,
|
||||
body.timeout_ms,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(get_content_as_filename::v3::Response {
|
||||
content_disposition: Some(format!("inline: filename={}", body.filename)),
|
||||
content_type: remote_content_response.content_type,
|
||||
file: remote_content_response.file,
|
||||
cross_origin_resource_policy: Some("cross-origin".to_owned()),
|
||||
cache_control: Some("public, max-age=31536000, immutable".to_owned()),
|
||||
})
|
||||
} else {
|
||||
Err(Error::BadRequest(ErrorKind::NotFound, "Media not found."))
|
||||
}
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/media/v1/download/{serverName}/{mediaId}/{fileName}`
|
||||
///
|
||||
/// Load media from our server or over federation, permitting desired filename.
|
||||
///
|
||||
/// This is a legacy endpoint ("/v1/") that some very old homeservers and/or
|
||||
/// clients may call. conduwuit adds these for compatibility purposes.
|
||||
/// See <https://spec.matrix.org/legacy/legacy/#id27>
|
||||
///
|
||||
/// - Only allows federation if `allow_remote` is true
|
||||
/// - Only redirects if `allow_redirect` is true
|
||||
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
||||
/// seconds
|
||||
pub async fn get_content_as_filename_v1_route(
|
||||
body: Ruma<get_content_as_filename::v3::Request>,
|
||||
) -> Result<RumaResponse<get_content_as_filename::v3::Response>> {
|
||||
let mxc = format!("mxc://{}/{}", body.server_name, body.media_id);
|
||||
|
||||
if let Some(FileMeta {
|
||||
content_type,
|
||||
file,
|
||||
..
|
||||
}) = services().media.get(mxc.clone()).await?
|
||||
{
|
||||
Ok(get_content_as_filename::v3::Response {
|
||||
file,
|
||||
content_type,
|
||||
content_disposition: Some(format!("inline; filename={}", body.filename)),
|
||||
cross_origin_resource_policy: Some("cross-origin".to_owned()),
|
||||
cache_control: Some("public, max-age=31536000, immutable".to_owned()),
|
||||
}
|
||||
.into())
|
||||
} else if &*body.server_name != services().globals.server_name() && body.allow_remote {
|
||||
let remote_content_response = get_remote_content(
|
||||
&mxc,
|
||||
&body.server_name,
|
||||
body.media_id.clone(),
|
||||
body.allow_redirect,
|
||||
body.timeout_ms,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(get_content_as_filename::v3::Response {
|
||||
content_disposition: Some(format!("inline: filename={}", body.filename)),
|
||||
content_type: remote_content_response.content_type,
|
||||
file: remote_content_response.file,
|
||||
cross_origin_resource_policy: Some("cross-origin".to_owned()),
|
||||
cache_control: Some("public, max-age=31536000, immutable".to_owned()),
|
||||
}
|
||||
.into())
|
||||
} else {
|
||||
Err(Error::BadRequest(ErrorKind::NotFound, "Media not found."))
|
||||
}
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/media/v3/thumbnail/{serverName}/{mediaId}`
|
||||
///
|
||||
/// Load media thumbnail from our server or over federation.
|
||||
///
|
||||
/// - Only allows federation if `allow_remote` is true
|
||||
/// - Only redirects if `allow_redirect` is true
|
||||
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
||||
/// seconds
|
||||
pub async fn get_content_thumbnail_route(
|
||||
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_type,
|
||||
file,
|
||||
..
|
||||
}) = services()
|
||||
.media
|
||||
.get_thumbnail(
|
||||
mxc.clone(),
|
||||
body.width
|
||||
.try_into()
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Width is invalid."))?,
|
||||
body.height
|
||||
.try_into()
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Height is invalid."))?,
|
||||
)
|
||||
.await?
|
||||
{
|
||||
Ok(get_content_thumbnail::v3::Response {
|
||||
file,
|
||||
content_type,
|
||||
cross_origin_resource_policy: Some("cross-origin".to_owned()),
|
||||
cache_control: Some("public, max-age=31536000, immutable".to_owned()),
|
||||
})
|
||||
} else if &*body.server_name != services().globals.server_name() && body.allow_remote {
|
||||
// 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.
|
||||
if services()
|
||||
.globals
|
||||
.prevent_media_downloads_from()
|
||||
.contains(&body.server_name.clone())
|
||||
{
|
||||
info!(
|
||||
"Received request for remote media `{}` but server is in our media server blocklist. Returning 404.",
|
||||
mxc
|
||||
);
|
||||
return Err(Error::BadRequest(ErrorKind::NotFound, "Media not found."));
|
||||
}
|
||||
|
||||
let get_thumbnail_response = services()
|
||||
.sending
|
||||
.send_federation_request(
|
||||
&body.server_name,
|
||||
get_content_thumbnail::v3::Request {
|
||||
allow_remote: body.allow_remote,
|
||||
height: body.height,
|
||||
width: body.width,
|
||||
method: body.method.clone(),
|
||||
server_name: body.server_name.clone(),
|
||||
media_id: body.media_id.clone(),
|
||||
timeout_ms: body.timeout_ms,
|
||||
allow_redirect: body.allow_redirect,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
services()
|
||||
.media
|
||||
.upload_thumbnail(
|
||||
None,
|
||||
mxc,
|
||||
None,
|
||||
get_thumbnail_response.content_type.as_deref(),
|
||||
body.width.try_into().expect("all UInts are valid u32s"),
|
||||
body.height.try_into().expect("all UInts are valid u32s"),
|
||||
&get_thumbnail_response.file,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(get_thumbnail_response)
|
||||
} else {
|
||||
Err(Error::BadRequest(ErrorKind::NotFound, "Media not found."))
|
||||
}
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/media/v1/thumbnail/{serverName}/{mediaId}`
|
||||
///
|
||||
/// Load media thumbnail from our server or over federation.
|
||||
///
|
||||
/// This is a legacy endpoint ("/v1/") that some very old homeservers and/or
|
||||
/// clients may call. conduwuit adds these for compatibility purposes.
|
||||
/// See <https://spec.matrix.org/legacy/legacy/#id27>
|
||||
///
|
||||
/// - Only allows federation if `allow_remote` is true
|
||||
/// - Only redirects if `allow_redirect` is true
|
||||
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
||||
/// seconds
|
||||
pub async fn get_content_thumbnail_v1_route(
|
||||
body: Ruma<get_content_thumbnail::v3::Request>,
|
||||
) -> Result<RumaResponse<get_content_thumbnail::v3::Response>> {
|
||||
let mxc = format!("mxc://{}/{}", body.server_name, body.media_id);
|
||||
|
||||
if let Some(FileMeta {
|
||||
content_type,
|
||||
file,
|
||||
..
|
||||
}) = services()
|
||||
.media
|
||||
.get_thumbnail(
|
||||
mxc.clone(),
|
||||
body.width
|
||||
.try_into()
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Width is invalid."))?,
|
||||
body.height
|
||||
.try_into()
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Height is invalid."))?,
|
||||
)
|
||||
.await?
|
||||
{
|
||||
Ok(get_content_thumbnail::v3::Response {
|
||||
file,
|
||||
content_type,
|
||||
cross_origin_resource_policy: Some("cross-origin".to_owned()),
|
||||
cache_control: Some("public, max-age=31536000, immutable".to_owned()),
|
||||
}
|
||||
.into())
|
||||
} else if &*body.server_name != services().globals.server_name() && body.allow_remote {
|
||||
// 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.
|
||||
if services()
|
||||
.globals
|
||||
.prevent_media_downloads_from()
|
||||
.contains(&body.server_name.clone())
|
||||
{
|
||||
info!(
|
||||
"Received request for remote media `{}` but server is in our media server blocklist. Returning 404.",
|
||||
mxc
|
||||
);
|
||||
return Err(Error::BadRequest(ErrorKind::NotFound, "Media not found."));
|
||||
}
|
||||
|
||||
let get_thumbnail_response = services()
|
||||
.sending
|
||||
.send_federation_request(
|
||||
&body.server_name,
|
||||
get_content_thumbnail::v3::Request {
|
||||
allow_remote: body.allow_remote,
|
||||
height: body.height,
|
||||
width: body.width,
|
||||
method: body.method.clone(),
|
||||
server_name: body.server_name.clone(),
|
||||
media_id: body.media_id.clone(),
|
||||
timeout_ms: body.timeout_ms,
|
||||
allow_redirect: body.allow_redirect,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
services()
|
||||
.media
|
||||
.upload_thumbnail(
|
||||
None,
|
||||
mxc,
|
||||
None,
|
||||
get_thumbnail_response.content_type.as_deref(),
|
||||
body.width.try_into().expect("all UInts are valid u32s"),
|
||||
body.height.try_into().expect("all UInts are valid u32s"),
|
||||
&get_thumbnail_response.file,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(get_thumbnail_response.into())
|
||||
} else {
|
||||
Err(Error::BadRequest(ErrorKind::NotFound, "Media not found."))
|
||||
}
|
||||
}
|
||||
|
||||
async fn download_image(client: &reqwest::Client, url: &str) -> Result<UrlPreviewData> {
|
||||
let image = client.get(url).send().await?.bytes().await?;
|
||||
let mxc = format!(
|
||||
@@ -506,8 +692,20 @@ async fn download_html(client: &reqwest::Client, url: &str) -> Result<UrlPreview
|
||||
|
||||
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 cidr_ranges_s = services().globals.ip_range_denylist().to_vec();
|
||||
let mut cidr_ranges: Vec<IPAddress> = Vec::new();
|
||||
|
||||
for cidr in cidr_ranges_s {
|
||||
cidr_ranges.push(IPAddress::parse(cidr).expect("we checked this at startup"));
|
||||
}
|
||||
|
||||
for cidr in cidr_ranges {
|
||||
if cidr.includes(&ip) {
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::forbidden(),
|
||||
"Requesting from this address is forbidden",
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -516,8 +714,20 @@ async fn request_url_preview(url: &str) -> Result<UrlPreviewData> {
|
||||
|
||||
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 cidr_ranges_s = services().globals.ip_range_denylist().to_vec();
|
||||
let mut cidr_ranges: Vec<IPAddress> = Vec::new();
|
||||
|
||||
for cidr in cidr_ranges_s {
|
||||
cidr_ranges.push(IPAddress::parse(cidr).expect("we checked this at startup"));
|
||||
}
|
||||
|
||||
for cidr in cidr_ranges {
|
||||
if cidr.includes(&ip) {
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::forbidden(),
|
||||
"Requesting from this address is forbidden",
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,9 +45,7 @@ use crate::{
|
||||
/// rules locally
|
||||
/// - If the server does not know about the room: asks other servers over
|
||||
/// federation
|
||||
pub(crate) async fn join_room_by_id_route(
|
||||
body: Ruma<join_room_by_id::v3::Request>,
|
||||
) -> Result<join_room_by_id::v3::Response> {
|
||||
pub async fn join_room_by_id_route(body: Ruma<join_room_by_id::v3::Request>) -> Result<join_room_by_id::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
if services().rooms.metadata.is_banned(&body.room_id)? && !services().users.is_admin(sender_user)? {
|
||||
@@ -117,10 +115,9 @@ pub(crate) async fn join_room_by_id_route(
|
||||
///
|
||||
/// - If the server knowns about this room: creates the join event and does auth
|
||||
/// rules locally
|
||||
/// - If the server does not know about the room: use the server name query
|
||||
/// param if specified. if not specified, asks other servers over federation
|
||||
/// via room alias server name and room ID server name
|
||||
pub(crate) async fn join_room_by_id_or_alias_route(
|
||||
/// - If the server does not know about the room: asks other servers over
|
||||
/// federation
|
||||
pub async fn join_room_by_id_or_alias_route(
|
||||
body: Ruma<join_room_by_id_or_alias::v3::Request>,
|
||||
) -> Result<join_room_by_id_or_alias::v3::Response> {
|
||||
let sender_user = body.sender_user.as_deref().expect("user is authenticated");
|
||||
@@ -155,6 +152,7 @@ pub(crate) async fn join_room_by_id_or_alias_route(
|
||||
}
|
||||
|
||||
let mut servers = body.server_name.clone();
|
||||
|
||||
servers.extend(
|
||||
services()
|
||||
.rooms
|
||||
@@ -183,24 +181,7 @@ pub(crate) async fn join_room_by_id_or_alias_route(
|
||||
(servers, room_id)
|
||||
},
|
||||
Err(room_alias) => {
|
||||
if services()
|
||||
.globals
|
||||
.config
|
||||
.forbidden_remote_server_names
|
||||
.contains(&room_alias.server_name().to_owned())
|
||||
&& !services().users.is_admin(sender_user)?
|
||||
{
|
||||
warn!(
|
||||
"User {sender_user} tried joining room alias {room_alias} which has a server name that is \
|
||||
globally forbidden. Rejecting.",
|
||||
);
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::forbidden(),
|
||||
"This remote server is banned on this homeserver.",
|
||||
));
|
||||
}
|
||||
|
||||
let response = get_alias_helper(room_alias.clone(), Some(body.server_name.clone())).await?;
|
||||
let response = get_alias_helper(room_alias.clone()).await?;
|
||||
|
||||
if services().rooms.metadata.is_banned(&response.room_id)? && !services().users.is_admin(sender_user)? {
|
||||
return Err(Error::BadRequest(
|
||||
@@ -217,9 +198,9 @@ pub(crate) async fn join_room_by_id_or_alias_route(
|
||||
&& !services().users.is_admin(sender_user)?
|
||||
{
|
||||
warn!(
|
||||
"User {sender_user} tried joining room alias {room_alias} with room ID {}, which the alias has a \
|
||||
server name that is globally forbidden. Rejecting.",
|
||||
&response.room_id
|
||||
"User {sender_user} tried joining room alias {} with room ID {} which has a server name that is \
|
||||
globally forbidden. Rejecting.",
|
||||
&room_alias, &response.room_id
|
||||
);
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::forbidden(),
|
||||
@@ -236,9 +217,9 @@ pub(crate) async fn join_room_by_id_or_alias_route(
|
||||
&& !services().users.is_admin(sender_user)?
|
||||
{
|
||||
warn!(
|
||||
"User {sender_user} tried joining room alias {room_alias} with room ID {}, which has a server \
|
||||
name that is globally forbidden. Rejecting.",
|
||||
&response.room_id
|
||||
"User {sender_user} tried joining room alias {} with room ID {} which has a server name that \
|
||||
is globally forbidden. Rejecting.",
|
||||
&room_alias, &response.room_id
|
||||
);
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::forbidden(),
|
||||
@@ -247,30 +228,7 @@ pub(crate) async fn join_room_by_id_or_alias_route(
|
||||
}
|
||||
}
|
||||
|
||||
let mut servers = body.server_name;
|
||||
servers.extend(response.servers);
|
||||
servers.extend(
|
||||
services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.servers_invite_via(&response.room_id)?
|
||||
.unwrap_or(
|
||||
services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.invite_state(sender_user, &response.room_id)?
|
||||
.unwrap_or_default()
|
||||
.iter()
|
||||
.filter_map(|event| serde_json::from_str(event.json().get()).ok())
|
||||
.filter_map(|event: serde_json::Value| event.get("sender").cloned())
|
||||
.filter_map(|sender| sender.as_str().map(ToOwned::to_owned))
|
||||
.filter_map(|sender| UserId::parse(sender).ok())
|
||||
.map(|user| user.server_name().to_owned())
|
||||
.collect(),
|
||||
),
|
||||
);
|
||||
|
||||
(servers, response.room_id)
|
||||
(response.servers, response.room_id)
|
||||
},
|
||||
};
|
||||
|
||||
@@ -293,7 +251,7 @@ pub(crate) async fn join_room_by_id_or_alias_route(
|
||||
/// Tries to leave the sender user from a room.
|
||||
///
|
||||
/// - This should always work if the user is currently joined.
|
||||
pub(crate) async fn leave_room_route(body: Ruma<leave_room::v3::Request>) -> Result<leave_room::v3::Response> {
|
||||
pub async fn leave_room_route(body: Ruma<leave_room::v3::Request>) -> Result<leave_room::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
leave_room(sender_user, &body.room_id, body.reason.clone()).await?;
|
||||
@@ -304,7 +262,7 @@ pub(crate) async fn leave_room_route(body: Ruma<leave_room::v3::Request>) -> Res
|
||||
/// # `POST /_matrix/client/r0/rooms/{roomId}/invite`
|
||||
///
|
||||
/// Tries to send an invite event into the room.
|
||||
pub(crate) async fn invite_user_route(body: Ruma<invite_user::v3::Request>) -> Result<invite_user::v3::Response> {
|
||||
pub async fn invite_user_route(body: Ruma<invite_user::v3::Request>) -> Result<invite_user::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
if !services().users.is_admin(sender_user)? && services().globals.block_non_admin_invites() {
|
||||
@@ -357,7 +315,7 @@ pub(crate) async fn invite_user_route(body: Ruma<invite_user::v3::Request>) -> R
|
||||
/// # `POST /_matrix/client/r0/rooms/{roomId}/kick`
|
||||
///
|
||||
/// Tries to send a kick event into the room.
|
||||
pub(crate) async fn kick_user_route(body: Ruma<kick_user::v3::Request>) -> Result<kick_user::v3::Response> {
|
||||
pub async fn kick_user_route(body: Ruma<kick_user::v3::Request>) -> Result<kick_user::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
if let Ok(true) = services()
|
||||
@@ -422,7 +380,7 @@ pub(crate) async fn kick_user_route(body: Ruma<kick_user::v3::Request>) -> Resul
|
||||
/// # `POST /_matrix/client/r0/rooms/{roomId}/ban`
|
||||
///
|
||||
/// Tries to send a ban event into the room.
|
||||
pub(crate) async fn ban_user_route(body: Ruma<ban_user::v3::Request>) -> Result<ban_user::v3::Response> {
|
||||
pub async fn ban_user_route(body: Ruma<ban_user::v3::Request>) -> Result<ban_user::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
if let Ok(Some(membership_event)) = services()
|
||||
@@ -508,7 +466,7 @@ pub(crate) async fn ban_user_route(body: Ruma<ban_user::v3::Request>) -> Result<
|
||||
/// # `POST /_matrix/client/r0/rooms/{roomId}/unban`
|
||||
///
|
||||
/// Tries to send an unban event into the room.
|
||||
pub(crate) async fn unban_user_route(body: Ruma<unban_user::v3::Request>) -> Result<unban_user::v3::Response> {
|
||||
pub async fn unban_user_route(body: Ruma<unban_user::v3::Request>) -> Result<unban_user::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
if let Ok(Some(membership_event)) = services()
|
||||
@@ -579,7 +537,7 @@ pub(crate) async fn unban_user_route(body: Ruma<unban_user::v3::Request>) -> Res
|
||||
///
|
||||
/// Note: Other devices of the user have no way of knowing the room was
|
||||
/// forgotten, so this has to be called from every device
|
||||
pub(crate) async fn forget_room_route(body: Ruma<forget_room::v3::Request>) -> Result<forget_room::v3::Response> {
|
||||
pub async fn forget_room_route(body: Ruma<forget_room::v3::Request>) -> Result<forget_room::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
services()
|
||||
@@ -593,7 +551,7 @@ pub(crate) async fn forget_room_route(body: Ruma<forget_room::v3::Request>) -> R
|
||||
/// # `POST /_matrix/client/r0/joined_rooms`
|
||||
///
|
||||
/// Lists all rooms the user has joined.
|
||||
pub(crate) async fn joined_rooms_route(body: Ruma<joined_rooms::v3::Request>) -> Result<joined_rooms::v3::Response> {
|
||||
pub async fn joined_rooms_route(body: Ruma<joined_rooms::v3::Request>) -> Result<joined_rooms::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
Ok(joined_rooms::v3::Response {
|
||||
@@ -612,7 +570,7 @@ pub(crate) async fn joined_rooms_route(body: Ruma<joined_rooms::v3::Request>) ->
|
||||
/// specific membership).
|
||||
///
|
||||
/// - Only works if the user is currently joined
|
||||
pub(crate) async fn get_member_events_route(
|
||||
pub async fn get_member_events_route(
|
||||
body: Ruma<get_member_events::v3::Request>,
|
||||
) -> Result<get_member_events::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -647,9 +605,7 @@ pub(crate) async fn get_member_events_route(
|
||||
///
|
||||
/// - The sender user must be in the room
|
||||
/// - TODO: An appservice just needs a puppet joined
|
||||
pub(crate) async fn joined_members_route(
|
||||
body: Ruma<joined_members::v3::Request>,
|
||||
) -> Result<joined_members::v3::Response> {
|
||||
pub async fn joined_members_route(body: Ruma<joined_members::v3::Request>) -> Result<joined_members::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
if !services()
|
||||
@@ -967,7 +923,7 @@ pub(crate) async fn join_room_by_id_helper(
|
||||
.add_pdu_outlier(&event_id, &value)?;
|
||||
}
|
||||
|
||||
debug!("Running send_join auth check");
|
||||
info!("Running send_join auth check");
|
||||
|
||||
let auth_check = state_res::event_auth::auth_check(
|
||||
&state_res::RoomVersion::new(&room_version_id).expect("room version is supported"),
|
||||
@@ -991,11 +947,11 @@ pub(crate) async fn join_room_by_id_helper(
|
||||
)
|
||||
.map_err(|e| {
|
||||
warn!("Auth check failed: {e}");
|
||||
Error::BadRequest(ErrorKind::forbidden(), "Auth check failed")
|
||||
Error::BadRequest(ErrorKind::InvalidParam, "Auth check failed")
|
||||
})?;
|
||||
|
||||
if !auth_check {
|
||||
return Err(Error::BadRequest(ErrorKind::forbidden(), "Auth check failed"));
|
||||
return Err(Error::BadRequest(ErrorKind::InvalidParam, "Auth check failed"));
|
||||
}
|
||||
|
||||
info!("Saving state from send_join");
|
||||
@@ -1150,7 +1106,7 @@ pub(crate) async fn join_room_by_id_helper(
|
||||
if !restriction_rooms.is_empty()
|
||||
&& servers
|
||||
.iter()
|
||||
.any(|s| *s != services().globals.server_name())
|
||||
.all(|s| *s != services().globals.server_name())
|
||||
{
|
||||
info!(
|
||||
"We couldn't do the join locally, maybe federation can help to satisfy the restricted join \
|
||||
@@ -1281,7 +1237,7 @@ pub(crate) async fn join_room_by_id_helper(
|
||||
services()
|
||||
.rooms
|
||||
.event_handler
|
||||
.handle_incoming_pdu(&remote_server, room_id, &signed_event_id, signed_value, true, &pub_key_map)
|
||||
.handle_incoming_pdu(&remote_server, &signed_event_id, room_id, signed_value, true, &pub_key_map)
|
||||
.await?;
|
||||
} else {
|
||||
return Err(error);
|
||||
@@ -1538,7 +1494,7 @@ pub(crate) async fn invite_helper(
|
||||
let pdu_id: Vec<u8> = services()
|
||||
.rooms
|
||||
.event_handler
|
||||
.handle_incoming_pdu(&origin, room_id, &event_id, value, true, &pub_key_map)
|
||||
.handle_incoming_pdu(&origin, &event_id, room_id, value, true, &pub_key_map)
|
||||
.await?
|
||||
.ok_or(Error::BadRequest(
|
||||
ErrorKind::InvalidParam,
|
||||
@@ -1604,7 +1560,7 @@ pub(crate) async fn invite_helper(
|
||||
}
|
||||
|
||||
// Make a user leave all their joined rooms
|
||||
pub(crate) async fn leave_all_rooms(user_id: &UserId) -> Result<()> {
|
||||
pub async fn leave_all_rooms(user_id: &UserId) -> Result<()> {
|
||||
let all_rooms = services()
|
||||
.rooms
|
||||
.state_cache
|
||||
@@ -1630,7 +1586,7 @@ pub(crate) async fn leave_all_rooms(user_id: &UserId) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn leave_room(user_id: &UserId, room_id: &RoomId, reason: Option<String>) -> Result<()> {
|
||||
pub async fn leave_room(user_id: &UserId, room_id: &RoomId, reason: Option<String>) -> Result<()> {
|
||||
// Ask a remote server if we don't have this room
|
||||
if !services()
|
||||
.rooms
|
||||
|
||||
@@ -28,7 +28,7 @@ use crate::{
|
||||
/// - The only requirement for the content is that it has to be valid json
|
||||
/// - Tries to send the event into the room, auth rules will determine if it is
|
||||
/// allowed
|
||||
pub(crate) async fn send_message_event_route(
|
||||
pub async fn send_message_event_route(
|
||||
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");
|
||||
@@ -119,7 +119,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(
|
||||
pub async fn get_message_events_route(
|
||||
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");
|
||||
|
||||
@@ -34,50 +34,50 @@ mod unversioned;
|
||||
mod user_directory;
|
||||
mod voip;
|
||||
|
||||
pub(crate) use account::*;
|
||||
pub(crate) use alias::*;
|
||||
pub(crate) use backup::*;
|
||||
pub(crate) use capabilities::*;
|
||||
pub(crate) use config::*;
|
||||
pub(crate) use context::*;
|
||||
pub(crate) use device::*;
|
||||
pub(crate) use directory::*;
|
||||
pub(crate) use filter::*;
|
||||
pub(crate) use keys::*;
|
||||
pub(crate) use media::*;
|
||||
pub(crate) use membership::*;
|
||||
pub(crate) use message::*;
|
||||
pub(crate) use presence::*;
|
||||
pub(crate) use profile::*;
|
||||
pub(crate) use push::*;
|
||||
pub(crate) use read_marker::*;
|
||||
pub(crate) use redact::*;
|
||||
pub(crate) use relations::*;
|
||||
pub(crate) use report::*;
|
||||
pub(crate) use room::*;
|
||||
pub(crate) use search::*;
|
||||
pub(crate) use session::*;
|
||||
pub(crate) use space::*;
|
||||
pub(crate) use state::*;
|
||||
pub(crate) use sync::*;
|
||||
pub(crate) use tag::*;
|
||||
pub(crate) use thirdparty::*;
|
||||
pub(crate) use threads::*;
|
||||
pub(crate) use to_device::*;
|
||||
pub(crate) use typing::*;
|
||||
pub(crate) use unstable::*;
|
||||
pub(crate) use unversioned::*;
|
||||
pub(crate) use user_directory::*;
|
||||
pub(crate) use voip::*;
|
||||
pub use account::*;
|
||||
pub use alias::*;
|
||||
pub use backup::*;
|
||||
pub use capabilities::*;
|
||||
pub use config::*;
|
||||
pub use context::*;
|
||||
pub use device::*;
|
||||
pub use directory::*;
|
||||
pub use filter::*;
|
||||
pub use keys::*;
|
||||
pub use media::*;
|
||||
pub use membership::*;
|
||||
pub use message::*;
|
||||
pub use presence::*;
|
||||
pub use profile::*;
|
||||
pub use push::*;
|
||||
pub use read_marker::*;
|
||||
pub use redact::*;
|
||||
pub use relations::*;
|
||||
pub use report::*;
|
||||
pub use room::*;
|
||||
pub use search::*;
|
||||
pub use session::*;
|
||||
pub use space::*;
|
||||
pub use state::*;
|
||||
pub use sync::*;
|
||||
pub use tag::*;
|
||||
pub use thirdparty::*;
|
||||
pub use threads::*;
|
||||
pub use to_device::*;
|
||||
pub use typing::*;
|
||||
pub use unstable::*;
|
||||
pub use unversioned::*;
|
||||
pub use user_directory::*;
|
||||
pub use voip::*;
|
||||
|
||||
/// generated device ID length
|
||||
const DEVICE_ID_LENGTH: usize = 10;
|
||||
pub const DEVICE_ID_LENGTH: usize = 10;
|
||||
|
||||
/// generated user access token length
|
||||
const TOKEN_LENGTH: usize = 32;
|
||||
pub const TOKEN_LENGTH: usize = 32;
|
||||
|
||||
/// generated user session ID length
|
||||
pub(crate) const SESSION_ID_LENGTH: usize = 32;
|
||||
pub const SESSION_ID_LENGTH: usize = 32;
|
||||
|
||||
/// auto-generated password length
|
||||
pub(crate) const AUTO_GEN_PASSWORD_LENGTH: usize = 25;
|
||||
pub const AUTO_GEN_PASSWORD_LENGTH: usize = 25;
|
||||
|
||||
@@ -10,7 +10,7 @@ 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(body: Ruma<set_presence::v3::Request>) -> Result<set_presence::v3::Response> {
|
||||
pub 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"));
|
||||
}
|
||||
@@ -28,7 +28,7 @@ pub(crate) async fn set_presence_route(body: Ruma<set_presence::v3::Request>) ->
|
||||
/// 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(body: Ruma<get_presence::v3::Request>) -> Result<get_presence::v3::Response> {
|
||||
pub 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"));
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ use crate::{service::pdu::PduBuilder, services, Error, Result, Ruma};
|
||||
/// Updates the displayname.
|
||||
///
|
||||
/// - Also makes sure other users receive the update using presence EDUs
|
||||
pub(crate) async fn set_displayname_route(
|
||||
pub async fn set_displayname_route(
|
||||
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");
|
||||
@@ -102,7 +102,7 @@ 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(
|
||||
pub async fn get_displayname_route(
|
||||
body: Ruma<get_display_name::v3::Request>,
|
||||
) -> Result<get_display_name::v3::Response> {
|
||||
if body.user_id.server_name() != services().globals.server_name() {
|
||||
@@ -157,9 +157,7 @@ pub(crate) async fn get_displayname_route(
|
||||
/// Updates the `avatar_url` and `blurhash`.
|
||||
///
|
||||
/// - Also makes sure other users receive the update using presence EDUs
|
||||
pub(crate) async fn set_avatar_url_route(
|
||||
body: Ruma<set_avatar_url::v3::Request>,
|
||||
) -> Result<set_avatar_url::v3::Response> {
|
||||
pub async fn set_avatar_url_route(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");
|
||||
|
||||
services()
|
||||
@@ -244,9 +242,7 @@ 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(
|
||||
body: Ruma<get_avatar_url::v3::Request>,
|
||||
) -> Result<get_avatar_url::v3::Response> {
|
||||
pub async fn get_avatar_url_route(body: Ruma<get_avatar_url::v3::Request>) -> Result<get_avatar_url::v3::Response> {
|
||||
if body.user_id.server_name() != services().globals.server_name() {
|
||||
// Create and update our local copy of the user
|
||||
if let Ok(response) = services()
|
||||
@@ -302,7 +298,7 @@ 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(body: Ruma<get_profile::v3::Request>) -> Result<get_profile::v3::Response> {
|
||||
pub async fn get_profile_route(body: Ruma<get_profile::v3::Request>) -> Result<get_profile::v3::Response> {
|
||||
if body.user_id.server_name() != services().globals.server_name() {
|
||||
// Create and update our local copy of the user
|
||||
if let Ok(response) = services()
|
||||
|
||||
@@ -15,7 +15,7 @@ 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(
|
||||
pub async fn get_pushrules_all_route(
|
||||
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");
|
||||
@@ -55,7 +55,7 @@ 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(body: Ruma<get_pushrule::v3::Request>) -> Result<get_pushrule::v3::Response> {
|
||||
pub 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()
|
||||
@@ -84,7 +84,7 @@ pub(crate) async fn get_pushrule_route(body: Ruma<get_pushrule::v3::Request>) ->
|
||||
/// # `PUT /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}`
|
||||
///
|
||||
/// Creates a single specified push rule for this user.
|
||||
pub(crate) async fn set_pushrule_route(body: Ruma<set_pushrule::v3::Request>) -> Result<set_pushrule::v3::Response> {
|
||||
pub 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;
|
||||
|
||||
@@ -147,7 +147,7 @@ pub(crate) async fn set_pushrule_route(body: Ruma<set_pushrule::v3::Request>) ->
|
||||
/// # `GET /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}/actions`
|
||||
///
|
||||
/// Gets the actions of a single specified push rule for this user.
|
||||
pub(crate) async fn get_pushrule_actions_route(
|
||||
pub async fn get_pushrule_actions_route(
|
||||
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");
|
||||
@@ -182,7 +182,7 @@ pub(crate) async fn get_pushrule_actions_route(
|
||||
/// # `PUT /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}/actions`
|
||||
///
|
||||
/// Sets the actions of a single specified push rule for this user.
|
||||
pub(crate) async fn set_pushrule_actions_route(
|
||||
pub async fn set_pushrule_actions_route(
|
||||
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");
|
||||
@@ -224,7 +224,7 @@ pub(crate) async fn set_pushrule_actions_route(
|
||||
/// # `GET /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}/enabled`
|
||||
///
|
||||
/// Gets the enabled status of a single specified push rule for this user.
|
||||
pub(crate) async fn get_pushrule_enabled_route(
|
||||
pub async fn get_pushrule_enabled_route(
|
||||
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");
|
||||
@@ -258,7 +258,7 @@ pub(crate) async fn get_pushrule_enabled_route(
|
||||
/// # `PUT /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}/enabled`
|
||||
///
|
||||
/// Sets the enabled status of a single specified push rule for this user.
|
||||
pub(crate) async fn set_pushrule_enabled_route(
|
||||
pub async fn set_pushrule_enabled_route(
|
||||
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");
|
||||
@@ -300,9 +300,7 @@ pub(crate) async fn set_pushrule_enabled_route(
|
||||
/// # `DELETE /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}`
|
||||
///
|
||||
/// Deletes a single specified push rule for this user.
|
||||
pub(crate) async fn delete_pushrule_route(
|
||||
body: Ruma<delete_pushrule::v3::Request>,
|
||||
) -> Result<delete_pushrule::v3::Response> {
|
||||
pub async fn delete_pushrule_route(body: Ruma<delete_pushrule::v3::Request>) -> Result<delete_pushrule::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
if body.scope != RuleScope::Global {
|
||||
@@ -349,7 +347,7 @@ 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(body: Ruma<get_pushers::v3::Request>) -> Result<get_pushers::v3::Response> {
|
||||
pub 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 {
|
||||
@@ -362,7 +360,7 @@ pub(crate) async fn get_pushers_route(body: Ruma<get_pushers::v3::Request>) -> R
|
||||
/// Adds a pusher for the sender user.
|
||||
///
|
||||
/// - TODO: Handle `append`
|
||||
pub(crate) async fn set_pushers_route(body: Ruma<set_pusher::v3::Request>) -> Result<set_pusher::v3::Response> {
|
||||
pub 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()
|
||||
|
||||
@@ -18,9 +18,7 @@ use crate::{service::rooms::timeline::PduCount, services, Error, Result, Ruma};
|
||||
/// - Updates fully-read account data event to `fully_read`
|
||||
/// - If `read_receipt` is set: Update private marker and public read receipt
|
||||
/// EDU
|
||||
pub(crate) async fn set_read_marker_route(
|
||||
body: Ruma<set_read_marker::v3::Request>,
|
||||
) -> Result<set_read_marker::v3::Response> {
|
||||
pub async fn set_read_marker_route(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");
|
||||
|
||||
if let Some(fully_read) = &body.fully_read {
|
||||
@@ -97,9 +95,7 @@ pub(crate) async fn set_read_marker_route(
|
||||
/// # `POST /_matrix/client/r0/rooms/{roomId}/receipt/{receiptType}/{eventId}`
|
||||
///
|
||||
/// Sets private read marker and public read receipt EDU.
|
||||
pub(crate) async fn create_receipt_route(
|
||||
body: Ruma<create_receipt::v3::Request>,
|
||||
) -> Result<create_receipt::v3::Response> {
|
||||
pub async fn create_receipt_route(body: Ruma<create_receipt::v3::Request>) -> Result<create_receipt::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
if matches!(
|
||||
|
||||
@@ -13,7 +13,7 @@ use crate::{service::pdu::PduBuilder, services, Result, Ruma};
|
||||
/// Tries to send a redaction event into the room.
|
||||
///
|
||||
/// - TODO: Handle txn id
|
||||
pub(crate) async fn redact_event_route(body: Ruma<redact_event::v3::Request>) -> Result<redact_event::v3::Response> {
|
||||
pub 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;
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ use ruma::api::client::relations::{
|
||||
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(
|
||||
pub async fn get_relating_events_with_rel_type_and_event_type_route(
|
||||
body: Ruma<get_relating_events_with_rel_type_and_event_type::v1::Request>,
|
||||
) -> Result<get_relating_events_with_rel_type_and_event_type::v1::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -35,7 +35,7 @@ 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(
|
||||
pub async fn get_relating_events_with_rel_type_route(
|
||||
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");
|
||||
@@ -65,7 +65,7 @@ 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(
|
||||
pub async fn get_relating_events_route(
|
||||
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");
|
||||
|
||||
@@ -14,9 +14,7 @@ use crate::{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(
|
||||
body: Ruma<report_content::v3::Request>,
|
||||
) -> Result<report_content::v3::Response> {
|
||||
pub async fn report_event_route(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");
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ use crate::{api::client_server::invite_helper, service::pdu::PduBuilder, service
|
||||
/// - Send events listed in initial state
|
||||
/// - Send events implied by `name` and `topic`
|
||||
/// - Send invite events
|
||||
pub(crate) async fn create_room_route(body: Ruma<create_room::v3::Request>) -> Result<create_room::v3::Response> {
|
||||
pub 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");
|
||||
@@ -603,9 +603,7 @@ pub(crate) async fn create_room_route(body: Ruma<create_room::v3::Request>) -> R
|
||||
///
|
||||
/// - You have to currently be joined to the room (TODO: Respect history
|
||||
/// visibility)
|
||||
pub(crate) async fn get_room_event_route(
|
||||
body: Ruma<get_room_event::v3::Request>,
|
||||
) -> Result<get_room_event::v3::Response> {
|
||||
pub async fn get_room_event_route(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()
|
||||
@@ -642,7 +640,7 @@ 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(body: Ruma<aliases::v3::Request>) -> Result<aliases::v3::Response> {
|
||||
pub 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()
|
||||
@@ -676,7 +674,7 @@ pub(crate) async fn get_room_aliases_route(body: Ruma<aliases::v3::Request>) ->
|
||||
/// - Transfers some state events
|
||||
/// - Moves local aliases
|
||||
/// - Modifies old room power levels to prevent users from speaking
|
||||
pub(crate) async fn upgrade_room_route(body: Ruma<upgrade_room::v3::Request>) -> Result<upgrade_room::v3::Response> {
|
||||
pub 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()
|
||||
|
||||
@@ -22,7 +22,7 @@ use crate::{services, 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(body: Ruma<search_events::v3::Request>) -> Result<search_events::v3::Response> {
|
||||
pub 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,9 +33,7 @@ struct Claims {
|
||||
///
|
||||
/// Get the supported login types of this server. One of these should be used as
|
||||
/// the `type` field when logging in.
|
||||
pub(crate) async fn get_login_types_route(
|
||||
_body: Ruma<get_login_types::v3::Request>,
|
||||
) -> Result<get_login_types::v3::Response> {
|
||||
pub async fn get_login_types_route(_body: Ruma<get_login_types::v3::Request>) -> Result<get_login_types::v3::Response> {
|
||||
Ok(get_login_types::v3::Response::new(vec to see
|
||||
/// supported login types.
|
||||
pub(crate) async fn login_route(body: Ruma<login::v3::Request>) -> Result<login::v3::Response> {
|
||||
pub 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 {
|
||||
@@ -237,7 +235,7 @@ pub(crate) async fn login_route(body: Ruma<login::v3::Request>) -> Result<login:
|
||||
/// last seen ts)
|
||||
/// - Forgets to-device events
|
||||
/// - Triggers device list updates
|
||||
pub(crate) async fn logout_route(body: Ruma<logout::v3::Request>) -> Result<logout::v3::Response> {
|
||||
pub 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");
|
||||
|
||||
@@ -262,7 +260,7 @@ pub(crate) async fn logout_route(body: Ruma<logout::v3::Request>) -> Result<logo
|
||||
/// 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(body: Ruma<logout_all::v3::Request>) -> Result<logout_all::v3::Response> {
|
||||
pub 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() {
|
||||
|
||||
@@ -11,7 +11,7 @@ use crate::{service::rooms::spaces::PagnationToken, services, Error, Result, Rum
|
||||
///
|
||||
/// 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(body: Ruma<get_hierarchy::v1::Request>) -> Result<get_hierarchy::v1::Response> {
|
||||
pub 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
|
||||
|
||||
@@ -8,7 +8,6 @@ use ruma::{
|
||||
events::{
|
||||
room::{
|
||||
canonical_alias::RoomCanonicalAliasEventContent,
|
||||
history_visibility::{HistoryVisibility, RoomHistoryVisibilityEventContent},
|
||||
join_rules::{JoinRule, RoomJoinRulesEventContent},
|
||||
},
|
||||
AnyStateEventContent, StateEventType,
|
||||
@@ -31,7 +30,7 @@ use crate::{
|
||||
/// - Tries to send the event into the room, auth rules will determine if it is
|
||||
/// allowed
|
||||
/// - If event is new `canonical_alias`: Rejects if alias is incorrect
|
||||
pub(crate) async fn send_state_event_for_key_route(
|
||||
pub async fn send_state_event_for_key_route(
|
||||
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");
|
||||
@@ -59,7 +58,7 @@ pub(crate) async fn send_state_event_for_key_route(
|
||||
/// - Tries to send the event into the room, auth rules will determine if it is
|
||||
/// allowed
|
||||
/// - If event is new `canonical_alias`: Rejects if alias is incorrect
|
||||
pub(crate) async fn send_state_event_for_empty_key_route(
|
||||
pub async fn send_state_event_for_empty_key_route(
|
||||
body: Ruma<send_state_event::v3::Request>,
|
||||
) -> Result<RumaResponse<send_state_event::v3::Response>> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -86,7 +85,7 @@ 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(
|
||||
pub async fn get_state_events_route(
|
||||
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");
|
||||
@@ -122,7 +121,7 @@ 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(
|
||||
pub async fn get_state_events_for_key_route(
|
||||
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");
|
||||
@@ -177,7 +176,7 @@ 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(
|
||||
pub async fn get_state_events_for_empty_key_route(
|
||||
body: Ruma<get_state_events_for_key::v3::Request>,
|
||||
) -> Result<RumaResponse<get_state_events_for_key::v3::Response>> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
@@ -252,23 +251,6 @@ async fn send_state_event_for_key_helper(
|
||||
}
|
||||
}
|
||||
},
|
||||
// admin room is a sensitive room, it should not ever be made world readable
|
||||
StateEventType::RoomHistoryVisibility => {
|
||||
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())
|
||||
{
|
||||
if visibility_content.history_visibility == HistoryVisibility::WorldReadable {
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::forbidden(),
|
||||
"Admin room is not allowed to be made world readable (public room history).",
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
// TODO: allow alias if it previously existed
|
||||
StateEventType::RoomCanonicalAlias => {
|
||||
if let Ok(canonical_alias) = serde_json::from_str::<RoomCanonicalAliasEventContent>(json.json().get()) {
|
||||
|
||||
@@ -77,7 +77,7 @@ use crate::{
|
||||
/// - Sync is handled in an async task, multiple requests from the same device
|
||||
/// with the same
|
||||
/// `since` will be cached
|
||||
pub(crate) async fn sync_events_route(
|
||||
pub async fn sync_events_route(
|
||||
body: Ruma<sync_events::v3::Request>,
|
||||
) -> Result<sync_events::v3::Response, RumaResponse<UiaaResponse>> {
|
||||
let sender_user = body.sender_user.expect("user is authenticated");
|
||||
@@ -1172,7 +1172,7 @@ fn share_encrypted_room(sender_user: &UserId, user_id: &UserId, ignore_room: &Ro
|
||||
/// POST `/_matrix/client/unstable/org.matrix.msc3575/sync`
|
||||
///
|
||||
/// Sliding Sync endpoint (future endpoint: `/_matrix/client/v4/sync`)
|
||||
pub(crate) async fn sync_events_v4_route(
|
||||
pub async fn sync_events_v4_route(
|
||||
body: Ruma<sync_events::v4::Request>,
|
||||
) -> Result<sync_events::v4::Response, RumaResponse<UiaaResponse>> {
|
||||
let sender_user = body.sender_user.expect("user is authenticated");
|
||||
|
||||
@@ -15,7 +15,7 @@ use crate::{services, Error, Result, Ruma};
|
||||
/// 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(body: Ruma<create_tag::v3::Request>) -> Result<create_tag::v3::Response> {
|
||||
pub 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()
|
||||
@@ -53,7 +53,7 @@ pub(crate) async fn update_tag_route(body: Ruma<create_tag::v3::Request>) -> Res
|
||||
/// 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(body: Ruma<delete_tag::v3::Request>) -> Result<delete_tag::v3::Response> {
|
||||
pub 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()
|
||||
@@ -88,7 +88,7 @@ pub(crate) async fn delete_tag_route(body: Ruma<delete_tag::v3::Request>) -> Res
|
||||
/// Returns tags on the room.
|
||||
///
|
||||
/// - Gets the tag event of the room account data.
|
||||
pub(crate) async fn get_tags_route(body: Ruma<get_tags::v3::Request>) -> Result<get_tags::v3::Response> {
|
||||
pub 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()
|
||||
|
||||
@@ -7,9 +7,7 @@ use crate::{Result, Ruma};
|
||||
/// # `GET /_matrix/client/r0/thirdparty/protocols`
|
||||
///
|
||||
/// TODO: Fetches all metadata about protocols supported by the homeserver.
|
||||
pub(crate) async fn get_protocols_route(
|
||||
_body: Ruma<get_protocols::v3::Request>,
|
||||
) -> Result<get_protocols::v3::Response> {
|
||||
pub async fn get_protocols_route(_body: Ruma<get_protocols::v3::Request>) -> Result<get_protocols::v3::Response> {
|
||||
// TODO
|
||||
Ok(get_protocols::v3::Response {
|
||||
protocols: BTreeMap::new(),
|
||||
|
||||
@@ -3,7 +3,7 @@ use ruma::api::client::{error::ErrorKind, threads::get_threads};
|
||||
use crate::{services, Error, Result, Ruma};
|
||||
|
||||
/// # `GET /_matrix/client/r0/rooms/{roomId}/threads`
|
||||
pub(crate) async fn get_threads_route(body: Ruma<get_threads::v1::Request>) -> Result<get_threads::v1::Response> {
|
||||
pub 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
|
||||
|
||||
@@ -13,7 +13,7 @@ use crate::{services, 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(
|
||||
pub async fn send_event_to_device_route(
|
||||
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");
|
||||
|
||||
@@ -5,7 +5,7 @@ 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(
|
||||
pub async fn create_typing_event_route(
|
||||
body: Ruma<create_typing_event::v3::Request>,
|
||||
) -> Result<create_typing_event::v3::Response> {
|
||||
use create_typing_event::v3::Typing;
|
||||
|
||||
@@ -12,7 +12,7 @@ use crate::{services, Error, Result, Ruma};
|
||||
/// TODO: Implement pagination, currently this just returns everything
|
||||
///
|
||||
/// An implementation of [MSC2666](https://github.com/matrix-org/matrix-spec-proposals/pull/2666)
|
||||
pub(crate) async fn get_mutual_rooms_route(
|
||||
pub async fn get_mutual_rooms_route(
|
||||
body: Ruma<mutual_rooms::unstable::Request>,
|
||||
) -> Result<mutual_rooms::unstable::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
@@ -24,7 +24,7 @@ use crate::{services, Error, Result, Ruma};
|
||||
///
|
||||
/// Note: Unstable features are used while developing new features. Clients
|
||||
/// should avoid using unstable features in their stable releases
|
||||
pub(crate) async fn get_supported_versions_route(
|
||||
pub async fn get_supported_versions_route(
|
||||
_body: Ruma<get_supported_versions::Request>,
|
||||
) -> Result<get_supported_versions::Response> {
|
||||
let resp = get_supported_versions::Response {
|
||||
@@ -60,9 +60,7 @@ pub(crate) async fn get_supported_versions_route(
|
||||
/// # `GET /.well-known/matrix/client`
|
||||
///
|
||||
/// Returns the .well-known URL if it is configured, otherwise returns 404.
|
||||
pub(crate) async fn well_known_client(
|
||||
_body: Ruma<discover_homeserver::Request>,
|
||||
) -> Result<discover_homeserver::Response> {
|
||||
pub async fn well_known_client(_body: Ruma<discover_homeserver::Request>) -> Result<discover_homeserver::Response> {
|
||||
let client_url = match services().globals.well_known_client() {
|
||||
Some(url) => url.to_string(),
|
||||
None => return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")),
|
||||
@@ -83,7 +81,7 @@ 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(_body: Ruma<discover_support::Request>) -> Result<discover_support::Response> {
|
||||
pub async fn well_known_support(_body: Ruma<discover_support::Request>) -> Result<discover_support::Response> {
|
||||
let support_page = services()
|
||||
.globals
|
||||
.well_known_support_page()
|
||||
@@ -133,7 +131,7 @@ pub(crate) async fn well_known_support(_body: Ruma<discover_support::Request>) -
|
||||
///
|
||||
/// Endpoint provided by sliding sync proxy used by some clients such as Element
|
||||
/// Web as a non-standard health check.
|
||||
pub(crate) async fn syncv3_client_server_json() -> Result<impl IntoResponse> {
|
||||
pub 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() {
|
||||
@@ -157,7 +155,7 @@ pub(crate) async fn syncv3_client_server_json() -> Result<impl IntoResponse> {
|
||||
///
|
||||
/// Conduwuit-specific API to get the server version, results akin to
|
||||
/// `/_matrix/federation/v1/version`
|
||||
pub(crate) async fn conduwuit_server_version() -> Result<impl IntoResponse> {
|
||||
pub async fn conduwuit_server_version() -> Result<impl IntoResponse> {
|
||||
let version = match option_env!("CONDUIT_VERSION_EXTRA") {
|
||||
Some(extra) => format!("{} ({})", env!("CARGO_PKG_VERSION"), extra),
|
||||
None => env!("CARGO_PKG_VERSION").to_owned(),
|
||||
|
||||
@@ -15,7 +15,7 @@ use crate::{services, 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(body: Ruma<search_users::v3::Request>) -> Result<search_users::v3::Response> {
|
||||
pub 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 = u64::from(body.limit) as usize;
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ type HmacSha1 = Hmac<Sha1>;
|
||||
/// # `GET /_matrix/client/r0/voip/turnServer`
|
||||
///
|
||||
/// TODO: Returns information about the recommended turn server.
|
||||
pub(crate) async fn turn_server_route(
|
||||
pub async fn turn_server_route(
|
||||
body: Ruma<get_turn_server_info::v3::Request>,
|
||||
) -> Result<get_turn_server_info::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
+3
-3
@@ -1,3 +1,3 @@
|
||||
pub(crate) mod client_server;
|
||||
pub(crate) mod ruma_wrapper;
|
||||
pub(crate) mod server_server;
|
||||
pub mod client_server;
|
||||
pub mod ruma_wrapper;
|
||||
pub mod server_server;
|
||||
|
||||
@@ -2,22 +2,17 @@ use std::{collections::BTreeMap, str};
|
||||
|
||||
use axum::{
|
||||
async_trait,
|
||||
extract::{FromRequest, Path},
|
||||
response::{IntoResponse, Response},
|
||||
RequestExt, RequestPartsExt,
|
||||
};
|
||||
use axum_extra::{
|
||||
body::{Full, HttpBody},
|
||||
extract::{rejection::TypedHeaderRejectionReason, FromRequest, Path, TypedHeader},
|
||||
headers::{
|
||||
authorization::{Bearer, Credentials},
|
||||
Authorization,
|
||||
},
|
||||
typed_header::TypedHeaderRejectionReason,
|
||||
TypedHeader,
|
||||
response::{IntoResponse, Response},
|
||||
BoxError, RequestExt, RequestPartsExt,
|
||||
};
|
||||
use bytes::{BufMut, BytesMut};
|
||||
use http::{uri::PathAndQuery, StatusCode};
|
||||
use http_body_util::Full;
|
||||
use hyper::Request;
|
||||
use bytes::{Buf, BufMut, Bytes, BytesMut};
|
||||
use http::{uri::PathAndQuery, Request, StatusCode};
|
||||
use ruma::{
|
||||
api::{client::error::ErrorKind, AuthScheme, IncomingRequest, OutgoingResponse},
|
||||
CanonicalJsonValue, OwnedDeviceId, OwnedServerName, OwnedUserId, UserId,
|
||||
@@ -42,27 +37,33 @@ struct QueryParams {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<T, S> FromRequest<S, axum::body::Body> for Ruma<T>
|
||||
impl<T, S, B> FromRequest<S, B> for Ruma<T>
|
||||
where
|
||||
T: IncomingRequest,
|
||||
B: HttpBody + Send + 'static,
|
||||
B::Data: Send,
|
||||
B::Error: Into<BoxError>,
|
||||
{
|
||||
type Rejection = Error;
|
||||
|
||||
#[allow(unused_qualifications)] // async traits
|
||||
async fn from_request(req: Request<axum::body::Body>, _state: &S) -> Result<Self, Self::Rejection> {
|
||||
let limited = req.with_limited_body();
|
||||
let (mut parts, body) = limited.into_parts();
|
||||
let mut body = axum::body::to_bytes(
|
||||
body,
|
||||
services()
|
||||
.globals
|
||||
.config
|
||||
.max_request_size
|
||||
.try_into()
|
||||
.expect("failed to convert max request size"),
|
||||
)
|
||||
.await
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::MissingToken, "Missing token."))?;
|
||||
async fn from_request(req: Request<B>, _state: &S) -> Result<Self, Self::Rejection> {
|
||||
let (mut parts, mut body) = match req.with_limited_body() {
|
||||
Ok(limited_req) => {
|
||||
let (parts, body) = limited_req.into_parts();
|
||||
let body = to_bytes(body)
|
||||
.await
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::MissingToken, "Missing token."))?;
|
||||
(parts, body)
|
||||
},
|
||||
Err(original_req) => {
|
||||
let (parts, body) = original_req.into_parts();
|
||||
let body = to_bytes(body)
|
||||
.await
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::MissingToken, "Missing token."))?;
|
||||
(parts, body)
|
||||
},
|
||||
};
|
||||
|
||||
let metadata = T::METADATA;
|
||||
let auth_header: Option<TypedHeader<Authorization<Bearer>>> = parts.extract().await?;
|
||||
@@ -132,7 +133,7 @@ where
|
||||
"Unknown access token.",
|
||||
))
|
||||
},
|
||||
(AuthScheme::AccessToken, Token::Appservice(info)) => {
|
||||
(AuthScheme::AccessToken | AuthScheme::AccessTokenOptional, Token::Appservice(info)) => {
|
||||
let user_id = query_params
|
||||
.user_id
|
||||
.map_or_else(
|
||||
@@ -156,10 +157,9 @@ where
|
||||
|
||||
(Some(user_id), None, None, Some(*info))
|
||||
},
|
||||
(
|
||||
AuthScheme::None | AuthScheme::AccessTokenOptional | AuthScheme::AppserviceToken,
|
||||
Token::Appservice(info),
|
||||
) => (None, None, None, Some(*info)),
|
||||
(AuthScheme::None | AuthScheme::AppserviceToken, Token::Appservice(info)) => {
|
||||
(None, None, None, Some(*info))
|
||||
},
|
||||
(AuthScheme::AccessToken, Token::None) => {
|
||||
return Err(Error::BadRequest(ErrorKind::MissingToken, "Missing access token."));
|
||||
},
|
||||
@@ -397,3 +397,55 @@ impl<T: OutgoingResponse> IntoResponse for RumaResponse<T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// copied from hyper under the following license:
|
||||
// Copyright (c) 2014-2021 Sean McArthur
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
pub(crate) async fn to_bytes<T>(body: T) -> Result<Bytes, T::Error>
|
||||
where
|
||||
T: HttpBody,
|
||||
{
|
||||
futures_util::pin_mut!(body);
|
||||
|
||||
// If there's only 1 chunk, we can just return Buf::to_bytes()
|
||||
let mut first = if let Some(buf) = body.data().await {
|
||||
buf?
|
||||
} else {
|
||||
return Ok(Bytes::new());
|
||||
};
|
||||
|
||||
let second = if let Some(buf) = body.data().await {
|
||||
buf?
|
||||
} else {
|
||||
return Ok(first.copy_to_bytes(first.remaining()));
|
||||
};
|
||||
|
||||
// With more than 1 buf, we gotta flatten into a Vec first.
|
||||
let cap = first.remaining() + second.remaining() + body.size_hint().lower() as usize;
|
||||
let mut vec = Vec::with_capacity(cap);
|
||||
vec.put(first);
|
||||
vec.put(second);
|
||||
|
||||
while let Some(buf) = body.data().await {
|
||||
vec.put(buf?);
|
||||
}
|
||||
|
||||
Ok(vec.into())
|
||||
}
|
||||
|
||||
@@ -7,13 +7,14 @@ use crate::{service::appservice::RegistrationInfo, Error};
|
||||
mod axum;
|
||||
|
||||
/// Extractor for Ruma request structs
|
||||
pub(crate) struct Ruma<T> {
|
||||
pub(crate) body: T,
|
||||
pub(crate) sender_user: Option<OwnedUserId>,
|
||||
pub(crate) sender_device: Option<OwnedDeviceId>,
|
||||
pub(crate) sender_servername: Option<OwnedServerName>,
|
||||
pub(crate) json_body: Option<CanonicalJsonValue>, // This is None when body is not a valid string
|
||||
pub(crate) appservice_info: Option<RegistrationInfo>,
|
||||
pub struct Ruma<T> {
|
||||
pub body: T,
|
||||
pub sender_user: Option<OwnedUserId>,
|
||||
pub sender_device: Option<OwnedDeviceId>,
|
||||
pub sender_servername: Option<OwnedServerName>,
|
||||
// This is None when body is not a valid string
|
||||
pub json_body: Option<CanonicalJsonValue>,
|
||||
pub appservice_info: Option<RegistrationInfo>,
|
||||
}
|
||||
|
||||
impl<T> Deref for Ruma<T> {
|
||||
@@ -23,7 +24,7 @@ impl<T> Deref for Ruma<T> {
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct RumaResponse<T>(pub(crate) T);
|
||||
pub struct RumaResponse<T>(pub T);
|
||||
|
||||
impl<T> From<T> for RumaResponse<T> {
|
||||
fn from(t: T) -> Self { Self(t) }
|
||||
|
||||
+99
-57
@@ -49,11 +49,10 @@ use ruma::{
|
||||
};
|
||||
use serde_json::value::{to_raw_value, RawValue as RawJsonValue};
|
||||
use tokio::sync::RwLock;
|
||||
use tracing::{debug, error, trace, warn};
|
||||
use tracing::{debug, error, info, trace, warn};
|
||||
|
||||
use crate::{
|
||||
api::client_server::{self, claim_keys_helper, get_keys_helper},
|
||||
debug_error,
|
||||
service::pdu::{gen_event_id_canonical_json, PduBuilder},
|
||||
services, utils, Error, PduEvent, Result, Ruma,
|
||||
};
|
||||
@@ -61,7 +60,7 @@ use crate::{
|
||||
/// # `GET /_matrix/federation/v1/version`
|
||||
///
|
||||
/// Get version information on this server.
|
||||
pub(crate) async fn get_server_version_route(
|
||||
pub async fn get_server_version_route(
|
||||
_body: Ruma<get_server_version::v1::Request>,
|
||||
) -> Result<get_server_version::v1::Response> {
|
||||
let version = match option_env!("CONDUIT_VERSION_EXTRA") {
|
||||
@@ -86,7 +85,7 @@ pub(crate) async fn get_server_version_route(
|
||||
/// forever.
|
||||
// Response type for this endpoint is Json because we need to calculate a
|
||||
// signature for the response
|
||||
pub(crate) async fn get_server_keys_route() -> Result<impl IntoResponse> {
|
||||
pub async fn get_server_keys_route() -> Result<impl IntoResponse> {
|
||||
let mut verify_keys: BTreeMap<OwnedServerSigningKeyId, VerifyKey> = BTreeMap::new();
|
||||
verify_keys.insert(
|
||||
format!("ed25519:{}", services().globals.keypair().version())
|
||||
@@ -133,12 +132,12 @@ pub(crate) async fn get_server_keys_route() -> Result<impl IntoResponse> {
|
||||
/// - Matrix does not support invalidating public keys, so the key returned by
|
||||
/// this will be valid
|
||||
/// forever.
|
||||
pub(crate) async fn get_server_keys_deprecated_route() -> impl IntoResponse { get_server_keys_route().await }
|
||||
pub async fn get_server_keys_deprecated_route() -> impl IntoResponse { get_server_keys_route().await }
|
||||
|
||||
/// # `POST /_matrix/federation/v1/publicRooms`
|
||||
///
|
||||
/// Lists the public rooms on this server.
|
||||
pub(crate) async fn get_public_rooms_filtered_route(
|
||||
pub async fn get_public_rooms_filtered_route(
|
||||
body: Ruma<get_public_rooms_filtered::v1::Request>,
|
||||
) -> Result<get_public_rooms_filtered::v1::Response> {
|
||||
if !services()
|
||||
@@ -156,7 +155,10 @@ pub(crate) async fn get_public_rooms_filtered_route(
|
||||
&body.room_network,
|
||||
)
|
||||
.await
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::Unknown, "Failed to return this server's public room list."))?;
|
||||
.map_err(|e| {
|
||||
warn!("Failed to return our /publicRooms: {e}");
|
||||
Error::BadRequest(ErrorKind::Unknown, "Failed to return this server's public room list.")
|
||||
})?;
|
||||
|
||||
Ok(get_public_rooms_filtered::v1::Response {
|
||||
chunk: response.chunk,
|
||||
@@ -169,7 +171,7 @@ pub(crate) async fn get_public_rooms_filtered_route(
|
||||
/// # `GET /_matrix/federation/v1/publicRooms`
|
||||
///
|
||||
/// Lists the public rooms on this server.
|
||||
pub(crate) async fn get_public_rooms_route(
|
||||
pub async fn get_public_rooms_route(
|
||||
body: Ruma<get_public_rooms::v1::Request>,
|
||||
) -> Result<get_public_rooms::v1::Response> {
|
||||
if !services()
|
||||
@@ -187,7 +189,10 @@ pub(crate) async fn get_public_rooms_route(
|
||||
&body.room_network,
|
||||
)
|
||||
.await
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::Unknown, "Failed to return this server's public room list."))?;
|
||||
.map_err(|e| {
|
||||
warn!("Failed to return our /publicRooms: {e}");
|
||||
Error::BadRequest(ErrorKind::Unknown, "Failed to return this server's public room list.")
|
||||
})?;
|
||||
|
||||
Ok(get_public_rooms::v1::Response {
|
||||
chunk: response.chunk,
|
||||
@@ -197,7 +202,7 @@ pub(crate) async fn get_public_rooms_route(
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn parse_incoming_pdu(pdu: &RawJsonValue) -> Result<(OwnedEventId, CanonicalJsonObject, OwnedRoomId)> {
|
||||
pub fn parse_incoming_pdu(pdu: &RawJsonValue) -> Result<(OwnedEventId, CanonicalJsonObject, OwnedRoomId)> {
|
||||
let value: CanonicalJsonObject = serde_json::from_str(pdu.get()).map_err(|e| {
|
||||
warn!("Error parsing incoming event {:?}: {:?}", pdu, e);
|
||||
Error::BadServerResponse("Invalid PDU in server response")
|
||||
@@ -209,7 +214,7 @@ pub(crate) fn parse_incoming_pdu(pdu: &RawJsonValue) -> Result<(OwnedEventId, Ca
|
||||
.ok_or(Error::BadRequest(ErrorKind::InvalidParam, "Invalid room id in pdu"))?;
|
||||
|
||||
let Ok(room_version_id) = services().rooms.state.get_room_version(&room_id) else {
|
||||
return Err(Error::Err(format!("Server is not in room {room_id}")));
|
||||
return Err(Error::Error(format!("Server is not in room {room_id}")));
|
||||
};
|
||||
|
||||
let Ok((event_id, value)) = gen_event_id_canonical_json(pdu, &room_version_id) else {
|
||||
@@ -226,7 +231,7 @@ pub(crate) fn parse_incoming_pdu(pdu: &RawJsonValue) -> Result<(OwnedEventId, Ca
|
||||
/// # `PUT /_matrix/federation/v1/send/{txnId}`
|
||||
///
|
||||
/// Push EDUs and PDUs to this server.
|
||||
pub(crate) async fn send_transaction_message_route(
|
||||
pub async fn send_transaction_message_route(
|
||||
body: Ruma<send_transaction_message::v1::Request>,
|
||||
) -> Result<send_transaction_message::v1::Response> {
|
||||
let sender_servername = body
|
||||
@@ -303,7 +308,7 @@ pub(crate) async fn send_transaction_message_route(
|
||||
services()
|
||||
.rooms
|
||||
.event_handler
|
||||
.handle_incoming_pdu(sender_servername, &room_id, &event_id, value, true, &pub_key_map)
|
||||
.handle_incoming_pdu(sender_servername, &event_id, &room_id, value, true, &pub_key_map)
|
||||
.await
|
||||
.map(|_| ()),
|
||||
);
|
||||
@@ -385,7 +390,7 @@ pub(crate) async fn send_transaction_message_route(
|
||||
.readreceipt_update(&user_id, &room_id, event)?;
|
||||
} else {
|
||||
// TODO fetch missing events
|
||||
debug_error!("No known event ids in read receipt: {:?}", user_updates);
|
||||
debug!("No known event ids in read receipt: {:?}", user_updates);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -448,7 +453,7 @@ pub(crate) async fn send_transaction_message_route(
|
||||
target_device_id,
|
||||
&ev_type.to_string(),
|
||||
event.deserialize_as().map_err(|e| {
|
||||
error!("To-Device event is invalid: {event:?} {e}");
|
||||
warn!("To-Device event is invalid: {event:?} {e}");
|
||||
Error::BadRequest(ErrorKind::InvalidParam, "Event is invalid")
|
||||
})?,
|
||||
)?;
|
||||
@@ -517,7 +522,7 @@ pub(crate) async fn send_transaction_message_route(
|
||||
///
|
||||
/// - Only works if a user of this server is currently invited or joined the
|
||||
/// room
|
||||
pub(crate) async fn get_event_route(body: Ruma<get_event::v1::Request>) -> Result<get_event::v1::Response> {
|
||||
pub async fn get_event_route(body: Ruma<get_event::v1::Request>) -> Result<get_event::v1::Response> {
|
||||
let sender_servername = body
|
||||
.sender_servername
|
||||
.as_ref()
|
||||
@@ -527,7 +532,10 @@ pub(crate) async fn get_event_route(body: Ruma<get_event::v1::Request>) -> Resul
|
||||
.rooms
|
||||
.timeline
|
||||
.get_pdu_json(&body.event_id)?
|
||||
.ok_or_else(|| Error::BadRequest(ErrorKind::NotFound, "Event not found."))?;
|
||||
.ok_or_else(|| {
|
||||
warn!("Event not found, event ID: {:?}", &body.event_id);
|
||||
Error::BadRequest(ErrorKind::NotFound, "Event not found.")
|
||||
})?;
|
||||
|
||||
let room_id_str = event
|
||||
.get("room_id")
|
||||
@@ -564,12 +572,14 @@ pub(crate) async fn get_event_route(body: Ruma<get_event::v1::Request>) -> Resul
|
||||
///
|
||||
/// Retrieves events from before the sender joined the room, if the room's
|
||||
/// history visibility allows.
|
||||
pub(crate) async fn get_backfill_route(body: Ruma<get_backfill::v1::Request>) -> Result<get_backfill::v1::Response> {
|
||||
pub async fn get_backfill_route(body: Ruma<get_backfill::v1::Request>) -> Result<get_backfill::v1::Response> {
|
||||
let sender_servername = body
|
||||
.sender_servername
|
||||
.as_ref()
|
||||
.expect("server is authenticated");
|
||||
|
||||
debug!("Got backfill request from: {}", sender_servername);
|
||||
|
||||
if !services()
|
||||
.rooms
|
||||
.state_cache
|
||||
@@ -625,7 +635,7 @@ pub(crate) async fn get_backfill_route(body: Ruma<get_backfill::v1::Request>) ->
|
||||
/// # `POST /_matrix/federation/v1/get_missing_events/{roomId}`
|
||||
///
|
||||
/// Retrieves events that the sender is missing.
|
||||
pub(crate) async fn get_missing_events_route(
|
||||
pub async fn get_missing_events_route(
|
||||
body: Ruma<get_missing_events::v1::Request>,
|
||||
) -> Result<get_missing_events::v1::Response> {
|
||||
let sender_servername = body
|
||||
@@ -661,7 +671,11 @@ pub(crate) async fn get_missing_events_route(
|
||||
.map_err(|_| Error::bad_database("Invalid room id field in event in database"))?;
|
||||
|
||||
if event_room_id != body.room_id {
|
||||
return Err(Error::BadRequest(ErrorKind::InvalidParam, "Event from wrong room"));
|
||||
warn!(
|
||||
"Evil event detected: Event {} found while searching in room {}",
|
||||
queued_events[i], body.room_id
|
||||
);
|
||||
return Err(Error::BadRequest(ErrorKind::InvalidParam, "Evil event detected"));
|
||||
}
|
||||
|
||||
if body.earliest_events.contains(&queued_events[i]) {
|
||||
@@ -704,7 +718,7 @@ pub(crate) async fn get_missing_events_route(
|
||||
/// Retrieves the auth chain for a given event.
|
||||
///
|
||||
/// - This does not include the event itself
|
||||
pub(crate) async fn get_event_authorization_route(
|
||||
pub async fn get_event_authorization_route(
|
||||
body: Ruma<get_event_authorization::v1::Request>,
|
||||
) -> Result<get_event_authorization::v1::Response> {
|
||||
let sender_servername = body
|
||||
@@ -729,7 +743,10 @@ pub(crate) async fn get_event_authorization_route(
|
||||
.rooms
|
||||
.timeline
|
||||
.get_pdu_json(&body.event_id)?
|
||||
.ok_or_else(|| Error::BadRequest(ErrorKind::NotFound, "Event not found."))?;
|
||||
.ok_or_else(|| {
|
||||
warn!("Event not found, event ID: {:?}", &body.event_id);
|
||||
Error::BadRequest(ErrorKind::NotFound, "Event not found.")
|
||||
})?;
|
||||
|
||||
let room_id_str = event
|
||||
.get("room_id")
|
||||
@@ -756,9 +773,7 @@ pub(crate) async fn get_event_authorization_route(
|
||||
/// # `GET /_matrix/federation/v1/state/{roomId}`
|
||||
///
|
||||
/// Retrieves the current state of the room.
|
||||
pub(crate) async fn get_room_state_route(
|
||||
body: Ruma<get_room_state::v1::Request>,
|
||||
) -> Result<get_room_state::v1::Response> {
|
||||
pub async fn get_room_state_route(body: Ruma<get_room_state::v1::Request>) -> Result<get_room_state::v1::Response> {
|
||||
let sender_servername = body
|
||||
.sender_servername
|
||||
.as_ref()
|
||||
@@ -810,12 +825,12 @@ pub(crate) async fn get_room_state_route(
|
||||
Ok(get_room_state::v1::Response {
|
||||
auth_chain: auth_chain_ids
|
||||
.filter_map(|id| {
|
||||
services()
|
||||
.rooms
|
||||
.timeline
|
||||
.get_pdu_json(&id)
|
||||
.ok()?
|
||||
.map(PduEvent::convert_to_outgoing_federation_event)
|
||||
if let Some(json) = services().rooms.timeline.get_pdu_json(&id).ok()? {
|
||||
Some(PduEvent::convert_to_outgoing_federation_event(json))
|
||||
} else {
|
||||
error!("Could not find event json for {id} in db.");
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
pdus,
|
||||
@@ -825,7 +840,7 @@ pub(crate) async fn get_room_state_route(
|
||||
/// # `GET /_matrix/federation/v1/state_ids/{roomId}`
|
||||
///
|
||||
/// Retrieves the current state of the room.
|
||||
pub(crate) async fn get_room_state_ids_route(
|
||||
pub async fn get_room_state_ids_route(
|
||||
body: Ruma<get_room_state_ids::v1::Request>,
|
||||
) -> Result<get_room_state_ids::v1::Response> {
|
||||
let sender_servername = body
|
||||
@@ -876,7 +891,7 @@ pub(crate) async fn get_room_state_ids_route(
|
||||
/// # `GET /_matrix/federation/v1/make_join/{roomId}/{userId}`
|
||||
///
|
||||
/// Creates a join template.
|
||||
pub(crate) async fn create_join_event_template_route(
|
||||
pub async fn create_join_event_template_route(
|
||||
body: Ruma<prepare_join_event::v1::Request>,
|
||||
) -> Result<prepare_join_event::v1::Response> {
|
||||
if !services().rooms.metadata.exists(&body.room_id)? {
|
||||
@@ -944,8 +959,10 @@ pub(crate) async fn create_join_event_template_route(
|
||||
let join_rules_event_content: Option<RoomJoinRulesEventContent> = join_rules_event
|
||||
.as_ref()
|
||||
.map(|join_rules_event| {
|
||||
serde_json::from_str(join_rules_event.content.get())
|
||||
.map_err(|_| Error::bad_database("Invalid join rules event in db."))
|
||||
serde_json::from_str(join_rules_event.content.get()).map_err(|e| {
|
||||
warn!("Invalid join rules event: {}", e);
|
||||
Error::bad_database("Invalid join rules event in db.")
|
||||
})
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
@@ -1131,7 +1148,10 @@ async fn create_join_event(
|
||||
&mut value,
|
||||
&room_version_id,
|
||||
)
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Failed to sign event."))?;
|
||||
.map_err(|e| {
|
||||
warn!("Failed to sign event: {e}");
|
||||
Error::BadRequest(ErrorKind::InvalidParam, "Failed to sign event.")
|
||||
})?;
|
||||
|
||||
let origin: OwnedServerName = serde_json::from_value(
|
||||
serde_json::to_value(
|
||||
@@ -1162,7 +1182,7 @@ async fn create_join_event(
|
||||
let pdu_id: Vec<u8> = services()
|
||||
.rooms
|
||||
.event_handler
|
||||
.handle_incoming_pdu(&origin, room_id, &event_id, value.clone(), true, &pub_key_map)
|
||||
.handle_incoming_pdu(&origin, &event_id, room_id, value.clone(), true, &pub_key_map)
|
||||
.await?
|
||||
.ok_or(Error::BadRequest(
|
||||
ErrorKind::InvalidParam,
|
||||
@@ -1204,7 +1224,7 @@ async fn create_join_event(
|
||||
/// # `PUT /_matrix/federation/v1/send_join/{roomId}/{eventId}`
|
||||
///
|
||||
/// Submits a signed join event.
|
||||
pub(crate) async fn create_join_event_v1_route(
|
||||
pub async fn create_join_event_v1_route(
|
||||
body: Ruma<create_join_event::v1::Request>,
|
||||
) -> Result<create_join_event::v1::Response> {
|
||||
let sender_servername = body
|
||||
@@ -1258,7 +1278,7 @@ pub(crate) async fn create_join_event_v1_route(
|
||||
/// # `PUT /_matrix/federation/v2/send_join/{roomId}/{eventId}`
|
||||
///
|
||||
/// Submits a signed join event.
|
||||
pub(crate) async fn create_join_event_v2_route(
|
||||
pub async fn create_join_event_v2_route(
|
||||
body: Ruma<create_join_event::v2::Request>,
|
||||
) -> Result<create_join_event::v2::Response> {
|
||||
let sender_servername = body
|
||||
@@ -1272,6 +1292,11 @@ pub(crate) async fn create_join_event_v2_route(
|
||||
.forbidden_remote_server_names
|
||||
.contains(sender_servername)
|
||||
{
|
||||
warn!(
|
||||
"Server {sender_servername} tried joining room ID {} who has a server name that is globally forbidden. \
|
||||
Rejecting.",
|
||||
&body.room_id,
|
||||
);
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::forbidden(),
|
||||
"Server is banned on this homeserver.",
|
||||
@@ -1313,7 +1338,7 @@ pub(crate) async fn create_join_event_v2_route(
|
||||
/// # `PUT /_matrix/federation/v1/make_leave/{roomId}/{eventId}`
|
||||
///
|
||||
/// Creates a leave template.
|
||||
pub(crate) async fn create_leave_event_template_route(
|
||||
pub async fn create_leave_event_template_route(
|
||||
body: Ruma<prepare_leave_event::v1::Request>,
|
||||
) -> Result<prepare_leave_event::v1::Response> {
|
||||
let sender_servername = body
|
||||
@@ -1381,6 +1406,7 @@ pub(crate) async fn create_leave_event_template_route(
|
||||
pdu_json.remove("event_id");
|
||||
},
|
||||
_ => {
|
||||
warn!("Unexpected or unsupported room version {room_version_id}");
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::BadJson,
|
||||
"Unexpected or unsupported room version found",
|
||||
@@ -1440,7 +1466,7 @@ async fn create_leave_event(sender_servername: &ServerName, room_id: &RoomId, pd
|
||||
let pdu_id: Vec<u8> = services()
|
||||
.rooms
|
||||
.event_handler
|
||||
.handle_incoming_pdu(&origin, room_id, &event_id, value, true, &pub_key_map)
|
||||
.handle_incoming_pdu(&origin, &event_id, room_id, value, true, &pub_key_map)
|
||||
.await?
|
||||
.ok_or(Error::BadRequest(
|
||||
ErrorKind::InvalidParam,
|
||||
@@ -1464,7 +1490,7 @@ async fn create_leave_event(sender_servername: &ServerName, room_id: &RoomId, pd
|
||||
/// # `PUT /_matrix/federation/v1/send_leave/{roomId}/{eventId}`
|
||||
///
|
||||
/// Submits a signed leave event.
|
||||
pub(crate) async fn create_leave_event_v1_route(
|
||||
pub async fn create_leave_event_v1_route(
|
||||
body: Ruma<create_leave_event::v1::Request>,
|
||||
) -> Result<create_leave_event::v1::Response> {
|
||||
let sender_servername = body
|
||||
@@ -1480,7 +1506,7 @@ pub(crate) async fn create_leave_event_v1_route(
|
||||
/// # `PUT /_matrix/federation/v2/send_leave/{roomId}/{eventId}`
|
||||
///
|
||||
/// Submits a signed leave event.
|
||||
pub(crate) async fn create_leave_event_v2_route(
|
||||
pub async fn create_leave_event_v2_route(
|
||||
body: Ruma<create_leave_event::v2::Request>,
|
||||
) -> Result<create_leave_event::v2::Response> {
|
||||
let sender_servername = body
|
||||
@@ -1496,7 +1522,7 @@ pub(crate) async fn create_leave_event_v2_route(
|
||||
/// # `PUT /_matrix/federation/v2/invite/{roomId}/{eventId}`
|
||||
///
|
||||
/// Invites a remote user to a room.
|
||||
pub(crate) async fn create_invite_route(body: Ruma<create_invite::v2::Request>) -> Result<create_invite::v2::Response> {
|
||||
pub async fn create_invite_route(body: Ruma<create_invite::v2::Request>) -> Result<create_invite::v2::Response> {
|
||||
let sender_servername = body
|
||||
.sender_servername
|
||||
.as_ref()
|
||||
@@ -1527,6 +1553,11 @@ pub(crate) async fn create_invite_route(body: Ruma<create_invite::v2::Request>)
|
||||
.forbidden_remote_server_names
|
||||
.contains(&server.to_owned())
|
||||
{
|
||||
warn!(
|
||||
"Received federated/remote invite from server {sender_servername} for room ID {} which has a banned \
|
||||
server name. Rejecting.",
|
||||
body.room_id
|
||||
);
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::forbidden(),
|
||||
"Server is banned on this homeserver.",
|
||||
@@ -1556,8 +1587,10 @@ pub(crate) async fn create_invite_route(body: Ruma<create_invite::v2::Request>)
|
||||
}
|
||||
}
|
||||
|
||||
let mut signed_event = utils::to_canonical_object(&body.event)
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Invite event is invalid."))?;
|
||||
let mut signed_event = utils::to_canonical_object(&body.event).map_err(|e| {
|
||||
error!("Failed to convert invite event to canonical JSON: {}", e);
|
||||
Error::BadRequest(ErrorKind::InvalidParam, "Invite event is invalid.")
|
||||
})?;
|
||||
|
||||
ruma::signatures::hash_and_sign_event(
|
||||
services().globals.server_name().as_str(),
|
||||
@@ -1597,6 +1630,10 @@ pub(crate) async fn create_invite_route(body: Ruma<create_invite::v2::Request>)
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "state_key is not a user id."))?;
|
||||
|
||||
if services().rooms.metadata.is_banned(&body.room_id)? && !services().users.is_admin(&invited_user)? {
|
||||
info!(
|
||||
"Received remote invite from server {} for room {} and for user {invited_user}, but room is banned by us.",
|
||||
&sender_servername, &body.room_id
|
||||
);
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::forbidden(),
|
||||
"This room is banned on this homeserver.",
|
||||
@@ -1604,6 +1641,11 @@ pub(crate) async fn create_invite_route(body: Ruma<create_invite::v2::Request>)
|
||||
}
|
||||
|
||||
if services().globals.block_non_admin_invites() && !services().users.is_admin(&invited_user)? {
|
||||
info!(
|
||||
"Received remote invite from server {} for room {} and for user {invited_user} who is not an admin, but \
|
||||
\"block_non_admin_invites\" is enabled, rejecting.",
|
||||
&sender_servername, &body.room_id
|
||||
);
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::forbidden(),
|
||||
"This server does not allow room invites.",
|
||||
@@ -1617,8 +1659,10 @@ pub(crate) async fn create_invite_route(body: Ruma<create_invite::v2::Request>)
|
||||
|
||||
event.insert("event_id".to_owned(), "$placeholder".into());
|
||||
|
||||
let pdu: PduEvent = serde_json::from_value(event.into())
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Invalid invite event."))?;
|
||||
let pdu: PduEvent = serde_json::from_value(event.into()).map_err(|e| {
|
||||
warn!("Invalid invite event: {}", e);
|
||||
Error::BadRequest(ErrorKind::InvalidParam, "Invalid invite event.")
|
||||
})?;
|
||||
|
||||
invite_state.push(pdu.to_stripped_state_event());
|
||||
|
||||
@@ -1648,7 +1692,7 @@ pub(crate) async fn create_invite_route(body: Ruma<create_invite::v2::Request>)
|
||||
/// # `GET /_matrix/federation/v1/user/devices/{userId}`
|
||||
///
|
||||
/// Gets information on all devices of the user.
|
||||
pub(crate) async fn get_devices_route(body: Ruma<get_devices::v1::Request>) -> Result<get_devices::v1::Response> {
|
||||
pub async fn get_devices_route(body: Ruma<get_devices::v1::Request>) -> Result<get_devices::v1::Response> {
|
||||
if body.user_id.server_name() != services().globals.server_name() {
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::InvalidParam,
|
||||
@@ -1702,7 +1746,7 @@ pub(crate) async fn get_devices_route(body: Ruma<get_devices::v1::Request>) -> R
|
||||
/// # `GET /_matrix/federation/v1/query/directory`
|
||||
///
|
||||
/// Resolve a room alias to a room id.
|
||||
pub(crate) async fn get_room_information_route(
|
||||
pub async fn get_room_information_route(
|
||||
body: Ruma<get_room_information::v1::Request>,
|
||||
) -> Result<get_room_information::v1::Response> {
|
||||
let room_id = services()
|
||||
@@ -1742,7 +1786,7 @@ pub(crate) async fn get_room_information_route(
|
||||
///
|
||||
///
|
||||
/// Gets information on a profile.
|
||||
pub(crate) async fn get_profile_information_route(
|
||||
pub async fn get_profile_information_route(
|
||||
body: Ruma<get_profile_information::v1::Request>,
|
||||
) -> Result<get_profile_information::v1::Response> {
|
||||
if !services()
|
||||
@@ -1793,7 +1837,7 @@ pub(crate) async fn get_profile_information_route(
|
||||
/// # `POST /_matrix/federation/v1/user/keys/query`
|
||||
///
|
||||
/// Gets devices and identity keys for the given users.
|
||||
pub(crate) async fn get_keys_route(body: Ruma<get_keys::v1::Request>) -> Result<get_keys::v1::Response> {
|
||||
pub async fn get_keys_route(body: Ruma<get_keys::v1::Request>) -> Result<get_keys::v1::Response> {
|
||||
if body
|
||||
.device_keys
|
||||
.iter()
|
||||
@@ -1823,7 +1867,7 @@ pub(crate) async fn get_keys_route(body: Ruma<get_keys::v1::Request>) -> Result<
|
||||
/// # `POST /_matrix/federation/v1/user/keys/claim`
|
||||
///
|
||||
/// Claims one-time keys.
|
||||
pub(crate) async fn claim_keys_route(body: Ruma<claim_keys::v1::Request>) -> Result<claim_keys::v1::Response> {
|
||||
pub async fn claim_keys_route(body: Ruma<claim_keys::v1::Request>) -> Result<claim_keys::v1::Response> {
|
||||
if body
|
||||
.one_time_keys
|
||||
.iter()
|
||||
@@ -1845,9 +1889,7 @@ pub(crate) async fn claim_keys_route(body: Ruma<claim_keys::v1::Request>) -> Res
|
||||
/// # `GET /.well-known/matrix/server`
|
||||
///
|
||||
/// Returns the .well-known URL if it is configured, otherwise returns 404.
|
||||
pub(crate) async fn well_known_server(
|
||||
_body: Ruma<discover_homeserver::Request>,
|
||||
) -> Result<discover_homeserver::Response> {
|
||||
pub async fn well_known_server(_body: Ruma<discover_homeserver::Request>) -> Result<discover_homeserver::Response> {
|
||||
Ok(discover_homeserver::Response {
|
||||
server: match services().globals.well_known_server() {
|
||||
Some(server_name) => server_name.to_owned(),
|
||||
@@ -1860,7 +1902,7 @@ pub(crate) async fn well_known_server(
|
||||
///
|
||||
/// Gets the space tree in a depth-first manner to locate child rooms of a given
|
||||
/// space.
|
||||
pub(crate) async fn get_hierarchy_route(body: Ruma<get_hierarchy::v1::Request>) -> Result<get_hierarchy::v1::Response> {
|
||||
pub async fn get_hierarchy_route(body: Ruma<get_hierarchy::v1::Request>) -> Result<get_hierarchy::v1::Response> {
|
||||
let sender_servername = body
|
||||
.sender_servername
|
||||
.as_ref()
|
||||
|
||||
@@ -22,12 +22,12 @@ fn version() -> String {
|
||||
/// Commandline arguments
|
||||
#[derive(Parser, Debug)]
|
||||
#[clap(version = version(), about, long_about = None)]
|
||||
pub(crate) struct Args {
|
||||
pub struct Args {
|
||||
#[arg(short, long)]
|
||||
/// Optional argument to the path of a conduwuit config TOML file
|
||||
pub(crate) config: Option<PathBuf>,
|
||||
pub config: Option<PathBuf>,
|
||||
}
|
||||
|
||||
/// Parse commandline arguments into structured data
|
||||
#[must_use]
|
||||
pub(crate) fn parse() -> Args { Args::parse() }
|
||||
pub fn parse() -> Args { Args::parse() }
|
||||
+3
-7
@@ -5,14 +5,10 @@ use tracing::{debug, error, info, warn};
|
||||
|
||||
use crate::{utils::error::Error, Config};
|
||||
|
||||
pub(crate) fn check(config: &Config) -> Result<(), Error> {
|
||||
pub fn check(config: &Config) -> Result<(), Error> {
|
||||
config.warn_deprecated();
|
||||
config.warn_unknown_key();
|
||||
|
||||
if config.sentry && config.sentry_endpoint.is_none() {
|
||||
return Err(Error::bad_config("Sentry cannot be enabled without an endpoint set"));
|
||||
}
|
||||
|
||||
if cfg!(feature = "hardened_malloc") && cfg!(feature = "jemalloc") {
|
||||
warn!(
|
||||
"hardened_malloc and jemalloc were built together, this causes neither to be used. Conduwuit will still \
|
||||
@@ -91,8 +87,8 @@ pub(crate) fn check(config: &Config) -> Result<(), Error> {
|
||||
return Err(Error::bad_config("Registration token was specified but is empty (\"\")"));
|
||||
}
|
||||
|
||||
if config.max_request_size < 5120000 {
|
||||
return Err(Error::bad_config("Max request size is less than 5MB. Please increase it."));
|
||||
if config.max_request_size < 16384 {
|
||||
return Err(Error::bad_config("Max request size is less than 16KB. Please increase it."));
|
||||
}
|
||||
|
||||
// check if user specified valid IP CIDR ranges on startup
|
||||
|
||||
+164
-209
@@ -22,337 +22,323 @@ use serde::{de::IgnoredAny, Deserialize};
|
||||
use tracing::{debug, error, warn};
|
||||
use url::Url;
|
||||
|
||||
use self::{check::check, proxy::ProxyConfig};
|
||||
use self::proxy::ProxyConfig;
|
||||
use crate::utils::error::Error;
|
||||
|
||||
pub(crate) mod check;
|
||||
mod check;
|
||||
mod proxy;
|
||||
|
||||
#[derive(Deserialize, Clone, Debug)]
|
||||
#[serde(transparent)]
|
||||
struct ListeningPort {
|
||||
pub struct ListeningPort {
|
||||
#[serde(with = "either::serde_untagged")]
|
||||
ports: Either<u16, Vec<u16>>,
|
||||
pub ports: Either<u16, Vec<u16>>,
|
||||
}
|
||||
|
||||
/// all the config options for conduwuit
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
pub(crate) struct Config {
|
||||
pub struct Config {
|
||||
/// [`IpAddr`] conduwuit will listen on (can be IPv4 or IPv6)
|
||||
#[serde(default = "default_address")]
|
||||
pub(crate) address: IpAddr,
|
||||
pub address: IpAddr,
|
||||
/// default TCP port(s) conduwuit will listen on
|
||||
#[serde(default = "default_port")]
|
||||
port: ListeningPort,
|
||||
pub(crate) tls: Option<TlsConfig>,
|
||||
pub(crate) unix_socket_path: Option<PathBuf>,
|
||||
pub port: ListeningPort,
|
||||
pub tls: Option<TlsConfig>,
|
||||
pub unix_socket_path: Option<PathBuf>,
|
||||
#[serde(default = "default_unix_socket_perms")]
|
||||
pub(crate) unix_socket_perms: u32,
|
||||
pub(crate) server_name: OwnedServerName,
|
||||
pub unix_socket_perms: u32,
|
||||
pub server_name: OwnedServerName,
|
||||
#[serde(default = "default_database_backend")]
|
||||
pub(crate) database_backend: String,
|
||||
pub(crate) database_path: PathBuf,
|
||||
pub(crate) database_backup_path: Option<PathBuf>,
|
||||
pub database_backend: String,
|
||||
pub database_path: PathBuf,
|
||||
pub database_backup_path: Option<PathBuf>,
|
||||
#[serde(default = "default_database_backups_to_keep")]
|
||||
pub(crate) database_backups_to_keep: i16,
|
||||
pub database_backups_to_keep: i16,
|
||||
#[serde(default = "default_db_cache_capacity_mb")]
|
||||
pub(crate) db_cache_capacity_mb: f64,
|
||||
pub db_cache_capacity_mb: f64,
|
||||
#[serde(default = "default_new_user_displayname_suffix")]
|
||||
pub(crate) new_user_displayname_suffix: String,
|
||||
pub new_user_displayname_suffix: String,
|
||||
#[serde(default)]
|
||||
pub(crate) allow_check_for_updates: bool,
|
||||
pub allow_check_for_updates: bool,
|
||||
|
||||
#[serde(default = "default_pdu_cache_capacity")]
|
||||
pub(crate) pdu_cache_capacity: u32,
|
||||
pub pdu_cache_capacity: u32,
|
||||
#[serde(default = "default_conduit_cache_capacity_modifier")]
|
||||
pub(crate) conduit_cache_capacity_modifier: f64,
|
||||
pub conduit_cache_capacity_modifier: f64,
|
||||
#[serde(default = "default_auth_chain_cache_capacity")]
|
||||
pub(crate) auth_chain_cache_capacity: u32,
|
||||
pub auth_chain_cache_capacity: u32,
|
||||
#[serde(default = "default_shorteventid_cache_capacity")]
|
||||
pub(crate) shorteventid_cache_capacity: u32,
|
||||
pub shorteventid_cache_capacity: u32,
|
||||
#[serde(default = "default_eventidshort_cache_capacity")]
|
||||
pub(crate) eventidshort_cache_capacity: u32,
|
||||
pub eventidshort_cache_capacity: u32,
|
||||
#[serde(default = "default_shortstatekey_cache_capacity")]
|
||||
pub(crate) shortstatekey_cache_capacity: u32,
|
||||
pub shortstatekey_cache_capacity: u32,
|
||||
#[serde(default = "default_statekeyshort_cache_capacity")]
|
||||
pub(crate) statekeyshort_cache_capacity: u32,
|
||||
pub statekeyshort_cache_capacity: u32,
|
||||
#[serde(default = "default_server_visibility_cache_capacity")]
|
||||
pub(crate) server_visibility_cache_capacity: u32,
|
||||
pub server_visibility_cache_capacity: u32,
|
||||
#[serde(default = "default_user_visibility_cache_capacity")]
|
||||
pub(crate) user_visibility_cache_capacity: u32,
|
||||
pub user_visibility_cache_capacity: u32,
|
||||
#[serde(default = "default_stateinfo_cache_capacity")]
|
||||
pub(crate) stateinfo_cache_capacity: u32,
|
||||
pub stateinfo_cache_capacity: u32,
|
||||
#[serde(default = "default_roomid_spacehierarchy_cache_capacity")]
|
||||
pub(crate) roomid_spacehierarchy_cache_capacity: u32,
|
||||
pub roomid_spacehierarchy_cache_capacity: u32,
|
||||
|
||||
#[serde(default = "default_cleanup_second_interval")]
|
||||
pub(crate) cleanup_second_interval: u32,
|
||||
|
||||
pub cleanup_second_interval: u32,
|
||||
#[serde(default = "default_dns_cache_entries")]
|
||||
pub(crate) dns_cache_entries: u32,
|
||||
pub dns_cache_entries: u32,
|
||||
#[serde(default = "default_dns_min_ttl")]
|
||||
pub(crate) dns_min_ttl: u64,
|
||||
pub dns_min_ttl: u64,
|
||||
#[serde(default = "default_dns_min_ttl_nxdomain")]
|
||||
pub(crate) dns_min_ttl_nxdomain: u64,
|
||||
pub dns_min_ttl_nxdomain: u64,
|
||||
#[serde(default = "default_dns_attempts")]
|
||||
pub(crate) dns_attempts: u16,
|
||||
pub dns_attempts: u16,
|
||||
#[serde(default = "default_dns_timeout")]
|
||||
pub(crate) dns_timeout: u64,
|
||||
pub dns_timeout: u64,
|
||||
#[serde(default = "true_fn")]
|
||||
pub(crate) dns_tcp_fallback: bool,
|
||||
pub dns_tcp_fallback: bool,
|
||||
#[serde(default = "true_fn")]
|
||||
pub(crate) query_all_nameservers: bool,
|
||||
|
||||
pub query_all_nameservers: bool,
|
||||
#[serde(default = "default_max_request_size")]
|
||||
pub(crate) max_request_size: u32,
|
||||
pub max_request_size: u32,
|
||||
#[serde(default = "default_max_concurrent_requests")]
|
||||
pub max_concurrent_requests: u16,
|
||||
#[serde(default = "default_max_fetch_prev_events")]
|
||||
pub(crate) max_fetch_prev_events: u16,
|
||||
|
||||
pub max_fetch_prev_events: u16,
|
||||
#[serde(default = "default_request_conn_timeout")]
|
||||
pub(crate) request_conn_timeout: u64,
|
||||
pub request_conn_timeout: u64,
|
||||
#[serde(default = "default_request_timeout")]
|
||||
pub(crate) request_timeout: u64,
|
||||
#[serde(default = "default_request_total_timeout")]
|
||||
pub(crate) request_total_timeout: u64,
|
||||
#[serde(default = "default_request_idle_timeout")]
|
||||
pub(crate) request_idle_timeout: u64,
|
||||
pub request_timeout: u64,
|
||||
#[serde(default = "default_request_idle_per_host")]
|
||||
pub(crate) request_idle_per_host: u16,
|
||||
pub request_idle_per_host: u16,
|
||||
#[serde(default = "default_request_idle_timeout")]
|
||||
pub request_idle_timeout: u64,
|
||||
#[serde(default = "default_well_known_conn_timeout")]
|
||||
pub(crate) well_known_conn_timeout: u64,
|
||||
pub well_known_conn_timeout: u64,
|
||||
#[serde(default = "default_well_known_timeout")]
|
||||
pub(crate) well_known_timeout: u64,
|
||||
pub well_known_timeout: u64,
|
||||
#[serde(default = "default_federation_timeout")]
|
||||
pub(crate) federation_timeout: u64,
|
||||
#[serde(default = "default_federation_idle_timeout")]
|
||||
pub(crate) federation_idle_timeout: u64,
|
||||
pub federation_timeout: u64,
|
||||
#[serde(default = "default_federation_idle_per_host")]
|
||||
pub(crate) federation_idle_per_host: u16,
|
||||
pub federation_idle_per_host: u16,
|
||||
#[serde(default = "default_federation_idle_timeout")]
|
||||
pub federation_idle_timeout: u64,
|
||||
#[serde(default = "default_sender_timeout")]
|
||||
pub(crate) sender_timeout: u64,
|
||||
pub sender_timeout: u64,
|
||||
#[serde(default = "default_sender_idle_timeout")]
|
||||
pub(crate) sender_idle_timeout: u64,
|
||||
#[serde(default = "default_sender_retry_backoff_limit")]
|
||||
pub(crate) sender_retry_backoff_limit: u64,
|
||||
pub sender_idle_timeout: u64,
|
||||
#[serde(default = "default_appservice_timeout")]
|
||||
pub(crate) appservice_timeout: u64,
|
||||
pub appservice_timeout: u64,
|
||||
#[serde(default = "default_appservice_idle_timeout")]
|
||||
pub(crate) appservice_idle_timeout: u64,
|
||||
pub appservice_idle_timeout: u64,
|
||||
#[serde(default = "default_pusher_idle_timeout")]
|
||||
pub(crate) pusher_idle_timeout: u64,
|
||||
|
||||
pub pusher_idle_timeout: u64,
|
||||
#[serde(default)]
|
||||
pub(crate) allow_registration: bool,
|
||||
pub allow_registration: bool,
|
||||
#[serde(default)]
|
||||
pub(crate) yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse: bool,
|
||||
pub(crate) registration_token: Option<String>,
|
||||
pub yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse: bool,
|
||||
pub registration_token: Option<String>,
|
||||
#[serde(default = "true_fn")]
|
||||
pub(crate) allow_encryption: bool,
|
||||
pub allow_encryption: bool,
|
||||
#[serde(default = "true_fn")]
|
||||
pub(crate) allow_federation: bool,
|
||||
pub allow_federation: bool,
|
||||
#[serde(default)]
|
||||
pub(crate) allow_public_room_directory_over_federation: bool,
|
||||
pub allow_public_room_directory_over_federation: bool,
|
||||
#[serde(default)]
|
||||
pub(crate) allow_public_room_directory_without_auth: bool,
|
||||
pub allow_public_room_directory_without_auth: bool,
|
||||
#[serde(default)]
|
||||
pub(crate) lockdown_public_room_directory: bool,
|
||||
pub lockdown_public_room_directory: bool,
|
||||
#[serde(default)]
|
||||
pub(crate) allow_device_name_federation: bool,
|
||||
pub allow_device_name_federation: bool,
|
||||
#[serde(default = "true_fn")]
|
||||
pub(crate) allow_profile_lookup_federation_requests: bool,
|
||||
pub allow_profile_lookup_federation_requests: bool,
|
||||
#[serde(default = "true_fn")]
|
||||
pub(crate) allow_room_creation: bool,
|
||||
pub allow_room_creation: bool,
|
||||
#[serde(default = "true_fn")]
|
||||
pub(crate) allow_unstable_room_versions: bool,
|
||||
pub allow_unstable_room_versions: bool,
|
||||
#[serde(default = "default_default_room_version")]
|
||||
pub(crate) default_room_version: RoomVersionId,
|
||||
pub default_room_version: RoomVersionId,
|
||||
#[serde(default)]
|
||||
pub(crate) well_known: WellKnownConfig,
|
||||
pub well_known: WellKnownConfig,
|
||||
#[serde(default)]
|
||||
#[cfg(feature = "perf_measurements")]
|
||||
pub(crate) allow_jaeger: bool,
|
||||
pub allow_jaeger: bool,
|
||||
#[serde(default)]
|
||||
#[cfg(feature = "perf_measurements")]
|
||||
pub(crate) tracing_flame: bool,
|
||||
pub tracing_flame: bool,
|
||||
#[serde(default)]
|
||||
pub(crate) proxy: ProxyConfig,
|
||||
pub(crate) jwt_secret: Option<String>,
|
||||
pub proxy: ProxyConfig,
|
||||
pub jwt_secret: Option<String>,
|
||||
#[serde(default = "default_trusted_servers")]
|
||||
pub(crate) trusted_servers: Vec<OwnedServerName>,
|
||||
pub trusted_servers: Vec<OwnedServerName>,
|
||||
#[serde(default = "true_fn")]
|
||||
pub(crate) query_trusted_key_servers_first: bool,
|
||||
pub query_trusted_key_servers_first: bool,
|
||||
#[serde(default = "default_log")]
|
||||
pub(crate) log: String,
|
||||
pub log: String,
|
||||
#[serde(default)]
|
||||
pub(crate) turn_username: String,
|
||||
pub turn_username: String,
|
||||
#[serde(default)]
|
||||
pub(crate) turn_password: String,
|
||||
pub turn_password: String,
|
||||
#[serde(default = "Vec::new")]
|
||||
pub(crate) turn_uris: Vec<String>,
|
||||
pub turn_uris: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub(crate) turn_secret: String,
|
||||
pub turn_secret: String,
|
||||
#[serde(default = "default_turn_ttl")]
|
||||
pub(crate) turn_ttl: u64,
|
||||
pub turn_ttl: u64,
|
||||
|
||||
#[serde(default = "Vec::new")]
|
||||
pub(crate) auto_join_rooms: Vec<OwnedRoomId>,
|
||||
pub auto_join_rooms: Vec<OwnedRoomId>,
|
||||
|
||||
#[serde(default = "default_rocksdb_log_level")]
|
||||
pub(crate) rocksdb_log_level: String,
|
||||
pub rocksdb_log_level: String,
|
||||
#[serde(default)]
|
||||
pub(crate) rocksdb_log_stderr: bool,
|
||||
pub rocksdb_log_stderr: bool,
|
||||
#[serde(default = "default_rocksdb_max_log_file_size")]
|
||||
pub(crate) rocksdb_max_log_file_size: usize,
|
||||
pub rocksdb_max_log_file_size: usize,
|
||||
#[serde(default = "default_rocksdb_log_time_to_roll")]
|
||||
pub(crate) rocksdb_log_time_to_roll: usize,
|
||||
pub rocksdb_log_time_to_roll: usize,
|
||||
#[serde(default)]
|
||||
pub(crate) rocksdb_optimize_for_spinning_disks: bool,
|
||||
pub rocksdb_optimize_for_spinning_disks: bool,
|
||||
#[serde(default = "default_rocksdb_parallelism_threads")]
|
||||
pub(crate) rocksdb_parallelism_threads: usize,
|
||||
pub rocksdb_parallelism_threads: usize,
|
||||
#[serde(default = "default_rocksdb_max_log_files")]
|
||||
pub(crate) rocksdb_max_log_files: usize,
|
||||
pub rocksdb_max_log_files: usize,
|
||||
#[serde(default = "default_rocksdb_compression_algo")]
|
||||
pub(crate) rocksdb_compression_algo: String,
|
||||
pub rocksdb_compression_algo: String,
|
||||
#[serde(default = "default_rocksdb_compression_level")]
|
||||
pub(crate) rocksdb_compression_level: i32,
|
||||
pub rocksdb_compression_level: i32,
|
||||
#[serde(default = "default_rocksdb_bottommost_compression_level")]
|
||||
pub(crate) rocksdb_bottommost_compression_level: i32,
|
||||
pub rocksdb_bottommost_compression_level: i32,
|
||||
#[serde(default)]
|
||||
pub(crate) rocksdb_bottommost_compression: bool,
|
||||
pub rocksdb_bottommost_compression: bool,
|
||||
#[serde(default = "default_rocksdb_recovery_mode")]
|
||||
pub(crate) rocksdb_recovery_mode: u8,
|
||||
pub rocksdb_recovery_mode: u8,
|
||||
#[serde(default)]
|
||||
pub(crate) rocksdb_repair: bool,
|
||||
pub rocksdb_repair: bool,
|
||||
#[serde(default)]
|
||||
pub(crate) rocksdb_read_only: bool,
|
||||
pub rocksdb_read_only: bool,
|
||||
#[serde(default)]
|
||||
pub(crate) rocksdb_periodic_cleanup: bool,
|
||||
#[serde(default)]
|
||||
pub(crate) rocksdb_compaction_prio_idle: bool,
|
||||
#[serde(default = "true_fn")]
|
||||
pub(crate) rocksdb_compaction_ioprio_idle: bool,
|
||||
pub rocksdb_periodic_cleanup: bool,
|
||||
|
||||
pub(crate) emergency_password: Option<String>,
|
||||
pub emergency_password: Option<String>,
|
||||
|
||||
#[serde(default = "default_notification_push_path")]
|
||||
pub(crate) notification_push_path: String,
|
||||
pub notification_push_path: String,
|
||||
|
||||
#[serde(default = "true_fn")]
|
||||
pub(crate) allow_local_presence: bool,
|
||||
pub allow_local_presence: bool,
|
||||
#[serde(default = "true_fn")]
|
||||
pub(crate) allow_incoming_presence: bool,
|
||||
pub allow_incoming_presence: bool,
|
||||
#[serde(default = "true_fn")]
|
||||
pub(crate) allow_outgoing_presence: bool,
|
||||
pub allow_outgoing_presence: bool,
|
||||
#[serde(default = "default_presence_idle_timeout_s")]
|
||||
pub(crate) presence_idle_timeout_s: u64,
|
||||
pub presence_idle_timeout_s: u64,
|
||||
#[serde(default = "default_presence_offline_timeout_s")]
|
||||
pub(crate) presence_offline_timeout_s: u64,
|
||||
pub presence_offline_timeout_s: u64,
|
||||
#[serde(default = "true_fn")]
|
||||
pub(crate) presence_timeout_remote_users: bool,
|
||||
pub presence_timeout_remote_users: bool,
|
||||
|
||||
#[serde(default = "true_fn")]
|
||||
pub(crate) allow_incoming_read_receipts: bool,
|
||||
pub allow_incoming_read_receipts: bool,
|
||||
#[serde(default = "true_fn")]
|
||||
pub(crate) allow_outgoing_read_receipts: bool,
|
||||
pub allow_outgoing_read_receipts: bool,
|
||||
|
||||
#[serde(default = "true_fn")]
|
||||
pub(crate) allow_outgoing_typing: bool,
|
||||
pub allow_outgoing_typing: bool,
|
||||
#[serde(default = "true_fn")]
|
||||
pub(crate) allow_incoming_typing: bool,
|
||||
pub allow_incoming_typing: bool,
|
||||
#[serde(default = "default_typing_federation_timeout_s")]
|
||||
pub(crate) typing_federation_timeout_s: u64,
|
||||
pub typing_federation_timeout_s: u64,
|
||||
#[serde(default = "default_typing_client_timeout_min_s")]
|
||||
pub(crate) typing_client_timeout_min_s: u64,
|
||||
pub typing_client_timeout_min_s: u64,
|
||||
#[serde(default = "default_typing_client_timeout_max_s")]
|
||||
pub(crate) typing_client_timeout_max_s: u64,
|
||||
pub typing_client_timeout_max_s: u64,
|
||||
|
||||
#[serde(default)]
|
||||
pub(crate) zstd_compression: bool,
|
||||
pub zstd_compression: bool,
|
||||
#[serde(default)]
|
||||
pub(crate) gzip_compression: bool,
|
||||
pub gzip_compression: bool,
|
||||
#[serde(default)]
|
||||
pub(crate) brotli_compression: bool,
|
||||
pub brotli_compression: bool,
|
||||
|
||||
#[serde(default)]
|
||||
pub(crate) allow_guest_registration: bool,
|
||||
pub allow_guest_registration: bool,
|
||||
#[serde(default)]
|
||||
pub(crate) log_guest_registrations: bool,
|
||||
pub log_guest_registrations: bool,
|
||||
#[serde(default)]
|
||||
pub(crate) allow_guests_auto_join_rooms: bool,
|
||||
pub allow_guests_auto_join_rooms: bool,
|
||||
|
||||
#[serde(default = "Vec::new")]
|
||||
pub(crate) prevent_media_downloads_from: Vec<OwnedServerName>,
|
||||
pub prevent_media_downloads_from: Vec<OwnedServerName>,
|
||||
#[serde(default = "Vec::new")]
|
||||
pub(crate) forbidden_remote_server_names: Vec<OwnedServerName>,
|
||||
pub forbidden_remote_server_names: Vec<OwnedServerName>,
|
||||
#[serde(default = "Vec::new")]
|
||||
pub(crate) forbidden_remote_room_directory_server_names: Vec<OwnedServerName>,
|
||||
pub forbidden_remote_room_directory_server_names: Vec<OwnedServerName>,
|
||||
|
||||
#[serde(default = "default_ip_range_denylist")]
|
||||
pub(crate) ip_range_denylist: Vec<String>,
|
||||
pub ip_range_denylist: Vec<String>,
|
||||
|
||||
#[serde(default = "Vec::new")]
|
||||
pub(crate) url_preview_domain_contains_allowlist: Vec<String>,
|
||||
pub url_preview_domain_contains_allowlist: Vec<String>,
|
||||
#[serde(default = "Vec::new")]
|
||||
pub(crate) url_preview_domain_explicit_allowlist: Vec<String>,
|
||||
pub url_preview_domain_explicit_allowlist: Vec<String>,
|
||||
#[serde(default = "Vec::new")]
|
||||
pub(crate) url_preview_domain_explicit_denylist: Vec<String>,
|
||||
pub url_preview_domain_explicit_denylist: Vec<String>,
|
||||
#[serde(default = "Vec::new")]
|
||||
pub(crate) url_preview_url_contains_allowlist: Vec<String>,
|
||||
pub url_preview_url_contains_allowlist: Vec<String>,
|
||||
#[serde(default = "default_url_preview_max_spider_size")]
|
||||
pub(crate) url_preview_max_spider_size: usize,
|
||||
pub url_preview_max_spider_size: usize,
|
||||
#[serde(default)]
|
||||
pub(crate) url_preview_check_root_domain: bool,
|
||||
pub url_preview_check_root_domain: bool,
|
||||
|
||||
#[serde(default = "RegexSet::empty")]
|
||||
#[serde(with = "serde_regex")]
|
||||
pub(crate) forbidden_alias_names: RegexSet,
|
||||
pub forbidden_alias_names: RegexSet,
|
||||
|
||||
#[serde(default = "RegexSet::empty")]
|
||||
#[serde(with = "serde_regex")]
|
||||
pub(crate) forbidden_usernames: RegexSet,
|
||||
pub forbidden_usernames: RegexSet,
|
||||
|
||||
#[serde(default = "true_fn")]
|
||||
pub(crate) startup_netburst: bool,
|
||||
pub startup_netburst: bool,
|
||||
#[serde(default = "default_startup_netburst_keep")]
|
||||
pub(crate) startup_netburst_keep: i64,
|
||||
pub startup_netburst_keep: i64,
|
||||
|
||||
#[serde(default)]
|
||||
pub(crate) block_non_admin_invites: bool,
|
||||
pub block_non_admin_invites: bool,
|
||||
|
||||
#[serde(default)]
|
||||
pub(crate) sentry: bool,
|
||||
#[serde(default = "default_sentry_endpoint")]
|
||||
pub(crate) sentry_endpoint: Option<Url>,
|
||||
pub sentry: bool,
|
||||
#[serde(default)]
|
||||
pub(crate) sentry_send_server_name: bool,
|
||||
pub sentry_send_server_name: bool,
|
||||
#[serde(default = "default_sentry_traces_sample_rate")]
|
||||
pub(crate) sentry_traces_sample_rate: f32,
|
||||
pub sentry_traces_sample_rate: f32,
|
||||
|
||||
#[serde(flatten)]
|
||||
#[allow(clippy::zero_sized_map_values)] // this is a catchall, the map shouldn't be zero at runtime
|
||||
catchall: BTreeMap<String, IgnoredAny>,
|
||||
pub catchall: BTreeMap<String, IgnoredAny>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
pub(crate) struct TlsConfig {
|
||||
pub(crate) certs: String,
|
||||
pub(crate) key: String,
|
||||
pub struct TlsConfig {
|
||||
pub certs: String,
|
||||
pub key: String,
|
||||
#[serde(default)]
|
||||
/// Whether to listen and allow for HTTP and HTTPS connections (insecure!)
|
||||
/// Only works / does something if the `axum_dual_protocol` feature flag was
|
||||
/// built
|
||||
pub(crate) dual_protocol: bool,
|
||||
pub dual_protocol: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Default)]
|
||||
pub(crate) struct WellKnownConfig {
|
||||
pub(crate) client: Option<Url>,
|
||||
pub(crate) server: Option<OwnedServerName>,
|
||||
pub(crate) support_page: Option<Url>,
|
||||
pub(crate) support_role: Option<ContactRole>,
|
||||
pub(crate) support_email: Option<String>,
|
||||
pub(crate) support_mxid: Option<OwnedUserId>,
|
||||
pub struct WellKnownConfig {
|
||||
pub client: Option<Url>,
|
||||
pub server: Option<OwnedServerName>,
|
||||
pub support_page: Option<Url>,
|
||||
pub support_role: Option<ContactRole>,
|
||||
pub support_email: Option<String>,
|
||||
pub support_mxid: Option<OwnedUserId>,
|
||||
}
|
||||
|
||||
const DEPRECATED_KEYS: &[&str] = &[
|
||||
@@ -367,26 +353,17 @@ const DEPRECATED_KEYS: &[&str] = &[
|
||||
|
||||
impl Config {
|
||||
/// Initialize config
|
||||
pub(crate) fn new(path: Option<PathBuf>) -> Result<Self, Error> {
|
||||
pub fn new(path: Option<PathBuf>) -> Result<Self, Error> {
|
||||
let raw_config = if let Some(config_file_env) = Env::var("CONDUIT_CONFIG") {
|
||||
Figment::new()
|
||||
.merge(Toml::file(config_file_env).nested())
|
||||
.merge(Env::prefixed("CONDUIT_").global())
|
||||
.merge(Env::prefixed("CONDUWUIT_").global())
|
||||
} else if let Some(config_file_arg) = Env::var("CONDUWUIT_CONFIG") {
|
||||
Figment::new()
|
||||
.merge(Toml::file(config_file_arg).nested())
|
||||
.merge(Env::prefixed("CONDUIT_").global())
|
||||
.merge(Env::prefixed("CONDUWUIT_").global())
|
||||
} else if let Some(config_file_arg) = path {
|
||||
Figment::new()
|
||||
.merge(Toml::file(config_file_arg).nested())
|
||||
.merge(Env::prefixed("CONDUIT_").global())
|
||||
.merge(Env::prefixed("CONDUWUIT_").global())
|
||||
} else {
|
||||
Figment::new()
|
||||
.merge(Env::prefixed("CONDUIT_").global())
|
||||
.merge(Env::prefixed("CONDUWUIT_").global())
|
||||
Figment::new().merge(Env::prefixed("CONDUIT_").global())
|
||||
};
|
||||
|
||||
let config = match raw_config.extract::<Config>() {
|
||||
@@ -394,6 +371,8 @@ impl Config {
|
||||
Ok(config) => config,
|
||||
};
|
||||
|
||||
check::check(&config)?;
|
||||
|
||||
// don't start if we're listening on both UNIX sockets and TCP at same time
|
||||
if config.is_dual_listening(&raw_config) {
|
||||
return Err(Error::bad_config("dual listening on UNIX and TCP sockets not allowed."));
|
||||
@@ -404,7 +383,7 @@ impl Config {
|
||||
|
||||
/// Iterates over all the keys in the config file and warns if there is a
|
||||
/// deprecated key specified
|
||||
pub(crate) fn warn_deprecated(&self) {
|
||||
pub fn warn_deprecated(&self) {
|
||||
debug!("Checking for deprecated config keys");
|
||||
let mut was_deprecated = false;
|
||||
for key in self
|
||||
@@ -426,7 +405,7 @@ impl Config {
|
||||
|
||||
/// iterates over all the catchall keys (unknown config options) and warns
|
||||
/// if there are any.
|
||||
pub(crate) fn warn_unknown_key(&self) {
|
||||
pub fn warn_unknown_key(&self) {
|
||||
debug!("Checking for unknown config keys");
|
||||
for key in self
|
||||
.catchall
|
||||
@@ -454,7 +433,7 @@ impl Config {
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub(crate) fn get_bind_addrs(&self) -> Vec<SocketAddr> {
|
||||
pub fn get_bind_addrs(&self) -> Vec<SocketAddr> {
|
||||
match &self.port.ports {
|
||||
Left(port) => {
|
||||
// Left is only 1 value, so make a vec with 1 value only
|
||||
@@ -473,8 +452,6 @@ impl Config {
|
||||
.collect::<Vec<_>>(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn check(&self) -> Result<(), Error> { check(self) }
|
||||
}
|
||||
|
||||
impl fmt::Display for Config {
|
||||
@@ -522,10 +499,9 @@ impl fmt::Display for Config {
|
||||
("DNS fallback to TCP", &self.dns_tcp_fallback.to_string()),
|
||||
("Query all nameservers", &self.query_all_nameservers.to_string()),
|
||||
("Maximum request size (bytes)", &self.max_request_size.to_string()),
|
||||
("Sender retry backoff limit", &self.sender_retry_backoff_limit.to_string()),
|
||||
("Maximum concurrent requests", &self.max_concurrent_requests.to_string()),
|
||||
("Request connect timeout", &self.request_conn_timeout.to_string()),
|
||||
("Request timeout", &self.request_timeout.to_string()),
|
||||
("Request total timeout", &self.request_total_timeout.to_string()),
|
||||
("Idle connections per host", &self.request_idle_per_host.to_string()),
|
||||
("Request pool idle timeout", &self.request_idle_timeout.to_string()),
|
||||
("Well_known connect timeout", &self.well_known_conn_timeout.to_string()),
|
||||
@@ -710,22 +686,9 @@ impl fmt::Display for Config {
|
||||
),
|
||||
#[cfg(feature = "rocksdb")]
|
||||
("RocksDB Recovery Mode", &self.rocksdb_recovery_mode.to_string()),
|
||||
#[cfg(feature = "rocksdb")]
|
||||
("RocksDB Repair Mode", &self.rocksdb_repair.to_string()),
|
||||
#[cfg(feature = "rocksdb")]
|
||||
("RocksDB Read-only Mode", &self.rocksdb_read_only.to_string()),
|
||||
#[cfg(feature = "rocksdb")]
|
||||
("RocksDB Periodic Cleanup", &self.rocksdb_periodic_cleanup.to_string()),
|
||||
#[cfg(feature = "rocksdb")]
|
||||
(
|
||||
"RocksDB Compaction Idle Priority",
|
||||
&self.rocksdb_compaction_prio_idle.to_string(),
|
||||
),
|
||||
#[cfg(feature = "rocksdb")]
|
||||
(
|
||||
"RocksDB Compaction Idle IOPriority",
|
||||
&self.rocksdb_compaction_ioprio_idle.to_string(),
|
||||
),
|
||||
("Prevent Media Downloads From", {
|
||||
let mut lst = vec![];
|
||||
for domain in &self.prevent_media_downloads_from {
|
||||
@@ -900,32 +863,30 @@ fn default_max_request_size() -> u32 {
|
||||
20 * 1024 * 1024 // Default to 20 MB
|
||||
}
|
||||
|
||||
fn default_max_concurrent_requests() -> u16 { 500 }
|
||||
|
||||
fn default_request_conn_timeout() -> u64 { 10 }
|
||||
|
||||
fn default_request_timeout() -> u64 { 35 }
|
||||
|
||||
fn default_request_total_timeout() -> u64 { 320 }
|
||||
fn default_request_idle_per_host() -> u16 { 1 }
|
||||
|
||||
fn default_request_idle_timeout() -> u64 { 5 }
|
||||
|
||||
fn default_request_idle_per_host() -> u16 { 1 }
|
||||
|
||||
fn default_well_known_conn_timeout() -> u64 { 6 }
|
||||
|
||||
fn default_well_known_timeout() -> u64 { 10 }
|
||||
|
||||
fn default_federation_timeout() -> u64 { 300 }
|
||||
|
||||
fn default_federation_idle_timeout() -> u64 { 25 }
|
||||
|
||||
fn default_federation_idle_per_host() -> u16 { 1 }
|
||||
|
||||
fn default_federation_idle_timeout() -> u64 { 25 }
|
||||
|
||||
fn default_sender_timeout() -> u64 { 180 }
|
||||
|
||||
fn default_sender_idle_timeout() -> u64 { 180 }
|
||||
|
||||
fn default_sender_retry_backoff_limit() -> u64 { 86400 }
|
||||
|
||||
fn default_appservice_timeout() -> u64 { 120 }
|
||||
|
||||
fn default_appservice_idle_timeout() -> u64 { 300 }
|
||||
@@ -941,7 +902,7 @@ fn default_log() -> String {
|
||||
if cfg!(debug_assertions) {
|
||||
"debug".to_owned()
|
||||
} else {
|
||||
"info".to_owned()
|
||||
"warn,ruma_state_res=warn".to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1021,12 +982,6 @@ fn default_url_preview_max_spider_size() -> usize {
|
||||
|
||||
fn default_new_user_displayname_suffix() -> String { "🏳️⚧️".to_owned() }
|
||||
|
||||
fn default_sentry_endpoint() -> Option<Url> {
|
||||
Url::parse("https://fe2eb4536aa04949e28eff3128d64757@o4506996327251968.ingest.us.sentry.io/4506996334657536")
|
||||
.unwrap()
|
||||
.into()
|
||||
}
|
||||
|
||||
fn default_sentry_traces_sample_rate() -> f32 { 0.15 }
|
||||
|
||||
fn default_startup_netburst_keep() -> i64 { 50 }
|
||||
|
||||
+4
-4
@@ -30,7 +30,7 @@ use crate::Result;
|
||||
/// `ordinary.onion`, `matrix.myspecial.onion`, but not `hello.myspecial.onion`.
|
||||
#[derive(Clone, Default, Debug, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub(crate) enum ProxyConfig {
|
||||
pub enum ProxyConfig {
|
||||
#[default]
|
||||
None,
|
||||
Global {
|
||||
@@ -40,7 +40,7 @@ pub(crate) enum ProxyConfig {
|
||||
ByDomain(Vec<PartialProxyConfig>),
|
||||
}
|
||||
impl ProxyConfig {
|
||||
pub(crate) fn to_proxy(&self) -> Result<Option<Proxy>> {
|
||||
pub fn to_proxy(&self) -> Result<Option<Proxy>> {
|
||||
Ok(match self.clone() {
|
||||
ProxyConfig::None => None,
|
||||
ProxyConfig::Global {
|
||||
@@ -55,7 +55,7 @@ impl ProxyConfig {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
pub(crate) struct PartialProxyConfig {
|
||||
pub struct PartialProxyConfig {
|
||||
#[serde(deserialize_with = "crate::utils::deserialize_from_str")]
|
||||
url: Url,
|
||||
#[serde(default)]
|
||||
@@ -64,7 +64,7 @@ pub(crate) struct PartialProxyConfig {
|
||||
exclude: Vec<WildCardedDomain>,
|
||||
}
|
||||
impl PartialProxyConfig {
|
||||
pub(crate) fn for_url(&self, url: &Url) -> Option<&Url> {
|
||||
pub fn for_url(&self, url: &Url) -> Option<&Url> {
|
||||
let domain = url.domain()?;
|
||||
let mut included_because = None; // most specific reason it was included
|
||||
let mut excluded_because = None; // most specific reason it was excluded
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::sync::Arc;
|
||||
|
||||
use super::KeyValueDatabaseEngine;
|
||||
|
||||
pub(crate) struct Cork {
|
||||
pub struct Cork {
|
||||
db: Arc<dyn KeyValueDatabaseEngine>,
|
||||
flush: bool,
|
||||
sync: bool,
|
||||
|
||||
@@ -4,13 +4,15 @@ use crate::{
|
||||
database::KeyValueDatabase,
|
||||
service::{
|
||||
self,
|
||||
sending::{Destination, SendingEvent},
|
||||
sending::{OutgoingDestination, SendingEventType},
|
||||
},
|
||||
services, utils, Error, Result,
|
||||
};
|
||||
|
||||
impl service::sending::Data for KeyValueDatabase {
|
||||
fn active_requests<'a>(&'a self) -> Box<dyn Iterator<Item = Result<(Vec<u8>, Destination, SendingEvent)>> + 'a> {
|
||||
fn active_requests<'a>(
|
||||
&'a self,
|
||||
) -> Box<dyn Iterator<Item = Result<(Vec<u8>, OutgoingDestination, SendingEventType)>> + 'a> {
|
||||
Box::new(
|
||||
self.servercurrentevent_data
|
||||
.iter()
|
||||
@@ -19,9 +21,9 @@ impl service::sending::Data for KeyValueDatabase {
|
||||
}
|
||||
|
||||
fn active_requests_for<'a>(
|
||||
&'a self, destination: &Destination,
|
||||
) -> Box<dyn Iterator<Item = Result<(Vec<u8>, SendingEvent)>> + 'a> {
|
||||
let prefix = destination.get_prefix();
|
||||
&'a self, outgoing_kind: &OutgoingDestination,
|
||||
) -> Box<dyn Iterator<Item = Result<(Vec<u8>, SendingEventType)>> + 'a> {
|
||||
let prefix = outgoing_kind.get_prefix();
|
||||
Box::new(
|
||||
self.servercurrentevent_data
|
||||
.scan_prefix(prefix)
|
||||
@@ -31,8 +33,8 @@ impl service::sending::Data for KeyValueDatabase {
|
||||
|
||||
fn delete_active_request(&self, key: Vec<u8>) -> Result<()> { self.servercurrentevent_data.remove(&key) }
|
||||
|
||||
fn delete_all_active_requests_for(&self, destination: &Destination) -> Result<()> {
|
||||
let prefix = destination.get_prefix();
|
||||
fn delete_all_active_requests_for(&self, outgoing_kind: &OutgoingDestination) -> Result<()> {
|
||||
let prefix = outgoing_kind.get_prefix();
|
||||
for (key, _) in self.servercurrentevent_data.scan_prefix(prefix) {
|
||||
self.servercurrentevent_data.remove(&key)?;
|
||||
}
|
||||
@@ -40,8 +42,8 @@ impl service::sending::Data for KeyValueDatabase {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn delete_all_requests_for(&self, destination: &Destination) -> Result<()> {
|
||||
let prefix = destination.get_prefix();
|
||||
fn delete_all_requests_for(&self, outgoing_kind: &OutgoingDestination) -> Result<()> {
|
||||
let prefix = outgoing_kind.get_prefix();
|
||||
for (key, _) in self.servercurrentevent_data.scan_prefix(prefix.clone()) {
|
||||
self.servercurrentevent_data.remove(&key).unwrap();
|
||||
}
|
||||
@@ -53,17 +55,17 @@ impl service::sending::Data for KeyValueDatabase {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn queue_requests(&self, requests: &[(&Destination, SendingEvent)]) -> Result<Vec<Vec<u8>>> {
|
||||
fn queue_requests(&self, requests: &[(&OutgoingDestination, SendingEventType)]) -> Result<Vec<Vec<u8>>> {
|
||||
let mut batch = Vec::new();
|
||||
let mut keys = Vec::new();
|
||||
for (destination, event) in requests {
|
||||
let mut key = destination.get_prefix();
|
||||
if let SendingEvent::Pdu(value) = &event {
|
||||
for (outgoing_kind, event) in requests {
|
||||
let mut key = outgoing_kind.get_prefix();
|
||||
if let SendingEventType::Pdu(value) = &event {
|
||||
key.extend_from_slice(value);
|
||||
} else {
|
||||
key.extend_from_slice(&services().globals.next_count()?.to_be_bytes());
|
||||
}
|
||||
let value = if let SendingEvent::Edu(value) = &event {
|
||||
let value = if let SendingEventType::Edu(value) = &event {
|
||||
&**value
|
||||
} else {
|
||||
&[]
|
||||
@@ -77,9 +79,9 @@ impl service::sending::Data for KeyValueDatabase {
|
||||
}
|
||||
|
||||
fn queued_requests<'a>(
|
||||
&'a self, destination: &Destination,
|
||||
) -> Box<dyn Iterator<Item = Result<(SendingEvent, Vec<u8>)>> + 'a> {
|
||||
let prefix = destination.get_prefix();
|
||||
&'a self, outgoing_kind: &OutgoingDestination,
|
||||
) -> Box<dyn Iterator<Item = Result<(SendingEventType, Vec<u8>)>> + 'a> {
|
||||
let prefix = outgoing_kind.get_prefix();
|
||||
return Box::new(
|
||||
self.servernameevent_data
|
||||
.scan_prefix(prefix)
|
||||
@@ -87,13 +89,13 @@ impl service::sending::Data for KeyValueDatabase {
|
||||
);
|
||||
}
|
||||
|
||||
fn mark_as_active(&self, events: &[(SendingEvent, Vec<u8>)]) -> Result<()> {
|
||||
fn mark_as_active(&self, events: &[(SendingEventType, Vec<u8>)]) -> Result<()> {
|
||||
for (e, key) in events {
|
||||
if key.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let value = if let SendingEvent::Edu(value) = &e {
|
||||
let value = if let SendingEventType::Edu(value) = &e {
|
||||
&**value
|
||||
} else {
|
||||
&[]
|
||||
@@ -120,7 +122,7 @@ impl service::sending::Data for KeyValueDatabase {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(key))]
|
||||
fn parse_servercurrentevent(key: &[u8], value: Vec<u8>) -> Result<(Destination, SendingEvent)> {
|
||||
fn parse_servercurrentevent(key: &[u8], value: Vec<u8>) -> Result<(OutgoingDestination, SendingEventType)> {
|
||||
// Appservices start with a plus
|
||||
Ok::<_, Error>(if key.starts_with(b"+") {
|
||||
let mut parts = key[1..].splitn(2, |&b| b == 0xFF);
|
||||
@@ -134,11 +136,11 @@ fn parse_servercurrentevent(key: &[u8], value: Vec<u8>) -> Result<(Destination,
|
||||
.map_err(|_| Error::bad_database("Invalid server bytes in server_currenttransaction"))?;
|
||||
|
||||
(
|
||||
Destination::Appservice(server),
|
||||
OutgoingDestination::Appservice(server),
|
||||
if value.is_empty() {
|
||||
SendingEvent::Pdu(event.to_vec())
|
||||
SendingEventType::Pdu(event.to_vec())
|
||||
} else {
|
||||
SendingEvent::Edu(value)
|
||||
SendingEventType::Edu(value)
|
||||
},
|
||||
)
|
||||
} else if key.starts_with(b"$") {
|
||||
@@ -161,12 +163,12 @@ fn parse_servercurrentevent(key: &[u8], value: Vec<u8>) -> Result<(Destination,
|
||||
.ok_or_else(|| Error::bad_database("Invalid bytes in servercurrentpdus."))?;
|
||||
|
||||
(
|
||||
Destination::Push(user_id, pushkey_string),
|
||||
OutgoingDestination::Push(user_id, pushkey_string),
|
||||
if value.is_empty() {
|
||||
SendingEvent::Pdu(event.to_vec())
|
||||
SendingEventType::Pdu(event.to_vec())
|
||||
} else {
|
||||
// I'm pretty sure this should never be called
|
||||
SendingEvent::Edu(value)
|
||||
SendingEventType::Edu(value)
|
||||
},
|
||||
)
|
||||
} else {
|
||||
@@ -181,14 +183,14 @@ fn parse_servercurrentevent(key: &[u8], value: Vec<u8>) -> Result<(Destination,
|
||||
.map_err(|_| Error::bad_database("Invalid server bytes in server_currenttransaction"))?;
|
||||
|
||||
(
|
||||
Destination::Normal(
|
||||
OutgoingDestination::Normal(
|
||||
ServerName::parse(server)
|
||||
.map_err(|_| Error::bad_database("Invalid server string in server_currenttransaction"))?,
|
||||
),
|
||||
if value.is_empty() {
|
||||
SendingEvent::Pdu(event.to_vec())
|
||||
SendingEventType::Pdu(event.to_vec())
|
||||
} else {
|
||||
SendingEvent::Edu(value)
|
||||
SendingEventType::Edu(value)
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
+106
-107
@@ -1,14 +1,14 @@
|
||||
mod cork;
|
||||
mod key_value;
|
||||
mod kvengine;
|
||||
mod kvtree;
|
||||
pub(crate) mod cork;
|
||||
pub(crate) mod key_value;
|
||||
pub(crate) mod kvengine;
|
||||
pub(crate) mod kvtree;
|
||||
mod migrations;
|
||||
|
||||
#[cfg(feature = "rocksdb")]
|
||||
mod rocksdb;
|
||||
pub(crate) mod rocksdb;
|
||||
|
||||
#[cfg(feature = "sqlite")]
|
||||
mod sqlite;
|
||||
pub mod sqlite;
|
||||
|
||||
#[cfg(any(feature = "sqlite", feature = "rocksdb"))]
|
||||
pub(crate) mod watchers;
|
||||
@@ -44,149 +44,149 @@ use crate::{
|
||||
SERVICES,
|
||||
};
|
||||
|
||||
pub(crate) struct KeyValueDatabase {
|
||||
pub struct KeyValueDatabase {
|
||||
db: Arc<dyn KeyValueDatabaseEngine>,
|
||||
|
||||
//pub(crate) globals: globals::Globals,
|
||||
pub(crate) global: Arc<dyn KvTree>,
|
||||
pub(crate) server_signingkeys: Arc<dyn KvTree>,
|
||||
//pub globals: globals::Globals,
|
||||
pub(super) global: Arc<dyn KvTree>,
|
||||
pub(super) server_signingkeys: Arc<dyn KvTree>,
|
||||
|
||||
pub(crate) roomid_inviteviaservers: Arc<dyn KvTree>,
|
||||
pub(super) roomid_inviteviaservers: Arc<dyn KvTree>,
|
||||
|
||||
//pub(crate) users: users::Users,
|
||||
pub(crate) userid_password: Arc<dyn KvTree>,
|
||||
pub(crate) userid_displayname: Arc<dyn KvTree>,
|
||||
pub(crate) userid_avatarurl: Arc<dyn KvTree>,
|
||||
pub(crate) userid_blurhash: Arc<dyn KvTree>,
|
||||
pub(crate) userdeviceid_token: Arc<dyn KvTree>,
|
||||
pub(crate) userdeviceid_metadata: Arc<dyn KvTree>, // This is also used to check if a device exists
|
||||
pub(crate) userid_devicelistversion: Arc<dyn KvTree>, // DevicelistVersion = u64
|
||||
pub(crate) token_userdeviceid: Arc<dyn KvTree>,
|
||||
//pub users: users::Users,
|
||||
pub(super) userid_password: Arc<dyn KvTree>,
|
||||
pub(super) userid_displayname: Arc<dyn KvTree>,
|
||||
pub(super) userid_avatarurl: Arc<dyn KvTree>,
|
||||
pub(super) userid_blurhash: Arc<dyn KvTree>,
|
||||
pub(super) userdeviceid_token: Arc<dyn KvTree>,
|
||||
pub(super) userdeviceid_metadata: Arc<dyn KvTree>, // This is also used to check if a device exists
|
||||
pub(super) userid_devicelistversion: Arc<dyn KvTree>, // DevicelistVersion = u64
|
||||
pub(super) token_userdeviceid: Arc<dyn KvTree>,
|
||||
|
||||
pub(crate) onetimekeyid_onetimekeys: Arc<dyn KvTree>, // OneTimeKeyId = UserId + DeviceKeyId
|
||||
pub(crate) userid_lastonetimekeyupdate: Arc<dyn KvTree>, // LastOneTimeKeyUpdate = Count
|
||||
pub(crate) keychangeid_userid: Arc<dyn KvTree>, // KeyChangeId = UserId/RoomId + Count
|
||||
pub(crate) keyid_key: Arc<dyn KvTree>, // KeyId = UserId + KeyId (depends on key type)
|
||||
pub(crate) userid_masterkeyid: Arc<dyn KvTree>,
|
||||
pub(crate) userid_selfsigningkeyid: Arc<dyn KvTree>,
|
||||
pub(crate) userid_usersigningkeyid: Arc<dyn KvTree>,
|
||||
pub(super) onetimekeyid_onetimekeys: Arc<dyn KvTree>, // OneTimeKeyId = UserId + DeviceKeyId
|
||||
pub(super) userid_lastonetimekeyupdate: Arc<dyn KvTree>, // LastOneTimeKeyUpdate = Count
|
||||
pub(super) keychangeid_userid: Arc<dyn KvTree>, // KeyChangeId = UserId/RoomId + Count
|
||||
pub(super) keyid_key: Arc<dyn KvTree>, // KeyId = UserId + KeyId (depends on key type)
|
||||
pub(super) userid_masterkeyid: Arc<dyn KvTree>,
|
||||
pub(super) userid_selfsigningkeyid: Arc<dyn KvTree>,
|
||||
pub(super) userid_usersigningkeyid: Arc<dyn KvTree>,
|
||||
|
||||
pub(crate) userfilterid_filter: Arc<dyn KvTree>, // UserFilterId = UserId + FilterId
|
||||
pub(crate) todeviceid_events: Arc<dyn KvTree>, // ToDeviceId = UserId + DeviceId + Count
|
||||
pub(crate) userid_presenceid: Arc<dyn KvTree>, // UserId => Count
|
||||
pub(crate) presenceid_presence: Arc<dyn KvTree>, // Count + UserId => Presence
|
||||
pub(super) userfilterid_filter: Arc<dyn KvTree>, // UserFilterId = UserId + FilterId
|
||||
pub(super) todeviceid_events: Arc<dyn KvTree>, // ToDeviceId = UserId + DeviceId + Count
|
||||
pub(super) userid_presenceid: Arc<dyn KvTree>, // UserId => Count
|
||||
pub(super) presenceid_presence: Arc<dyn KvTree>, // Count + UserId => Presence
|
||||
|
||||
//pub(crate) uiaa: uiaa::Uiaa,
|
||||
pub(crate) userdevicesessionid_uiaainfo: Arc<dyn KvTree>, // User-interactive authentication
|
||||
pub(crate) userdevicesessionid_uiaarequest:
|
||||
//pub uiaa: uiaa::Uiaa,
|
||||
pub(super) userdevicesessionid_uiaainfo: Arc<dyn KvTree>, // User-interactive authentication
|
||||
pub(super) userdevicesessionid_uiaarequest:
|
||||
RwLock<BTreeMap<(OwnedUserId, OwnedDeviceId, String), CanonicalJsonValue>>,
|
||||
|
||||
//pub(crate) edus: RoomEdus,
|
||||
pub(crate) readreceiptid_readreceipt: Arc<dyn KvTree>, // ReadReceiptId = RoomId + Count + UserId
|
||||
pub(crate) roomuserid_privateread: Arc<dyn KvTree>, // RoomUserId = Room + User, PrivateRead = Count
|
||||
pub(crate) roomuserid_lastprivatereadupdate: Arc<dyn KvTree>, // LastPrivateReadUpdate = Count
|
||||
//pub edus: RoomEdus,
|
||||
pub(super) readreceiptid_readreceipt: Arc<dyn KvTree>, // ReadReceiptId = RoomId + Count + UserId
|
||||
pub(super) roomuserid_privateread: Arc<dyn KvTree>, // RoomUserId = Room + User, PrivateRead = Count
|
||||
pub(super) roomuserid_lastprivatereadupdate: Arc<dyn KvTree>, // LastPrivateReadUpdate = Count
|
||||
|
||||
//pub(crate) rooms: rooms::Rooms,
|
||||
pub(crate) pduid_pdu: Arc<dyn KvTree>, // PduId = ShortRoomId + Count
|
||||
pub(crate) eventid_pduid: Arc<dyn KvTree>,
|
||||
pub(crate) roomid_pduleaves: Arc<dyn KvTree>,
|
||||
pub(crate) alias_roomid: Arc<dyn KvTree>,
|
||||
pub(crate) aliasid_alias: Arc<dyn KvTree>, // AliasId = RoomId + Count
|
||||
pub(crate) publicroomids: Arc<dyn KvTree>,
|
||||
//pub rooms: rooms::Rooms,
|
||||
pub(super) pduid_pdu: Arc<dyn KvTree>, // PduId = ShortRoomId + Count
|
||||
pub(super) eventid_pduid: Arc<dyn KvTree>,
|
||||
pub(super) roomid_pduleaves: Arc<dyn KvTree>,
|
||||
pub(super) alias_roomid: Arc<dyn KvTree>,
|
||||
pub(super) aliasid_alias: Arc<dyn KvTree>, // AliasId = RoomId + Count
|
||||
pub(super) publicroomids: Arc<dyn KvTree>,
|
||||
|
||||
pub(crate) threadid_userids: Arc<dyn KvTree>, // ThreadId = RoomId + Count
|
||||
pub(super) threadid_userids: Arc<dyn KvTree>, // ThreadId = RoomId + Count
|
||||
|
||||
pub(crate) tokenids: Arc<dyn KvTree>, // TokenId = ShortRoomId + Token + PduIdCount
|
||||
pub(super) tokenids: Arc<dyn KvTree>, // TokenId = ShortRoomId + Token + PduIdCount
|
||||
|
||||
/// Participating servers in a room.
|
||||
pub(crate) roomserverids: Arc<dyn KvTree>, // RoomServerId = RoomId + ServerName
|
||||
pub(crate) serverroomids: Arc<dyn KvTree>, // ServerRoomId = ServerName + RoomId
|
||||
pub(super) roomserverids: Arc<dyn KvTree>, // RoomServerId = RoomId + ServerName
|
||||
pub(super) serverroomids: Arc<dyn KvTree>, // ServerRoomId = ServerName + RoomId
|
||||
|
||||
pub(crate) userroomid_joined: Arc<dyn KvTree>,
|
||||
pub(crate) roomuserid_joined: Arc<dyn KvTree>,
|
||||
pub(crate) roomid_joinedcount: Arc<dyn KvTree>,
|
||||
pub(crate) roomid_invitedcount: Arc<dyn KvTree>,
|
||||
pub(crate) roomuseroncejoinedids: Arc<dyn KvTree>,
|
||||
pub(crate) userroomid_invitestate: Arc<dyn KvTree>, // InviteState = Vec<Raw<Pdu>>
|
||||
pub(crate) roomuserid_invitecount: Arc<dyn KvTree>, // InviteCount = Count
|
||||
pub(crate) userroomid_leftstate: Arc<dyn KvTree>,
|
||||
pub(crate) roomuserid_leftcount: Arc<dyn KvTree>,
|
||||
pub(super) userroomid_joined: Arc<dyn KvTree>,
|
||||
pub(super) roomuserid_joined: Arc<dyn KvTree>,
|
||||
pub(super) roomid_joinedcount: Arc<dyn KvTree>,
|
||||
pub(super) roomid_invitedcount: Arc<dyn KvTree>,
|
||||
pub(super) roomuseroncejoinedids: Arc<dyn KvTree>,
|
||||
pub(super) userroomid_invitestate: Arc<dyn KvTree>, // InviteState = Vec<Raw<Pdu>>
|
||||
pub(super) roomuserid_invitecount: Arc<dyn KvTree>, // InviteCount = Count
|
||||
pub(super) userroomid_leftstate: Arc<dyn KvTree>,
|
||||
pub(super) roomuserid_leftcount: Arc<dyn KvTree>,
|
||||
|
||||
pub(crate) disabledroomids: Arc<dyn KvTree>, // Rooms where incoming federation handling is disabled
|
||||
pub(super) disabledroomids: Arc<dyn KvTree>, // Rooms where incoming federation handling is disabled
|
||||
|
||||
pub(crate) bannedroomids: Arc<dyn KvTree>, // Rooms where local users are not allowed to join
|
||||
pub(super) bannedroomids: Arc<dyn KvTree>, // Rooms where local users are not allowed to join
|
||||
|
||||
pub(crate) lazyloadedids: Arc<dyn KvTree>, // LazyLoadedIds = UserId + DeviceId + RoomId + LazyLoadedUserId
|
||||
pub(super) lazyloadedids: Arc<dyn KvTree>, // LazyLoadedIds = UserId + DeviceId + RoomId + LazyLoadedUserId
|
||||
|
||||
pub(crate) userroomid_notificationcount: Arc<dyn KvTree>, // NotifyCount = u64
|
||||
pub(crate) userroomid_highlightcount: Arc<dyn KvTree>, // HightlightCount = u64
|
||||
pub(crate) roomuserid_lastnotificationread: Arc<dyn KvTree>, // LastNotificationRead = u64
|
||||
pub(super) userroomid_notificationcount: Arc<dyn KvTree>, // NotifyCount = u64
|
||||
pub(super) userroomid_highlightcount: Arc<dyn KvTree>, // HightlightCount = u64
|
||||
pub(super) roomuserid_lastnotificationread: Arc<dyn KvTree>, // LastNotificationRead = u64
|
||||
|
||||
/// Remember the current state hash of a room.
|
||||
pub(crate) roomid_shortstatehash: Arc<dyn KvTree>,
|
||||
pub(crate) roomsynctoken_shortstatehash: Arc<dyn KvTree>,
|
||||
pub(super) roomid_shortstatehash: Arc<dyn KvTree>,
|
||||
pub(super) roomsynctoken_shortstatehash: Arc<dyn KvTree>,
|
||||
/// Remember the state hash at events in the past.
|
||||
pub(crate) shorteventid_shortstatehash: Arc<dyn KvTree>,
|
||||
pub(crate) statekey_shortstatekey: Arc<dyn KvTree>, /* StateKey = EventType + StateKey, ShortStateKey =
|
||||
pub(super) shorteventid_shortstatehash: Arc<dyn KvTree>,
|
||||
pub(super) statekey_shortstatekey: Arc<dyn KvTree>, /* StateKey = EventType + StateKey, ShortStateKey =
|
||||
* Count */
|
||||
pub(crate) shortstatekey_statekey: Arc<dyn KvTree>,
|
||||
pub(super) shortstatekey_statekey: Arc<dyn KvTree>,
|
||||
|
||||
pub(crate) roomid_shortroomid: Arc<dyn KvTree>,
|
||||
pub(super) roomid_shortroomid: Arc<dyn KvTree>,
|
||||
|
||||
pub(crate) shorteventid_eventid: Arc<dyn KvTree>,
|
||||
pub(crate) eventid_shorteventid: Arc<dyn KvTree>,
|
||||
pub(super) shorteventid_eventid: Arc<dyn KvTree>,
|
||||
pub(super) eventid_shorteventid: Arc<dyn KvTree>,
|
||||
|
||||
pub(crate) statehash_shortstatehash: Arc<dyn KvTree>,
|
||||
pub(crate) shortstatehash_statediff: Arc<dyn KvTree>, /* StateDiff = parent (or 0) +
|
||||
pub(super) statehash_shortstatehash: Arc<dyn KvTree>,
|
||||
pub(super) shortstatehash_statediff: Arc<dyn KvTree>, /* StateDiff = parent (or 0) +
|
||||
* (shortstatekey+shorteventid++) + 0_u64 +
|
||||
* (shortstatekey+shorteventid--) */
|
||||
|
||||
pub(crate) shorteventid_authchain: Arc<dyn KvTree>,
|
||||
pub(super) shorteventid_authchain: Arc<dyn KvTree>,
|
||||
|
||||
/// RoomId + EventId -> outlier PDU.
|
||||
/// Any pdu that has passed the steps 1-8 in the incoming event
|
||||
/// /federation/send/txn.
|
||||
pub(crate) eventid_outlierpdu: Arc<dyn KvTree>,
|
||||
pub(crate) softfailedeventids: Arc<dyn KvTree>,
|
||||
pub(super) eventid_outlierpdu: Arc<dyn KvTree>,
|
||||
pub(super) softfailedeventids: Arc<dyn KvTree>,
|
||||
|
||||
/// ShortEventId + ShortEventId -> ().
|
||||
pub(crate) tofrom_relation: Arc<dyn KvTree>,
|
||||
pub(super) tofrom_relation: Arc<dyn KvTree>,
|
||||
/// RoomId + EventId -> Parent PDU EventId.
|
||||
pub(crate) referencedevents: Arc<dyn KvTree>,
|
||||
pub(super) referencedevents: Arc<dyn KvTree>,
|
||||
|
||||
//pub(crate) account_data: account_data::AccountData,
|
||||
pub(crate) roomuserdataid_accountdata: Arc<dyn KvTree>, // RoomUserDataId = Room + User + Count + Type
|
||||
pub(crate) roomusertype_roomuserdataid: Arc<dyn KvTree>, // RoomUserType = Room + User + Type
|
||||
//pub account_data: account_data::AccountData,
|
||||
pub(super) roomuserdataid_accountdata: Arc<dyn KvTree>, // RoomUserDataId = Room + User + Count + Type
|
||||
pub(super) roomusertype_roomuserdataid: Arc<dyn KvTree>, // RoomUserType = Room + User + Type
|
||||
|
||||
//pub(crate) media: media::Media,
|
||||
pub(crate) mediaid_file: Arc<dyn KvTree>, // MediaId = MXC + WidthHeight + ContentDisposition + ContentType
|
||||
pub(crate) url_previews: Arc<dyn KvTree>,
|
||||
pub(crate) mediaid_user: Arc<dyn KvTree>,
|
||||
//pub(crate) key_backups: key_backups::KeyBackups,
|
||||
pub(crate) backupid_algorithm: Arc<dyn KvTree>, // BackupId = UserId + Version(Count)
|
||||
pub(crate) backupid_etag: Arc<dyn KvTree>, // BackupId = UserId + Version(Count)
|
||||
pub(crate) backupkeyid_backup: Arc<dyn KvTree>, // BackupKeyId = UserId + Version + RoomId + SessionId
|
||||
//pub media: media::Media,
|
||||
pub(super) mediaid_file: Arc<dyn KvTree>, // MediaId = MXC + WidthHeight + ContentDisposition + ContentType
|
||||
pub(super) url_previews: Arc<dyn KvTree>,
|
||||
pub(super) mediaid_user: Arc<dyn KvTree>,
|
||||
//pub key_backups: key_backups::KeyBackups,
|
||||
pub(super) backupid_algorithm: Arc<dyn KvTree>, // BackupId = UserId + Version(Count)
|
||||
pub(super) backupid_etag: Arc<dyn KvTree>, // BackupId = UserId + Version(Count)
|
||||
pub(super) backupkeyid_backup: Arc<dyn KvTree>, // BackupKeyId = UserId + Version + RoomId + SessionId
|
||||
|
||||
//pub(crate) transaction_ids: transaction_ids::TransactionIds,
|
||||
pub(crate) userdevicetxnid_response: Arc<dyn KvTree>, /* Response can be empty (/sendToDevice) or the event id
|
||||
//pub transaction_ids: transaction_ids::TransactionIds,
|
||||
pub(super) userdevicetxnid_response: Arc<dyn KvTree>, /* Response can be empty (/sendToDevice) or the event id
|
||||
* (/send) */
|
||||
//pub(crate) sending: sending::Sending,
|
||||
pub(crate) servername_educount: Arc<dyn KvTree>, // EduCount: Count of last EDU sync
|
||||
pub(crate) servernameevent_data: Arc<dyn KvTree>, /* ServernameEvent = (+ / $)SenderKey / ServerName / UserId +
|
||||
//pub sending: sending::Sending,
|
||||
pub(super) servername_educount: Arc<dyn KvTree>, // EduCount: Count of last EDU sync
|
||||
pub(super) servernameevent_data: Arc<dyn KvTree>, /* ServernameEvent = (+ / $)SenderKey / ServerName / UserId +
|
||||
* PduId / Id (for edus), Data = EDU content */
|
||||
pub(crate) servercurrentevent_data: Arc<dyn KvTree>, /* ServerCurrentEvents = (+ / $)ServerName / UserId + PduId
|
||||
pub(super) servercurrentevent_data: Arc<dyn KvTree>, /* ServerCurrentEvents = (+ / $)ServerName / UserId + PduId
|
||||
* / Id (for edus), Data = EDU content */
|
||||
|
||||
//pub(crate) appservice: appservice::Appservice,
|
||||
pub(crate) id_appserviceregistrations: Arc<dyn KvTree>,
|
||||
//pub appservice: appservice::Appservice,
|
||||
pub(super) id_appserviceregistrations: Arc<dyn KvTree>,
|
||||
|
||||
//pub(crate) pusher: pusher::PushData,
|
||||
pub(crate) senderkey_pusher: Arc<dyn KvTree>,
|
||||
//pub pusher: pusher::PushData,
|
||||
pub(super) senderkey_pusher: Arc<dyn KvTree>,
|
||||
|
||||
pub(crate) auth_chain_cache: Mutex<LruCache<Vec<u64>, Arc<[u64]>>>,
|
||||
pub(crate) our_real_users_cache: RwLock<HashMap<OwnedRoomId, Arc<HashSet<OwnedUserId>>>>,
|
||||
pub(crate) appservice_in_room_cache: RwLock<HashMap<OwnedRoomId, HashMap<String, bool>>>,
|
||||
pub(crate) lasttimelinecount_cache: Mutex<HashMap<OwnedRoomId, PduCount>>,
|
||||
pub(super) auth_chain_cache: Mutex<LruCache<Vec<u64>, Arc<[u64]>>>,
|
||||
pub(super) our_real_users_cache: RwLock<HashMap<OwnedRoomId, Arc<HashSet<OwnedUserId>>>>,
|
||||
pub(super) appservice_in_room_cache: RwLock<HashMap<OwnedRoomId, HashMap<String, bool>>>,
|
||||
pub(super) lasttimelinecount_cache: Mutex<HashMap<OwnedRoomId, PduCount>>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -203,7 +203,7 @@ struct CheckForUpdatesResponse {
|
||||
impl KeyValueDatabase {
|
||||
/// Load an existing database or create a new one.
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub(crate) async fn load_or_create(
|
||||
pub async fn load_or_create(
|
||||
config: Config,
|
||||
tracing_reload_handler: tracing_subscriber::reload::Handle<
|
||||
tracing_subscriber::EnvFilter,
|
||||
@@ -544,8 +544,7 @@ impl KeyValueDatabase {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn flush(&self) -> Result<()> {
|
||||
pub fn flush(&self) -> Result<()> {
|
||||
let start = std::time::Instant::now();
|
||||
|
||||
let res = self.db.flush();
|
||||
|
||||
@@ -5,10 +5,10 @@ use rust_rocksdb::WriteBatchWithTransaction;
|
||||
use super::{watchers::Watchers, Engine, KeyValueDatabaseEngine, KvTree};
|
||||
use crate::{utils, Result};
|
||||
|
||||
pub(crate) struct RocksDbEngineTree<'a> {
|
||||
pub(crate) db: Arc<Engine>,
|
||||
pub(crate) name: &'a str,
|
||||
pub(crate) watchers: Watchers,
|
||||
pub(super) struct RocksDbEngineTree<'a> {
|
||||
pub db: Arc<Engine>,
|
||||
pub name: &'a str,
|
||||
pub watchers: Watchers,
|
||||
}
|
||||
|
||||
impl RocksDbEngineTree<'_> {
|
||||
|
||||
@@ -41,9 +41,9 @@ impl KeyValueDatabaseEngine for Arc<Engine> {
|
||||
let mut col_cache = HashMap::new();
|
||||
col_cache.insert("primary".to_owned(), Cache::new_lru_cache(col_cache_capacity_bytes));
|
||||
|
||||
let mut db_env = Env::new()?;
|
||||
let db_env = Env::new()?;
|
||||
let row_cache = Cache::new_lru_cache(row_cache_capacity_bytes);
|
||||
let db_opts = db_options(config, &mut db_env, &row_cache, col_cache.get("primary").expect("cache"));
|
||||
let db_opts = db_options(config, &db_env, &row_cache, col_cache.get("primary").expect("cache"));
|
||||
|
||||
let load_time = std::time::Instant::now();
|
||||
if config.rocksdb_repair {
|
||||
|
||||
@@ -14,7 +14,7 @@ use super::Config;
|
||||
/// resulting value. Note that we require special per-column options on some
|
||||
/// columns, therefor columns should only be opened after passing this result
|
||||
/// through cf_options().
|
||||
pub(crate) fn db_options(config: &Config, env: &mut Env, row_cache: &Cache, col_cache: &Cache) -> Options {
|
||||
pub(crate) fn db_options(config: &Config, env: &Env, row_cache: &Cache, col_cache: &Cache) -> Options {
|
||||
let mut opts = Options::default();
|
||||
|
||||
// Logging
|
||||
@@ -22,7 +22,7 @@ pub(crate) fn db_options(config: &Config, env: &mut Env, row_cache: &Cache, col_
|
||||
|
||||
// Processing
|
||||
let threads = if config.rocksdb_parallelism_threads == 0 {
|
||||
num_cpus::get() // max cores if user specified 0
|
||||
num_cpus::get_physical() // max cores if user specified 0
|
||||
} else {
|
||||
config.rocksdb_parallelism_threads
|
||||
};
|
||||
@@ -30,9 +30,6 @@ pub(crate) fn db_options(config: &Config, env: &mut Env, row_cache: &Cache, col_
|
||||
opts.set_max_background_jobs(threads.try_into().unwrap());
|
||||
opts.set_max_subcompactions(threads.try_into().unwrap());
|
||||
opts.set_max_file_opening_threads(0);
|
||||
if config.rocksdb_compaction_prio_idle {
|
||||
env.lower_thread_pool_cpu_priority();
|
||||
}
|
||||
|
||||
// IO
|
||||
opts.set_manual_wal_flush(true);
|
||||
@@ -44,9 +41,6 @@ pub(crate) fn db_options(config: &Config, env: &mut Env, row_cache: &Cache, col_
|
||||
opts.set_skip_stats_update_on_db_open(true);
|
||||
//opts.set_max_file_opening_threads(threads.try_into().unwrap());
|
||||
}
|
||||
if config.rocksdb_compaction_ioprio_idle {
|
||||
env.lower_thread_pool_io_priority();
|
||||
}
|
||||
|
||||
// Blocks
|
||||
let mut table_opts = table_options(config);
|
||||
|
||||
@@ -20,8 +20,8 @@ thread_local! {
|
||||
}
|
||||
|
||||
struct PreparedStatementIterator<'a> {
|
||||
iterator: Box<dyn Iterator<Item = TupleOfBytes> + 'a>,
|
||||
_statement_ref: NonAliasingBox<rusqlite::Statement<'a>>,
|
||||
pub iterator: Box<dyn Iterator<Item = TupleOfBytes> + 'a>,
|
||||
pub _statement_ref: NonAliasingBox<rusqlite::Statement<'a>>,
|
||||
}
|
||||
|
||||
impl Iterator for PreparedStatementIterator<'_> {
|
||||
@@ -77,7 +77,7 @@ impl Engine {
|
||||
.get_or(|| Self::prepare_conn(&self.path, self.cache_size_per_thread).unwrap())
|
||||
}
|
||||
|
||||
fn flush_wal(self: &Arc<Self>) -> Result<()> {
|
||||
pub fn flush_wal(self: &Arc<Self>) -> Result<()> {
|
||||
self.write_lock()
|
||||
.pragma_update(Some(Main), "wal_checkpoint", "RESTART")?;
|
||||
Ok(())
|
||||
@@ -130,7 +130,7 @@ impl KeyValueDatabaseEngine for Arc<Engine> {
|
||||
fn cleanup(&self) -> Result<()> { self.flush_wal() }
|
||||
}
|
||||
|
||||
struct SqliteTable {
|
||||
pub struct SqliteTable {
|
||||
engine: Arc<Engine>,
|
||||
name: String,
|
||||
watchers: Watchers,
|
||||
@@ -154,7 +154,7 @@ impl SqliteTable {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn iter_with_guard<'a>(&'a self, guard: &'a Connection) -> Box<dyn Iterator<Item = TupleOfBytes> + 'a> {
|
||||
pub fn iter_with_guard<'a>(&'a self, guard: &'a Connection) -> Box<dyn Iterator<Item = TupleOfBytes> + 'a> {
|
||||
let statement = Box::leak(Box::new(
|
||||
guard
|
||||
.prepare(&format!("SELECT key, value FROM {} ORDER BY key ASC", &self.name))
|
||||
|
||||
@@ -10,12 +10,12 @@ use tokio::sync::watch;
|
||||
type Watcher = RwLock<HashMap<Vec<u8>, (watch::Sender<()>, watch::Receiver<()>)>>;
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct Watchers {
|
||||
pub(super) struct Watchers {
|
||||
watchers: Watcher,
|
||||
}
|
||||
|
||||
impl Watchers {
|
||||
pub(crate) fn watch<'a>(&'a self, prefix: &[u8]) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||
pub(super) fn watch<'a>(&'a self, prefix: &[u8]) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||
let mut rx = match self.watchers.write().unwrap().entry(prefix.to_vec()) {
|
||||
hash_map::Entry::Occupied(o) => o.get().1.clone(),
|
||||
hash_map::Entry::Vacant(v) => {
|
||||
@@ -31,7 +31,7 @@ impl Watchers {
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn wake(&self, key: &[u8]) {
|
||||
pub(super) fn wake(&self, key: &[u8]) {
|
||||
let watchers = self.watchers.read().unwrap();
|
||||
let mut triggered = Vec::new();
|
||||
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
pub mod api;
|
||||
pub mod clap;
|
||||
mod config;
|
||||
mod database;
|
||||
mod service;
|
||||
mod utils;
|
||||
|
||||
// Not async due to services() being used in many closures, and async closures
|
||||
// are not stable as of writing This is the case for every other occurence of
|
||||
// sync Mutex/RwLock, except for database related ones, where the current
|
||||
// maintainer (Timo) has asked to not modify those
|
||||
use std::sync::RwLock;
|
||||
|
||||
pub use api::ruma_wrapper::{Ruma, RumaResponse};
|
||||
pub use config::Config;
|
||||
pub use database::KeyValueDatabase;
|
||||
pub use service::{pdu::PduEvent, Services};
|
||||
pub use utils::error::{Error, Result};
|
||||
|
||||
pub static SERVICES: RwLock<Option<&'static Services<'static>>> = RwLock::new(None);
|
||||
|
||||
pub fn services() -> &'static Services<'static> {
|
||||
SERVICES
|
||||
.read()
|
||||
.unwrap()
|
||||
.expect("SERVICES should be initialized when this is called")
|
||||
}
|
||||
+62
-133
@@ -3,13 +3,8 @@ use std::fs::Permissions; // not unix specific, just only for UNIX sockets stuff
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::fs::PermissionsExt as _; /* not unix specific, just only for UNIX sockets stuff and *nix
|
||||
* container checks */
|
||||
// Not async due to services() being used in many closures, and async closures
|
||||
// are not stable as of writing This is the case for every other occurence of
|
||||
// sync Mutex/RwLock, except for database related ones
|
||||
use std::sync::RwLock;
|
||||
use std::{any::Any, io, net::SocketAddr, sync::atomic, time::Duration};
|
||||
use std::{io, net::SocketAddr, sync::atomic, time::Duration};
|
||||
|
||||
use api::ruma_wrapper::{Ruma, RumaResponse};
|
||||
use axum::{
|
||||
extract::{DefaultBodyLimit, MatchedPath},
|
||||
response::IntoResponse,
|
||||
@@ -18,18 +13,17 @@ use axum::{
|
||||
use axum_server::{bind, bind_rustls, tls_rustls::RustlsConfig, Handle as ServerHandle};
|
||||
#[cfg(feature = "axum_dual_protocol")]
|
||||
use axum_server_dual_protocol::ServerExt;
|
||||
use config::Config;
|
||||
use database::KeyValueDatabase;
|
||||
pub use conduit::*; // Re-export everything from the library crate
|
||||
use http::{
|
||||
header::{self, HeaderName},
|
||||
Method, StatusCode, Uri,
|
||||
Method, StatusCode,
|
||||
};
|
||||
#[cfg(unix)]
|
||||
use hyperlocal::SocketIncoming;
|
||||
use ruma::api::client::{
|
||||
error::{Error as RumaError, ErrorBody, ErrorKind},
|
||||
uiaa::UiaaResponse,
|
||||
};
|
||||
use service::{pdu::PduEvent, Services};
|
||||
use tokio::{
|
||||
signal,
|
||||
sync::oneshot::{self, Sender},
|
||||
@@ -37,40 +31,26 @@ use tokio::{
|
||||
};
|
||||
use tower::ServiceBuilder;
|
||||
use tower_http::{
|
||||
catch_panic::CatchPanicLayer,
|
||||
cors::{self, CorsLayer},
|
||||
trace::{DefaultOnFailure, DefaultOnRequest, DefaultOnResponse, TraceLayer},
|
||||
trace::{DefaultOnFailure, TraceLayer},
|
||||
ServiceBuilderExt as _,
|
||||
};
|
||||
use tracing::{debug, error, info, trace, warn, Level};
|
||||
use tracing::{debug, error, info, warn, Level};
|
||||
use tracing_subscriber::{prelude::*, reload, EnvFilter, Registry};
|
||||
use utils::{
|
||||
clap,
|
||||
error::{Error, Result},
|
||||
};
|
||||
|
||||
mod api;
|
||||
mod config;
|
||||
mod database;
|
||||
mod routes;
|
||||
mod service;
|
||||
mod utils;
|
||||
|
||||
pub(crate) static SERVICES: RwLock<Option<&'static Services<'static>>> = RwLock::new(None);
|
||||
|
||||
#[must_use]
|
||||
pub(crate) fn services() -> &'static Services<'static> {
|
||||
SERVICES
|
||||
.read()
|
||||
.unwrap()
|
||||
.expect("SERVICES should be initialized when this is called")
|
||||
}
|
||||
|
||||
#[cfg(all(not(target_env = "msvc"), feature = "jemalloc", not(feature = "hardened_malloc")))]
|
||||
#[global_allocator]
|
||||
static GLOBAL: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
|
||||
|
||||
#[cfg(all(not(target_env = "msvc"), feature = "hardened_malloc", target_os = "linux", not(feature = "jemalloc")))]
|
||||
#[cfg(all(
|
||||
not(target_env = "msvc"),
|
||||
not(target_os = "macos"),
|
||||
feature = "hardened_malloc",
|
||||
target_os = "linux",
|
||||
not(feature = "jemalloc")
|
||||
))]
|
||||
#[global_allocator]
|
||||
static GLOBAL: hardened_malloc_rs::HardenedMalloc = hardened_malloc_rs::HardenedMalloc;
|
||||
|
||||
@@ -97,17 +77,17 @@ fn main() -> Result<(), Error> {
|
||||
async fn async_main(server: &Server) -> Result<(), Error> {
|
||||
if let Err(error) = start(server).await {
|
||||
error!("Critical error starting server: {error}");
|
||||
return Err(Error::Err(format!("{error}")));
|
||||
return Err(Error::Error(format!("{error}")));
|
||||
}
|
||||
|
||||
if let Err(error) = run(server).await {
|
||||
error!("Critical error running server: {error}");
|
||||
return Err(Error::Err(format!("{error}")));
|
||||
}
|
||||
return Err(Error::Error(format!("{error}")));
|
||||
};
|
||||
|
||||
if let Err(error) = stop(server).await {
|
||||
error!("Critical error stopping server: {error}");
|
||||
return Err(Error::Err(format!("{error}")));
|
||||
return Err(Error::Error(format!("{error}")));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -206,7 +186,6 @@ async fn run_tls_server(
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[allow(unused_variables)]
|
||||
async fn run_unix_socket_server(
|
||||
server: &Server, app: axum::routing::IntoMakeService<Router>, rx: oneshot::Receiver<()>,
|
||||
) -> io::Result<()> {
|
||||
@@ -224,15 +203,25 @@ async fn run_unix_socket_server(
|
||||
|
||||
let socket_perms = server.config.unix_socket_perms.to_string();
|
||||
let octal_perms = u32::from_str_radix(&socket_perms, 8).unwrap();
|
||||
tokio::fs::set_permissions(&path, Permissions::from_mode(octal_perms))
|
||||
|
||||
let listener = tokio::net::UnixListener::bind(path.clone())?;
|
||||
tokio::fs::set_permissions(path, Permissions::from_mode(octal_perms))
|
||||
.await
|
||||
.unwrap();
|
||||
let socket = SocketIncoming::from_listener(listener);
|
||||
|
||||
#[allow(clippy::let_underscore_untyped)] // error[E0658]: attributes on expressions are experimental
|
||||
#[cfg(feature = "systemd")]
|
||||
let _ = sd_notify::notify(true, &[sd_notify::NotifyState::Ready]);
|
||||
let bind = tokio::net::UnixListener::bind(path)?;
|
||||
info!("Listening at {:?}", path);
|
||||
let server = hyper::Server::builder(socket).serve(app);
|
||||
let graceful = server.with_graceful_shutdown(async {
|
||||
rx.await.ok();
|
||||
});
|
||||
|
||||
if let Err(e) = graceful.await {
|
||||
error!("Server error: {:?}", e);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -305,11 +294,9 @@ async fn build(server: &Server) -> io::Result<axum::routing::IntoMakeService<Rou
|
||||
.layer(
|
||||
TraceLayer::new_for_http()
|
||||
.make_span_with(tracing_span::<_>)
|
||||
.on_failure(DefaultOnFailure::new().level(Level::ERROR))
|
||||
.on_request(DefaultOnRequest::new().level(Level::TRACE))
|
||||
.on_response(DefaultOnResponse::new().level(Level::DEBUG)),
|
||||
.on_failure(DefaultOnFailure::new().level(Level::INFO)),
|
||||
)
|
||||
.layer(axum::middleware::from_fn(request_handle))
|
||||
.layer(axum::middleware::from_fn(request_handler))
|
||||
.layer(cors_layer(server))
|
||||
.layer(DefaultBodyLimit::max(
|
||||
server
|
||||
@@ -317,8 +304,7 @@ async fn build(server: &Server) -> io::Result<axum::routing::IntoMakeService<Rou
|
||||
.max_request_size
|
||||
.try_into()
|
||||
.expect("failed to convert max request size"),
|
||||
))
|
||||
.layer(CatchPanicLayer::custom(catch_panic_layer));
|
||||
));
|
||||
|
||||
#[cfg(any(feature = "zstd_compression", feature = "gzip_compression", feature = "brotli_compression"))]
|
||||
{
|
||||
@@ -333,71 +319,44 @@ async fn build(server: &Server) -> io::Result<axum::routing::IntoMakeService<Rou
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all, name = "spawn")]
|
||||
async fn request_spawn(
|
||||
req: http::Request<axum::body::Body>, next: axum::middleware::Next,
|
||||
async fn request_spawn<B: Send + 'static>(
|
||||
req: http::Request<B>, next: axum::middleware::Next<B>,
|
||||
) -> Result<axum::response::Response, StatusCode> {
|
||||
if services().globals.shutdown.load(atomic::Ordering::Relaxed) {
|
||||
return Err(StatusCode::SERVICE_UNAVAILABLE);
|
||||
}
|
||||
|
||||
let fut = next.run(req);
|
||||
let task = tokio::spawn(fut);
|
||||
task.await.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)
|
||||
tokio::spawn(next.run(req))
|
||||
.await
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all, name = "handle")]
|
||||
async fn request_handle(
|
||||
req: http::Request<axum::body::Body>, next: axum::middleware::Next,
|
||||
async fn request_handler<B: Send + 'static>(
|
||||
req: http::Request<B>, next: axum::middleware::Next<B>,
|
||||
) -> Result<axum::response::Response, StatusCode> {
|
||||
let method = req.method().clone();
|
||||
let uri = req.uri().clone();
|
||||
let result = next.run(req).await;
|
||||
request_result(&method, &uri, result)
|
||||
}
|
||||
|
||||
fn request_result(
|
||||
method: &Method, uri: &Uri, result: axum::response::Response,
|
||||
) -> Result<axum::response::Response, StatusCode> {
|
||||
request_result_log(method, uri, &result);
|
||||
match result.status() {
|
||||
StatusCode::METHOD_NOT_ALLOWED => request_result_403(method, uri, &result),
|
||||
_ => Ok(result),
|
||||
let inner = next.run(req).await;
|
||||
if inner.status() == StatusCode::METHOD_NOT_ALLOWED {
|
||||
if uri.path().contains("_matrix/") {
|
||||
warn!("Method not allowed: {method} {uri}");
|
||||
} else {
|
||||
info!("Method not allowed: {method} {uri}");
|
||||
}
|
||||
return Ok(RumaResponse(UiaaResponse::MatrixError(RumaError {
|
||||
body: ErrorBody::Standard {
|
||||
kind: ErrorKind::Unrecognized,
|
||||
message: "M_UNRECOGNIZED: Method not allowed for endpoint".to_owned(),
|
||||
},
|
||||
status_code: StatusCode::METHOD_NOT_ALLOWED,
|
||||
}))
|
||||
.into_response());
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::unnecessary_wraps)]
|
||||
fn request_result_403(
|
||||
_method: &Method, _uri: &Uri, result: &axum::response::Response,
|
||||
) -> Result<axum::response::Response, StatusCode> {
|
||||
let error = UiaaResponse::MatrixError(RumaError {
|
||||
status_code: result.status(),
|
||||
body: ErrorBody::Standard {
|
||||
kind: ErrorKind::Unrecognized,
|
||||
message: "M_UNRECOGNIZED: Method not allowed for endpoint".to_owned(),
|
||||
},
|
||||
});
|
||||
|
||||
Ok(RumaResponse(error).into_response())
|
||||
}
|
||||
|
||||
fn request_result_log(method: &Method, uri: &Uri, result: &axum::response::Response) {
|
||||
let status = result.status();
|
||||
let reason = status.canonical_reason().unwrap_or("Unknown Reason");
|
||||
let code = status.as_u16();
|
||||
if status.is_server_error() {
|
||||
error!(method = ?method, uri = ?uri, "{code} {reason}");
|
||||
} else if status.is_client_error() {
|
||||
debug_error!(method = ?method, uri = ?uri, "{code} {reason}");
|
||||
} else if status.is_redirection() {
|
||||
debug!(method = ?method, uri = ?uri, "{code} {reason}");
|
||||
} else {
|
||||
trace!(method = ?method, uri = ?uri, "{code} {reason}");
|
||||
}
|
||||
Ok(inner)
|
||||
}
|
||||
|
||||
fn cors_layer(_server: &Server) -> CorsLayer {
|
||||
const METHODS: [Method; 6] = [
|
||||
let methods = [
|
||||
Method::GET,
|
||||
Method::HEAD,
|
||||
Method::POST,
|
||||
@@ -406,9 +365,9 @@ fn cors_layer(_server: &Server) -> CorsLayer {
|
||||
Method::OPTIONS,
|
||||
];
|
||||
|
||||
let headers: [HeaderName; 5] = [
|
||||
let headers = [
|
||||
header::ORIGIN,
|
||||
HeaderName::from_lowercase(b"x-requested-with").unwrap(),
|
||||
HeaderName::from_static("x-requested-with"),
|
||||
header::CONTENT_TYPE,
|
||||
header::ACCEPT,
|
||||
header::AUTHORIZATION,
|
||||
@@ -416,7 +375,7 @@ fn cors_layer(_server: &Server) -> CorsLayer {
|
||||
|
||||
CorsLayer::new()
|
||||
.allow_origin(cors::Any)
|
||||
.allow_methods(METHODS)
|
||||
.allow_methods(methods)
|
||||
.allow_headers(headers)
|
||||
.max_age(Duration::from_secs(86400))
|
||||
}
|
||||
@@ -462,7 +421,7 @@ fn tracing_span<T>(request: &http::Request<T>) -> tracing::Span {
|
||||
request.uri().path()
|
||||
};
|
||||
|
||||
tracing::info_span!("router:", %path)
|
||||
tracing::info_span!("handle", %path)
|
||||
}
|
||||
|
||||
/// Non-async initializations
|
||||
@@ -495,8 +454,6 @@ fn init(args: clap::Args) -> Result<Server, Error> {
|
||||
tracing_reload_handle = init_tracing_sub(&config);
|
||||
};
|
||||
|
||||
config.check()?;
|
||||
|
||||
info!(
|
||||
server_name = ?config.server_name,
|
||||
database_path = ?config.database_path,
|
||||
@@ -515,7 +472,7 @@ fn init(args: clap::Args) -> Result<Server, Error> {
|
||||
.enable_io()
|
||||
.enable_time()
|
||||
.thread_name("conduwuit:worker")
|
||||
.worker_threads(num_cpus::get())
|
||||
.worker_threads(num_cpus::get_physical())
|
||||
.build()
|
||||
.unwrap(),
|
||||
|
||||
@@ -529,11 +486,7 @@ fn init(args: clap::Args) -> Result<Server, Error> {
|
||||
#[cfg(feature = "sentry_telemetry")]
|
||||
fn init_sentry(config: &Config) -> sentry::ClientInitGuard {
|
||||
sentry::init((
|
||||
config
|
||||
.sentry_endpoint
|
||||
.as_ref()
|
||||
.expect("init_sentry should only be called if sentry is enabled and this is not None")
|
||||
.as_str(),
|
||||
"https://fe2eb4536aa04949e28eff3128d64757@o4506996327251968.ingest.us.sentry.io/4506996334657536",
|
||||
sentry::ClientOptions {
|
||||
release: sentry::release_name!(),
|
||||
traces_sample_rate: config.sentry_traces_sample_rate,
|
||||
@@ -649,27 +602,3 @@ fn maximize_fd_limit() -> Result<(), nix::errno::Errno> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
fn catch_panic_layer(err: Box<dyn Any + Send + 'static>) -> http::Response<http_body_util::Full<bytes::Bytes>> {
|
||||
let details = if let Some(s) = err.downcast_ref::<String>() {
|
||||
s.clone()
|
||||
} else if let Some(s) = err.downcast_ref::<&str>() {
|
||||
s.to_string()
|
||||
} else {
|
||||
"Unknown internal server error occurred.".to_owned()
|
||||
};
|
||||
|
||||
let body = serde_json::json!({
|
||||
"errcode": "M_UNKNOWN",
|
||||
"error": "M_UNKNOWN: Internal server error occurred",
|
||||
"details": details,
|
||||
})
|
||||
.to_string();
|
||||
|
||||
http::Response::builder()
|
||||
.status(StatusCode::INTERNAL_SERVER_ERROR)
|
||||
.header(header::CONTENT_TYPE, "application/json")
|
||||
.body(http_body_util::Full::from(body))
|
||||
.expect("Failed to create response for our panic catcher?")
|
||||
}
|
||||
|
||||
+13
-10
@@ -6,15 +6,15 @@ use axum::{
|
||||
routing::{any, get, on, post, MethodFilter},
|
||||
Router,
|
||||
};
|
||||
use http::{Method, Uri};
|
||||
use ruma::api::{client::error::ErrorKind, IncomingRequest};
|
||||
|
||||
use crate::{
|
||||
use conduit::{
|
||||
api::{client_server, server_server},
|
||||
Config, Error, Result, Ruma, RumaResponse,
|
||||
};
|
||||
use http::{Method, Uri};
|
||||
use ruma::api::{client::error::ErrorKind, IncomingRequest};
|
||||
use tracing::{info, warn};
|
||||
|
||||
pub(crate) fn routes(config: &Config) -> Router {
|
||||
pub fn routes(config: &Config) -> Router {
|
||||
let router = Router::new()
|
||||
.ruma_route(client_server::get_supported_versions_route)
|
||||
.ruma_route(client_server::get_register_available_route)
|
||||
@@ -208,9 +208,6 @@ pub(crate) fn routes(config: &Config) -> Router {
|
||||
.ruma_route(server_server::get_event_authorization_route)
|
||||
.ruma_route(server_server::get_room_state_route)
|
||||
.ruma_route(server_server::get_room_state_ids_route)
|
||||
.ruma_route(server_server::create_leave_event_template_route)
|
||||
.ruma_route(server_server::create_leave_event_v1_route)
|
||||
.ruma_route(server_server::create_leave_event_v2_route)
|
||||
.ruma_route(server_server::create_join_event_template_route)
|
||||
.ruma_route(server_server::create_join_event_v1_route)
|
||||
.ruma_route(server_server::create_join_event_v2_route)
|
||||
@@ -230,7 +227,13 @@ pub(crate) fn routes(config: &Config) -> Router {
|
||||
}
|
||||
}
|
||||
|
||||
async fn not_found(_uri: Uri) -> impl IntoResponse {
|
||||
async fn not_found(uri: Uri) -> impl IntoResponse {
|
||||
if uri.path().contains("_matrix/") {
|
||||
warn!("Not found: {uri}");
|
||||
} else {
|
||||
info!("Not found: {uri}");
|
||||
}
|
||||
|
||||
Error::BadRequest(ErrorKind::Unrecognized, "Unrecognized request")
|
||||
}
|
||||
|
||||
@@ -259,7 +262,7 @@ impl RouterExt for Router {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait RumaHandler<T> {
|
||||
pub trait RumaHandler<T> {
|
||||
// Can't transform to a handler without boxing or relying on the nightly-only
|
||||
// impl-trait-in-traits feature. Moving a small amount of extra logic into the
|
||||
// trait allows bypassing both.
|
||||
|
||||
@@ -8,7 +8,7 @@ use ruma::{
|
||||
|
||||
use crate::Result;
|
||||
|
||||
pub(crate) trait Data: Send + Sync {
|
||||
pub trait Data: Send + Sync {
|
||||
/// Places one event in the account data of the user and removes the
|
||||
/// previous entry.
|
||||
fn update(
|
||||
|
||||
@@ -11,15 +11,15 @@ use ruma::{
|
||||
|
||||
use crate::Result;
|
||||
|
||||
pub(crate) struct Service {
|
||||
pub(crate) db: &'static dyn Data,
|
||||
pub struct Service {
|
||||
pub db: &'static dyn Data,
|
||||
}
|
||||
|
||||
impl Service {
|
||||
/// Places one event in the account data of the user and removes the
|
||||
/// previous entry.
|
||||
#[tracing::instrument(skip(self, room_id, user_id, event_type, data))]
|
||||
pub(crate) fn update(
|
||||
pub fn update(
|
||||
&self, room_id: Option<&RoomId>, user_id: &UserId, event_type: RoomAccountDataEventType,
|
||||
data: &serde_json::Value,
|
||||
) -> Result<()> {
|
||||
@@ -28,7 +28,7 @@ impl Service {
|
||||
|
||||
/// Searches the account data for a specific kind.
|
||||
#[tracing::instrument(skip(self, room_id, user_id, event_type))]
|
||||
pub(crate) fn get(
|
||||
pub fn get(
|
||||
&self, room_id: Option<&RoomId>, user_id: &UserId, event_type: RoomAccountDataEventType,
|
||||
) -> Result<Option<Box<serde_json::value::RawValue>>> {
|
||||
self.db.get(room_id, user_id, event_type)
|
||||
@@ -36,7 +36,7 @@ impl Service {
|
||||
|
||||
/// Returns all changes to the account data that happened after `since`.
|
||||
#[tracing::instrument(skip(self, room_id, user_id, since))]
|
||||
pub(crate) fn changes_since(
|
||||
pub fn changes_since(
|
||||
&self, room_id: Option<&RoomId>, user_id: &UserId, since: u64,
|
||||
) -> Result<HashMap<RoomAccountDataEventType, Raw<AnyEphemeralRoomEvent>>> {
|
||||
self.db.changes_since(room_id, user_id, since)
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
use clap::Subcommand;
|
||||
use ruma::{api::appservice::Registration, events::room::message::RoomMessageEventContent};
|
||||
|
||||
use crate::{service::admin::escape_html, services, Result};
|
||||
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
#[derive(Subcommand)]
|
||||
pub(crate) enum AppserviceCommand {
|
||||
/// - Register an appservice using its registration YAML
|
||||
///
|
||||
/// This command needs a YAML generated by an appservice (such as a bridge),
|
||||
/// which must be provided in a Markdown code block below the command.
|
||||
///
|
||||
/// Registering a new bridge using the ID of an existing bridge will replace
|
||||
/// the old one.
|
||||
Register,
|
||||
|
||||
/// - Unregister an appservice using its ID
|
||||
///
|
||||
/// You can find the ID using the `list-appservices` command.
|
||||
Unregister {
|
||||
/// The appservice to unregister
|
||||
appservice_identifier: String,
|
||||
},
|
||||
|
||||
/// - Show an appservice's config using its ID
|
||||
///
|
||||
/// You can find the ID using the `list-appservices` command.
|
||||
Show {
|
||||
/// The appservice to show
|
||||
appservice_identifier: String,
|
||||
},
|
||||
|
||||
/// - List all the currently registered appservices
|
||||
List,
|
||||
}
|
||||
|
||||
pub(crate) async fn process(command: AppserviceCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
match command {
|
||||
AppserviceCommand::Register => {
|
||||
if body.len() > 2 && body[0].trim().starts_with("```") && body.last().unwrap().trim() == "```" {
|
||||
let appservice_config = body[1..body.len() - 1].join("\n");
|
||||
let parsed_config = serde_yaml::from_str::<Registration>(&appservice_config);
|
||||
match parsed_config {
|
||||
Ok(yaml) => match services().appservice.register_appservice(yaml).await {
|
||||
Ok(id) => Ok(RoomMessageEventContent::text_plain(format!(
|
||||
"Appservice registered with ID: {id}."
|
||||
))),
|
||||
Err(e) => Ok(RoomMessageEventContent::text_plain(format!(
|
||||
"Failed to register appservice: {e}"
|
||||
))),
|
||||
},
|
||||
Err(e) => Ok(RoomMessageEventContent::text_plain(format!(
|
||||
"Could not parse appservice config: {e}"
|
||||
))),
|
||||
}
|
||||
} else {
|
||||
Ok(RoomMessageEventContent::text_plain(
|
||||
"Expected code block in command body. Add --help for details.",
|
||||
))
|
||||
}
|
||||
},
|
||||
AppserviceCommand::Unregister {
|
||||
appservice_identifier,
|
||||
} => match services()
|
||||
.appservice
|
||||
.unregister_appservice(&appservice_identifier)
|
||||
.await
|
||||
{
|
||||
Ok(()) => Ok(RoomMessageEventContent::text_plain("Appservice unregistered.")),
|
||||
Err(e) => Ok(RoomMessageEventContent::text_plain(format!(
|
||||
"Failed to unregister appservice: {e}"
|
||||
))),
|
||||
},
|
||||
AppserviceCommand::Show {
|
||||
appservice_identifier,
|
||||
} => match services()
|
||||
.appservice
|
||||
.get_registration(&appservice_identifier)
|
||||
.await
|
||||
{
|
||||
Some(config) => {
|
||||
let config_str = serde_yaml::to_string(&config).expect("config should've been validated on register");
|
||||
let output = format!("Config for {}:\n\n```yaml\n{}\n```", appservice_identifier, config_str,);
|
||||
let output_html = format!(
|
||||
"Config for {}:\n\n<pre><code class=\"language-yaml\">{}</code></pre>",
|
||||
escape_html(&appservice_identifier),
|
||||
escape_html(&config_str),
|
||||
);
|
||||
Ok(RoomMessageEventContent::text_html(output, output_html))
|
||||
},
|
||||
None => Ok(RoomMessageEventContent::text_plain("Appservice does not exist.")),
|
||||
},
|
||||
AppserviceCommand::List => {
|
||||
let appservices = services().appservice.iter_ids().await;
|
||||
let output = format!("Appservices ({}): {}", appservices.len(), appservices.join(", "));
|
||||
Ok(RoomMessageEventContent::text_plain(output))
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
use ruma::{api::appservice::Registration, events::room::message::RoomMessageEventContent};
|
||||
|
||||
use crate::{service::admin::escape_html, services, Result};
|
||||
|
||||
pub(crate) async fn register(body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
if body.len() > 2 && body[0].trim().starts_with("```") && body.last().unwrap().trim() == "```" {
|
||||
let appservice_config = body[1..body.len() - 1].join("\n");
|
||||
let parsed_config = serde_yaml::from_str::<Registration>(&appservice_config);
|
||||
match parsed_config {
|
||||
Ok(yaml) => match services().appservice.register_appservice(yaml).await {
|
||||
Ok(id) => Ok(RoomMessageEventContent::text_plain(format!(
|
||||
"Appservice registered with ID: {id}."
|
||||
))),
|
||||
Err(e) => Ok(RoomMessageEventContent::text_plain(format!(
|
||||
"Failed to register appservice: {e}"
|
||||
))),
|
||||
},
|
||||
Err(e) => Ok(RoomMessageEventContent::text_plain(format!(
|
||||
"Could not parse appservice config: {e}"
|
||||
))),
|
||||
}
|
||||
} else {
|
||||
Ok(RoomMessageEventContent::text_plain(
|
||||
"Expected code block in command body. Add --help for details.",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn unregister(_body: Vec<&str>, appservice_identifier: String) -> Result<RoomMessageEventContent> {
|
||||
match services()
|
||||
.appservice
|
||||
.unregister_appservice(&appservice_identifier)
|
||||
.await
|
||||
{
|
||||
Ok(()) => Ok(RoomMessageEventContent::text_plain("Appservice unregistered.")),
|
||||
Err(e) => Ok(RoomMessageEventContent::text_plain(format!(
|
||||
"Failed to unregister appservice: {e}"
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn show(_body: Vec<&str>, appservice_identifier: String) -> Result<RoomMessageEventContent> {
|
||||
match services()
|
||||
.appservice
|
||||
.get_registration(&appservice_identifier)
|
||||
.await
|
||||
{
|
||||
Some(config) => {
|
||||
let config_str = serde_yaml::to_string(&config).expect("config should've been validated on register");
|
||||
let output = format!("Config for {}:\n\n```yaml\n{}\n```", appservice_identifier, config_str,);
|
||||
let output_html = format!(
|
||||
"Config for {}:\n\n<pre><code class=\"language-yaml\">{}</code></pre>",
|
||||
escape_html(&appservice_identifier),
|
||||
escape_html(&config_str),
|
||||
);
|
||||
Ok(RoomMessageEventContent::text_html(output, output_html))
|
||||
},
|
||||
None => Ok(RoomMessageEventContent::text_plain("Appservice does not exist.")),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) 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))
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
use clap::Subcommand;
|
||||
use ruma::events::room::message::RoomMessageEventContent;
|
||||
|
||||
use self::appservice_command::{list, register, show, unregister};
|
||||
use crate::Result;
|
||||
|
||||
pub(crate) mod appservice_command;
|
||||
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
#[derive(Subcommand)]
|
||||
pub(crate) enum AppserviceCommand {
|
||||
/// - Register an appservice using its registration YAML
|
||||
///
|
||||
/// This command needs a YAML generated by an appservice (such as a bridge),
|
||||
/// which must be provided in a Markdown code block below the command.
|
||||
///
|
||||
/// Registering a new bridge using the ID of an existing bridge will replace
|
||||
/// the old one.
|
||||
Register,
|
||||
|
||||
/// - Unregister an appservice using its ID
|
||||
///
|
||||
/// You can find the ID using the `list-appservices` command.
|
||||
Unregister {
|
||||
/// The appservice to unregister
|
||||
appservice_identifier: String,
|
||||
},
|
||||
|
||||
/// - Show an appservice's config using its ID
|
||||
///
|
||||
/// You can find the ID using the `list-appservices` command.
|
||||
Show {
|
||||
/// The appservice to show
|
||||
appservice_identifier: String,
|
||||
},
|
||||
|
||||
/// - List all the currently registered appservices
|
||||
List,
|
||||
}
|
||||
|
||||
pub(crate) 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?,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,432 @@
|
||||
use std::{collections::BTreeMap, sync::Arc, time::Instant};
|
||||
|
||||
use clap::Subcommand;
|
||||
use ruma::{
|
||||
api::client::error::ErrorKind, events::room::message::RoomMessageEventContent, CanonicalJsonObject, EventId,
|
||||
RoomId, RoomVersionId, ServerName,
|
||||
};
|
||||
use tokio::sync::RwLock;
|
||||
use tracing::{debug, error, info, warn};
|
||||
use tracing_subscriber::EnvFilter;
|
||||
|
||||
use crate::{api::server_server::parse_incoming_pdu, services, utils::HtmlEscape, Error, PduEvent, Result};
|
||||
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
#[derive(Subcommand)]
|
||||
pub(crate) enum DebugCommand {
|
||||
/// - Get the auth_chain of a PDU
|
||||
GetAuthChain {
|
||||
/// An event ID (the $ character followed by the base64 reference hash)
|
||||
event_id: Box<EventId>,
|
||||
},
|
||||
|
||||
/// - Parse and print a PDU from a JSON
|
||||
///
|
||||
/// The PDU event is only checked for validity and is not added to the
|
||||
/// database.
|
||||
///
|
||||
/// This command needs a JSON blob provided in a Markdown code block below
|
||||
/// the command.
|
||||
ParsePdu,
|
||||
|
||||
/// - Retrieve and print a PDU by ID from the conduwuit database
|
||||
GetPdu {
|
||||
/// An event ID (a $ followed by the base64 reference hash)
|
||||
event_id: Box<EventId>,
|
||||
},
|
||||
|
||||
/// - Attempts to retrieve a PDU from a remote server. Inserts it into our
|
||||
/// database/timeline if found and we do not have this PDU already
|
||||
/// (following normal event auth rules, handles it as an incoming PDU).
|
||||
GetRemotePdu {
|
||||
/// An event ID (a $ followed by the base64 reference hash)
|
||||
event_id: Box<EventId>,
|
||||
|
||||
/// Argument for us to attempt to fetch the event from the
|
||||
/// specified remote server.
|
||||
server: Box<ServerName>,
|
||||
},
|
||||
|
||||
/// - Gets all the room state events for the specified room.
|
||||
///
|
||||
/// This is functionally equivalent to `GET
|
||||
/// /_matrix/client/v3/rooms/{roomid}/state`, except the admin command does
|
||||
/// *not* check if the sender user is allowed to see state events. This is
|
||||
/// done because it's implied that server admins here have database access
|
||||
/// and can see/get room info themselves anyways if they were malicious
|
||||
/// admins.
|
||||
///
|
||||
/// Of course the check is still done on the actual client API.
|
||||
GetRoomState {
|
||||
/// Room ID
|
||||
room_id: Box<RoomId>,
|
||||
},
|
||||
|
||||
/// - 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
|
||||
Ping {
|
||||
server: Box<ServerName>,
|
||||
},
|
||||
|
||||
/// - Forces device lists for all local and remote users to be updated (as
|
||||
/// having new keys available)
|
||||
ForceDeviceListUpdates,
|
||||
|
||||
/// - Change tracing log level/filter on the fly
|
||||
///
|
||||
/// This accepts the same format as the `log` config option.
|
||||
ChangeLogLevel {
|
||||
/// Log level/filter
|
||||
filter: Option<String>,
|
||||
|
||||
/// Resets the log level/filter to the one in your config
|
||||
#[arg(short, long)]
|
||||
reset: bool,
|
||||
},
|
||||
}
|
||||
|
||||
pub(crate) async fn process(command: DebugCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
Ok(match command {
|
||||
DebugCommand::GetAuthChain {
|
||||
event_id,
|
||||
} => {
|
||||
let event_id = Arc::<EventId>::from(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())
|
||||
.ok_or_else(|| Error::bad_database("Invalid event in database"))?;
|
||||
|
||||
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 = services()
|
||||
.rooms
|
||||
.auth_chain
|
||||
.event_ids_iter(room_id, vec![event_id])
|
||||
.await?
|
||||
.count();
|
||||
let elapsed = start.elapsed();
|
||||
RoomMessageEventContent::text_plain(format!("Loaded auth chain with length {count} in {elapsed:?}"))
|
||||
} else {
|
||||
RoomMessageEventContent::text_plain("Event not found.")
|
||||
}
|
||||
},
|
||||
DebugCommand::ParsePdu => {
|
||||
if body.len() > 2 && body[0].trim().starts_with("```") && body.last().unwrap().trim() == "```" {
|
||||
let string = body[1..body.len() - 1].join("\n");
|
||||
match serde_json::from_str(&string) {
|
||||
Ok(value) => match ruma::signatures::reference_hash(&value, &RoomVersionId::V6) {
|
||||
Ok(hash) => {
|
||||
let event_id = EventId::parse(format!("${hash}"));
|
||||
|
||||
match serde_json::from_value::<PduEvent>(
|
||||
serde_json::to_value(value).expect("value is json"),
|
||||
) {
|
||||
Ok(pdu) => {
|
||||
RoomMessageEventContent::text_plain(format!("EventId: {event_id:?}\n{pdu:#?}"))
|
||||
},
|
||||
Err(e) => RoomMessageEventContent::text_plain(format!(
|
||||
"EventId: {event_id:?}\nCould not parse event: {e}"
|
||||
)),
|
||||
}
|
||||
},
|
||||
Err(e) => RoomMessageEventContent::text_plain(format!("Could not parse PDU JSON: {e:?}")),
|
||||
},
|
||||
Err(e) => RoomMessageEventContent::text_plain(format!("Invalid json in command body: {e}")),
|
||||
}
|
||||
} else {
|
||||
RoomMessageEventContent::text_plain("Expected code block in command body.")
|
||||
}
|
||||
},
|
||||
DebugCommand::GetPdu {
|
||||
event_id,
|
||||
} => {
|
||||
let mut outlier = false;
|
||||
let mut pdu_json = services()
|
||||
.rooms
|
||||
.timeline
|
||||
.get_non_outlier_pdu_json(&event_id)?;
|
||||
if pdu_json.is_none() {
|
||||
outlier = true;
|
||||
pdu_json = services().rooms.timeline.get_pdu_json(&event_id)?;
|
||||
}
|
||||
match pdu_json {
|
||||
Some(json) => {
|
||||
let json_text = serde_json::to_string_pretty(&json).expect("canonical json is valid json");
|
||||
return Ok(RoomMessageEventContent::text_html(
|
||||
format!(
|
||||
"{}\n```json\n{}\n```",
|
||||
if outlier {
|
||||
"Outlier PDU found in our database"
|
||||
} else {
|
||||
"PDU found in our database"
|
||||
},
|
||||
json_text
|
||||
),
|
||||
format!(
|
||||
"<p>{}</p>\n<pre><code class=\"language-json\">{}\n</code></pre>\n",
|
||||
if outlier {
|
||||
"Outlier PDU found in our database"
|
||||
} else {
|
||||
"PDU found in our database"
|
||||
},
|
||||
HtmlEscape(&json_text)
|
||||
),
|
||||
));
|
||||
},
|
||||
None => {
|
||||
return Ok(RoomMessageEventContent::text_plain("PDU not found locally."));
|
||||
},
|
||||
}
|
||||
},
|
||||
DebugCommand::GetRemotePdu {
|
||||
event_id,
|
||||
server,
|
||||
} => {
|
||||
if !services().globals.config.allow_federation {
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
"Federation is disabled on this homeserver.",
|
||||
));
|
||||
}
|
||||
|
||||
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.",
|
||||
));
|
||||
}
|
||||
|
||||
// TODO: use Futures as some requests may take a while so we dont block the
|
||||
// admin room
|
||||
match services()
|
||||
.sending
|
||||
.send_federation_request(
|
||||
&server,
|
||||
ruma::api::federation::event::get_event::v1::Request {
|
||||
event_id: event_id.clone().into(),
|
||||
},
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(response) => {
|
||||
let json: CanonicalJsonObject = serde_json::from_str(response.pdu.get()).map_err(|e| {
|
||||
warn!(
|
||||
"Requested event ID {event_id} from server but failed to convert from RawValue to \
|
||||
CanonicalJsonObject (malformed event/response?): {e}"
|
||||
);
|
||||
Error::BadRequest(ErrorKind::Unknown, "Received response from server but failed to parse PDU")
|
||||
})?;
|
||||
|
||||
debug!("Attempting to parse PDU: {:?}", &response.pdu);
|
||||
let parsed_pdu = {
|
||||
let parsed_result = parse_incoming_pdu(&response.pdu);
|
||||
let (event_id, value, room_id) = match parsed_result {
|
||||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
warn!("Failed to parse PDU: {e}");
|
||||
info!("Full PDU: {:?}", &response.pdu);
|
||||
return Ok(RoomMessageEventContent::text_plain(format!(
|
||||
"Failed to parse PDU remote server {server} sent us: {e}"
|
||||
)));
|
||||
},
|
||||
};
|
||||
|
||||
vec![(event_id, value, room_id)]
|
||||
};
|
||||
|
||||
let pub_key_map = RwLock::new(BTreeMap::new());
|
||||
|
||||
debug!("Attempting to fetch homeserver signing keys for {server}");
|
||||
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| {
|
||||
warn!("Could not fetch all signatures for PDUs from {server}: {e:?}");
|
||||
});
|
||||
|
||||
info!("Attempting to handle event ID {event_id} as backfilled PDU");
|
||||
services()
|
||||
.rooms
|
||||
.timeline
|
||||
.backfill_pdu(&server, response.pdu, &pub_key_map)
|
||||
.await?;
|
||||
|
||||
let json_text = serde_json::to_string_pretty(&json).expect("canonical json is valid json");
|
||||
|
||||
return Ok(RoomMessageEventContent::text_html(
|
||||
format!(
|
||||
"{}\n```json\n{}\n```",
|
||||
"Got PDU from specified server and handled as backfilled PDU successfully. Event body:",
|
||||
json_text
|
||||
),
|
||||
format!(
|
||||
"<p>{}</p>\n<pre><code class=\"language-json\">{}\n</code></pre>\n",
|
||||
"Got PDU from specified server and handled as backfilled PDU successfully. Event body:",
|
||||
HtmlEscape(&json_text)
|
||||
),
|
||||
));
|
||||
},
|
||||
Err(e) => {
|
||||
return Ok(RoomMessageEventContent::text_plain(format!(
|
||||
"Remote server did not have PDU or failed sending request to remote server: {e}"
|
||||
)));
|
||||
},
|
||||
}
|
||||
},
|
||||
DebugCommand::GetRoomState {
|
||||
room_id,
|
||||
} => {
|
||||
let room_state = services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_full(&room_id)
|
||||
.await?
|
||||
.values()
|
||||
.map(|pdu| pdu.to_state_event())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if room_state.is_empty() {
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
"Unable to find room state in our database (vector is empty)",
|
||||
));
|
||||
}
|
||||
|
||||
let json_text = serde_json::to_string_pretty(&room_state).map_err(|e| {
|
||||
error!("Failed converting room state vector in our database to pretty JSON: {e}");
|
||||
Error::bad_database(
|
||||
"Failed to convert room state events to pretty JSON, possible invalid room state events in our \
|
||||
database",
|
||||
)
|
||||
})?;
|
||||
|
||||
return Ok(RoomMessageEventContent::text_html(
|
||||
format!("{}\n```json\n{}\n```", "Found full room state", json_text),
|
||||
format!(
|
||||
"<p>{}</p>\n<pre><code class=\"language-json\">{}\n</code></pre>\n",
|
||||
"Found full room state",
|
||||
HtmlEscape(&json_text)
|
||||
),
|
||||
));
|
||||
},
|
||||
DebugCommand::Ping {
|
||||
server,
|
||||
} => {
|
||||
if server == services().globals.server_name() {
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
"Not allowed to send federation requests to ourselves.",
|
||||
));
|
||||
}
|
||||
|
||||
let timer = tokio::time::Instant::now();
|
||||
|
||||
match services()
|
||||
.sending
|
||||
.send_federation_request(&server, ruma::api::federation::discovery::get_server_version::v1::Request {})
|
||||
.await
|
||||
{
|
||||
Ok(response) => {
|
||||
let ping_time = timer.elapsed();
|
||||
|
||||
let json_text_res = serde_json::to_string_pretty(&response.server);
|
||||
|
||||
if let Ok(json) = json_text_res {
|
||||
return Ok(RoomMessageEventContent::text_html(
|
||||
format!("Got response which took {ping_time:?} time:\n```json\n{json}\n```"),
|
||||
format!(
|
||||
"<p>Got response which took {ping_time:?} time:</p>\n<pre><code \
|
||||
class=\"language-json\">{}\n</code></pre>\n",
|
||||
HtmlEscape(&json)
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
return Ok(RoomMessageEventContent::text_plain(format!(
|
||||
"Got non-JSON response which took {ping_time:?} time:\n{0:?}",
|
||||
response
|
||||
)));
|
||||
},
|
||||
Err(e) => {
|
||||
error!("Failed sending federation request to specified server from ping debug command: {e}");
|
||||
return Ok(RoomMessageEventContent::text_plain(format!(
|
||||
"Failed sending federation request to specified server:\n\n{e}",
|
||||
)));
|
||||
},
|
||||
}
|
||||
},
|
||||
DebugCommand::ForceDeviceListUpdates => {
|
||||
// Force E2EE device list updates for all users
|
||||
for user_id in services().users.iter().filter_map(Result::ok) {
|
||||
services().users.mark_device_key_update(&user_id)?;
|
||||
}
|
||||
RoomMessageEventContent::text_plain("Marked all devices for all users as having new keys to update")
|
||||
},
|
||||
DebugCommand::ChangeLogLevel {
|
||||
filter,
|
||||
reset,
|
||||
} => {
|
||||
if reset {
|
||||
let old_filter_layer = match EnvFilter::try_new(&services().globals.config.log) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
return Ok(RoomMessageEventContent::text_plain(format!(
|
||||
"Log level from config appears to be invalid now: {e}"
|
||||
)));
|
||||
},
|
||||
};
|
||||
|
||||
match services()
|
||||
.globals
|
||||
.tracing_reload_handle
|
||||
.modify(|filter| *filter = old_filter_layer)
|
||||
{
|
||||
Ok(()) => {
|
||||
return Ok(RoomMessageEventContent::text_plain(format!(
|
||||
"Successfully changed log level back to config value {}",
|
||||
services().globals.config.log
|
||||
)));
|
||||
},
|
||||
Err(e) => {
|
||||
return Ok(RoomMessageEventContent::text_plain(format!(
|
||||
"Failed to modify and reload the global tracing log level: {e}"
|
||||
)));
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(filter) = filter {
|
||||
let new_filter_layer = match EnvFilter::try_new(filter) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
return Ok(RoomMessageEventContent::text_plain(format!(
|
||||
"Invalid log level filter specified: {e}"
|
||||
)));
|
||||
},
|
||||
};
|
||||
|
||||
match services()
|
||||
.globals
|
||||
.tracing_reload_handle
|
||||
.modify(|filter| *filter = new_filter_layer)
|
||||
{
|
||||
Ok(()) => {
|
||||
return Ok(RoomMessageEventContent::text_plain("Successfully changed log level"));
|
||||
},
|
||||
Err(e) => {
|
||||
return Ok(RoomMessageEventContent::text_plain(format!(
|
||||
"Failed to modify and reload the global tracing log level: {e}"
|
||||
)));
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(RoomMessageEventContent::text_plain("No log level was specified."));
|
||||
},
|
||||
})
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user