Compare commits
28 commits
next
...
refactor-c
Author | SHA1 | Date | |
---|---|---|---|
|
9d853d34a4 | ||
|
6f7a510046 | ||
|
dc28536690 | ||
|
ec2ef37619 | ||
|
419da573d9 | ||
|
342c9bff4f | ||
|
cc1e4be53d | ||
|
62cbb57ebc | ||
|
f33dbb855a | ||
|
769c1ecdd4 | ||
|
0f618de7fd | ||
|
9b08525a18 | ||
|
79e7115b62 | ||
|
d6b32b6ffe | ||
|
c8f921c532 | ||
|
3a09c8e180 | ||
|
f107f8d160 | ||
|
d1138204a6 | ||
|
5108ce52c2 | ||
|
ada1251a52 | ||
|
a10e7e7263 | ||
|
8f4f3314c6 | ||
|
173f8b1b4d | ||
|
b5305ba217 | ||
|
7c166aa468 | ||
|
03b2867a84 | ||
|
3fd7f6efc2 | ||
|
9c71a2cd5e |
162 changed files with 5191 additions and 10892 deletions
|
@ -25,4 +25,4 @@ docker-compose*
|
||||||
rustfmt.toml
|
rustfmt.toml
|
||||||
|
|
||||||
# Documentation
|
# Documentation
|
||||||
#*.md
|
*.md
|
||||||
|
|
5
.envrc
5
.envrc
|
@ -1,5 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
use flake
|
|
||||||
|
|
||||||
PATH_add bin
|
|
11
.gitignore
vendored
11
.gitignore
vendored
|
@ -31,6 +31,7 @@ modules.xml
|
||||||
|
|
||||||
### vscode ###
|
### vscode ###
|
||||||
.vscode/*
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
!.vscode/tasks.json
|
!.vscode/tasks.json
|
||||||
!.vscode/launch.json
|
!.vscode/launch.json
|
||||||
!.vscode/extensions.json
|
!.vscode/extensions.json
|
||||||
|
@ -61,13 +62,3 @@ conduit.db
|
||||||
|
|
||||||
# Etc.
|
# Etc.
|
||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
cached_target
|
|
||||||
|
|
||||||
# Nix artifacts
|
|
||||||
/result*
|
|
||||||
|
|
||||||
# Direnv cache
|
|
||||||
/.direnv
|
|
||||||
|
|
||||||
# Gitlab CI cache
|
|
||||||
/.gitlab-ci.d
|
|
||||||
|
|
500
.gitlab-ci.yml
500
.gitlab-ci.yml
|
@ -1,180 +1,382 @@
|
||||||
stages:
|
stages:
|
||||||
- ci
|
- build
|
||||||
- artifacts
|
- build docker image
|
||||||
- publish
|
- test
|
||||||
|
- upload artifacts
|
||||||
|
|
||||||
variables:
|
variables:
|
||||||
# Makes some things print in color
|
GIT_SUBMODULE_STRATEGY: recursive
|
||||||
TERM: ansi
|
FF_USE_FASTZIP: 1
|
||||||
|
CACHE_COMPRESSION_LEVEL: fastest
|
||||||
|
# Docker in Docker
|
||||||
|
DOCKER_HOST: tcp://docker:2375/
|
||||||
|
DOCKER_TLS_CERTDIR: ""
|
||||||
|
DOCKER_DRIVER: overlay2
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------- #
|
||||||
|
# Cargo: Compiling for different architectures #
|
||||||
|
# --------------------------------------------------------------------- #
|
||||||
|
|
||||||
|
.build-cargo-shared-settings:
|
||||||
|
stage: "build"
|
||||||
|
needs: []
|
||||||
|
rules:
|
||||||
|
- if: '$CI_COMMIT_BRANCH == "master"'
|
||||||
|
- if: '$CI_COMMIT_BRANCH == "next"'
|
||||||
|
- if: "$CI_COMMIT_TAG"
|
||||||
|
- if: '($CI_MERGE_REQUEST_APPROVED == "true") || $BUILD_EVERYTHING' # Once MR is approved, test all builds. Or if BUILD_EVERYTHING is set.
|
||||||
|
interruptible: true
|
||||||
|
image: "registry.gitlab.com/jfowl/conduit-containers/rust-with-tools:commit-aa74ef11"
|
||||||
|
tags: ["docker"]
|
||||||
|
services: ["docker:dind"]
|
||||||
|
variables:
|
||||||
|
SHARED_PATH: $CI_PROJECT_DIR/shared
|
||||||
|
CARGO_PROFILE_RELEASE_LTO: "true"
|
||||||
|
CARGO_PROFILE_RELEASE_CODEGEN_UNITS: "1"
|
||||||
|
CARGO_INCREMENTAL: "false" # https://matklad.github.io/2021/09/04/fast-rust-builds.html#ci-workflow
|
||||||
before_script:
|
before_script:
|
||||||
# Enable nix-command and flakes
|
- 'echo "Building for target $TARGET"'
|
||||||
- if command -v nix > /dev/null; then echo "experimental-features = nix-command flakes" >> /etc/nix/nix.conf; fi
|
- "rustup show && rustc --version && cargo --version" # Print version info for debugging
|
||||||
|
# fix cargo and rustup mounts from this container (https://gitlab.com/gitlab-org/gitlab-foss/-/issues/41227)
|
||||||
# Add our own binary cache
|
- "mkdir -p $SHARED_PATH/cargo"
|
||||||
- if command -v nix > /dev/null; then echo "extra-substituters = https://nix.computer.surgery/conduit" >> /etc/nix/nix.conf; fi
|
- "cp -r $CARGO_HOME/bin $SHARED_PATH/cargo"
|
||||||
- if command -v nix > /dev/null; then echo "extra-trusted-public-keys = conduit:ZGAf6P6LhNvnoJJ3Me3PRg7tlLSrPxcQ2RiE5LIppjo=" >> /etc/nix/nix.conf; fi
|
- "cp -r $RUSTUP_HOME $SHARED_PATH"
|
||||||
|
- "export CARGO_HOME=$SHARED_PATH/cargo RUSTUP_HOME=$SHARED_PATH/rustup"
|
||||||
# Add crane binary cache
|
|
||||||
- if command -v nix > /dev/null; then echo "extra-substituters = https://crane.cachix.org" >> /etc/nix/nix.conf; fi
|
|
||||||
- if command -v nix > /dev/null; then echo "extra-trusted-public-keys = crane.cachix.org-1:8Scfpmn9w+hGdXH/Q9tTLiYAE/2dnJYRJP7kl80GuRk=" >> /etc/nix/nix.conf; fi
|
|
||||||
|
|
||||||
# Add nix-community binary cache
|
|
||||||
- if command -v nix > /dev/null; then echo "extra-substituters = https://nix-community.cachix.org" >> /etc/nix/nix.conf; fi
|
|
||||||
- if command -v nix > /dev/null; then echo "extra-trusted-public-keys = nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=" >> /etc/nix/nix.conf; fi
|
|
||||||
|
|
||||||
# Install direnv and nix-direnv
|
|
||||||
- if command -v nix > /dev/null; then nix-env -iA nixpkgs.direnv nixpkgs.nix-direnv; fi
|
|
||||||
|
|
||||||
# Allow .envrc
|
|
||||||
- if command -v nix > /dev/null; then direnv allow; fi
|
|
||||||
|
|
||||||
# Set CARGO_HOME to a cacheable path
|
|
||||||
- export CARGO_HOME="$(git rev-parse --show-toplevel)/.gitlab-ci.d/cargo"
|
|
||||||
|
|
||||||
ci:
|
|
||||||
stage: ci
|
|
||||||
image: nixos/nix:2.19.2
|
|
||||||
script:
|
script:
|
||||||
- direnv exec . engage
|
# cross-compile conduit for target
|
||||||
|
- 'time cross build --target="$TARGET" --locked --release'
|
||||||
|
- 'mv "target/$TARGET/release/conduit" "conduit-$TARGET"'
|
||||||
|
# print information about linking for debugging
|
||||||
|
- "file conduit-$TARGET" # print file information
|
||||||
|
- 'readelf --dynamic conduit-$TARGET | sed -e "/NEEDED/q1"' # ensure statically linked
|
||||||
cache:
|
cache:
|
||||||
key: nix
|
# https://doc.rust-lang.org/cargo/guide/cargo-home.html#caching-the-cargo-home-in-ci
|
||||||
|
key: "cargo-cache-$TARGET"
|
||||||
paths:
|
paths:
|
||||||
- target
|
- $SHARED_PATH/cargo/registry/index
|
||||||
- .gitlab-ci.d
|
- $SHARED_PATH/cargo/registry/cache
|
||||||
|
- $SHARED_PATH/cargo/git/db
|
||||||
static:x86_64-unknown-linux-musl:
|
|
||||||
stage: artifacts
|
|
||||||
image: nixos/nix:2.19.2
|
|
||||||
script:
|
|
||||||
# Push artifacts and build requirements to binary cache
|
|
||||||
- ./bin/nix-build-and-cache .#static-x86_64-unknown-linux-musl
|
|
||||||
|
|
||||||
# Make the output less difficult to find
|
|
||||||
- cp result/bin/conduit conduit
|
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
expire_in: never
|
||||||
- conduit
|
|
||||||
|
|
||||||
static:aarch64-unknown-linux-musl:
|
build:release:cargo:x86_64-unknown-linux-musl-with-debug:
|
||||||
stage: artifacts
|
extends: .build-cargo-shared-settings
|
||||||
image: nixos/nix:2.19.2
|
variables:
|
||||||
script:
|
CARGO_PROFILE_RELEASE_DEBUG: 2 # Enable debug info for flamegraph profiling
|
||||||
# Push artifacts and build requirements to binary cache
|
TARGET: "x86_64-unknown-linux-musl"
|
||||||
- ./bin/nix-build-and-cache .#static-aarch64-unknown-linux-musl
|
after_script:
|
||||||
|
- "mv ./conduit-x86_64-unknown-linux-musl ./conduit-x86_64-unknown-linux-musl-with-debug"
|
||||||
# Make the output less difficult to find
|
|
||||||
- cp result/bin/conduit conduit
|
|
||||||
artifacts:
|
artifacts:
|
||||||
|
name: "conduit-x86_64-unknown-linux-musl-with-debug"
|
||||||
paths:
|
paths:
|
||||||
- conduit
|
- "conduit-x86_64-unknown-linux-musl-with-debug"
|
||||||
|
expose_as: "Conduit for x86_64-unknown-linux-musl-with-debug"
|
||||||
|
|
||||||
# Note that although we have an `oci-image-x86_64-unknown-linux-musl` output,
|
build:release:cargo:x86_64-unknown-linux-musl:
|
||||||
# we don't build it because it would be largely redundant to this one since it's
|
extends: .build-cargo-shared-settings
|
||||||
# all containerized anyway.
|
variables:
|
||||||
oci-image:x86_64-unknown-linux-gnu:
|
TARGET: "x86_64-unknown-linux-musl"
|
||||||
stage: artifacts
|
|
||||||
image: nixos/nix:2.19.2
|
|
||||||
script:
|
|
||||||
# Push artifacts and build requirements to binary cache
|
|
||||||
#
|
|
||||||
# Since the OCI image package is based on the binary package, this has the
|
|
||||||
# fun side effect of uploading the normal binary too. Conduit users who are
|
|
||||||
# deploying with Nix can leverage this fact by adding our binary cache to
|
|
||||||
# their systems.
|
|
||||||
- ./bin/nix-build-and-cache .#oci-image
|
|
||||||
|
|
||||||
# Make the output less difficult to find
|
|
||||||
- cp result oci-image-amd64.tar.gz
|
|
||||||
artifacts:
|
artifacts:
|
||||||
|
name: "conduit-x86_64-unknown-linux-musl"
|
||||||
paths:
|
paths:
|
||||||
- oci-image-amd64.tar.gz
|
- "conduit-x86_64-unknown-linux-musl"
|
||||||
|
expose_as: "Conduit for x86_64-unknown-linux-musl"
|
||||||
|
|
||||||
oci-image:aarch64-unknown-linux-musl:
|
build:release:cargo:arm-unknown-linux-musleabihf:
|
||||||
stage: artifacts
|
extends: .build-cargo-shared-settings
|
||||||
needs:
|
variables:
|
||||||
# Wait for the static binary job to finish before starting so we don't have
|
TARGET: "arm-unknown-linux-musleabihf"
|
||||||
# to build that twice for no reason
|
|
||||||
- static:aarch64-unknown-linux-musl
|
|
||||||
image: nixos/nix:2.19.2
|
|
||||||
script:
|
|
||||||
# Push artifacts and build requirements to binary cache
|
|
||||||
- ./bin/nix-build-and-cache .#oci-image-aarch64-unknown-linux-musl
|
|
||||||
|
|
||||||
# Make the output less difficult to find
|
|
||||||
- cp result oci-image-arm64v8.tar.gz
|
|
||||||
artifacts:
|
artifacts:
|
||||||
|
name: "conduit-arm-unknown-linux-musleabihf"
|
||||||
paths:
|
paths:
|
||||||
- oci-image-arm64v8.tar.gz
|
- "conduit-arm-unknown-linux-musleabihf"
|
||||||
|
expose_as: "Conduit for arm-unknown-linux-musleabihf"
|
||||||
|
|
||||||
debian:x86_64-unknown-linux-gnu:
|
build:release:cargo:armv7-unknown-linux-musleabihf:
|
||||||
stage: artifacts
|
extends: .build-cargo-shared-settings
|
||||||
# See also `rust-toolchain.toml`
|
variables:
|
||||||
image: rust:1.75.0
|
TARGET: "armv7-unknown-linux-musleabihf"
|
||||||
script:
|
|
||||||
- apt-get update && apt-get install -y --no-install-recommends libclang-dev
|
|
||||||
- cargo install cargo-deb
|
|
||||||
- cargo deb
|
|
||||||
|
|
||||||
# Make the output less difficult to find
|
|
||||||
- mv target/debian/*.deb conduit.deb
|
|
||||||
artifacts:
|
artifacts:
|
||||||
|
name: "conduit-armv7-unknown-linux-musleabihf"
|
||||||
paths:
|
paths:
|
||||||
- conduit.deb
|
- "conduit-armv7-unknown-linux-musleabihf"
|
||||||
|
expose_as: "Conduit for armv7-unknown-linux-musleabihf"
|
||||||
|
|
||||||
|
build:release:cargo:aarch64-unknown-linux-musl:
|
||||||
|
extends: .build-cargo-shared-settings
|
||||||
|
variables:
|
||||||
|
TARGET: "aarch64-unknown-linux-musl"
|
||||||
|
artifacts:
|
||||||
|
name: "conduit-aarch64-unknown-linux-musl"
|
||||||
|
paths:
|
||||||
|
- "conduit-aarch64-unknown-linux-musl"
|
||||||
|
expose_as: "Conduit for aarch64-unknown-linux-musl"
|
||||||
|
|
||||||
|
.cargo-debug-shared-settings:
|
||||||
|
extends: ".build-cargo-shared-settings"
|
||||||
|
rules:
|
||||||
|
- when: "always"
|
||||||
cache:
|
cache:
|
||||||
key: debian
|
key: "build_cache--$TARGET--$CI_COMMIT_BRANCH--debug"
|
||||||
paths:
|
script:
|
||||||
- target
|
# cross-compile conduit for target
|
||||||
- .gitlab-ci.d
|
- 'time time cross build --target="$TARGET" --locked'
|
||||||
|
- 'mv "target/$TARGET/debug/conduit" "conduit-debug-$TARGET"'
|
||||||
|
# print information about linking for debugging
|
||||||
|
- "file conduit-debug-$TARGET" # print file information
|
||||||
|
- 'readelf --dynamic conduit-debug-$TARGET | sed -e "/NEEDED/q1"' # ensure statically linked
|
||||||
|
artifacts:
|
||||||
|
expire_in: 4 weeks
|
||||||
|
|
||||||
.push-oci-image:
|
build:debug:cargo:x86_64-unknown-linux-musl:
|
||||||
stage: publish
|
extends: ".cargo-debug-shared-settings"
|
||||||
image: docker:25.0.0
|
variables:
|
||||||
|
TARGET: "x86_64-unknown-linux-musl"
|
||||||
|
artifacts:
|
||||||
|
name: "conduit-debug-x86_64-unknown-linux-musl"
|
||||||
|
paths:
|
||||||
|
- "conduit-debug-x86_64-unknown-linux-musl"
|
||||||
|
expose_as: "Conduit DEBUG for x86_64-unknown-linux-musl"
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------- #
|
||||||
|
# Create and publish docker image #
|
||||||
|
# --------------------------------------------------------------------- #
|
||||||
|
|
||||||
|
.docker-shared-settings:
|
||||||
|
stage: "build docker image"
|
||||||
|
image: jdrouet/docker-with-buildx:stable
|
||||||
|
tags: ["docker"]
|
||||||
services:
|
services:
|
||||||
- docker:25.0.0-dind
|
- docker:dind
|
||||||
|
needs:
|
||||||
|
- "build:release:cargo:x86_64-unknown-linux-musl"
|
||||||
|
- "build:release:cargo:arm-unknown-linux-musleabihf"
|
||||||
|
- "build:release:cargo:armv7-unknown-linux-musleabihf"
|
||||||
|
- "build:release:cargo:aarch64-unknown-linux-musl"
|
||||||
variables:
|
variables:
|
||||||
IMAGE_SUFFIX_AMD64: amd64
|
PLATFORMS: "linux/arm/v6,linux/arm/v7,linux/arm64,linux/amd64"
|
||||||
IMAGE_SUFFIX_ARM64V8: arm64v8
|
DOCKER_FILE: "docker/ci-binaries-packaging.Dockerfile"
|
||||||
|
cache:
|
||||||
|
paths:
|
||||||
|
- docker_cache
|
||||||
|
key: "$CI_JOB_NAME"
|
||||||
|
before_script:
|
||||||
|
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
|
||||||
|
# Only log in to Dockerhub if the credentials are given:
|
||||||
|
- if [ -n "${DOCKER_HUB}" ]; then docker login -u "$DOCKER_HUB_USER" -p "$DOCKER_HUB_PASSWORD" "$DOCKER_HUB"; fi
|
||||||
script:
|
script:
|
||||||
- docker load -i oci-image-amd64.tar.gz
|
# Prepare buildx to build multiarch stuff:
|
||||||
- IMAGE_ID_AMD64=$(docker images -q conduit:next)
|
- docker context create 'ci-context'
|
||||||
- docker load -i oci-image-arm64v8.tar.gz
|
- docker buildx create --name 'multiarch-builder' --use 'ci-context'
|
||||||
- IMAGE_ID_ARM64V8=$(docker images -q conduit:next)
|
# Copy binaries to their docker arch path
|
||||||
# Tag and push the architecture specific images
|
- mkdir -p linux/ && mv ./conduit-x86_64-unknown-linux-musl linux/amd64
|
||||||
- docker tag $IMAGE_ID_AMD64 $IMAGE_NAME:$CI_COMMIT_SHA-$IMAGE_SUFFIX_AMD64
|
- mkdir -p linux/arm/ && mv ./conduit-arm-unknown-linux-musleabihf linux/arm/v6
|
||||||
- docker tag $IMAGE_ID_ARM64V8 $IMAGE_NAME:$CI_COMMIT_SHA-$IMAGE_SUFFIX_ARM64V8
|
- mkdir -p linux/arm/ && mv ./conduit-armv7-unknown-linux-musleabihf linux/arm/v7
|
||||||
- docker push $IMAGE_NAME:$CI_COMMIT_SHA-$IMAGE_SUFFIX_AMD64
|
- mv ./conduit-aarch64-unknown-linux-musl linux/arm64
|
||||||
- docker push $IMAGE_NAME:$CI_COMMIT_SHA-$IMAGE_SUFFIX_ARM64V8
|
- 'export CREATED=$(date -u +''%Y-%m-%dT%H:%M:%SZ'') && echo "Docker image creation date: $CREATED"'
|
||||||
# Tag the multi-arch image
|
# Build and push image:
|
||||||
- docker manifest create $IMAGE_NAME:$CI_COMMIT_SHA --amend $IMAGE_NAME:$CI_COMMIT_SHA-$IMAGE_SUFFIX_AMD64 --amend $IMAGE_NAME:$CI_COMMIT_SHA-$IMAGE_SUFFIX_ARM64V8
|
- >
|
||||||
- docker manifest push $IMAGE_NAME:$CI_COMMIT_SHA
|
docker buildx build
|
||||||
# Tag and push the git ref
|
--pull
|
||||||
- docker manifest create $IMAGE_NAME:$CI_COMMIT_REF_NAME --amend $IMAGE_NAME:$CI_COMMIT_SHA-$IMAGE_SUFFIX_AMD64 --amend $IMAGE_NAME:$CI_COMMIT_SHA-$IMAGE_SUFFIX_ARM64V8
|
--push
|
||||||
- docker manifest push $IMAGE_NAME:$CI_COMMIT_REF_NAME
|
--cache-from=type=local,src=$CI_PROJECT_DIR/docker_cache
|
||||||
# Tag git tags as 'latest'
|
--cache-to=type=local,dest=$CI_PROJECT_DIR/docker_cache
|
||||||
- |
|
--build-arg CREATED=$CREATED
|
||||||
if [[ -n "$CI_COMMIT_TAG" ]]; then
|
--build-arg VERSION=$(grep -m1 -o '[0-9].[0-9].[0-9]' Cargo.toml)
|
||||||
docker manifest create $IMAGE_NAME:latest --amend $IMAGE_NAME:$CI_COMMIT_SHA-$IMAGE_SUFFIX_AMD64 --amend $IMAGE_NAME:$CI_COMMIT_SHA-$IMAGE_SUFFIX_ARM64V8
|
--build-arg "GIT_REF=$CI_COMMIT_SHORT_SHA"
|
||||||
docker manifest push $IMAGE_NAME:latest
|
--platform "$PLATFORMS"
|
||||||
fi
|
--tag "$TAG"
|
||||||
dependencies:
|
--tag "$TAG-alpine"
|
||||||
- oci-image:x86_64-unknown-linux-gnu
|
--tag "$TAG-commit-$CI_COMMIT_SHORT_SHA"
|
||||||
- oci-image:aarch64-unknown-linux-musl
|
--file "$DOCKER_FILE" .
|
||||||
only:
|
|
||||||
- next
|
|
||||||
- master
|
|
||||||
- tags
|
|
||||||
|
|
||||||
oci-image:push-gitlab:
|
docker:next:gitlab:
|
||||||
extends: .push-oci-image
|
extends: .docker-shared-settings
|
||||||
|
rules:
|
||||||
|
- if: '$CI_COMMIT_BRANCH == "next"'
|
||||||
variables:
|
variables:
|
||||||
IMAGE_NAME: $CI_REGISTRY_IMAGE/matrix-conduit
|
TAG: "$CI_REGISTRY_IMAGE/matrix-conduit:next"
|
||||||
before_script:
|
|
||||||
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
|
|
||||||
|
|
||||||
oci-image:push-dockerhub:
|
docker:next:dockerhub:
|
||||||
extends: .push-oci-image
|
extends: .docker-shared-settings
|
||||||
|
rules:
|
||||||
|
- if: '$CI_COMMIT_BRANCH == "next" && $DOCKER_HUB'
|
||||||
variables:
|
variables:
|
||||||
IMAGE_NAME: matrixconduit/matrix-conduit
|
TAG: "$DOCKER_HUB_IMAGE/matrixconduit/matrix-conduit:next"
|
||||||
|
|
||||||
|
docker:master:gitlab:
|
||||||
|
extends: .docker-shared-settings
|
||||||
|
rules:
|
||||||
|
- if: '$CI_COMMIT_BRANCH == "master"'
|
||||||
|
variables:
|
||||||
|
TAG: "$CI_REGISTRY_IMAGE/matrix-conduit:latest"
|
||||||
|
|
||||||
|
docker:master:dockerhub:
|
||||||
|
extends: .docker-shared-settings
|
||||||
|
rules:
|
||||||
|
- if: '$CI_COMMIT_BRANCH == "master" && $DOCKER_HUB'
|
||||||
|
variables:
|
||||||
|
TAG: "$DOCKER_HUB_IMAGE/matrixconduit/matrix-conduit:latest"
|
||||||
|
|
||||||
|
docker:tags:gitlab:
|
||||||
|
extends: .docker-shared-settings
|
||||||
|
rules:
|
||||||
|
- if: "$CI_COMMIT_TAG"
|
||||||
|
variables:
|
||||||
|
TAG: "$CI_REGISTRY_IMAGE/matrix-conduit:$CI_COMMIT_TAG"
|
||||||
|
|
||||||
|
docker:tags:dockerhub:
|
||||||
|
extends: .docker-shared-settings
|
||||||
|
rules:
|
||||||
|
- if: "$CI_COMMIT_TAG && $DOCKER_HUB"
|
||||||
|
variables:
|
||||||
|
TAG: "$DOCKER_HUB_IMAGE/matrixconduit/matrix-conduit:$CI_COMMIT_TAG"
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------- #
|
||||||
|
# Run tests #
|
||||||
|
# --------------------------------------------------------------------- #
|
||||||
|
|
||||||
|
.test-shared-settings:
|
||||||
|
stage: "test"
|
||||||
|
needs: []
|
||||||
|
image: "registry.gitlab.com/jfowl/conduit-containers/rust-with-tools:latest"
|
||||||
|
tags: ["docker"]
|
||||||
|
variables:
|
||||||
|
CARGO_INCREMENTAL: "false" # https://matklad.github.io/2021/09/04/fast-rust-builds.html#ci-workflow
|
||||||
|
interruptible: true
|
||||||
|
|
||||||
|
test:cargo:
|
||||||
|
extends: .test-shared-settings
|
||||||
before_script:
|
before_script:
|
||||||
- docker login -u $DOCKER_HUB_USER -p $DOCKER_HUB_PASSWORD
|
- rustup component add clippy
|
||||||
|
script:
|
||||||
|
- rustc --version && cargo --version # Print version info for debugging
|
||||||
|
- "cargo test --color always --workspace --verbose --locked --no-fail-fast -- -Z unstable-options --format json | gitlab-report -p test > $CI_PROJECT_DIR/report.xml"
|
||||||
|
- "cargo clippy --color always --verbose --message-format=json | gitlab-report -p clippy > $CI_PROJECT_DIR/gl-code-quality-report.json"
|
||||||
|
artifacts:
|
||||||
|
when: always
|
||||||
|
reports:
|
||||||
|
junit: report.xml
|
||||||
|
codequality: gl-code-quality-report.json
|
||||||
|
|
||||||
|
test:format:
|
||||||
|
extends: .test-shared-settings
|
||||||
|
before_script:
|
||||||
|
- rustup component add rustfmt
|
||||||
|
script:
|
||||||
|
- cargo fmt --all -- --check
|
||||||
|
|
||||||
|
test:audit:
|
||||||
|
extends: .test-shared-settings
|
||||||
|
allow_failure: true
|
||||||
|
script:
|
||||||
|
- cargo audit --color always || true
|
||||||
|
- cargo audit --stale --json | gitlab-report -p audit > gl-sast-report.json
|
||||||
|
artifacts:
|
||||||
|
when: always
|
||||||
|
reports:
|
||||||
|
sast: gl-sast-report.json
|
||||||
|
|
||||||
|
test:sytest:
|
||||||
|
stage: "test"
|
||||||
|
allow_failure: true
|
||||||
|
needs:
|
||||||
|
- "build:debug:cargo:x86_64-unknown-linux-musl"
|
||||||
|
image:
|
||||||
|
name: "valkum/sytest-conduit:latest"
|
||||||
|
entrypoint: [""]
|
||||||
|
tags: ["docker"]
|
||||||
|
variables:
|
||||||
|
PLUGINS: "https://github.com/valkum/sytest_conduit/archive/master.tar.gz"
|
||||||
|
interruptible: true
|
||||||
|
before_script:
|
||||||
|
- "mkdir -p /app"
|
||||||
|
- "cp ./conduit-debug-x86_64-unknown-linux-musl /app/conduit"
|
||||||
|
- "chmod +x /app/conduit"
|
||||||
|
- "rm -rf /src && ln -s $CI_PROJECT_DIR/ /src"
|
||||||
|
- "mkdir -p /work/server-0/database/ && mkdir -p /work/server-1/database/ && mkdir -p /work/server-2/database/"
|
||||||
|
- "cd /"
|
||||||
|
script:
|
||||||
|
- "SYTEST_EXIT_CODE=0"
|
||||||
|
- "/bootstrap.sh conduit || SYTEST_EXIT_CODE=1"
|
||||||
|
- 'perl /sytest/tap-to-junit-xml.pl --puretap --input /logs/results.tap --output $CI_PROJECT_DIR/sytest.xml "Sytest" && cp /logs/results.tap $CI_PROJECT_DIR/results.tap'
|
||||||
|
- "exit $SYTEST_EXIT_CODE"
|
||||||
|
artifacts:
|
||||||
|
when: always
|
||||||
|
paths:
|
||||||
|
- "$CI_PROJECT_DIR/sytest.xml"
|
||||||
|
- "$CI_PROJECT_DIR/results.tap"
|
||||||
|
reports:
|
||||||
|
junit: "$CI_PROJECT_DIR/sytest.xml"
|
||||||
|
|
||||||
|
test:dockerlint:
|
||||||
|
stage: "test"
|
||||||
|
needs: []
|
||||||
|
image: "ghcr.io/hadolint/hadolint@sha256:6c4b7c23f96339489dd35f21a711996d7ce63047467a9a562287748a03ad5242" # 2.8.0-alpine
|
||||||
|
interruptible: true
|
||||||
|
script:
|
||||||
|
- hadolint --version
|
||||||
|
# First pass: Print for CI log:
|
||||||
|
- >
|
||||||
|
hadolint
|
||||||
|
--no-fail --verbose
|
||||||
|
./Dockerfile
|
||||||
|
./docker/ci-binaries-packaging.Dockerfile
|
||||||
|
# Then output the results into a json for GitLab to pretty-print this in the MR:
|
||||||
|
- >
|
||||||
|
hadolint
|
||||||
|
--format gitlab_codeclimate
|
||||||
|
--failure-threshold error
|
||||||
|
./Dockerfile
|
||||||
|
./docker/ci-binaries-packaging.Dockerfile > dockerlint.json
|
||||||
|
artifacts:
|
||||||
|
when: always
|
||||||
|
reports:
|
||||||
|
codequality: dockerlint.json
|
||||||
|
paths:
|
||||||
|
- dockerlint.json
|
||||||
|
rules:
|
||||||
|
- if: '$CI_COMMIT_REF_NAME != "master"'
|
||||||
|
changes:
|
||||||
|
- docker/*Dockerfile
|
||||||
|
- Dockerfile
|
||||||
|
- .gitlab-ci.yml
|
||||||
|
- if: '$CI_COMMIT_REF_NAME == "master"'
|
||||||
|
- if: '$CI_COMMIT_REF_NAME == "next"'
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------- #
|
||||||
|
# Store binaries as package so they have download urls #
|
||||||
|
# --------------------------------------------------------------------- #
|
||||||
|
|
||||||
|
publish:package:
|
||||||
|
stage: "upload artifacts"
|
||||||
|
needs:
|
||||||
|
- "build:release:cargo:x86_64-unknown-linux-musl"
|
||||||
|
- "build:release:cargo:arm-unknown-linux-musleabihf"
|
||||||
|
- "build:release:cargo:armv7-unknown-linux-musleabihf"
|
||||||
|
- "build:release:cargo:aarch64-unknown-linux-musl"
|
||||||
|
# - "build:cargo-deb:x86_64-unknown-linux-gnu"
|
||||||
|
rules:
|
||||||
|
- if: '$CI_COMMIT_BRANCH == "master"'
|
||||||
|
- if: '$CI_COMMIT_BRANCH == "next"'
|
||||||
|
- if: "$CI_COMMIT_TAG"
|
||||||
|
image: curlimages/curl:latest
|
||||||
|
tags: ["docker"]
|
||||||
|
variables:
|
||||||
|
GIT_STRATEGY: "none" # Don't need a clean copy of the code, we just operate on artifacts
|
||||||
|
script:
|
||||||
|
- 'BASE_URL="${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/conduit-${CI_COMMIT_REF_SLUG}/build-${CI_PIPELINE_ID}"'
|
||||||
|
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file conduit-x86_64-unknown-linux-musl "${BASE_URL}/conduit-x86_64-unknown-linux-musl"'
|
||||||
|
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file conduit-arm-unknown-linux-musleabihf "${BASE_URL}/conduit-arm-unknown-linux-musleabihf"'
|
||||||
|
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file conduit-armv7-unknown-linux-musleabihf "${BASE_URL}/conduit-armv7-unknown-linux-musleabihf"'
|
||||||
|
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file conduit-aarch64-unknown-linux-musl "${BASE_URL}/conduit-aarch64-unknown-linux-musl"'
|
||||||
|
|
||||||
|
# Avoid duplicate pipelines
|
||||||
|
# See: https://docs.gitlab.com/ee/ci/yaml/workflow.html#switch-between-branch-pipelines-and-merge-request-pipelines
|
||||||
|
workflow:
|
||||||
|
rules:
|
||||||
|
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
||||||
|
- if: "$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS"
|
||||||
|
when: never
|
||||||
|
- if: "$CI_COMMIT_BRANCH"
|
||||||
|
- if: "$CI_COMMIT_TAG"
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
# Nix things
|
|
||||||
.envrc @CobaltCause
|
|
||||||
flake.lock @CobaltCause
|
|
||||||
flake.nix @CobaltCause
|
|
||||||
nix/ @CobaltCause
|
|
|
@ -1,37 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
set -eux
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------- #
|
|
||||||
# #
|
|
||||||
# Configures docker buildx to use a remote server for arm building. #
|
|
||||||
# Expects $SSH_PRIVATE_KEY to be a valid ssh ed25519 private key with #
|
|
||||||
# access to the server $ARM_SERVER_USER@$ARM_SERVER_IP #
|
|
||||||
# #
|
|
||||||
# This is expected to only be used in the official CI/CD pipeline! #
|
|
||||||
# #
|
|
||||||
# Requirements: openssh-client, docker buildx #
|
|
||||||
# Inspired by: https://depot.dev/blog/building-arm-containers #
|
|
||||||
# #
|
|
||||||
# --------------------------------------------------------------------- #
|
|
||||||
|
|
||||||
cat "$BUILD_SERVER_SSH_PRIVATE_KEY" | ssh-add -
|
|
||||||
|
|
||||||
# Test server connections:
|
|
||||||
ssh "$ARM_SERVER_USER@$ARM_SERVER_IP" "uname -a"
|
|
||||||
ssh "$AMD_SERVER_USER@$AMD_SERVER_IP" "uname -a"
|
|
||||||
|
|
||||||
# Connect remote arm64 server for all arm builds:
|
|
||||||
docker buildx create \
|
|
||||||
--name "multi" \
|
|
||||||
--driver "docker-container" \
|
|
||||||
--platform "linux/arm64,linux/arm/v7" \
|
|
||||||
"ssh://$ARM_SERVER_USER@$ARM_SERVER_IP"
|
|
||||||
|
|
||||||
# Connect remote amd64 server for adm64 builds:
|
|
||||||
docker buildx create --append \
|
|
||||||
--name "multi" \
|
|
||||||
--driver "docker-container" \
|
|
||||||
--platform "linux/amd64" \
|
|
||||||
"ssh://$AMD_SERVER_USER@$AMD_SERVER_IP"
|
|
||||||
|
|
||||||
docker buildx use multi
|
|
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"rust-analyzer.procMacro.enable": true,
|
||||||
|
}
|
|
@ -1,134 +0,0 @@
|
||||||
|
|
||||||
# Contributor Covenant Code of Conduct
|
|
||||||
|
|
||||||
## Our Pledge
|
|
||||||
|
|
||||||
We as members, contributors, and leaders pledge to make participation in our
|
|
||||||
community a harassment-free experience for everyone, regardless of age, body
|
|
||||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
|
||||||
identity and expression, level of experience, education, socio-economic status,
|
|
||||||
nationality, personal appearance, race, caste, color, religion, or sexual
|
|
||||||
identity and orientation.
|
|
||||||
|
|
||||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
|
||||||
diverse, inclusive, and healthy community.
|
|
||||||
|
|
||||||
## Our Standards
|
|
||||||
|
|
||||||
Examples of behavior that contributes to a positive environment for our
|
|
||||||
community include:
|
|
||||||
|
|
||||||
* Demonstrating empathy and kindness toward other people
|
|
||||||
* Being respectful of differing opinions, viewpoints, and experiences
|
|
||||||
* Giving and gracefully accepting constructive feedback
|
|
||||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
|
||||||
and learning from the experience
|
|
||||||
* Focusing on what is best not just for us as individuals, but for the overall
|
|
||||||
community
|
|
||||||
|
|
||||||
Examples of unacceptable behavior include:
|
|
||||||
|
|
||||||
* The use of sexualized language or imagery, and sexual attention or advances of
|
|
||||||
any kind
|
|
||||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
|
||||||
* Public or private harassment
|
|
||||||
* Publishing others' private information, such as a physical or email address,
|
|
||||||
without their explicit permission
|
|
||||||
* Other conduct which could reasonably be considered inappropriate in a
|
|
||||||
professional setting
|
|
||||||
|
|
||||||
## Enforcement Responsibilities
|
|
||||||
|
|
||||||
Community leaders are responsible for clarifying and enforcing our standards of
|
|
||||||
acceptable behavior and will take appropriate and fair corrective action in
|
|
||||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
|
||||||
or harmful.
|
|
||||||
|
|
||||||
Community leaders have the right and responsibility to remove, edit, or reject
|
|
||||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
|
||||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
|
||||||
decisions when appropriate.
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
This Code of Conduct applies within all community spaces, and also applies when
|
|
||||||
an individual is officially representing the community in public spaces.
|
|
||||||
Examples of representing our community include using an official e-mail address,
|
|
||||||
posting via an official social media account, or acting as an appointed
|
|
||||||
representative at an online or offline event.
|
|
||||||
|
|
||||||
## Enforcement
|
|
||||||
|
|
||||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
|
||||||
reported to the community leaders responsible for enforcement over email at
|
|
||||||
coc@koesters.xyz or over Matrix at @timo:conduit.rs.
|
|
||||||
All complaints will be reviewed and investigated promptly and fairly.
|
|
||||||
|
|
||||||
All community leaders are obligated to respect the privacy and security of the
|
|
||||||
reporter of any incident.
|
|
||||||
|
|
||||||
## Enforcement Guidelines
|
|
||||||
|
|
||||||
Community leaders will follow these Community Impact Guidelines in determining
|
|
||||||
the consequences for any action they deem in violation of this Code of Conduct:
|
|
||||||
|
|
||||||
### 1. Correction
|
|
||||||
|
|
||||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
|
||||||
unprofessional or unwelcome in the community.
|
|
||||||
|
|
||||||
**Consequence**: A private, written warning from community leaders, providing
|
|
||||||
clarity around the nature of the violation and an explanation of why the
|
|
||||||
behavior was inappropriate. A public apology may be requested.
|
|
||||||
|
|
||||||
### 2. Warning
|
|
||||||
|
|
||||||
**Community Impact**: A violation through a single incident or series of
|
|
||||||
actions.
|
|
||||||
|
|
||||||
**Consequence**: A warning with consequences for continued behavior. No
|
|
||||||
interaction with the people involved, including unsolicited interaction with
|
|
||||||
those enforcing the Code of Conduct, for a specified period of time. This
|
|
||||||
includes avoiding interactions in community spaces as well as external channels
|
|
||||||
like social media. Violating these terms may lead to a temporary or permanent
|
|
||||||
ban.
|
|
||||||
|
|
||||||
### 3. Temporary Ban
|
|
||||||
|
|
||||||
**Community Impact**: A serious violation of community standards, including
|
|
||||||
sustained inappropriate behavior.
|
|
||||||
|
|
||||||
**Consequence**: A temporary ban from any sort of interaction or public
|
|
||||||
communication with the community for a specified period of time. No public or
|
|
||||||
private interaction with the people involved, including unsolicited interaction
|
|
||||||
with those enforcing the Code of Conduct, is allowed during this period.
|
|
||||||
Violating these terms may lead to a permanent ban.
|
|
||||||
|
|
||||||
### 4. Permanent Ban
|
|
||||||
|
|
||||||
**Community Impact**: Demonstrating a pattern of violation of community
|
|
||||||
standards, including sustained inappropriate behavior, harassment of an
|
|
||||||
individual, or aggression toward or disparagement of classes of individuals.
|
|
||||||
|
|
||||||
**Consequence**: A permanent ban from any sort of public interaction within the
|
|
||||||
community.
|
|
||||||
|
|
||||||
## Attribution
|
|
||||||
|
|
||||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
|
||||||
version 2.1, available at
|
|
||||||
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
|
|
||||||
|
|
||||||
Community Impact Guidelines were inspired by
|
|
||||||
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
|
|
||||||
|
|
||||||
For answers to common questions about this code of conduct, see the FAQ at
|
|
||||||
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
|
|
||||||
[https://www.contributor-covenant.org/translations][translations].
|
|
||||||
|
|
||||||
[homepage]: https://www.contributor-covenant.org
|
|
||||||
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
|
||||||
[Mozilla CoC]: https://github.com/mozilla/diversity
|
|
||||||
[FAQ]: https://www.contributor-covenant.org/faq
|
|
||||||
[translations]: https://www.contributor-covenant.org/translations
|
|
||||||
|
|
2538
Cargo.lock
generated
2538
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
131
Cargo.toml
131
Cargo.toml
|
@ -1,14 +1,3 @@
|
||||||
# Keep alphabetically sorted
|
|
||||||
[workspace.lints.rust]
|
|
||||||
explicit_outlives_requirements = "warn"
|
|
||||||
unused_qualifications = "warn"
|
|
||||||
|
|
||||||
# Keep alphabetically sorted
|
|
||||||
[workspace.lints.clippy]
|
|
||||||
cloned_instead_of_copied = "warn"
|
|
||||||
dbg_macro = "warn"
|
|
||||||
str_to_string = "warn"
|
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "conduit"
|
name = "conduit"
|
||||||
description = "A Matrix homeserver written in Rust"
|
description = "A Matrix homeserver written in Rust"
|
||||||
|
@ -17,119 +6,103 @@ authors = ["timokoesters <timo@koesters.xyz>"]
|
||||||
homepage = "https://conduit.rs"
|
homepage = "https://conduit.rs"
|
||||||
repository = "https://gitlab.com/famedly/conduit"
|
repository = "https://gitlab.com/famedly/conduit"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
version = "0.7.0-alpha"
|
version = "0.3.0-next"
|
||||||
|
rust-version = "1.63"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See also `rust-toolchain.toml`
|
|
||||||
rust-version = "1.75.0"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Web framework
|
# Web framework
|
||||||
axum = { version = "0.6.18", default-features = false, features = ["form", "headers", "http1", "http2", "json", "matched-path"], optional = true }
|
axum = { version = "0.5.8", default-features = false, features = ["form", "headers", "http1", "http2", "json", "matched-path"], optional = true }
|
||||||
axum-server = { version = "0.5.1", features = ["tls-rustls"] }
|
axum-server = { version = "0.4.0", features = ["tls-rustls"] }
|
||||||
tower = { version = "0.4.13", features = ["util"] }
|
tower = { version = "0.4.8", features = ["util"] }
|
||||||
tower-http = { version = "0.4.1", features = ["add-extension", "cors", "sensitive-headers", "trace", "util"] }
|
tower-http = { version = "0.3.4", features = ["add-extension", "cors", "compression-full", "sensitive-headers", "trace", "util"] }
|
||||||
|
|
||||||
# Used for matrix spec type definitions and helpers
|
# 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 = { 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 = "1a1c61ee1e8f0936e956a3b69c931ce12ee28475", 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" ] }
|
ruma = { git = "https://github.com/ruma/ruma", rev = "d614ad1422d6c4b3437ebc318ca8514ae338fd6d", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "state-res", "unstable-msc2448", "unstable-pre-spec", "unstable-exhaustive-types"] }
|
||||||
#ruma = { git = "https://github.com/timokoesters/ruma", rev = "4ec9c69bb7e09391add2382b3ebac97b6e8f4c64", 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" ] }
|
#ruma = { git = "https://github.com/timokoesters/ruma", rev = "50c1db7e0a3a21fc794b0cce3b64285a4c750c71", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "state-res", "unstable-pre-spec", "unstable-exhaustive-types"] }
|
||||||
#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" ] }
|
#ruma = { path = "../ruma/crates/ruma", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "state-res", "unstable-pre-spec", "unstable-exhaustive-types"] }
|
||||||
|
|
||||||
# Async runtime and utilities
|
# Async runtime and utilities
|
||||||
tokio = { version = "1.28.1", features = ["fs", "macros", "signal", "sync"] }
|
tokio = { version = "1.11.0", features = ["fs", "macros", "signal", "sync"] }
|
||||||
# Used for storing data permanently
|
# Used for storing data permanently
|
||||||
#sled = { version = "0.34.7", features = ["compression", "no_metrics"], optional = true }
|
sled = { version = "0.34.7", features = ["compression", "no_metrics"], optional = true }
|
||||||
#sled = { git = "https://github.com/spacejam/sled.git", rev = "e4640e0773595229f398438886f19bca6f7326a2", features = ["compression"] }
|
#sled = { git = "https://github.com/spacejam/sled.git", rev = "e4640e0773595229f398438886f19bca6f7326a2", features = ["compression"] }
|
||||||
persy = { version = "1.4.4", optional = true, features = ["background_ops"] }
|
persy = { version = "1.0.0", optional = true, features = ["background_ops"] }
|
||||||
|
|
||||||
# Used for the http request / response body type for Ruma endpoints used with reqwest
|
# Used for the http request / response body type for Ruma endpoints used with reqwest
|
||||||
bytes = "1.4.0"
|
bytes = "1.1.0"
|
||||||
http = "0.2.9"
|
http = "0.2.4"
|
||||||
# Used to find data directory for default db path
|
# Used to find data directory for default db path
|
||||||
directories = "4.0.1"
|
directories = "4.0.0"
|
||||||
# Used for ruma wrapper
|
# Used for ruma wrapper
|
||||||
serde_json = { version = "1.0.96", features = ["raw_value"] }
|
serde_json = { version = "1.0.68", features = ["raw_value"] }
|
||||||
# Used for appservice registration files
|
# Used for appservice registration files
|
||||||
serde_yaml = "0.9.21"
|
serde_yaml = "0.8.21"
|
||||||
# Used for pdu definition
|
# Used for pdu definition
|
||||||
serde = { version = "1.0.163", features = ["rc"] }
|
serde = { version = "1.0.130", features = ["rc"] }
|
||||||
# Used for secure identifiers
|
# Used for secure identifiers
|
||||||
rand = "0.8.5"
|
rand = "0.8.4"
|
||||||
# Used to hash passwords
|
# Used to hash passwords
|
||||||
rust-argon2 = "1.0.0"
|
rust-argon2 = "0.8.3"
|
||||||
# Used to send requests
|
# Used to send requests
|
||||||
hyper = "0.14.26"
|
reqwest = { default-features = false, features = ["rustls-tls-native-roots", "socks"], git = "https://github.com/timokoesters/reqwest", rev = "57b7cf4feb921573dfafad7d34b9ac6e44ead0bd" }
|
||||||
reqwest = { version = "0.11.18", default-features = false, features = ["rustls-tls-native-roots", "socks"] }
|
|
||||||
# Used for conduit::Error type
|
# Used for conduit::Error type
|
||||||
thiserror = "1.0.40"
|
thiserror = "1.0.29"
|
||||||
# Used to generate thumbnails for images
|
# Used to generate thumbnails for images
|
||||||
image = { version = "0.24.6", default-features = false, features = ["jpeg", "png", "gif"] }
|
image = { version = "0.23.14", default-features = false, features = ["jpeg", "png", "gif"] }
|
||||||
# Used to encode server public key
|
# Used to encode server public key
|
||||||
base64 = "0.21.2"
|
base64 = "0.13.0"
|
||||||
# Used when hashing the state
|
# Used when hashing the state
|
||||||
ring = "0.17.7"
|
ring = "0.16.20"
|
||||||
# Used when querying the SRV record of other servers
|
# Used when querying the SRV record of other servers
|
||||||
trust-dns-resolver = "0.22.0"
|
trust-dns-resolver = "0.20.3"
|
||||||
# Used to find matching events for appservices
|
# Used to find matching events for appservices
|
||||||
regex = "1.8.1"
|
regex = "1.5.4"
|
||||||
# jwt jsonwebtokens
|
# jwt jsonwebtokens
|
||||||
jsonwebtoken = "9.2.0"
|
jsonwebtoken = "7.2.0"
|
||||||
# Performance measurements
|
# Performance measurements
|
||||||
tracing = { version = "0.1.37", features = [] }
|
tracing = { version = "0.1.27", features = [] }
|
||||||
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
|
tracing-subscriber = "0.2.22"
|
||||||
tracing-flame = "0.2.0"
|
tracing-flame = "0.1.0"
|
||||||
opentelemetry = { version = "0.18.0", features = ["rt-tokio"] }
|
opentelemetry = { version = "0.16.0", features = ["rt-tokio"] }
|
||||||
opentelemetry-jaeger = { version = "0.17.0", features = ["rt-tokio"] }
|
opentelemetry-jaeger = { version = "0.15.0", features = ["rt-tokio"] }
|
||||||
tracing-opentelemetry = "0.18.0"
|
|
||||||
lru-cache = "0.1.2"
|
lru-cache = "0.1.2"
|
||||||
rusqlite = { version = "0.29.0", optional = true, features = ["bundled"] }
|
rusqlite = { version = "0.25.3", optional = true, features = ["bundled"] }
|
||||||
parking_lot = { version = "0.12.1", optional = true }
|
parking_lot = { version = "0.11.2", optional = true }
|
||||||
# crossbeam = { version = "0.8.2", optional = true }
|
crossbeam = { version = "0.8.1", optional = true }
|
||||||
num_cpus = "1.15.0"
|
num_cpus = "1.13.0"
|
||||||
threadpool = "1.8.1"
|
threadpool = "1.8.1"
|
||||||
# heed = { git = "https://github.com/timokoesters/heed.git", rev = "f6f825da7fb2c758867e05ad973ef800a6fe1d5d", optional = true }
|
heed = { git = "https://github.com/timokoesters/heed.git", rev = "f6f825da7fb2c758867e05ad973ef800a6fe1d5d", optional = true }
|
||||||
# Used for ruma wrapper
|
rocksdb = { version = "0.17.0", default-features = true, features = ["multi-threaded-cf", "zstd"], optional = true }
|
||||||
serde_html_form = "0.2.0"
|
|
||||||
|
|
||||||
rocksdb = { version = "0.21.0", default-features = true, features = ["multi-threaded-cf", "zstd"], optional = true }
|
thread_local = "1.1.3"
|
||||||
|
|
||||||
thread_local = "1.1.7"
|
|
||||||
# used for TURN server authentication
|
# used for TURN server authentication
|
||||||
hmac = "0.12.1"
|
hmac = "0.11.0"
|
||||||
sha-1 = "0.10.1"
|
sha-1 = "0.9.8"
|
||||||
# used for conduit's CLI and admin room command parsing
|
# used for conduit's CLI and admin room command parsing
|
||||||
clap = { version = "4.3.0", default-features = false, features = ["std", "derive", "help", "usage", "error-context"] }
|
clap = { version = "3.2.5", default-features = false, features = ["std", "derive"] }
|
||||||
futures-util = { version = "0.3.28", default-features = false }
|
futures-util = { version = "0.3.17", default-features = false }
|
||||||
# Used for reading the configuration from conduit.toml & environment variables
|
# Used for reading the configuration from conduit.toml & environment variables
|
||||||
figment = { version = "0.10.8", features = ["env", "toml"] }
|
figment = { version = "0.10.6", features = ["env", "toml"] }
|
||||||
|
|
||||||
tikv-jemalloc-ctl = { version = "0.5.0", features = ["use_std"], optional = true }
|
tikv-jemalloc-ctl = { version = "0.4.2", features = ["use_std"], optional = true }
|
||||||
tikv-jemallocator = { version = "0.5.0", features = ["unprefixed_malloc_on_supported_platforms"], optional = true }
|
tikv-jemallocator = { version = "0.4.1", features = ["unprefixed_malloc_on_supported_platforms"], optional = true }
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
async-trait = "0.1.68"
|
async-trait = "0.1.57"
|
||||||
|
|
||||||
sd-notify = { version = "0.4.1", optional = true }
|
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
|
||||||
nix = { version = "0.26.2", features = ["resource"] }
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["conduit_bin", "backend_sqlite", "backend_rocksdb", "systemd"]
|
default = ["conduit_bin", "backend_sqlite", "backend_rocksdb", "jemalloc"]
|
||||||
#backend_sled = ["sled"]
|
backend_sled = ["sled"]
|
||||||
backend_persy = ["persy", "parking_lot"]
|
backend_persy = ["persy", "parking_lot"]
|
||||||
backend_sqlite = ["sqlite"]
|
backend_sqlite = ["sqlite"]
|
||||||
#backend_heed = ["heed", "crossbeam"]
|
backend_heed = ["heed", "crossbeam"]
|
||||||
backend_rocksdb = ["rocksdb"]
|
backend_rocksdb = ["rocksdb"]
|
||||||
jemalloc = ["tikv-jemalloc-ctl", "tikv-jemallocator"]
|
jemalloc = ["tikv-jemalloc-ctl", "tikv-jemallocator"]
|
||||||
sqlite = ["rusqlite", "parking_lot", "tokio/signal"]
|
sqlite = ["rusqlite", "parking_lot", "tokio/signal"]
|
||||||
conduit_bin = ["axum"]
|
conduit_bin = ["axum"]
|
||||||
systemd = ["sd-notify"]
|
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "conduit"
|
name = "conduit"
|
||||||
|
@ -152,7 +125,7 @@ instead of a server that has high scalability."""
|
||||||
section = "net"
|
section = "net"
|
||||||
priority = "optional"
|
priority = "optional"
|
||||||
assets = [
|
assets = [
|
||||||
["debian/README.md", "usr/share/doc/matrix-conduit/README.Debian", "644"],
|
["debian/README.Debian", "usr/share/doc/matrix-conduit/", "644"],
|
||||||
["README.md", "usr/share/doc/matrix-conduit/", "644"],
|
["README.md", "usr/share/doc/matrix-conduit/", "644"],
|
||||||
["target/release/conduit", "usr/sbin/matrix-conduit", "755"],
|
["target/release/conduit", "usr/sbin/matrix-conduit", "755"],
|
||||||
]
|
]
|
||||||
|
|
23
Cross.toml
Normal file
23
Cross.toml
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
[build.env]
|
||||||
|
# CI uses an S3 endpoint to store sccache artifacts, so their config needs to
|
||||||
|
# be available in the cross container as well
|
||||||
|
passthrough = [
|
||||||
|
"RUSTC_WRAPPER",
|
||||||
|
"AWS_ACCESS_KEY_ID",
|
||||||
|
"AWS_SECRET_ACCESS_KEY",
|
||||||
|
"SCCACHE_BUCKET",
|
||||||
|
"SCCACHE_ENDPOINT",
|
||||||
|
"SCCACHE_S3_USE_SSL",
|
||||||
|
]
|
||||||
|
|
||||||
|
[target.aarch64-unknown-linux-musl]
|
||||||
|
image = "registry.gitlab.com/jfowl/conduit-containers/rust-cross-aarch64-unknown-linux-musl:latest"
|
||||||
|
|
||||||
|
[target.arm-unknown-linux-musleabihf]
|
||||||
|
image = "registry.gitlab.com/jfowl/conduit-containers/rust-cross-arm-unknown-linux-musleabihf:latest"
|
||||||
|
|
||||||
|
[target.armv7-unknown-linux-musleabihf]
|
||||||
|
image = "registry.gitlab.com/jfowl/conduit-containers/rust-cross-armv7-unknown-linux-musleabihf:latest"
|
||||||
|
|
||||||
|
[target.x86_64-unknown-linux-musl]
|
||||||
|
image = "registry.gitlab.com/jfowl/conduit-containers/rust-cross-x86_64-unknown-linux-musl@sha256:b6d689e42f0236c8a38b961bca2a12086018b85ed20e0826310421daf182e2bb"
|
135
DEPLOY.md
135
DEPLOY.md
|
@ -2,92 +2,59 @@
|
||||||
|
|
||||||
> ## Getting help
|
> ## Getting help
|
||||||
>
|
>
|
||||||
> If you run into any problems while setting up Conduit, write an email to `conduit@koesters.xyz`, ask us
|
> If you run into any problems while setting up Conduit, write an email to `timo@koesters.xyz`, ask us
|
||||||
> in `#conduit:fachschaften.org` or [open an issue on GitLab](https://gitlab.com/famedly/conduit/-/issues/new).
|
> in `#conduit:fachschaften.org` or [open an issue on GitLab](https://gitlab.com/famedly/conduit/-/issues/new).
|
||||||
|
|
||||||
## Installing Conduit
|
## Installing Conduit
|
||||||
|
|
||||||
Although you might be able to compile Conduit for Windows, we do recommend running it on a Linux server. We therefore
|
Although you might be able to compile Conduit for Windows, we do recommend running it on a linux server. We therefore
|
||||||
only offer Linux binaries.
|
only offer Linux binaries.
|
||||||
|
|
||||||
You may simply download the binary that fits your machine. Run `uname -m` to see what you need. Now copy the appropriate url:
|
You may simply download the binary that fits your machine. Run `uname -m` to see what you need. Now copy the right url:
|
||||||
|
|
||||||
**Stable versions:**
|
| CPU Architecture | Download stable version | Download development version |
|
||||||
|
| ------------------------------------------- | ------------------------------ | ---------------------------- |
|
||||||
|
| x84_64 / amd64 (Most servers and computers) | [Download][x84_64-musl-master] | [Download][x84_64-musl-next] |
|
||||||
|
| armv6 | [Download][armv6-musl-master] | [Download][armv6-musl-next] |
|
||||||
|
| armv7 (e.g. Raspberry Pi by default) | [Download][armv7-musl-master] | [Download][armv7-musl-next] |
|
||||||
|
| armv8 / aarch64 | [Download][armv8-musl-master] | [Download][armv8-musl-next] |
|
||||||
|
|
||||||
| CPU Architecture | Download stable version |
|
[x84_64-musl-master]: https://gitlab.com/famedly/conduit/-/jobs/artifacts/master/raw/conduit-x86_64-unknown-linux-musl?job=build:release:cargo:x86_64-unknown-linux-musl
|
||||||
| ------------------------------------------- | --------------------------------------------------------------- |
|
[armv6-musl-master]: https://gitlab.com/famedly/conduit/-/jobs/artifacts/master/raw/conduit-arm-unknown-linux-musleabihf?job=build:release:cargo:arm-unknown-linux-musleabihf
|
||||||
| x84_64 / amd64 (Most servers and computers) | [Binary][x84_64-glibc-master] / [.deb][x84_64-glibc-master-deb] |
|
[armv7-musl-master]: https://gitlab.com/famedly/conduit/-/jobs/artifacts/master/raw/conduit-armv7-unknown-linux-musleabihf?job=build:release:cargo:armv7-unknown-linux-musleabihf
|
||||||
| armv7 (e.g. Raspberry Pi by default) | [Binary][armv7-glibc-master] / [.deb][armv7-glibc-master-deb] |
|
[armv8-musl-master]: https://gitlab.com/famedly/conduit/-/jobs/artifacts/master/raw/conduit-aarch64-unknown-linux-musl?job=build:release:cargo:aarch64-unknown-linux-musl
|
||||||
| armv8 / aarch64 | [Binary][armv8-glibc-master] / [.deb][armv8-glibc-master-deb] |
|
[x84_64-musl-next]: https://gitlab.com/famedly/conduit/-/jobs/artifacts/next/raw/conduit-x86_64-unknown-linux-musl?job=build:release:cargo:x86_64-unknown-linux-musl
|
||||||
|
[armv6-musl-next]: https://gitlab.com/famedly/conduit/-/jobs/artifacts/next/raw/conduit-arm-unknown-linux-musleabihf?job=build:release:cargo:arm-unknown-linux-musleabihf
|
||||||
These builds were created on and linked against the glibc version shipped with Debian bullseye.
|
[armv7-musl-next]: https://gitlab.com/famedly/conduit/-/jobs/artifacts/next/raw/conduit-armv7-unknown-linux-musleabihf?job=build:release:cargo:armv7-unknown-linux-musleabihf
|
||||||
If you use a system with an older glibc version (e.g. RHEL8), you might need to compile Conduit yourself.
|
[armv8-musl-next]: https://gitlab.com/famedly/conduit/-/jobs/artifacts/next/raw/conduit-aarch64-unknown-linux-musl?job=build:release:cargo:aarch64-unknown-linux-musl
|
||||||
|
|
||||||
[x84_64-glibc-master]: https://gitlab.com/famedly/conduit/-/jobs/artifacts/master/raw/build-output/linux_amd64/conduit?job=docker:master
|
|
||||||
[armv7-glibc-master]: https://gitlab.com/famedly/conduit/-/jobs/artifacts/master/raw/build-output/linux_arm_v7/conduit?job=docker:master
|
|
||||||
[armv8-glibc-master]: https://gitlab.com/famedly/conduit/-/jobs/artifacts/master/raw/build-output/linux_arm64/conduit?job=docker:master
|
|
||||||
[x84_64-glibc-master-deb]: https://gitlab.com/famedly/conduit/-/jobs/artifacts/master/raw/build-output/linux_amd64/conduit.deb?job=docker:master
|
|
||||||
[armv7-glibc-master-deb]: https://gitlab.com/famedly/conduit/-/jobs/artifacts/master/raw/build-output/linux_arm_v7/conduit.deb?job=docker:master
|
|
||||||
[armv8-glibc-master-deb]: https://gitlab.com/famedly/conduit/-/jobs/artifacts/master/raw/build-output/linux_arm64/conduit.deb?job=docker:master
|
|
||||||
|
|
||||||
**Latest versions:**
|
|
||||||
|
|
||||||
| Target | Type | Download |
|
|
||||||
|-|-|-|
|
|
||||||
| `x86_64-unknown-linux-gnu` | Dynamically linked Debian package | [link](https://gitlab.com/api/v4/projects/famedly%2Fconduit/jobs/artifacts/next/raw/conduit.deb?job=debian:x86_64-unknown-linux-gnu) |
|
|
||||||
| `x86_64-unknown-linux-musl` | Statically linked binary | [link](https://gitlab.com/api/v4/projects/famedly%2Fconduit/jobs/artifacts/next/raw/conduit?job=static:x86_64-unknown-linux-musl) |
|
|
||||||
| `aarch64-unknown-linux-musl` | Statically linked binary | [link](https://gitlab.com/api/v4/projects/famedly%2Fconduit/jobs/artifacts/next/raw/conduit?job=static:aarch64-unknown-linux-musl) |
|
|
||||||
| `x86_64-unknown-linux-musl` | OCI image | [link](https://gitlab.com/api/v4/projects/famedly%2Fconduit/jobs/artifacts/next/raw/oci-image-amd64.tar.gz?job=oci-image:x86_64-unknown-linux-musl) |
|
|
||||||
| `aarch64-unknown-linux-musl` | OCI image | [link](https://gitlab.com/api/v4/projects/famedly%2Fconduit/jobs/artifacts/next/raw/oci-image-arm64v8.tar.gz?job=oci-image:aarch64-unknown-linux-musl) |
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ sudo wget -O /usr/local/bin/matrix-conduit <url>
|
$ sudo wget -O /usr/local/bin/matrix-conduit <url>
|
||||||
$ sudo chmod +x /usr/local/bin/matrix-conduit
|
$ sudo chmod +x /usr/local/bin/matrix-conduit
|
||||||
```
|
```
|
||||||
|
|
||||||
Alternatively, you may compile the binary yourself. First, install any dependencies:
|
Alternatively, you may compile the binary yourself
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Debian
|
|
||||||
$ sudo apt install libclang-dev build-essential
|
$ sudo apt install libclang-dev build-essential
|
||||||
|
|
||||||
# RHEL
|
|
||||||
$ sudo dnf install clang
|
|
||||||
```
|
```
|
||||||
Then, `cd` into the source tree of conduit-next and run:
|
|
||||||
```bash
|
```bash
|
||||||
$ cargo build --release
|
$ cargo build --release
|
||||||
```
|
```
|
||||||
|
|
||||||
If you want to cross compile Conduit to another architecture, read the guide below.
|
|
||||||
|
|
||||||
<details>
|
If you want to cross compile Conduit to another architecture, read the [Cross-Compile Guide](cross/README.md).
|
||||||
<summary>Cross compilation</summary>
|
|
||||||
|
|
||||||
As easiest way to compile conduit for another platform [cross-rs](https://github.com/cross-rs/cross) is recommended, so install it first.
|
|
||||||
|
|
||||||
In order to use RockDB as storage backend append `-latomic` to linker flags.
|
|
||||||
|
|
||||||
For example, to build a binary for Raspberry Pi Zero W (ARMv6) you need `arm-unknown-linux-gnueabihf` as compilation
|
|
||||||
target.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git clone https://gitlab.com/famedly/conduit.git
|
|
||||||
cd conduit
|
|
||||||
export RUSTFLAGS='-C link-arg=-lgcc -Clink-arg=-latomic -Clink-arg=-static-libgcc'
|
|
||||||
cross build --release --no-default-features --features conduit_bin,backend_rocksdb,jemalloc --target=arm-unknown-linux-gnueabihf
|
|
||||||
```
|
|
||||||
</details>
|
|
||||||
|
|
||||||
## Adding a Conduit user
|
## Adding a Conduit user
|
||||||
|
|
||||||
While Conduit 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.
|
you to make sure that the file permissions are correctly set up.
|
||||||
|
|
||||||
In Debian or RHEL, you can use this command to create a Conduit user:
|
In Debian you can use this command to create a Conduit user:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo adduser --system conduit --group --disabled-login --no-create-home
|
sudo adduser --system conduit --no-create-home
|
||||||
```
|
```
|
||||||
|
|
||||||
## Forwarding ports in the firewall or the router
|
## Forwarding ports in the firewall or the router
|
||||||
|
@ -96,19 +63,6 @@ Conduit uses the ports 443 and 8448 both of which need to be open in the firewal
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
## Optional: Avoid port 8448
|
|
||||||
|
|
||||||
If Conduit runs behind Cloudflare reverse proxy, which doesn't support port 8448 on free plans, [delegation](https://matrix-org.github.io/synapse/latest/delegate.html) can be set up to have federation traffic routed to port 443:
|
|
||||||
```apache
|
|
||||||
# .well-known delegation on Apache
|
|
||||||
<Files "/.well-known/matrix/server">
|
|
||||||
ErrorDocument 200 '{"m.server": "your.server.name:443"}'
|
|
||||||
Header always set Content-Type application/json
|
|
||||||
Header always set Access-Control-Allow-Origin *
|
|
||||||
</Files>
|
|
||||||
```
|
|
||||||
[SRV DNS record](https://spec.matrix.org/latest/server-server-api/#resolving-server-names) delegation is also [possible](https://www.cloudflare.com/en-gb/learning/dns/dns-records/dns-srv-record/).
|
|
||||||
|
|
||||||
## Setting up a systemd service
|
## Setting up a systemd service
|
||||||
|
|
||||||
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
|
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
|
||||||
|
@ -123,7 +77,7 @@ After=network.target
|
||||||
[Service]
|
[Service]
|
||||||
Environment="CONDUIT_CONFIG=/etc/matrix-conduit/conduit.toml"
|
Environment="CONDUIT_CONFIG=/etc/matrix-conduit/conduit.toml"
|
||||||
User=conduit
|
User=conduit
|
||||||
Group=conduit
|
Group=nogroup
|
||||||
Restart=always
|
Restart=always
|
||||||
ExecStart=/usr/local/bin/matrix-conduit
|
ExecStart=/usr/local/bin/matrix-conduit
|
||||||
|
|
||||||
|
@ -178,13 +132,11 @@ max_request_size = 20_000_000 # in bytes
|
||||||
allow_registration = true
|
allow_registration = true
|
||||||
|
|
||||||
allow_federation = true
|
allow_federation = true
|
||||||
allow_check_for_updates = true
|
|
||||||
|
|
||||||
# Server to get public keys from. You probably shouldn't change this
|
|
||||||
trusted_servers = ["matrix.org"]
|
trusted_servers = ["matrix.org"]
|
||||||
|
|
||||||
#max_concurrent_requests = 100 # How many requests Conduit sends to other servers at the same time
|
#max_concurrent_requests = 100 # How many requests Conduit sends to other servers at the same time
|
||||||
#log = "warn,state_res=warn,rocket=off,_=off,sled=off"
|
#log = "info,state_res=warn,rocket=off,_=off,sled=off"
|
||||||
|
|
||||||
address = "127.0.0.1" # This makes sure Conduit can only be reached using the reverse proxy
|
address = "127.0.0.1" # This makes sure Conduit can only be reached using the reverse proxy
|
||||||
#address = "0.0.0.0" # If Conduit is running in a container, make sure the reverse proxy (ie. Traefik) can reach it.
|
#address = "0.0.0.0" # If Conduit is running in a container, make sure the reverse proxy (ie. Traefik) can reach it.
|
||||||
|
@ -193,7 +145,7 @@ address = "127.0.0.1" # This makes sure Conduit can only be reached using the re
|
||||||
## Setting the correct file permissions
|
## Setting the correct file permissions
|
||||||
|
|
||||||
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
|
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:
|
Debian:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo chown -R root:root /etc/matrix-conduit
|
sudo chown -R root:root /etc/matrix-conduit
|
||||||
|
@ -204,7 +156,7 @@ If you use the default database path you also need to run this:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo mkdir -p /var/lib/matrix-conduit/
|
sudo mkdir -p /var/lib/matrix-conduit/
|
||||||
sudo chown -R conduit:conduit /var/lib/matrix-conduit/
|
sudo chown -R conduit:nogroup /var/lib/matrix-conduit/
|
||||||
sudo chmod 700 /var/lib/matrix-conduit/
|
sudo chmod 700 /var/lib/matrix-conduit/
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -217,11 +169,6 @@ This depends on whether you use Apache, Caddy, Nginx or another web server.
|
||||||
Create `/etc/apache2/sites-enabled/050-conduit.conf` and copy-and-paste this:
|
Create `/etc/apache2/sites-enabled/050-conduit.conf` and copy-and-paste this:
|
||||||
|
|
||||||
```apache
|
```apache
|
||||||
# Requires mod_proxy and mod_proxy_http
|
|
||||||
#
|
|
||||||
# On Apache instance compiled from source,
|
|
||||||
# paste into httpd-ssl.conf or httpd.conf
|
|
||||||
|
|
||||||
Listen 8448
|
Listen 8448
|
||||||
|
|
||||||
<VirtualHost *:443 *:8448>
|
<VirtualHost *:443 *:8448>
|
||||||
|
@ -229,7 +176,7 @@ Listen 8448
|
||||||
ServerName your.server.name # EDIT THIS
|
ServerName your.server.name # EDIT THIS
|
||||||
|
|
||||||
AllowEncodedSlashes NoDecode
|
AllowEncodedSlashes NoDecode
|
||||||
ProxyPass /_matrix/ http://127.0.0.1:6167/_matrix/ timeout=300 nocanon
|
ProxyPass /_matrix/ http://127.0.0.1:6167/_matrix/ nocanon
|
||||||
ProxyPassReverse /_matrix/ http://127.0.0.1:6167/_matrix/
|
ProxyPassReverse /_matrix/ http://127.0.0.1:6167/_matrix/
|
||||||
|
|
||||||
</VirtualHost>
|
</VirtualHost>
|
||||||
|
@ -238,29 +185,22 @@ ProxyPassReverse /_matrix/ http://127.0.0.1:6167/_matrix/
|
||||||
**You need to make some edits again.** When you are done, run
|
**You need to make some edits again.** When you are done, run
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Debian
|
|
||||||
$ sudo systemctl reload apache2
|
$ sudo systemctl reload apache2
|
||||||
|
|
||||||
# Installed from source
|
|
||||||
$ sudo apachectl -k graceful
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Caddy
|
### Caddy
|
||||||
|
|
||||||
Create `/etc/caddy/conf.d/conduit_caddyfile` and enter this (substitute for your server name).
|
Create `/etc/caddy/conf.d/conduit_caddyfile` and enter this (substitute for your server name).
|
||||||
|
|
||||||
```caddy
|
```caddy
|
||||||
your.server.name, your.server.name:8448 {
|
your.server.name, your.server.name:8448 {
|
||||||
reverse_proxy /_matrix/* 127.0.0.1:6167
|
reverse_proxy /_matrix/* 127.0.0.1:6167
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
That's it! Just start or enable the service and you're set.
|
That's it! Just start or enable the service and you're set.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ sudo systemctl enable caddy
|
$ sudo systemctl enable caddy
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
### Nginx
|
### Nginx
|
||||||
|
|
||||||
If you use Nginx and not Apache, add the following server section inside the http section of `/etc/nginx/nginx.conf`
|
If you use Nginx and not Apache, add the following server section inside the http section of `/etc/nginx/nginx.conf`
|
||||||
|
@ -274,15 +214,10 @@ server {
|
||||||
server_name your.server.name; # EDIT THIS
|
server_name your.server.name; # EDIT THIS
|
||||||
merge_slashes off;
|
merge_slashes off;
|
||||||
|
|
||||||
# Nginx defaults to only allow 1MB uploads
|
|
||||||
# Increase this to allow posting large files such as videos
|
|
||||||
client_max_body_size 20M;
|
|
||||||
|
|
||||||
location /_matrix/ {
|
location /_matrix/ {
|
||||||
proxy_pass http://127.0.0.1:6167;
|
proxy_pass http://127.0.0.1:6167$request_uri;
|
||||||
proxy_set_header Host $http_host;
|
proxy_set_header Host $http_host;
|
||||||
proxy_buffering off;
|
proxy_buffering off;
|
||||||
proxy_read_timeout 5m;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ssl_certificate /etc/letsencrypt/live/your.server.name/fullchain.pem; # EDIT THIS
|
ssl_certificate /etc/letsencrypt/live/your.server.name/fullchain.pem; # EDIT THIS
|
||||||
|
@ -302,19 +237,11 @@ $ sudo systemctl reload nginx
|
||||||
|
|
||||||
If you chose Caddy as your web proxy SSL certificates are handled automatically and you can skip this step.
|
If you chose Caddy as your web proxy SSL certificates are handled automatically and you can skip this step.
|
||||||
|
|
||||||
The easiest way to get an SSL certificate, if you don't have one already, is to [install](https://certbot.eff.org/instructions) `certbot` and run this:
|
The easiest way to get an SSL certificate, if you don't have one already, is to install `certbot` and run this:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# To use ECC for the private key,
|
|
||||||
# paste into /etc/letsencrypt/cli.ini:
|
|
||||||
# key-type = ecdsa
|
|
||||||
# elliptic-curve = secp384r1
|
|
||||||
|
|
||||||
$ sudo certbot -d your.server.name
|
$ sudo certbot -d your.server.name
|
||||||
```
|
```
|
||||||
[Automated renewal](https://eff-certbot.readthedocs.io/en/stable/using.html#automated-renewals) is usually preconfigured.
|
|
||||||
|
|
||||||
If using Cloudflare, configure instead the edge and origin certificates in dashboard. In case you’re already running a website on the same Apache server, you can just copy-and-paste the SSL configuration from your main virtual host on port 443 into the above-mentioned vhost.
|
|
||||||
|
|
||||||
## You're done!
|
## You're done!
|
||||||
|
|
||||||
|
@ -338,8 +265,6 @@ You can also use these commands as a quick health check.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ curl https://your.server.name/_matrix/client/versions
|
$ curl https://your.server.name/_matrix/client/versions
|
||||||
|
|
||||||
# If using port 8448
|
|
||||||
$ curl https://your.server.name:8448/_matrix/client/versions
|
$ curl https://your.server.name:8448/_matrix/client/versions
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
83
Dockerfile
Normal file
83
Dockerfile
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
FROM docker.io/rust:1.58-bullseye AS builder
|
||||||
|
WORKDIR /usr/src/conduit
|
||||||
|
|
||||||
|
# Install required packages to build Conduit and it's dependencies
|
||||||
|
RUN apt-get update && \
|
||||||
|
apt-get -y --no-install-recommends install libclang-dev=1:11.0-51+nmu5
|
||||||
|
|
||||||
|
# == Build dependencies without our own code separately for caching ==
|
||||||
|
#
|
||||||
|
# Need a fake main.rs since Cargo refuses to build anything otherwise.
|
||||||
|
#
|
||||||
|
# See https://github.com/rust-lang/cargo/issues/2644 for a Cargo feature
|
||||||
|
# request that would allow just dependencies to be compiled, presumably
|
||||||
|
# regardless of whether source files are available.
|
||||||
|
RUN mkdir src && touch src/lib.rs && echo 'fn main() {}' > src/main.rs
|
||||||
|
COPY Cargo.toml Cargo.lock ./
|
||||||
|
RUN cargo build --release && rm -r src
|
||||||
|
|
||||||
|
# Copy over actual Conduit sources
|
||||||
|
COPY src src
|
||||||
|
|
||||||
|
# main.rs and lib.rs need their timestamp updated for this to work correctly since
|
||||||
|
# otherwise the build with the fake main.rs from above is newer than the
|
||||||
|
# source files (COPY preserves timestamps).
|
||||||
|
#
|
||||||
|
# Builds conduit and places the binary at /usr/src/conduit/target/release/conduit
|
||||||
|
RUN touch src/main.rs && touch src/lib.rs && cargo build --release
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------------------------------------------
|
||||||
|
# Stuff below this line actually ends up in the resulting docker image
|
||||||
|
# ---------------------------------------------------------------------------------------------------------------
|
||||||
|
FROM docker.io/debian:bullseye-slim AS runner
|
||||||
|
|
||||||
|
# Standard port on which Conduit launches.
|
||||||
|
# You still need to map the port when using the docker command or docker-compose.
|
||||||
|
EXPOSE 6167
|
||||||
|
|
||||||
|
ENV CONDUIT_PORT=6167 \
|
||||||
|
CONDUIT_ADDRESS="0.0.0.0" \
|
||||||
|
CONDUIT_DATABASE_PATH=/var/lib/matrix-conduit \
|
||||||
|
CONDUIT_CONFIG=''
|
||||||
|
# └─> Set no config file to do all configuration with env vars
|
||||||
|
|
||||||
|
# Conduit needs:
|
||||||
|
# ca-certificates: for https
|
||||||
|
# iproute2 & wget: for the healthcheck script
|
||||||
|
RUN apt-get update && apt-get -y --no-install-recommends install \
|
||||||
|
ca-certificates \
|
||||||
|
iproute2 \
|
||||||
|
wget \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Created directory for the database and media files
|
||||||
|
RUN mkdir -p /srv/conduit/.local/share/conduit
|
||||||
|
|
||||||
|
# Test if Conduit is still alive, uses the same endpoint as Element
|
||||||
|
COPY ./docker/healthcheck.sh /srv/conduit/healthcheck.sh
|
||||||
|
HEALTHCHECK --start-period=5s --interval=5s CMD ./healthcheck.sh
|
||||||
|
|
||||||
|
# Copy over the actual Conduit binary from the builder stage
|
||||||
|
COPY --from=builder /usr/src/conduit/target/release/conduit /srv/conduit/conduit
|
||||||
|
|
||||||
|
# Improve security: Don't run stuff as root, that does not need to run as root
|
||||||
|
# Most distros also use 1000:1000 for the first real user, so this should resolve volume mounting problems.
|
||||||
|
ARG USER_ID=1000
|
||||||
|
ARG GROUP_ID=1000
|
||||||
|
RUN set -x ; \
|
||||||
|
groupadd -r -g ${GROUP_ID} conduit ; \
|
||||||
|
useradd -l -r -M -d /srv/conduit -o -u ${USER_ID} -g conduit conduit && exit 0 ; exit 1
|
||||||
|
|
||||||
|
# Change ownership of Conduit files to conduit user and group and make the healthcheck executable:
|
||||||
|
RUN chown -cR conduit:conduit /srv/conduit && \
|
||||||
|
chmod +x /srv/conduit/healthcheck.sh
|
||||||
|
|
||||||
|
# Change user to conduit, no root permissions afterwards:
|
||||||
|
USER conduit
|
||||||
|
# Set container home directory
|
||||||
|
WORKDIR /srv/conduit
|
||||||
|
|
||||||
|
# Run Conduit and print backtraces on panics
|
||||||
|
ENV RUST_BACKTRACE=1
|
||||||
|
ENTRYPOINT [ "/srv/conduit/conduit" ]
|
30
README.md
30
README.md
|
@ -1,11 +1,6 @@
|
||||||
# Conduit
|
# Conduit
|
||||||
### A Matrix homeserver written in Rust
|
|
||||||
|
|
||||||
#### What is Matrix?
|
### A Matrix homeserver written in Rust
|
||||||
[Matrix](https://matrix.org) is an open network for secure and decentralized
|
|
||||||
communication. Users from every Matrix homeserver can chat with users from all
|
|
||||||
other Matrix servers. You can even use bridges (also called Matrix appservices)
|
|
||||||
to communicate with users outside of Matrix, like a community on Discord.
|
|
||||||
|
|
||||||
#### What is the goal?
|
#### What is the goal?
|
||||||
|
|
||||||
|
@ -16,9 +11,11 @@ friends or company.
|
||||||
#### Can I try it out?
|
#### Can I try it out?
|
||||||
|
|
||||||
Yes! You can test our Conduit instance by opening a Matrix client (<https://app.element.io> or Element Android for
|
Yes! You can test our Conduit instance by opening a Matrix client (<https://app.element.io> or Element Android for
|
||||||
example) and registering on the `conduit.rs` homeserver. The registration token is "for_testing_only". Don't share personal information.
|
example) and registering on the `conduit.rs` homeserver.
|
||||||
|
|
||||||
Server hosting for conduit.rs is donated by the Matrix.org Foundation.
|
It is hosted on a ODROID HC 2 with 2GB RAM and a SAMSUNG Exynos 5422 CPU, which
|
||||||
|
was used in the Samsung Galaxy S5. It joined many big rooms including Matrix
|
||||||
|
HQ.
|
||||||
|
|
||||||
#### What is the current status?
|
#### What is the current status?
|
||||||
|
|
||||||
|
@ -28,16 +25,15 @@ from time to time.
|
||||||
|
|
||||||
There are still a few important features missing:
|
There are still a few important features missing:
|
||||||
|
|
||||||
- E2EE emoji comparison over federation (E2EE chat works)
|
- E2EE verification over federation
|
||||||
- Outgoing read receipts, typing, presence over federation (incoming works)
|
- Outgoing read receipts, typing, presence over federation
|
||||||
|
|
||||||
Check out the [Conduit 1.0 Release Milestone](https://gitlab.com/famedly/conduit/-/milestones/3).
|
Check out the [Conduit 1.0 Release Milestone](https://gitlab.com/famedly/conduit/-/milestones/3).
|
||||||
|
|
||||||
#### How can I deploy my own?
|
#### How can I deploy my own?
|
||||||
|
|
||||||
- Simple install (this was tested the most): [DEPLOY.md](DEPLOY.md)
|
- Simple install (this was tested the most): [DEPLOY.md](DEPLOY.md)
|
||||||
- Debian package: [debian/README.md](debian/README.md)
|
- Debian package: [debian/README.Debian](debian/README.Debian)
|
||||||
- Nix/NixOS: [nix/README.md](nix/README.md)
|
|
||||||
- Docker: [docker/README.md](docker/README.md)
|
- Docker: [docker/README.md](docker/README.md)
|
||||||
|
|
||||||
If you want to connect an Appservice to Conduit, take a look at [APPSERVICES.md](APPSERVICES.md).
|
If you want to connect an Appservice to Conduit, take a look at [APPSERVICES.md](APPSERVICES.md).
|
||||||
|
@ -53,21 +49,13 @@ If you want to connect an Appservice to Conduit, take a look at [APPSERVICES.md]
|
||||||
|
|
||||||
#### Thanks to
|
#### Thanks to
|
||||||
|
|
||||||
Thanks to FUTO, Famedly, Prototype Fund (DLR and German BMBF) and all individuals for financially supporting this project.
|
Thanks to Famedly, Prototype Fund (DLR and German BMBF) and all other individuals for financially supporting this project.
|
||||||
|
|
||||||
Thanks to the contributors to Conduit and all libraries we use, for example:
|
Thanks to the contributors to Conduit and all libraries we use, for example:
|
||||||
|
|
||||||
- Ruma: A clean library for the Matrix Spec in Rust
|
- Ruma: A clean library for the Matrix Spec in Rust
|
||||||
- axum: A modular web framework
|
- axum: A modular web framework
|
||||||
|
|
||||||
#### Contact
|
|
||||||
|
|
||||||
If you run into any question, feel free to
|
|
||||||
- Ask us in `#conduit:fachschaften.org` on Matrix
|
|
||||||
- Write an E-Mail to `conduit@koesters.xyz`
|
|
||||||
- Send an direct message to `timokoesters@fachschaften.org` on Matrix
|
|
||||||
- [Open an issue on GitLab](https://gitlab.com/famedly/conduit/-/issues/new)
|
|
||||||
|
|
||||||
#### Donate
|
#### Donate
|
||||||
|
|
||||||
Liberapay: <https://liberapay.com/timokoesters/>\
|
Liberapay: <https://liberapay.com/timokoesters/>\
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
# Path to Complement's source code
|
|
||||||
COMPLEMENT_SRC="$1"
|
|
||||||
|
|
||||||
# A `.jsonl` file to write test logs to
|
|
||||||
LOG_FILE="$2"
|
|
||||||
|
|
||||||
# A `.jsonl` file to write test results to
|
|
||||||
RESULTS_FILE="$3"
|
|
||||||
|
|
||||||
OCI_IMAGE="complement-conduit:dev"
|
|
||||||
|
|
||||||
env \
|
|
||||||
-C "$(git rev-parse --show-toplevel)" \
|
|
||||||
docker build \
|
|
||||||
--tag "$OCI_IMAGE" \
|
|
||||||
--file 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 -json ./tests | tee "$LOG_FILE"
|
|
||||||
set -o pipefail
|
|
||||||
|
|
||||||
# Post-process the results into an easy-to-compare format
|
|
||||||
cat "$LOG_FILE" | jq -c '
|
|
||||||
select(
|
|
||||||
(.Action == "pass" or .Action == "fail" or .Action == "skip")
|
|
||||||
and .Test != null
|
|
||||||
) | {Action: .Action, Test: .Test}
|
|
||||||
' | sort > "$RESULTS_FILE"
|
|
|
@ -1,31 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
# The first argument must be the desired installable
|
|
||||||
INSTALLABLE="$1"
|
|
||||||
|
|
||||||
# Build the installable and forward any other arguments too
|
|
||||||
nix build "$@"
|
|
||||||
|
|
||||||
if [ ! -z ${ATTIC_TOKEN+x} ]; then
|
|
||||||
|
|
||||||
nix run --inputs-from . attic -- login \
|
|
||||||
conduit \
|
|
||||||
https://nix.computer.surgery/conduit \
|
|
||||||
"$ATTIC_TOKEN"
|
|
||||||
|
|
||||||
push_args=(
|
|
||||||
# Attic and its build dependencies
|
|
||||||
"$(nix path-info --inputs-from . attic)"
|
|
||||||
"$(nix path-info --inputs-from . attic --derivation)"
|
|
||||||
|
|
||||||
# The target installable and its build dependencies
|
|
||||||
"$(nix path-info "$INSTALLABLE" --derivation)"
|
|
||||||
"$(nix path-info "$INSTALLABLE")"
|
|
||||||
)
|
|
||||||
|
|
||||||
nix run --inputs-from . attic -- push conduit "${push_args[@]}"
|
|
||||||
else
|
|
||||||
echo "\$ATTIC_TOKEN is unset, skipping uploading to the binary cache"
|
|
||||||
fi
|
|
|
@ -1,45 +0,0 @@
|
||||||
FROM rust:1.75.0
|
|
||||||
|
|
||||||
WORKDIR /workdir
|
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
||||||
libclang-dev
|
|
||||||
|
|
||||||
COPY Cargo.toml Cargo.toml
|
|
||||||
COPY Cargo.lock Cargo.lock
|
|
||||||
COPY src src
|
|
||||||
RUN cargo build --release \
|
|
||||||
&& mv target/release/conduit conduit \
|
|
||||||
&& rm -rf target
|
|
||||||
|
|
||||||
# Install caddy
|
|
||||||
RUN apt-get update \
|
|
||||||
&& apt-get install -y \
|
|
||||||
debian-keyring \
|
|
||||||
debian-archive-keyring \
|
|
||||||
apt-transport-https \
|
|
||||||
curl \
|
|
||||||
&& curl -1sLf 'https://dl.cloudsmith.io/public/caddy/testing/gpg.key' \
|
|
||||||
| gpg --dearmor -o /usr/share/keyrings/caddy-testing-archive-keyring.gpg \
|
|
||||||
&& curl -1sLf 'https://dl.cloudsmith.io/public/caddy/testing/debian.deb.txt' \
|
|
||||||
| tee /etc/apt/sources.list.d/caddy-testing.list \
|
|
||||||
&& apt-get update \
|
|
||||||
&& apt-get install -y caddy
|
|
||||||
|
|
||||||
COPY conduit-example.toml conduit.toml
|
|
||||||
COPY complement/caddy.json caddy.json
|
|
||||||
|
|
||||||
ENV SERVER_NAME=localhost
|
|
||||||
ENV CONDUIT_CONFIG=/workdir/conduit.toml
|
|
||||||
|
|
||||||
RUN sed -i "s/port = 6167/port = 8008/g" conduit.toml
|
|
||||||
RUN echo "log = \"warn,_=off,sled=off\"" >> conduit.toml
|
|
||||||
RUN sed -i "s/address = \"127.0.0.1\"/address = \"0.0.0.0\"/g" conduit.toml
|
|
||||||
|
|
||||||
EXPOSE 8008 8448
|
|
||||||
|
|
||||||
CMD uname -a && \
|
|
||||||
sed -i "s/#server_name = \"your.server.name\"/server_name = \"${SERVER_NAME}\"/g" conduit.toml && \
|
|
||||||
sed -i "s/your.server.name/${SERVER_NAME}/g" caddy.json && \
|
|
||||||
caddy start --config caddy.json > /dev/null && \
|
|
||||||
/workdir/conduit
|
|
|
@ -1,11 +0,0 @@
|
||||||
# Complement
|
|
||||||
|
|
||||||
## What's that?
|
|
||||||
|
|
||||||
Have a look at [its repository](https://github.com/matrix-org/complement).
|
|
||||||
|
|
||||||
## How do I use it with Conduit?
|
|
||||||
|
|
||||||
The script at [`../bin/complement`](../bin/complement) has automation for this.
|
|
||||||
It takes a few command line arguments, you can read the script to find out what
|
|
||||||
those are.
|
|
|
@ -1,72 +0,0 @@
|
||||||
{
|
|
||||||
"logging": {
|
|
||||||
"logs": {
|
|
||||||
"default": {
|
|
||||||
"level": "WARN"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"apps": {
|
|
||||||
"http": {
|
|
||||||
"https_port": 8448,
|
|
||||||
"servers": {
|
|
||||||
"srv0": {
|
|
||||||
"listen": [":8448"],
|
|
||||||
"routes": [{
|
|
||||||
"match": [{
|
|
||||||
"host": ["your.server.name"]
|
|
||||||
}],
|
|
||||||
"handle": [{
|
|
||||||
"handler": "subroute",
|
|
||||||
"routes": [{
|
|
||||||
"handle": [{
|
|
||||||
"handler": "reverse_proxy",
|
|
||||||
"upstreams": [{
|
|
||||||
"dial": "127.0.0.1:8008"
|
|
||||||
}]
|
|
||||||
}]
|
|
||||||
}]
|
|
||||||
}],
|
|
||||||
"terminal": true
|
|
||||||
}],
|
|
||||||
"tls_connection_policies": [{
|
|
||||||
"match": {
|
|
||||||
"sni": ["your.server.name"]
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"pki": {
|
|
||||||
"certificate_authorities": {
|
|
||||||
"local": {
|
|
||||||
"name": "Complement CA",
|
|
||||||
"root": {
|
|
||||||
"certificate": "/complement/ca/ca.crt",
|
|
||||||
"private_key": "/complement/ca/ca.key"
|
|
||||||
},
|
|
||||||
"intermediate": {
|
|
||||||
"certificate": "/complement/ca/ca.crt",
|
|
||||||
"private_key": "/complement/ca/ca.key"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"tls": {
|
|
||||||
"automation": {
|
|
||||||
"policies": [{
|
|
||||||
"subjects": ["your.server.name"],
|
|
||||||
"issuers": [{
|
|
||||||
"module": "internal"
|
|
||||||
}],
|
|
||||||
"on_demand": true
|
|
||||||
}, {
|
|
||||||
"issuers": [{
|
|
||||||
"module": "internal",
|
|
||||||
"ca": "local"
|
|
||||||
}]
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -39,19 +39,11 @@ max_request_size = 20_000_000 # in bytes
|
||||||
allow_registration = true
|
allow_registration = true
|
||||||
|
|
||||||
allow_federation = true
|
allow_federation = true
|
||||||
allow_check_for_updates = true
|
|
||||||
|
|
||||||
# Enable the display name lightning bolt on registration.
|
|
||||||
enable_lightning_bolt = true
|
|
||||||
|
|
||||||
# Servers listed here will be used to gather public keys of other servers.
|
|
||||||
# Generally, copying this exactly should be enough. (Currently, Conduit doesn't
|
|
||||||
# support batched key requests, so this list should only contain Synapse
|
|
||||||
# servers.)
|
|
||||||
trusted_servers = ["matrix.org"]
|
trusted_servers = ["matrix.org"]
|
||||||
|
|
||||||
#max_concurrent_requests = 100 # How many requests Conduit sends to other servers at the same time
|
#max_concurrent_requests = 100 # How many requests Conduit sends to other servers at the same time
|
||||||
#log = "warn,state_res=warn,rocket=off,_=off,sled=off"
|
#log = "info,state_res=warn,rocket=off,_=off,sled=off"
|
||||||
|
|
||||||
address = "127.0.0.1" # This makes sure Conduit can only be reached using the reverse proxy
|
address = "127.0.0.1" # This makes sure Conduit can only be reached using the reverse proxy
|
||||||
#address = "0.0.0.0" # If Conduit is running in a container, make sure the reverse proxy (ie. Traefik) can reach it.
|
#address = "0.0.0.0" # If Conduit is running in a container, make sure the reverse proxy (ie. Traefik) can reach it.
|
||||||
|
|
18
debian/README.md → debian/README.Debian
vendored
18
debian/README.md → debian/README.Debian
vendored
|
@ -1,36 +1,28 @@
|
||||||
Conduit for Debian
|
Conduit for Debian
|
||||||
==================
|
==================
|
||||||
|
|
||||||
Installation
|
|
||||||
------------
|
|
||||||
|
|
||||||
Information about downloading, building and deploying the Debian package, see
|
|
||||||
the "Installing Conduit" section in [DEPLOY.md](../DEPLOY.md).
|
|
||||||
All following sections until "Setting up the Reverse Proxy" be ignored because
|
|
||||||
this is handled automatically by the packaging.
|
|
||||||
|
|
||||||
Configuration
|
Configuration
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
When installed, Debconf generates the configuration of the homeserver
|
When installed, Debconf generates the configuration of the homeserver
|
||||||
(host)name, the address and port it listens on. This configuration ends up in
|
(host)name, the address and port it listens on. This configuration ends up in
|
||||||
`/etc/matrix-conduit/conduit.toml`.
|
/etc/matrix-conduit/conduit.toml.
|
||||||
|
|
||||||
You can tweak more detailed settings by uncommenting and setting the variables
|
You can tweak more detailed settings by uncommenting and setting the variables
|
||||||
in `/etc/matrix-conduit/conduit.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.
|
file size for download/upload, enabling federation, etc.
|
||||||
|
|
||||||
Running
|
Running
|
||||||
-------
|
-------
|
||||||
|
|
||||||
The package uses the `matrix-conduit.service` systemd unit file to start and
|
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
|
stop Conduit. It loads the configuration file mentioned above to set up the
|
||||||
environment before running the server.
|
environment before running the server.
|
||||||
|
|
||||||
This package assumes by default that Conduit will be placed behind a reverse
|
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
|
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
|
on 127.0.0.1 and the free port 6167 and is reachable via a client using the URL
|
||||||
<http://localhost:6167>.
|
http://localhost:6167.
|
||||||
|
|
||||||
At a later stage this packaging may support also setting up TLS and running
|
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
|
stand-alone. In this case, however, you need to set up some certificates and
|
9
debian/postinst
vendored
9
debian/postinst
vendored
|
@ -19,11 +19,11 @@ case "$1" in
|
||||||
_matrix-conduit
|
_matrix-conduit
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Create the database path if it does not exist yet and fix up ownership
|
# Create the database path if it does not exist yet.
|
||||||
# and permissions.
|
if [ ! -d "$CONDUIT_DATABASE_PATH" ]; then
|
||||||
mkdir -p "$CONDUIT_DATABASE_PATH"
|
mkdir -p "$CONDUIT_DATABASE_PATH"
|
||||||
chown _matrix-conduit "$CONDUIT_DATABASE_PATH"
|
chown _matrix-conduit "$CONDUIT_DATABASE_PATH"
|
||||||
chmod 700 "$CONDUIT_DATABASE_PATH"
|
fi
|
||||||
|
|
||||||
if [ ! -e "$CONDUIT_CONFIG_FILE" ]; then
|
if [ ! -e "$CONDUIT_CONFIG_FILE" ]; then
|
||||||
# Write the debconf values in the config.
|
# Write the debconf values in the config.
|
||||||
|
@ -73,12 +73,11 @@ max_request_size = 20_000_000 # in bytes
|
||||||
allow_registration = true
|
allow_registration = true
|
||||||
|
|
||||||
allow_federation = true
|
allow_federation = true
|
||||||
allow_check_for_updates = true
|
|
||||||
|
|
||||||
trusted_servers = ["matrix.org"]
|
trusted_servers = ["matrix.org"]
|
||||||
|
|
||||||
#max_concurrent_requests = 100 # How many requests Conduit sends to other servers at the same time
|
#max_concurrent_requests = 100 # How many requests Conduit sends to other servers at the same time
|
||||||
#log = "warn,state_res=warn,rocket=off,_=off,sled=off"
|
#log = "info,state_res=warn,rocket=off,_=off,sled=off"
|
||||||
EOF
|
EOF
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
|
|
10
default.nix
10
default.nix
|
@ -1,10 +0,0 @@
|
||||||
(import
|
|
||||||
(
|
|
||||||
let lock = builtins.fromJSON (builtins.readFile ./flake.lock); in
|
|
||||||
fetchTarball {
|
|
||||||
url = lock.nodes.flake-compat.locked.url or "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz";
|
|
||||||
sha256 = lock.nodes.flake-compat.locked.narHash;
|
|
||||||
}
|
|
||||||
)
|
|
||||||
{ src = ./.; }
|
|
||||||
).defaultNix
|
|
|
@ -29,10 +29,9 @@ services:
|
||||||
CONDUIT_MAX_REQUEST_SIZE: 20_000_000 # in bytes, ~20 MB
|
CONDUIT_MAX_REQUEST_SIZE: 20_000_000 # in bytes, ~20 MB
|
||||||
CONDUIT_ALLOW_REGISTRATION: 'true'
|
CONDUIT_ALLOW_REGISTRATION: 'true'
|
||||||
CONDUIT_ALLOW_FEDERATION: 'true'
|
CONDUIT_ALLOW_FEDERATION: 'true'
|
||||||
CONDUIT_ALLOW_CHECK_FOR_UPDATES: 'true'
|
|
||||||
CONDUIT_TRUSTED_SERVERS: '["matrix.org"]'
|
CONDUIT_TRUSTED_SERVERS: '["matrix.org"]'
|
||||||
#CONDUIT_MAX_CONCURRENT_REQUESTS: 100
|
#CONDUIT_MAX_CONCURRENT_REQUESTS: 100
|
||||||
#CONDUIT_LOG: warn,rocket=off,_=off,sled=off
|
#CONDUIT_LOG: info,rocket=off,_=off,sled=off
|
||||||
CONDUIT_ADDRESS: 0.0.0.0
|
CONDUIT_ADDRESS: 0.0.0.0
|
||||||
CONDUIT_CONFIG: '' # Ignore this
|
CONDUIT_CONFIG: '' # Ignore this
|
||||||
#
|
#
|
116
docker/README.md
116
docker/README.md
|
@ -4,36 +4,7 @@
|
||||||
|
|
||||||
## Docker
|
## Docker
|
||||||
|
|
||||||
To run Conduit with Docker you can either build the image yourself or pull it from a registry.
|
### Build & Dockerfile
|
||||||
|
|
||||||
|
|
||||||
### Use a registry
|
|
||||||
|
|
||||||
OCI images for Conduit are available in the registries listed below. We recommend using the image tagged as `latest` from GitLab's own registry.
|
|
||||||
|
|
||||||
| Registry | Image | Size | Notes |
|
|
||||||
| --------------- | --------------------------------------------------------------- | ----------------------------- | ---------------------- |
|
|
||||||
| GitLab Registry | [registry.gitlab.com/famedly/conduit/matrix-conduit:latest][gl] | ![Image Size][shield-latest] | Stable image. |
|
|
||||||
| Docker Hub | [docker.io/matrixconduit/matrix-conduit:latest][dh] | ![Image Size][shield-latest] | Stable image. |
|
|
||||||
| GitLab Registry | [registry.gitlab.com/famedly/conduit/matrix-conduit:next][gl] | ![Image Size][shield-next] | Development version. |
|
|
||||||
| Docker Hub | [docker.io/matrixconduit/matrix-conduit:next][dh] | ![Image Size][shield-next] | Development version. |
|
|
||||||
|
|
||||||
|
|
||||||
[dh]: https://hub.docker.com/r/matrixconduit/matrix-conduit
|
|
||||||
[gl]: https://gitlab.com/famedly/conduit/container_registry/2497937
|
|
||||||
[shield-latest]: https://img.shields.io/docker/image-size/matrixconduit/matrix-conduit/latest
|
|
||||||
[shield-next]: https://img.shields.io/docker/image-size/matrixconduit/matrix-conduit/next
|
|
||||||
|
|
||||||
|
|
||||||
Use
|
|
||||||
```bash
|
|
||||||
docker image pull <link>
|
|
||||||
```
|
|
||||||
to pull it to your machine.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Build using a dockerfile
|
|
||||||
|
|
||||||
The Dockerfile provided by Conduit has two stages, each of which creates an image.
|
The Dockerfile provided by Conduit has two stages, each of which creates an image.
|
||||||
|
|
||||||
|
@ -48,11 +19,9 @@ docker build --tag matrixconduit/matrix-conduit:latest .
|
||||||
|
|
||||||
which also will tag the resulting image as `matrixconduit/matrix-conduit:latest`.
|
which also will tag the resulting image as `matrixconduit/matrix-conduit:latest`.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Run
|
### Run
|
||||||
|
|
||||||
When you have the image you can simply run it with
|
After building the image you can simply run it with
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run -d -p 8448:6167 \
|
docker run -d -p 8448:6167 \
|
||||||
|
@ -64,11 +33,20 @@ docker run -d -p 8448:6167 \
|
||||||
-e CONDUIT_MAX_REQUEST_SIZE="20_000_000" \
|
-e CONDUIT_MAX_REQUEST_SIZE="20_000_000" \
|
||||||
-e CONDUIT_TRUSTED_SERVERS="[\"matrix.org\"]" \
|
-e CONDUIT_TRUSTED_SERVERS="[\"matrix.org\"]" \
|
||||||
-e CONDUIT_MAX_CONCURRENT_REQUESTS="100" \
|
-e CONDUIT_MAX_CONCURRENT_REQUESTS="100" \
|
||||||
-e CONDUIT_LOG="warn,rocket=off,_=off,sled=off" \
|
-e CONDUIT_LOG="info,rocket=off,_=off,sled=off" \
|
||||||
--name conduit <link>
|
--name conduit matrixconduit/matrix-conduit:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
or you can use [docker-compose](#docker-compose).
|
or you can skip the build step and pull the image from one of the following registries:
|
||||||
|
|
||||||
|
| Registry | Image | Size |
|
||||||
|
| --------------- | --------------------------------------------------------------- | --------------------- |
|
||||||
|
| Docker Hub | [matrixconduit/matrix-conduit:latest][dh] | ![Image Size][shield] |
|
||||||
|
| GitLab Registry | [registry.gitlab.com/famedly/conduit/matrix-conduit:latest][gl] | ![Image Size][shield] |
|
||||||
|
|
||||||
|
[dh]: https://hub.docker.com/r/matrixconduit/matrix-conduit
|
||||||
|
[gl]: https://gitlab.com/famedly/conduit/container_registry/2497937
|
||||||
|
[shield]: https://img.shields.io/docker/image-size/matrixconduit/matrix-conduit/latest
|
||||||
|
|
||||||
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](../conduit-example.toml).
|
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](../conduit-example.toml).
|
||||||
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
|
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
|
||||||
|
@ -76,7 +54,7 @@ to pass `-e CONDUIT_CONFIG=""` into your container. For an overview of possible
|
||||||
|
|
||||||
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.
|
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
|
## Docker-compose
|
||||||
|
|
||||||
If the `docker run` command is not for you or your setup, you can also use one of the provided `docker-compose` files.
|
If the `docker run` command is not for you or your setup, you can also use one of the provided `docker-compose` files.
|
||||||
|
|
||||||
|
@ -117,7 +95,7 @@ As a container user, you probably know about Traefik. It is a easy to use revers
|
||||||
containerized app and services available through the web. With the two provided files,
|
containerized app and services available through the web. With the two provided files,
|
||||||
[`docker-compose.for-traefik.yml`](docker-compose.for-traefik.yml) (or
|
[`docker-compose.for-traefik.yml`](docker-compose.for-traefik.yml) (or
|
||||||
[`docker-compose.with-traefik.yml`](docker-compose.with-traefik.yml)) and
|
[`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
|
[`docker-compose.override.yml`](docker-compose.override.traefik.yml), it is equally easy to deploy
|
||||||
and use Conduit, 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
|
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 Conduit to federate, we need to
|
loadbalancer and is not able to serve any kind of content, but for Conduit to federate, we need to
|
||||||
|
@ -128,8 +106,7 @@ With the service `well-known` we use a single `nginx` container that will serve
|
||||||
|
|
||||||
So...step by step:
|
So...step by step:
|
||||||
|
|
||||||
1. Copy [`docker-compose.for-traefik.yml`](docker-compose.for-traefik.yml) (or
|
1. Copy [`docker-compose.traefik.yml`](docker-compose.traefik.yml) and [`docker-compose.override.traefik.yml`](docker-compose.override.traefik.yml) from the repository and remove `.traefik` from the filenames.
|
||||||
[`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.
|
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 `conduit.toml` config file, an example can be found [here](../conduit-example.toml), or set `CONDUIT_CONFIG=""` and configure Conduit per env vars.
|
3. Create the `conduit.toml` config file, an example can be found [here](../conduit-example.toml), 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`.
|
4. Uncomment the `element-web` service if you want to host your own Element Web Client and create a `element_config.json`.
|
||||||
|
@ -144,12 +121,12 @@ So...step by step:
|
||||||
|
|
||||||
location /.well-known/matrix/server {
|
location /.well-known/matrix/server {
|
||||||
return 200 '{"m.server": "<SUBDOMAIN>.<DOMAIN>:443"}';
|
return 200 '{"m.server": "<SUBDOMAIN>.<DOMAIN>:443"}';
|
||||||
types { } default_type "application/json; charset=utf-8";
|
add_header Content-Type application/json;
|
||||||
}
|
}
|
||||||
|
|
||||||
location /.well-known/matrix/client {
|
location /.well-known/matrix/client {
|
||||||
return 200 '{"m.homeserver": {"base_url": "https://<SUBDOMAIN>.<DOMAIN>"}}';
|
return 200 '{"m.homeserver": {"base_url": "https://<SUBDOMAIN>.<DOMAIN>"}}';
|
||||||
types { } default_type "application/json; charset=utf-8";
|
add_header Content-Type application/json;
|
||||||
add_header "Access-Control-Allow-Origin" *;
|
add_header "Access-Control-Allow-Origin" *;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,58 +138,3 @@ 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.
|
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.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Voice communication
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
Create a configuration file called `coturn.conf` containing:
|
|
||||||
|
|
||||||
```conf
|
|
||||||
use-auth-secret
|
|
||||||
static-auth-secret=<a secret key>
|
|
||||||
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 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>"
|
|
||||||
```
|
|
||||||
or append the following to the docker environment variables dependig on which configuration method you used earlier:
|
|
||||||
```yml
|
|
||||||
CONDUIT_TURN_URIS: '["turn:<your server domain>?transport=udp", "turn:<your server domain>?transport=tcp"]'
|
|
||||||
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
|
|
||||||
```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.
|
|
||||||
|
|
||||||
```yml
|
|
||||||
version: 3
|
|
||||||
services:
|
|
||||||
turn:
|
|
||||||
container_name: coturn-server
|
|
||||||
image: docker.io/coturn/coturn
|
|
||||||
restart: unless-stopped
|
|
||||||
network_mode: "host"
|
|
||||||
volumes:
|
|
||||||
- ./coturn.conf:/etc/coturn/turnserver.conf
|
|
||||||
```
|
|
||||||
|
|
||||||
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).
|
|
||||||
|
|
||||||
|
|
|
@ -29,10 +29,9 @@ services:
|
||||||
CONDUIT_MAX_REQUEST_SIZE: 20_000_000 # in bytes, ~20 MB
|
CONDUIT_MAX_REQUEST_SIZE: 20_000_000 # in bytes, ~20 MB
|
||||||
CONDUIT_ALLOW_REGISTRATION: 'true'
|
CONDUIT_ALLOW_REGISTRATION: 'true'
|
||||||
CONDUIT_ALLOW_FEDERATION: 'true'
|
CONDUIT_ALLOW_FEDERATION: 'true'
|
||||||
CONDUIT_ALLOW_CHECK_FOR_UPDATES: 'true'
|
|
||||||
CONDUIT_TRUSTED_SERVERS: '["matrix.org"]'
|
CONDUIT_TRUSTED_SERVERS: '["matrix.org"]'
|
||||||
#CONDUIT_MAX_CONCURRENT_REQUESTS: 100
|
#CONDUIT_MAX_CONCURRENT_REQUESTS: 100
|
||||||
#CONDUIT_LOG: warn,rocket=off,_=off,sled=off
|
#CONDUIT_LOG: info,rocket=off,_=off,sled=off
|
||||||
CONDUIT_ADDRESS: 0.0.0.0
|
CONDUIT_ADDRESS: 0.0.0.0
|
||||||
CONDUIT_CONFIG: '' # Ignore this
|
CONDUIT_CONFIG: '' # Ignore this
|
||||||
|
|
||||||
|
|
|
@ -33,11 +33,10 @@ services:
|
||||||
# CONDUIT_PORT: 6167
|
# CONDUIT_PORT: 6167
|
||||||
# CONDUIT_CONFIG: '/srv/conduit/conduit.toml' # if you want to configure purely by env vars, set this to an empty string ''
|
# CONDUIT_CONFIG: '/srv/conduit/conduit.toml' # if you want to configure purely by env vars, set this to an empty string ''
|
||||||
# Available levels are: error, warn, info, debug, trace - more info at: https://docs.rs/env_logger/*/env_logger/#enabling-logging
|
# Available levels are: error, warn, info, debug, trace - more info at: https://docs.rs/env_logger/*/env_logger/#enabling-logging
|
||||||
# CONDUIT_LOG: info # default is: "warn,_=off,sled=off"
|
# CONDUIT_LOG: info # default is: "info,_=off,sled=off"
|
||||||
# CONDUIT_ALLOW_JAEGER: 'false'
|
# CONDUIT_ALLOW_JAEGER: 'false'
|
||||||
# CONDUIT_ALLOW_ENCRYPTION: 'true'
|
# CONDUIT_ALLOW_ENCRYPTION: 'false'
|
||||||
# CONDUIT_ALLOW_FEDERATION: 'true'
|
# CONDUIT_ALLOW_FEDERATION: 'false'
|
||||||
# CONDUIT_ALLOW_CHECK_FOR_UPDATES: 'true'
|
|
||||||
# CONDUIT_DATABASE_PATH: /srv/conduit/.local/share/conduit
|
# CONDUIT_DATABASE_PATH: /srv/conduit/.local/share/conduit
|
||||||
# CONDUIT_WORKERS: 10
|
# CONDUIT_WORKERS: 10
|
||||||
# CONDUIT_MAX_REQUEST_SIZE: 20_000_000 # in bytes, ~20 MB
|
# CONDUIT_MAX_REQUEST_SIZE: 20_000_000 # in bytes, ~20 MB
|
||||||
|
|
|
@ -6,14 +6,9 @@ if [ -z "${CONDUIT_PORT}" ]; then
|
||||||
CONDUIT_PORT=$(ss -tlpn | grep conduit | grep -m1 -o ':[0-9]*' | grep -m1 -o '[0-9]*')
|
CONDUIT_PORT=$(ss -tlpn | grep conduit | grep -m1 -o ':[0-9]*' | grep -m1 -o '[0-9]*')
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# If CONDUIT_ADDRESS is not set try to get the address from the process list
|
|
||||||
if [ -z "${CONDUIT_ADDRESS}" ]; then
|
|
||||||
CONDUIT_ADDRESS=$(ss -tlpn | awk -F ' +|:' '/conduit/ { print $4 }')
|
|
||||||
fi
|
|
||||||
|
|
||||||
# The actual health check.
|
# The actual health check.
|
||||||
# We try to first get a response on HTTP and when that fails on HTTPS and when that fails, we exit with code 1.
|
# We try to first get a response on HTTP and when that fails on HTTPS and when that fails, we exit with code 1.
|
||||||
# TODO: Change this to a single wget call. Do we have a config value that we can check for that?
|
# TODO: Change this to a single wget call. Do we have a config value that we can check for that?
|
||||||
wget --no-verbose --tries=1 --spider "http://${CONDUIT_ADDRESS}:${CONDUIT_PORT}/_matrix/client/versions" || \
|
wget --no-verbose --tries=1 --spider "http://localhost:${CONDUIT_PORT}/_matrix/client/versions" || \
|
||||||
wget --no-verbose --tries=1 --spider "https://${CONDUIT_ADDRESS}:${CONDUIT_PORT}/_matrix/client/versions" || \
|
wget --no-verbose --tries=1 --spider "https://localhost:${CONDUIT_PORT}/_matrix/client/versions" || \
|
||||||
exit 1
|
exit 1
|
||||||
|
|
64
engage.toml
64
engage.toml
|
@ -1,64 +0,0 @@
|
||||||
interpreter = ["bash", "-euo", "pipefail", "-c"]
|
|
||||||
|
|
||||||
[[task]]
|
|
||||||
name = "engage"
|
|
||||||
group = "versions"
|
|
||||||
script = "engage --version"
|
|
||||||
|
|
||||||
[[task]]
|
|
||||||
name = "rustc"
|
|
||||||
group = "versions"
|
|
||||||
script = "rustc --version"
|
|
||||||
|
|
||||||
[[task]]
|
|
||||||
name = "cargo"
|
|
||||||
group = "versions"
|
|
||||||
script = "cargo --version"
|
|
||||||
|
|
||||||
[[task]]
|
|
||||||
name = "cargo-fmt"
|
|
||||||
group = "versions"
|
|
||||||
script = "cargo fmt --version"
|
|
||||||
|
|
||||||
[[task]]
|
|
||||||
name = "rustdoc"
|
|
||||||
group = "versions"
|
|
||||||
script = "rustdoc --version"
|
|
||||||
|
|
||||||
[[task]]
|
|
||||||
name = "cargo-clippy"
|
|
||||||
group = "versions"
|
|
||||||
script = "cargo clippy -- --version"
|
|
||||||
|
|
||||||
[[task]]
|
|
||||||
name = "cargo-fmt"
|
|
||||||
group = "lints"
|
|
||||||
script = "cargo fmt --check -- --color=always"
|
|
||||||
|
|
||||||
[[task]]
|
|
||||||
name = "cargo-doc"
|
|
||||||
group = "lints"
|
|
||||||
script = """
|
|
||||||
RUSTDOCFLAGS="-D warnings" cargo doc \
|
|
||||||
--workspace \
|
|
||||||
--no-deps \
|
|
||||||
--document-private-items \
|
|
||||||
--color always
|
|
||||||
"""
|
|
||||||
|
|
||||||
[[task]]
|
|
||||||
name = "cargo-clippy"
|
|
||||||
group = "lints"
|
|
||||||
script = "cargo clippy --workspace --all-targets --color=always -- -D warnings"
|
|
||||||
|
|
||||||
[[task]]
|
|
||||||
name = "cargo"
|
|
||||||
group = "tests"
|
|
||||||
script = """
|
|
||||||
cargo test \
|
|
||||||
--workspace \
|
|
||||||
--all-targets \
|
|
||||||
--color=always \
|
|
||||||
-- \
|
|
||||||
--color=always
|
|
||||||
"""
|
|
263
flake.lock
263
flake.lock
|
@ -1,263 +0,0 @@
|
||||||
{
|
|
||||||
"nodes": {
|
|
||||||
"attic": {
|
|
||||||
"inputs": {
|
|
||||||
"crane": "crane",
|
|
||||||
"flake-compat": "flake-compat",
|
|
||||||
"flake-utils": "flake-utils",
|
|
||||||
"nixpkgs": "nixpkgs",
|
|
||||||
"nixpkgs-stable": "nixpkgs-stable"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1705617092,
|
|
||||||
"narHash": "sha256-n9PK4O4X4S1JkwpkMuYm1wHZYJzRqif8g3RuVIPD+rY=",
|
|
||||||
"owner": "zhaofengli",
|
|
||||||
"repo": "attic",
|
|
||||||
"rev": "fbe252a5c21febbe920c025560cbd63b20e24f3b",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "zhaofengli",
|
|
||||||
"ref": "main",
|
|
||||||
"repo": "attic",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"crane": {
|
|
||||||
"inputs": {
|
|
||||||
"nixpkgs": [
|
|
||||||
"attic",
|
|
||||||
"nixpkgs"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1702918879,
|
|
||||||
"narHash": "sha256-tWJqzajIvYcaRWxn+cLUB9L9Pv4dQ3Bfit/YjU5ze3g=",
|
|
||||||
"owner": "ipetkov",
|
|
||||||
"repo": "crane",
|
|
||||||
"rev": "7195c00c272fdd92fc74e7d5a0a2844b9fadb2fb",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "ipetkov",
|
|
||||||
"repo": "crane",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"crane_2": {
|
|
||||||
"inputs": {
|
|
||||||
"nixpkgs": [
|
|
||||||
"nixpkgs"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1706473964,
|
|
||||||
"narHash": "sha256-Fq6xleee/TsX6NbtoRuI96bBuDHMU57PrcK9z1QEKbk=",
|
|
||||||
"owner": "ipetkov",
|
|
||||||
"repo": "crane",
|
|
||||||
"rev": "c798790eabec3e3da48190ae3698ac227aab770c",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "ipetkov",
|
|
||||||
"ref": "master",
|
|
||||||
"repo": "crane",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"fenix": {
|
|
||||||
"inputs": {
|
|
||||||
"nixpkgs": [
|
|
||||||
"nixpkgs"
|
|
||||||
],
|
|
||||||
"rust-analyzer-src": "rust-analyzer-src"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1705559032,
|
|
||||||
"narHash": "sha256-Cb+Jd1+Gz4Wi+8elPnUIHnqQmE1qjDRZ+PsJaPaAffY=",
|
|
||||||
"owner": "nix-community",
|
|
||||||
"repo": "fenix",
|
|
||||||
"rev": "e132ea0eb0c799a2109a91688e499d7bf4962801",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nix-community",
|
|
||||||
"repo": "fenix",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"flake-compat": {
|
|
||||||
"flake": false,
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1673956053,
|
|
||||||
"narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
|
|
||||||
"owner": "edolstra",
|
|
||||||
"repo": "flake-compat",
|
|
||||||
"rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "edolstra",
|
|
||||||
"repo": "flake-compat",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"flake-compat_2": {
|
|
||||||
"flake": false,
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1696426674,
|
|
||||||
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
|
|
||||||
"owner": "edolstra",
|
|
||||||
"repo": "flake-compat",
|
|
||||||
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "edolstra",
|
|
||||||
"repo": "flake-compat",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"flake-utils": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1667395993,
|
|
||||||
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"flake-utils_2": {
|
|
||||||
"inputs": {
|
|
||||||
"systems": "systems"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1705309234,
|
|
||||||
"narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=",
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nix-filter": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1705332318,
|
|
||||||
"narHash": "sha256-kcw1yFeJe9N4PjQji9ZeX47jg0p9A0DuU4djKvg1a7I=",
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "nix-filter",
|
|
||||||
"rev": "3449dc925982ad46246cfc36469baf66e1b64f17",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "nix-filter",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1702539185,
|
|
||||||
"narHash": "sha256-KnIRG5NMdLIpEkZTnN5zovNYc0hhXjAgv6pfd5Z4c7U=",
|
|
||||||
"owner": "NixOS",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "aa9d4729cbc99dabacb50e3994dcefb3ea0f7447",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "NixOS",
|
|
||||||
"ref": "nixpkgs-unstable",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs-stable": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1702780907,
|
|
||||||
"narHash": "sha256-blbrBBXjjZt6OKTcYX1jpe9SRof2P9ZYWPzq22tzXAA=",
|
|
||||||
"owner": "NixOS",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "1e2e384c5b7c50dbf8e9c441a9e58d85f408b01f",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "NixOS",
|
|
||||||
"ref": "nixos-23.11",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs_2": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1705496572,
|
|
||||||
"narHash": "sha256-rPIe9G5EBLXdBdn9ilGc0nq082lzQd0xGGe092R/5QE=",
|
|
||||||
"owner": "NixOS",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "842d9d80cfd4560648c785f8a4e6f3b096790e19",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "NixOS",
|
|
||||||
"ref": "nixos-unstable",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"root": {
|
|
||||||
"inputs": {
|
|
||||||
"attic": "attic",
|
|
||||||
"crane": "crane_2",
|
|
||||||
"fenix": "fenix",
|
|
||||||
"flake-compat": "flake-compat_2",
|
|
||||||
"flake-utils": "flake-utils_2",
|
|
||||||
"nix-filter": "nix-filter",
|
|
||||||
"nixpkgs": "nixpkgs_2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"rust-analyzer-src": {
|
|
||||||
"flake": false,
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1705523001,
|
|
||||||
"narHash": "sha256-TWq5vJ6m+9HGSDMsQAmz1TMegMi79R3TTyKjnPWsQp8=",
|
|
||||||
"owner": "rust-lang",
|
|
||||||
"repo": "rust-analyzer",
|
|
||||||
"rev": "9d9b34354d2f13e33568c9c55b226dd014a146a0",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "rust-lang",
|
|
||||||
"ref": "nightly",
|
|
||||||
"repo": "rust-analyzer",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"systems": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1681028828,
|
|
||||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
|
||||||
"owner": "nix-systems",
|
|
||||||
"repo": "default",
|
|
||||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nix-systems",
|
|
||||||
"repo": "default",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"root": "root",
|
|
||||||
"version": 7
|
|
||||||
}
|
|
259
flake.nix
259
flake.nix
|
@ -1,259 +0,0 @@
|
||||||
{
|
|
||||||
inputs = {
|
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs?ref=nixos-unstable";
|
|
||||||
flake-utils.url = "github:numtide/flake-utils";
|
|
||||||
nix-filter.url = "github:numtide/nix-filter";
|
|
||||||
flake-compat = {
|
|
||||||
url = "github:edolstra/flake-compat";
|
|
||||||
flake = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
fenix = {
|
|
||||||
url = "github:nix-community/fenix";
|
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
|
||||||
};
|
|
||||||
crane = {
|
|
||||||
url = "github:ipetkov/crane?ref=master";
|
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
|
||||||
};
|
|
||||||
attic.url = "github:zhaofengli/attic?ref=main";
|
|
||||||
};
|
|
||||||
|
|
||||||
outputs =
|
|
||||||
{ self
|
|
||||||
, nixpkgs
|
|
||||||
, flake-utils
|
|
||||||
, nix-filter
|
|
||||||
|
|
||||||
, fenix
|
|
||||||
, crane
|
|
||||||
, ...
|
|
||||||
}: flake-utils.lib.eachDefaultSystem (system:
|
|
||||||
let
|
|
||||||
pkgsHost = nixpkgs.legacyPackages.${system};
|
|
||||||
|
|
||||||
# Nix-accessible `Cargo.toml`
|
|
||||||
cargoToml = builtins.fromTOML (builtins.readFile ./Cargo.toml);
|
|
||||||
|
|
||||||
# The Rust toolchain to use
|
|
||||||
toolchain = fenix.packages.${system}.fromToolchainFile {
|
|
||||||
file = ./rust-toolchain.toml;
|
|
||||||
|
|
||||||
# See also `rust-toolchain.toml`
|
|
||||||
sha256 = "sha256-SXRtAuO4IqNOQq+nLbrsDFbVk+3aVA8NNpSZsKlVH/8=";
|
|
||||||
};
|
|
||||||
|
|
||||||
builder = pkgs:
|
|
||||||
((crane.mkLib pkgs).overrideToolchain toolchain).buildPackage;
|
|
||||||
|
|
||||||
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.buildPackages.rustPlatform.bindgenHook
|
|
||||||
];
|
|
||||||
|
|
||||||
env = pkgs: {
|
|
||||||
ROCKSDB_INCLUDE_DIR = "${pkgs.rocksdb}/include";
|
|
||||||
ROCKSDB_LIB_DIR = "${pkgs.rocksdb}/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/612f97239e2cc474c13c9dafa0df378058c5ad8d/pkgs/build-support/rust/lib/default.nix#L36-L39
|
|
||||||
(
|
|
||||||
# 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/612f97239e2cc474c13c9dafa0df378058c5ad8d/pkgs/build-support/rust/lib/default.nix#L64-L78
|
|
||||||
// (
|
|
||||||
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.buildPackages.stdenv.cc}/bin/cc";
|
|
||||||
HOST_CXX = "${pkgs.buildPackages.stdenv.cc}/bin/c++";
|
|
||||||
}
|
|
||||||
));
|
|
||||||
|
|
||||||
package = pkgs: builder pkgs {
|
|
||||||
src = nix-filter {
|
|
||||||
root = ./.;
|
|
||||||
include = [
|
|
||||||
"src"
|
|
||||||
"Cargo.toml"
|
|
||||||
"Cargo.lock"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
# This is redundant with CI
|
|
||||||
doCheck = false;
|
|
||||||
|
|
||||||
env = env pkgs;
|
|
||||||
nativeBuildInputs = nativeBuildInputs pkgs;
|
|
||||||
|
|
||||||
meta.mainProgram = cargoToml.package.name;
|
|
||||||
};
|
|
||||||
|
|
||||||
mkOciImage = pkgs: package:
|
|
||||||
pkgs.dockerTools.buildImage {
|
|
||||||
name = package.pname;
|
|
||||||
tag = "next";
|
|
||||||
copyToRoot = [
|
|
||||||
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 = package pkgsHost;
|
|
||||||
oci-image = mkOciImage pkgsHost self.packages.${system}.default;
|
|
||||||
}
|
|
||||||
//
|
|
||||||
builtins.listToAttrs
|
|
||||||
(builtins.concatLists
|
|
||||||
(builtins.map
|
|
||||||
(crossSystem:
|
|
||||||
let
|
|
||||||
binaryName = "static-${crossSystem}";
|
|
||||||
pkgsCrossStatic =
|
|
||||||
(import nixpkgs {
|
|
||||||
inherit system;
|
|
||||||
crossSystem = {
|
|
||||||
config = crossSystem;
|
|
||||||
};
|
|
||||||
}).pkgsStatic;
|
|
||||||
in
|
|
||||||
[
|
|
||||||
# An output for a statically-linked binary
|
|
||||||
{
|
|
||||||
name = binaryName;
|
|
||||||
value = package pkgsCrossStatic;
|
|
||||||
}
|
|
||||||
|
|
||||||
# An output for an OCI image based on that binary
|
|
||||||
{
|
|
||||||
name = "oci-image-${crossSystem}";
|
|
||||||
value = mkOciImage
|
|
||||||
pkgsCrossStatic
|
|
||||||
self.packages.${system}.${binaryName};
|
|
||||||
}
|
|
||||||
]
|
|
||||||
)
|
|
||||||
[
|
|
||||||
"x86_64-unknown-linux-musl"
|
|
||||||
"aarch64-unknown-linux-musl"
|
|
||||||
]
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
devShells.default = pkgsHost.mkShell {
|
|
||||||
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";
|
|
||||||
};
|
|
||||||
|
|
||||||
# Development tools
|
|
||||||
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.
|
|
||||||
fenix.packages.${system}.latest.rustfmt
|
|
||||||
|
|
||||||
toolchain
|
|
||||||
] ++ (with pkgsHost; [
|
|
||||||
engage
|
|
||||||
|
|
||||||
# Needed for Complement
|
|
||||||
go
|
|
||||||
olm
|
|
||||||
|
|
||||||
# Needed for our script for Complement
|
|
||||||
jq
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
198
nix/README.md
198
nix/README.md
|
@ -1,198 +0,0 @@
|
||||||
# Conduit for Nix/NixOS
|
|
||||||
|
|
||||||
This guide assumes you have a recent version of Nix (^2.4) installed.
|
|
||||||
|
|
||||||
Since Conduit ships as a Nix flake, you'll first need to [enable
|
|
||||||
flakes][enable_flakes].
|
|
||||||
|
|
||||||
You can now use the usual Nix commands to interact with Conduit's flake. For
|
|
||||||
example, `nix run gitlab:famedly/conduit` will run Conduit (though you'll need
|
|
||||||
to provide configuration and such manually as usual).
|
|
||||||
|
|
||||||
If your NixOS configuration is defined as a flake, you can depend on this flake
|
|
||||||
to provide a more up-to-date version than provided by `nixpkgs`. In your flake,
|
|
||||||
add the following to your `inputs`:
|
|
||||||
|
|
||||||
```nix
|
|
||||||
conduit = {
|
|
||||||
url = "gitlab:famedly/conduit";
|
|
||||||
|
|
||||||
# Assuming you have an input for nixpkgs called `nixpkgs`. If you experience
|
|
||||||
# build failures while using this, try commenting/deleting this line. This
|
|
||||||
# will probably also require you to always build from source.
|
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
Next, make sure you're passing your flake inputs to the `specialArgs` argument
|
|
||||||
of `nixpkgs.lib.nixosSystem` [as explained here][specialargs]. This guide will
|
|
||||||
assume you've named the group `flake-inputs`.
|
|
||||||
|
|
||||||
Now you can configure Conduit and a reverse proxy for it. Add the following to
|
|
||||||
a new Nix file and include it in your configuration:
|
|
||||||
|
|
||||||
```nix
|
|
||||||
{ config
|
|
||||||
, pkgs
|
|
||||||
, flake-inputs
|
|
||||||
, ...
|
|
||||||
}:
|
|
||||||
|
|
||||||
let
|
|
||||||
# You'll need to edit these values
|
|
||||||
|
|
||||||
# The hostname that will appear in your user and room IDs
|
|
||||||
server_name = "example.com";
|
|
||||||
|
|
||||||
# The hostname that Conduit actually runs on
|
|
||||||
#
|
|
||||||
# This can be the same as `server_name` if you want. This is only necessary
|
|
||||||
# when Conduit is running on a different machine than the one hosting your
|
|
||||||
# root domain. This configuration also assumes this is all running on a single
|
|
||||||
# machine, some tweaks will need to be made if this is not the case.
|
|
||||||
matrix_hostname = "matrix.${server_name}";
|
|
||||||
|
|
||||||
# An admin email for TLS certificate notifications
|
|
||||||
admin_email = "admin@${server_name}";
|
|
||||||
|
|
||||||
# These ones you can leave alone
|
|
||||||
|
|
||||||
# Build a dervation that stores the content of `${server_name}/.well-known/matrix/server`
|
|
||||||
well_known_server = pkgs.writeText "well-known-matrix-server" ''
|
|
||||||
{
|
|
||||||
"m.server": "${matrix_hostname}"
|
|
||||||
}
|
|
||||||
'';
|
|
||||||
|
|
||||||
# Build a dervation that stores the content of `${server_name}/.well-known/matrix/client`
|
|
||||||
well_known_client = pkgs.writeText "well-known-matrix-client" ''
|
|
||||||
{
|
|
||||||
"m.homeserver": {
|
|
||||||
"base_url": "https://${matrix_hostname}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
'';
|
|
||||||
in
|
|
||||||
|
|
||||||
{
|
|
||||||
# Configure Conduit itself
|
|
||||||
services.matrix-conduit = {
|
|
||||||
enable = true;
|
|
||||||
|
|
||||||
# This causes NixOS to use the flake defined in this repository instead of
|
|
||||||
# the build of Conduit built into nixpkgs.
|
|
||||||
package = flake-inputs.conduit.packages.${pkgs.system}.default;
|
|
||||||
|
|
||||||
settings.global = {
|
|
||||||
inherit server_name;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
# Configure automated TLS acquisition/renewal
|
|
||||||
security.acme = {
|
|
||||||
acceptTerms = true;
|
|
||||||
defaults = {
|
|
||||||
email = admin_email;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
# ACME data must be readable by the NGINX user
|
|
||||||
users.users.nginx.extraGroups = [
|
|
||||||
"acme"
|
|
||||||
];
|
|
||||||
|
|
||||||
# Configure NGINX as a reverse proxy
|
|
||||||
services.nginx = {
|
|
||||||
enable = true;
|
|
||||||
recommendedProxySettings = true;
|
|
||||||
|
|
||||||
virtualHosts = {
|
|
||||||
"${matrix_hostname}" = {
|
|
||||||
forceSSL = true;
|
|
||||||
enableACME = true;
|
|
||||||
|
|
||||||
listen = [
|
|
||||||
{
|
|
||||||
addr = "0.0.0.0";
|
|
||||||
port = 443;
|
|
||||||
ssl = true;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
addr = "[::]";
|
|
||||||
port = 443;
|
|
||||||
ssl = true;
|
|
||||||
} {
|
|
||||||
addr = "0.0.0.0";
|
|
||||||
port = 8448;
|
|
||||||
ssl = true;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
addr = "[::]";
|
|
||||||
port = 8448;
|
|
||||||
ssl = true;
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
locations."/_matrix/" = {
|
|
||||||
proxyPass = "http://backend_conduit$request_uri";
|
|
||||||
proxyWebsockets = true;
|
|
||||||
extraConfig = ''
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_buffering off;
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
extraConfig = ''
|
|
||||||
merge_slashes off;
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
"${server_name}" = {
|
|
||||||
forceSSL = true;
|
|
||||||
enableACME = true;
|
|
||||||
|
|
||||||
locations."=/.well-known/matrix/server" = {
|
|
||||||
# Use the contents of the derivation built previously
|
|
||||||
alias = "${well_known_server}";
|
|
||||||
|
|
||||||
extraConfig = ''
|
|
||||||
# Set the header since by default NGINX thinks it's just bytes
|
|
||||||
default_type application/json;
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
locations."=/.well-known/matrix/client" = {
|
|
||||||
# Use the contents of the derivation built previously
|
|
||||||
alias = "${well_known_client}";
|
|
||||||
|
|
||||||
extraConfig = ''
|
|
||||||
# Set the header since by default NGINX thinks it's just bytes
|
|
||||||
default_type application/json;
|
|
||||||
|
|
||||||
# https://matrix.org/docs/spec/client_server/r0.4.0#web-browser-clients
|
|
||||||
add_header Access-Control-Allow-Origin "*";
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
upstreams = {
|
|
||||||
"backend_conduit" = {
|
|
||||||
servers = {
|
|
||||||
"[::1]:${toString config.services.matrix-conduit.settings.global.port}" = { };
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
# Open firewall ports for HTTP, HTTPS, and Matrix federation
|
|
||||||
networking.firewall.allowedTCPPorts = [ 80 443 8448 ];
|
|
||||||
networking.firewall.allowedUDPPorts = [ 80 443 8448 ];
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Now you can rebuild your system configuration and you should be good to go!
|
|
||||||
|
|
||||||
[enable_flakes]: https://nixos.wiki/wiki/Flakes#Enable_flakes
|
|
||||||
|
|
||||||
[specialargs]: https://nixos.wiki/wiki/Flakes#Using_nix_flakes_with_NixOS
|
|
|
@ -1,22 +0,0 @@
|
||||||
# This is the authoritiative configuration of this project's Rust toolchain.
|
|
||||||
#
|
|
||||||
# Other files that need upkeep when this changes:
|
|
||||||
#
|
|
||||||
# * `.gitlab-ci.yml`
|
|
||||||
# * `Cargo.toml`
|
|
||||||
# * `flake.nix`
|
|
||||||
#
|
|
||||||
# Search in those files for `rust-toolchain.toml` to find the relevant places.
|
|
||||||
# If you're having trouble making the relevant changes, bug a maintainer.
|
|
||||||
|
|
||||||
[toolchain]
|
|
||||||
channel = "1.75.0"
|
|
||||||
components = [
|
|
||||||
# For rust-analyzer
|
|
||||||
"rust-src",
|
|
||||||
]
|
|
||||||
targets = [
|
|
||||||
"x86_64-unknown-linux-gnu",
|
|
||||||
"x86_64-unknown-linux-musl",
|
|
||||||
"aarch64-unknown-linux-musl",
|
|
||||||
]
|
|
|
@ -18,7 +18,7 @@ where
|
||||||
let mut http_request = request
|
let mut http_request = request
|
||||||
.try_into_http_request::<BytesMut>(
|
.try_into_http_request::<BytesMut>(
|
||||||
destination,
|
destination,
|
||||||
SendAccessToken::IfRequired(hs_token),
|
SendAccessToken::IfRequired(""),
|
||||||
&[MatrixVersion::V1_0],
|
&[MatrixVersion::V1_0],
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
@ -45,23 +45,11 @@ where
|
||||||
*reqwest_request.timeout_mut() = Some(Duration::from_secs(30));
|
*reqwest_request.timeout_mut() = Some(Duration::from_secs(30));
|
||||||
|
|
||||||
let url = reqwest_request.url().clone();
|
let url = reqwest_request.url().clone();
|
||||||
let mut response = match services()
|
let mut response = services()
|
||||||
.globals
|
.globals
|
||||||
.default_client()
|
.default_client()
|
||||||
.execute(reqwest_request)
|
.execute(reqwest_request)
|
||||||
.await
|
.await?;
|
||||||
{
|
|
||||||
Ok(r) => r,
|
|
||||||
Err(e) => {
|
|
||||||
warn!(
|
|
||||||
"Could not send request to appservice {:?} at {}: {}",
|
|
||||||
registration.get("id"),
|
|
||||||
destination,
|
|
||||||
e
|
|
||||||
);
|
|
||||||
return Err(e.into());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// reqwest::Response -> http::Response conversion
|
// reqwest::Response -> http::Response conversion
|
||||||
let status = response.status();
|
let status = response.status();
|
||||||
|
|
|
@ -3,9 +3,8 @@ use crate::{api::client_server, services, utils, Error, Result, Ruma};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::{
|
api::client::{
|
||||||
account::{
|
account::{
|
||||||
change_password, deactivate, get_3pids, get_username_availability, register,
|
change_password, deactivate, get_3pids, get_username_availability, register, whoami,
|
||||||
request_3pid_management_token_via_email, request_3pid_management_token_via_msisdn,
|
ThirdPartyIdRemovalStatus,
|
||||||
whoami, ThirdPartyIdRemovalStatus,
|
|
||||||
},
|
},
|
||||||
error::ErrorKind,
|
error::ErrorKind,
|
||||||
uiaa::{AuthFlow, AuthType, UiaaInfo},
|
uiaa::{AuthFlow, AuthType, UiaaInfo},
|
||||||
|
@ -13,6 +12,7 @@ use ruma::{
|
||||||
events::{room::message::RoomMessageEventContent, GlobalAccountDataEventType},
|
events::{room::message::RoomMessageEventContent, GlobalAccountDataEventType},
|
||||||
push, UserId,
|
push, UserId,
|
||||||
};
|
};
|
||||||
|
|
||||||
use tracing::{info, warn};
|
use tracing::{info, warn};
|
||||||
|
|
||||||
use register::RegistrationKind;
|
use register::RegistrationKind;
|
||||||
|
@ -30,7 +30,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
|
/// Note: This will not reserve the username, so the username might become invalid when trying to register
|
||||||
pub async fn get_register_available_route(
|
pub async fn get_register_available_route(
|
||||||
body: Ruma<get_username_availability::v3::Request>,
|
body: Ruma<get_username_availability::v3::IncomingRequest>,
|
||||||
) -> Result<get_username_availability::v3::Response> {
|
) -> Result<get_username_availability::v3::Response> {
|
||||||
// Validate user id
|
// Validate user id
|
||||||
let user_id = UserId::parse_with_server_name(
|
let user_id = UserId::parse_with_server_name(
|
||||||
|
@ -73,11 +73,10 @@ pub async fn get_register_available_route(
|
||||||
/// - If type is not guest and no username is given: Always fails after UIAA check
|
/// - If type is not guest and no username is given: Always fails after UIAA check
|
||||||
/// - Creates a new account and populates it with default account data
|
/// - Creates a new account and populates it with default account data
|
||||||
/// - If `inhibit_login` is false: Creates a device and returns device id and access_token
|
/// - If `inhibit_login` is false: Creates a device and returns device id and access_token
|
||||||
pub async fn register_route(body: Ruma<register::v3::Request>) -> Result<register::v3::Response> {
|
pub async fn register_route(
|
||||||
if !services().globals.allow_registration()
|
body: Ruma<register::v3::IncomingRequest>,
|
||||||
&& !body.from_appservice
|
) -> Result<register::v3::Response> {
|
||||||
&& services().globals.config.registration_token.is_none()
|
if !services().globals.allow_registration() && !body.from_appservice {
|
||||||
{
|
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::Forbidden,
|
ErrorKind::Forbidden,
|
||||||
"Registration has been disabled.",
|
"Registration has been disabled.",
|
||||||
|
@ -124,11 +123,7 @@ pub async fn register_route(body: Ruma<register::v3::Request>) -> Result<registe
|
||||||
// UIAA
|
// UIAA
|
||||||
let mut uiaainfo = UiaaInfo {
|
let mut uiaainfo = UiaaInfo {
|
||||||
flows: vec![AuthFlow {
|
flows: vec![AuthFlow {
|
||||||
stages: if services().globals.config.registration_token.is_some() {
|
stages: vec![AuthType::Dummy],
|
||||||
vec![AuthType::RegistrationToken]
|
|
||||||
} else {
|
|
||||||
vec![AuthType::Dummy]
|
|
||||||
},
|
|
||||||
}],
|
}],
|
||||||
completed: Vec::new(),
|
completed: Vec::new(),
|
||||||
params: Default::default(),
|
params: Default::default(),
|
||||||
|
@ -136,7 +131,7 @@ pub async fn register_route(body: Ruma<register::v3::Request>) -> Result<registe
|
||||||
auth_error: None,
|
auth_error: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
if !body.from_appservice && !is_guest {
|
if !body.from_appservice {
|
||||||
if let Some(auth) = &body.auth {
|
if let Some(auth) = &body.auth {
|
||||||
let (worked, uiaainfo) = services().uiaa.try_auth(
|
let (worked, uiaainfo) = services().uiaa.try_auth(
|
||||||
&UserId::parse_with_server_name("", services().globals.server_name())
|
&UserId::parse_with_server_name("", services().globals.server_name())
|
||||||
|
@ -174,13 +169,7 @@ pub async fn register_route(body: Ruma<register::v3::Request>) -> Result<registe
|
||||||
services().users.create(&user_id, password)?;
|
services().users.create(&user_id, password)?;
|
||||||
|
|
||||||
// Default to pretty displayname
|
// Default to pretty displayname
|
||||||
let mut displayname = user_id.localpart().to_owned();
|
let displayname = format!("{} ⚡️", user_id.localpart());
|
||||||
|
|
||||||
// If enabled append lightning bolt to display name (default true)
|
|
||||||
if services().globals.enable_lightning_bolt() {
|
|
||||||
displayname.push_str(" ⚡️");
|
|
||||||
}
|
|
||||||
|
|
||||||
services()
|
services()
|
||||||
.users
|
.users
|
||||||
.set_displayname(&user_id, Some(displayname.clone()))?;
|
.set_displayname(&user_id, Some(displayname.clone()))?;
|
||||||
|
@ -204,8 +193,6 @@ pub async fn register_route(body: Ruma<register::v3::Request>) -> Result<registe
|
||||||
access_token: None,
|
access_token: None,
|
||||||
user_id,
|
user_id,
|
||||||
device_id: None,
|
device_id: None,
|
||||||
refresh_token: None,
|
|
||||||
expires_in: None,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,13 +216,12 @@ pub async fn register_route(body: Ruma<register::v3::Request>) -> Result<registe
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
info!("New user {} registered on this server.", user_id);
|
info!("New user {} registered on this server.", user_id);
|
||||||
if !body.from_appservice && !is_guest {
|
|
||||||
services()
|
services()
|
||||||
.admin
|
.admin
|
||||||
.send_message(RoomMessageEventContent::notice_plain(format!(
|
.send_message(RoomMessageEventContent::notice_plain(format!(
|
||||||
"New user {user_id} registered on this server."
|
"New user {} registered on this server.",
|
||||||
|
user_id
|
||||||
)));
|
)));
|
||||||
}
|
|
||||||
|
|
||||||
// If this is the first real user, grant them admin privileges
|
// If this is the first real user, grant them admin privileges
|
||||||
// Note: the server user, @conduit:servername, is generated first
|
// Note: the server user, @conduit:servername, is generated first
|
||||||
|
@ -252,8 +238,6 @@ pub async fn register_route(body: Ruma<register::v3::Request>) -> Result<registe
|
||||||
access_token: Some(token),
|
access_token: Some(token),
|
||||||
user_id,
|
user_id,
|
||||||
device_id: Some(device_id),
|
device_id: Some(device_id),
|
||||||
refresh_token: None,
|
|
||||||
expires_in: None,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,7 +256,7 @@ pub async fn register_route(body: Ruma<register::v3::Request>) -> Result<registe
|
||||||
/// - Forgets to-device events
|
/// - Forgets to-device events
|
||||||
/// - Triggers device list updates
|
/// - Triggers device list updates
|
||||||
pub async fn change_password_route(
|
pub async fn change_password_route(
|
||||||
body: Ruma<change_password::v3::Request>,
|
body: Ruma<change_password::v3::IncomingRequest>,
|
||||||
) -> Result<change_password::v3::Response> {
|
) -> Result<change_password::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
||||||
|
@ -326,7 +310,8 @@ pub async fn change_password_route(
|
||||||
services()
|
services()
|
||||||
.admin
|
.admin
|
||||||
.send_message(RoomMessageEventContent::notice_plain(format!(
|
.send_message(RoomMessageEventContent::notice_plain(format!(
|
||||||
"User {sender_user} changed their password."
|
"User {} changed their password.",
|
||||||
|
sender_user
|
||||||
)));
|
)));
|
||||||
|
|
||||||
Ok(change_password::v3::Response {})
|
Ok(change_password::v3::Response {})
|
||||||
|
@ -344,7 +329,7 @@ pub async fn whoami_route(body: Ruma<whoami::v3::Request>) -> Result<whoami::v3:
|
||||||
Ok(whoami::v3::Response {
|
Ok(whoami::v3::Response {
|
||||||
user_id: sender_user.clone(),
|
user_id: sender_user.clone(),
|
||||||
device_id,
|
device_id,
|
||||||
is_guest: services().users.is_deactivated(sender_user)? && !body.from_appservice,
|
is_guest: services().users.is_deactivated(&sender_user)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -359,7 +344,7 @@ pub async fn whoami_route(body: Ruma<whoami::v3::Request>) -> Result<whoami::v3:
|
||||||
/// - Triggers device list updates
|
/// - Triggers device list updates
|
||||||
/// - Removes ability to log in again
|
/// - Removes ability to log in again
|
||||||
pub async fn deactivate_route(
|
pub async fn deactivate_route(
|
||||||
body: Ruma<deactivate::v3::Request>,
|
body: Ruma<deactivate::v3::IncomingRequest>,
|
||||||
) -> Result<deactivate::v3::Response> {
|
) -> Result<deactivate::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
||||||
|
@ -394,7 +379,7 @@ pub async fn deactivate_route(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make the user leave all rooms before deactivation
|
// Make the user leave all rooms before deactivation
|
||||||
client_server::leave_all_rooms(sender_user).await?;
|
client_server::leave_all_rooms(&sender_user).await?;
|
||||||
|
|
||||||
// Remove devices and mark account as deactivated
|
// Remove devices and mark account as deactivated
|
||||||
services().users.deactivate_account(sender_user)?;
|
services().users.deactivate_account(sender_user)?;
|
||||||
|
@ -403,7 +388,8 @@ pub async fn deactivate_route(
|
||||||
services()
|
services()
|
||||||
.admin
|
.admin
|
||||||
.send_message(RoomMessageEventContent::notice_plain(format!(
|
.send_message(RoomMessageEventContent::notice_plain(format!(
|
||||||
"User {sender_user} deactivated their account."
|
"User {} deactivated their account.",
|
||||||
|
sender_user
|
||||||
)));
|
)));
|
||||||
|
|
||||||
Ok(deactivate::v3::Response {
|
Ok(deactivate::v3::Response {
|
||||||
|
@ -411,7 +397,7 @@ pub async fn deactivate_route(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # `GET _matrix/client/v3/account/3pid`
|
/// # `GET _matrix/client/r0/account/3pid`
|
||||||
///
|
///
|
||||||
/// Get a list of third party identifiers associated with this account.
|
/// Get a list of third party identifiers associated with this account.
|
||||||
///
|
///
|
||||||
|
@ -423,31 +409,3 @@ pub async fn third_party_route(
|
||||||
|
|
||||||
Ok(get_3pids::v3::Response::new(Vec::new()))
|
Ok(get_3pids::v3::Response::new(Vec::new()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # `POST /_matrix/client/v3/account/3pid/email/requestToken`
|
|
||||||
///
|
|
||||||
/// "This API should be used to request validation tokens when adding an email address to an account"
|
|
||||||
///
|
|
||||||
/// - 403 signals that The homeserver does not allow the third party identifier as a contact option.
|
|
||||||
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(
|
|
||||||
ErrorKind::ThreepidDenied,
|
|
||||||
"Third party identifier is not allowed",
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # `POST /_matrix/client/v3/account/3pid/msisdn/requestToken`
|
|
||||||
///
|
|
||||||
/// "This API should be used to request validation tokens when adding an phone number to an account"
|
|
||||||
///
|
|
||||||
/// - 403 signals that The homeserver does not allow the third party identifier as a contact option.
|
|
||||||
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(
|
|
||||||
ErrorKind::ThreepidDenied,
|
|
||||||
"Third party identifier is not allowed",
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use crate::{services, Error, Result, Ruma};
|
use crate::{services, Error, Result, Ruma};
|
||||||
use rand::seq::SliceRandom;
|
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::{
|
api::{
|
||||||
|
@ -10,14 +9,14 @@ use ruma::{
|
||||||
},
|
},
|
||||||
federation,
|
federation,
|
||||||
},
|
},
|
||||||
OwnedRoomAliasId,
|
RoomAliasId,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// # `PUT /_matrix/client/r0/directory/room/{roomAlias}`
|
/// # `PUT /_matrix/client/r0/directory/room/{roomAlias}`
|
||||||
///
|
///
|
||||||
/// Creates a new room alias on this server.
|
/// Creates a new room alias on this server.
|
||||||
pub async fn create_alias_route(
|
pub async fn create_alias_route(
|
||||||
body: Ruma<create_alias::v3::Request>,
|
body: Ruma<create_alias::v3::IncomingRequest>,
|
||||||
) -> Result<create_alias::v3::Response> {
|
) -> Result<create_alias::v3::Response> {
|
||||||
if body.room_alias.server_name() != services().globals.server_name() {
|
if body.room_alias.server_name() != services().globals.server_name() {
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
|
@ -50,7 +49,7 @@ pub async fn create_alias_route(
|
||||||
/// - TODO: additional access control checks
|
/// - TODO: additional access control checks
|
||||||
/// - TODO: Update canonical alias event
|
/// - TODO: Update canonical alias event
|
||||||
pub async fn delete_alias_route(
|
pub async fn delete_alias_route(
|
||||||
body: Ruma<delete_alias::v3::Request>,
|
body: Ruma<delete_alias::v3::IncomingRequest>,
|
||||||
) -> Result<delete_alias::v3::Response> {
|
) -> Result<delete_alias::v3::Response> {
|
||||||
if body.room_alias.server_name() != services().globals.server_name() {
|
if body.room_alias.server_name() != services().globals.server_name() {
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
|
@ -72,33 +71,29 @@ pub async fn delete_alias_route(
|
||||||
///
|
///
|
||||||
/// - TODO: Suggest more servers to join via
|
/// - TODO: Suggest more servers to join via
|
||||||
pub async fn get_alias_route(
|
pub async fn get_alias_route(
|
||||||
body: Ruma<get_alias::v3::Request>,
|
body: Ruma<get_alias::v3::IncomingRequest>,
|
||||||
) -> Result<get_alias::v3::Response> {
|
) -> Result<get_alias::v3::Response> {
|
||||||
get_alias_helper(body.body.room_alias).await
|
get_alias_helper(&body.room_alias).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn get_alias_helper(
|
pub(crate) async fn get_alias_helper(room_alias: &RoomAliasId) -> Result<get_alias::v3::Response> {
|
||||||
room_alias: OwnedRoomAliasId,
|
|
||||||
) -> Result<get_alias::v3::Response> {
|
|
||||||
if room_alias.server_name() != services().globals.server_name() {
|
if room_alias.server_name() != services().globals.server_name() {
|
||||||
let response = services()
|
let response = services()
|
||||||
.sending
|
.sending
|
||||||
.send_federation_request(
|
.send_federation_request(
|
||||||
room_alias.server_name(),
|
room_alias.server_name(),
|
||||||
federation::query::get_room_information::v1::Request {
|
federation::query::get_room_information::v1::Request { room_alias },
|
||||||
room_alias: room_alias.to_owned(),
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let mut servers = response.servers;
|
return Ok(get_alias::v3::Response::new(
|
||||||
servers.shuffle(&mut rand::thread_rng());
|
response.room_id,
|
||||||
|
response.servers,
|
||||||
return Ok(get_alias::v3::Response::new(response.room_id, servers));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut room_id = None;
|
let mut room_id = None;
|
||||||
match services().rooms.alias.resolve_local_alias(&room_alias)? {
|
match services().rooms.alias.resolve_local_alias(room_alias)? {
|
||||||
Some(r) => room_id = Some(r),
|
Some(r) => room_id = Some(r),
|
||||||
None => {
|
None => {
|
||||||
for (_id, registration) in services().appservice.all()? {
|
for (_id, registration) in services().appservice.all()? {
|
||||||
|
@ -120,9 +115,7 @@ pub(crate) async fn get_alias_helper(
|
||||||
.sending
|
.sending
|
||||||
.send_appservice_request(
|
.send_appservice_request(
|
||||||
registration,
|
registration,
|
||||||
appservice::query::query_room_alias::v1::Request {
|
appservice::query::query_room_alias::v1::Request { room_alias },
|
||||||
room_alias: room_alias.clone(),
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.is_ok()
|
.is_ok()
|
||||||
|
@ -131,7 +124,7 @@ pub(crate) async fn get_alias_helper(
|
||||||
services()
|
services()
|
||||||
.rooms
|
.rooms
|
||||||
.alias
|
.alias
|
||||||
.resolve_local_alias(&room_alias)?
|
.resolve_local_alias(room_alias)?
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
Error::bad_config("Appservice lied to us. Room does not exist.")
|
Error::bad_config("Appservice lied to us. Room does not exist.")
|
||||||
})?,
|
})?,
|
||||||
|
|
|
@ -28,7 +28,7 @@ pub async fn create_backup_version_route(
|
||||||
///
|
///
|
||||||
/// Update information about an existing backup. Only `auth_data` can be modified.
|
/// Update information about an existing backup. Only `auth_data` can be modified.
|
||||||
pub async fn update_backup_version_route(
|
pub async fn update_backup_version_route(
|
||||||
body: Ruma<update_backup_version::v3::Request>,
|
body: Ruma<update_backup_version::v3::IncomingRequest>,
|
||||||
) -> Result<update_backup_version::v3::Response> {
|
) -> Result<update_backup_version::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
services()
|
services()
|
||||||
|
@ -66,7 +66,7 @@ pub async fn get_latest_backup_info_route(
|
||||||
///
|
///
|
||||||
/// Get information about an existing backup.
|
/// Get information about an existing backup.
|
||||||
pub async fn get_backup_info_route(
|
pub async fn get_backup_info_route(
|
||||||
body: Ruma<get_backup_info::v3::Request>,
|
body: Ruma<get_backup_info::v3::IncomingRequest>,
|
||||||
) -> Result<get_backup_info::v3::Response> {
|
) -> Result<get_backup_info::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
let algorithm = services()
|
let algorithm = services()
|
||||||
|
@ -96,7 +96,7 @@ pub async fn get_backup_info_route(
|
||||||
///
|
///
|
||||||
/// - Deletes both information about the backup, as well as all key data related to the backup
|
/// - Deletes both information about the backup, as well as all key data related to the backup
|
||||||
pub async fn delete_backup_version_route(
|
pub async fn delete_backup_version_route(
|
||||||
body: Ruma<delete_backup_version::v3::Request>,
|
body: Ruma<delete_backup_version::v3::IncomingRequest>,
|
||||||
) -> Result<delete_backup_version::v3::Response> {
|
) -> Result<delete_backup_version::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
|
@ -115,7 +115,7 @@ pub async fn delete_backup_version_route(
|
||||||
/// - Adds the keys to the backup
|
/// - Adds the keys to the backup
|
||||||
/// - Returns the new number of keys in this backup and the etag
|
/// - Returns the new number of keys in this backup and the etag
|
||||||
pub async fn add_backup_keys_route(
|
pub async fn add_backup_keys_route(
|
||||||
body: Ruma<add_backup_keys::v3::Request>,
|
body: Ruma<add_backup_keys::v3::IncomingRequest>,
|
||||||
) -> Result<add_backup_keys::v3::Response> {
|
) -> Result<add_backup_keys::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
|
@ -162,7 +162,7 @@ pub async fn add_backup_keys_route(
|
||||||
/// - Adds the keys to the backup
|
/// - Adds the keys to the backup
|
||||||
/// - Returns the new number of keys in this backup and the etag
|
/// - Returns the new number of keys in this backup and the etag
|
||||||
pub 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>,
|
body: Ruma<add_backup_keys_for_room::v3::IncomingRequest>,
|
||||||
) -> Result<add_backup_keys_for_room::v3::Response> {
|
) -> Result<add_backup_keys_for_room::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
|
@ -207,7 +207,7 @@ pub async fn add_backup_keys_for_room_route(
|
||||||
/// - Adds the keys to the backup
|
/// - Adds the keys to the backup
|
||||||
/// - Returns the new number of keys in this backup and the etag
|
/// - Returns the new number of keys in this backup and the etag
|
||||||
pub 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>,
|
body: Ruma<add_backup_keys_for_session::v3::IncomingRequest>,
|
||||||
) -> Result<add_backup_keys_for_session::v3::Response> {
|
) -> Result<add_backup_keys_for_session::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
|
@ -246,7 +246,7 @@ pub async fn add_backup_keys_for_session_route(
|
||||||
///
|
///
|
||||||
/// Retrieves all keys from the backup.
|
/// Retrieves all keys from the backup.
|
||||||
pub async fn get_backup_keys_route(
|
pub async fn get_backup_keys_route(
|
||||||
body: Ruma<get_backup_keys::v3::Request>,
|
body: Ruma<get_backup_keys::v3::IncomingRequest>,
|
||||||
) -> Result<get_backup_keys::v3::Response> {
|
) -> Result<get_backup_keys::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
|
@ -259,7 +259,7 @@ pub async fn get_backup_keys_route(
|
||||||
///
|
///
|
||||||
/// Retrieves all keys from the backup for a given room.
|
/// Retrieves all keys from the backup for a given room.
|
||||||
pub 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>,
|
body: Ruma<get_backup_keys_for_room::v3::IncomingRequest>,
|
||||||
) -> Result<get_backup_keys_for_room::v3::Response> {
|
) -> Result<get_backup_keys_for_room::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
|
@ -274,7 +274,7 @@ pub async fn get_backup_keys_for_room_route(
|
||||||
///
|
///
|
||||||
/// Retrieves a key from the backup.
|
/// Retrieves a key from the backup.
|
||||||
pub 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>,
|
body: Ruma<get_backup_keys_for_session::v3::IncomingRequest>,
|
||||||
) -> Result<get_backup_keys_for_session::v3::Response> {
|
) -> Result<get_backup_keys_for_session::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
|
@ -293,7 +293,7 @@ pub async fn get_backup_keys_for_session_route(
|
||||||
///
|
///
|
||||||
/// Delete the keys from the backup.
|
/// Delete the keys from the backup.
|
||||||
pub async fn delete_backup_keys_route(
|
pub async fn delete_backup_keys_route(
|
||||||
body: Ruma<delete_backup_keys::v3::Request>,
|
body: Ruma<delete_backup_keys::v3::IncomingRequest>,
|
||||||
) -> Result<delete_backup_keys::v3::Response> {
|
) -> Result<delete_backup_keys::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
|
@ -316,7 +316,7 @@ pub async fn delete_backup_keys_route(
|
||||||
///
|
///
|
||||||
/// Delete the keys from the backup for a given room.
|
/// Delete the keys from the backup for a given room.
|
||||||
pub 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>,
|
body: Ruma<delete_backup_keys_for_room::v3::IncomingRequest>,
|
||||||
) -> Result<delete_backup_keys_for_room::v3::Response> {
|
) -> Result<delete_backup_keys_for_room::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
|
@ -339,7 +339,7 @@ pub async fn delete_backup_keys_for_room_route(
|
||||||
///
|
///
|
||||||
/// Delete a key from the backup.
|
/// Delete a key from the backup.
|
||||||
pub 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>,
|
body: Ruma<delete_backup_keys_for_session::v3::IncomingRequest>,
|
||||||
) -> Result<delete_backup_keys_for_session::v3::Response> {
|
) -> Result<delete_backup_keys_for_session::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
|
|
|
@ -8,12 +8,18 @@ use std::collections::BTreeMap;
|
||||||
///
|
///
|
||||||
/// Get information on the supported feature set and other relevent capabilities of this server.
|
/// Get information on the supported feature set and other relevent capabilities of this server.
|
||||||
pub async fn get_capabilities_route(
|
pub async fn get_capabilities_route(
|
||||||
_body: Ruma<get_capabilities::v3::Request>,
|
_body: Ruma<get_capabilities::v3::IncomingRequest>,
|
||||||
) -> Result<get_capabilities::v3::Response> {
|
) -> Result<get_capabilities::v3::Response> {
|
||||||
let mut available = BTreeMap::new();
|
let mut available = BTreeMap::new();
|
||||||
|
if services().globals.allow_unstable_room_versions() {
|
||||||
|
for room_version in &services().globals.unstable_room_versions {
|
||||||
|
available.insert(room_version.clone(), RoomVersionStability::Stable);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
for room_version in &services().globals.unstable_room_versions {
|
for room_version in &services().globals.unstable_room_versions {
|
||||||
available.insert(room_version.clone(), RoomVersionStability::Unstable);
|
available.insert(room_version.clone(), RoomVersionStability::Unstable);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
for room_version in &services().globals.stable_room_versions {
|
for room_version in &services().globals.stable_room_versions {
|
||||||
available.insert(room_version.clone(), RoomVersionStability::Stable);
|
available.insert(room_version.clone(), RoomVersionStability::Stable);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ use serde_json::{json, value::RawValue as RawJsonValue};
|
||||||
///
|
///
|
||||||
/// Sets some account data for the sender user.
|
/// Sets some account data for the sender user.
|
||||||
pub async fn set_global_account_data_route(
|
pub async fn set_global_account_data_route(
|
||||||
body: Ruma<set_global_account_data::v3::Request>,
|
body: Ruma<set_global_account_data::v3::IncomingRequest>,
|
||||||
) -> Result<set_global_account_data::v3::Response> {
|
) -> Result<set_global_account_data::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ pub async fn set_global_account_data_route(
|
||||||
///
|
///
|
||||||
/// Sets some room account data for the sender user.
|
/// Sets some room account data for the sender user.
|
||||||
pub async fn set_room_account_data_route(
|
pub async fn set_room_account_data_route(
|
||||||
body: Ruma<set_room_account_data::v3::Request>,
|
body: Ruma<set_room_account_data::v3::IncomingRequest>,
|
||||||
) -> Result<set_room_account_data::v3::Response> {
|
) -> Result<set_room_account_data::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
|
@ -69,13 +69,13 @@ pub async fn set_room_account_data_route(
|
||||||
///
|
///
|
||||||
/// Gets some account data for the sender user.
|
/// Gets some account data for the sender user.
|
||||||
pub async fn get_global_account_data_route(
|
pub async fn get_global_account_data_route(
|
||||||
body: Ruma<get_global_account_data::v3::Request>,
|
body: Ruma<get_global_account_data::v3::IncomingRequest>,
|
||||||
) -> Result<get_global_account_data::v3::Response> {
|
) -> Result<get_global_account_data::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
let event: Box<RawJsonValue> = services()
|
let event: Box<RawJsonValue> = services()
|
||||||
.account_data
|
.account_data
|
||||||
.get(None, sender_user, body.event_type.to_string().into())?
|
.get(None, sender_user, body.event_type.clone().into())?
|
||||||
.ok_or(Error::BadRequest(ErrorKind::NotFound, "Data not found."))?;
|
.ok_or(Error::BadRequest(ErrorKind::NotFound, "Data not found."))?;
|
||||||
|
|
||||||
let account_data = serde_json::from_str::<ExtractGlobalEventContent>(event.get())
|
let account_data = serde_json::from_str::<ExtractGlobalEventContent>(event.get())
|
||||||
|
@ -89,13 +89,17 @@ pub async fn get_global_account_data_route(
|
||||||
///
|
///
|
||||||
/// Gets some room account data for the sender user.
|
/// Gets some room account data for the sender user.
|
||||||
pub async fn get_room_account_data_route(
|
pub async fn get_room_account_data_route(
|
||||||
body: Ruma<get_room_account_data::v3::Request>,
|
body: Ruma<get_room_account_data::v3::IncomingRequest>,
|
||||||
) -> Result<get_room_account_data::v3::Response> {
|
) -> Result<get_room_account_data::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
let event: Box<RawJsonValue> = services()
|
let event: Box<RawJsonValue> = services()
|
||||||
.account_data
|
.account_data
|
||||||
.get(Some(&body.room_id), sender_user, body.event_type.clone())?
|
.get(
|
||||||
|
Some(&body.room_id),
|
||||||
|
sender_user,
|
||||||
|
body.event_type.clone().into(),
|
||||||
|
)?
|
||||||
.ok_or(Error::BadRequest(ErrorKind::NotFound, "Data not found."))?;
|
.ok_or(Error::BadRequest(ErrorKind::NotFound, "Data not found."))?;
|
||||||
|
|
||||||
let account_data = serde_json::from_str::<ExtractRoomEventContent>(event.get())
|
let account_data = serde_json::from_str::<ExtractRoomEventContent>(event.get())
|
||||||
|
|
|
@ -3,7 +3,7 @@ use ruma::{
|
||||||
api::client::{context::get_context, error::ErrorKind, filter::LazyLoadOptions},
|
api::client::{context::get_context, error::ErrorKind, filter::LazyLoadOptions},
|
||||||
events::StateEventType,
|
events::StateEventType,
|
||||||
};
|
};
|
||||||
use std::collections::HashSet;
|
use std::{collections::HashSet, convert::TryFrom};
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
|
|
||||||
/// # `GET /_matrix/client/r0/rooms/{roomId}/context`
|
/// # `GET /_matrix/client/r0/rooms/{roomId}/context`
|
||||||
|
@ -13,7 +13,7 @@ use tracing::error;
|
||||||
/// - Only works if the user is joined (TODO: always allow, but only show events if the user was
|
/// - Only works if the user is joined (TODO: always allow, but only show events if the user was
|
||||||
/// joined, depending on history_visibility)
|
/// joined, depending on history_visibility)
|
||||||
pub async fn get_context_route(
|
pub async fn get_context_route(
|
||||||
body: Ruma<get_context::v3::Request>,
|
body: Ruma<get_context::v3::IncomingRequest>,
|
||||||
) -> Result<get_context::v3::Response> {
|
) -> Result<get_context::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
||||||
|
@ -27,20 +27,21 @@ pub async fn get_context_route(
|
||||||
|
|
||||||
let mut lazy_loaded = HashSet::new();
|
let mut lazy_loaded = HashSet::new();
|
||||||
|
|
||||||
let base_token = services()
|
let base_pdu_id = services()
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.get_pdu_count(&body.event_id)?
|
.get_pdu_id(&body.event_id)?
|
||||||
.ok_or(Error::BadRequest(
|
.ok_or(Error::BadRequest(
|
||||||
ErrorKind::NotFound,
|
ErrorKind::NotFound,
|
||||||
"Base event id not found.",
|
"Base event id not found.",
|
||||||
))?;
|
))?;
|
||||||
|
|
||||||
let base_event =
|
let base_token = services().rooms.timeline.pdu_count(&base_pdu_id)?;
|
||||||
services()
|
|
||||||
|
let base_event = services()
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.get_pdu(&body.event_id)?
|
.get_pdu_from_id(&base_pdu_id)?
|
||||||
.ok_or(Error::BadRequest(
|
.ok_or(Error::BadRequest(
|
||||||
ErrorKind::NotFound,
|
ErrorKind::NotFound,
|
||||||
"Base event not found.",
|
"Base event not found.",
|
||||||
|
@ -50,12 +51,12 @@ pub async fn get_context_route(
|
||||||
|
|
||||||
if !services()
|
if !services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_cache
|
||||||
.user_can_see_event(sender_user, &room_id, &body.event_id)?
|
.is_joined(sender_user, &room_id)?
|
||||||
{
|
{
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::Forbidden,
|
ErrorKind::Forbidden,
|
||||||
"You don't have permission to view this event.",
|
"You don't have permission to view this room.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,24 +70,19 @@ pub async fn get_context_route(
|
||||||
lazy_loaded.insert(base_event.sender.as_str().to_owned());
|
lazy_loaded.insert(base_event.sender.as_str().to_owned());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use limit with maximum 100
|
|
||||||
let limit = u64::from(body.limit).min(100) as usize;
|
|
||||||
|
|
||||||
let base_event = base_event.to_room_event();
|
let base_event = base_event.to_room_event();
|
||||||
|
|
||||||
let events_before: Vec<_> = services()
|
let events_before: Vec<_> = services()
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.pdus_until(sender_user, &room_id, base_token)?
|
.pdus_until(sender_user, &room_id, base_token)?
|
||||||
.take(limit / 2)
|
.take(
|
||||||
|
u32::try_from(body.limit).map_err(|_| {
|
||||||
|
Error::BadRequest(ErrorKind::InvalidParam, "Limit value is invalid.")
|
||||||
|
})? as usize
|
||||||
|
/ 2,
|
||||||
|
)
|
||||||
.filter_map(|r| r.ok()) // Remove buggy events
|
.filter_map(|r| r.ok()) // Remove buggy events
|
||||||
.filter(|(_, pdu)| {
|
|
||||||
services()
|
|
||||||
.rooms
|
|
||||||
.state_accessor
|
|
||||||
.user_can_see_event(sender_user, &room_id, &pdu.event_id)
|
|
||||||
.unwrap_or(false)
|
|
||||||
})
|
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
for (_, event) in &events_before {
|
for (_, event) in &events_before {
|
||||||
|
@ -103,8 +99,8 @@ pub async fn get_context_route(
|
||||||
|
|
||||||
let start_token = events_before
|
let start_token = events_before
|
||||||
.last()
|
.last()
|
||||||
.map(|(count, _)| count.stringify())
|
.and_then(|(pdu_id, _)| services().rooms.timeline.pdu_count(pdu_id).ok())
|
||||||
.unwrap_or_else(|| base_token.stringify());
|
.map(|count| count.to_string());
|
||||||
|
|
||||||
let events_before: Vec<_> = events_before
|
let events_before: Vec<_> = events_before
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -115,15 +111,13 @@ pub async fn get_context_route(
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.pdus_after(sender_user, &room_id, base_token)?
|
.pdus_after(sender_user, &room_id, base_token)?
|
||||||
.take(limit / 2)
|
.take(
|
||||||
|
u32::try_from(body.limit).map_err(|_| {
|
||||||
|
Error::BadRequest(ErrorKind::InvalidParam, "Limit value is invalid.")
|
||||||
|
})? as usize
|
||||||
|
/ 2,
|
||||||
|
)
|
||||||
.filter_map(|r| r.ok()) // Remove buggy events
|
.filter_map(|r| r.ok()) // Remove buggy events
|
||||||
.filter(|(_, pdu)| {
|
|
||||||
services()
|
|
||||||
.rooms
|
|
||||||
.state_accessor
|
|
||||||
.user_can_see_event(sender_user, &room_id, &pdu.event_id)
|
|
||||||
.unwrap_or(false)
|
|
||||||
})
|
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
for (_, event) in &events_after {
|
for (_, event) in &events_after {
|
||||||
|
@ -159,8 +153,8 @@ pub async fn get_context_route(
|
||||||
|
|
||||||
let end_token = events_after
|
let end_token = events_after
|
||||||
.last()
|
.last()
|
||||||
.map(|(count, _)| count.stringify())
|
.and_then(|(pdu_id, _)| services().rooms.timeline.pdu_count(pdu_id).ok())
|
||||||
.unwrap_or_else(|| base_token.stringify());
|
.map(|count| count.to_string());
|
||||||
|
|
||||||
let events_after: Vec<_> = events_after
|
let events_after: Vec<_> = events_after
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -197,8 +191,8 @@ pub async fn get_context_route(
|
||||||
}
|
}
|
||||||
|
|
||||||
let resp = get_context::v3::Response {
|
let resp = get_context::v3::Response {
|
||||||
start: Some(start_token),
|
start: start_token,
|
||||||
end: Some(end_token),
|
end: end_token,
|
||||||
events_before,
|
events_before,
|
||||||
event: Some(base_event),
|
event: Some(base_event),
|
||||||
events_after,
|
events_after,
|
||||||
|
|
|
@ -28,7 +28,7 @@ pub async fn get_devices_route(
|
||||||
///
|
///
|
||||||
/// Get metadata on a single device of the sender user.
|
/// Get metadata on a single device of the sender user.
|
||||||
pub async fn get_device_route(
|
pub async fn get_device_route(
|
||||||
body: Ruma<get_device::v3::Request>,
|
body: Ruma<get_device::v3::IncomingRequest>,
|
||||||
) -> Result<get_device::v3::Response> {
|
) -> Result<get_device::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ pub async fn get_device_route(
|
||||||
///
|
///
|
||||||
/// Updates the metadata on a given device of the sender user.
|
/// Updates the metadata on a given device of the sender user.
|
||||||
pub async fn update_device_route(
|
pub async fn update_device_route(
|
||||||
body: Ruma<update_device::v3::Request>,
|
body: Ruma<update_device::v3::IncomingRequest>,
|
||||||
) -> Result<update_device::v3::Response> {
|
) -> Result<update_device::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ pub async fn update_device_route(
|
||||||
/// - Forgets to-device events
|
/// - Forgets to-device events
|
||||||
/// - Triggers device list updates
|
/// - Triggers device list updates
|
||||||
pub async fn delete_device_route(
|
pub async fn delete_device_route(
|
||||||
body: Ruma<delete_device::v3::Request>,
|
body: Ruma<delete_device::v3::IncomingRequest>,
|
||||||
) -> Result<delete_device::v3::Response> {
|
) -> Result<delete_device::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
||||||
|
@ -126,7 +126,7 @@ pub async fn delete_device_route(
|
||||||
/// - Forgets to-device events
|
/// - Forgets to-device events
|
||||||
/// - Triggers device list updates
|
/// - Triggers device list updates
|
||||||
pub async fn delete_devices_route(
|
pub async fn delete_devices_route(
|
||||||
body: Ruma<delete_devices::v3::Request>,
|
body: Ruma<delete_devices::v3::IncomingRequest>,
|
||||||
) -> Result<delete_devices::v3::Response> {
|
) -> Result<delete_devices::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
||||||
|
|
|
@ -11,15 +11,18 @@ use ruma::{
|
||||||
},
|
},
|
||||||
federation,
|
federation,
|
||||||
},
|
},
|
||||||
directory::{Filter, PublicRoomJoinRule, PublicRoomsChunk, RoomNetwork},
|
directory::{
|
||||||
|
Filter, IncomingFilter, IncomingRoomNetwork, PublicRoomJoinRule, PublicRoomsChunk,
|
||||||
|
RoomNetwork,
|
||||||
|
},
|
||||||
events::{
|
events::{
|
||||||
room::{
|
room::{
|
||||||
avatar::RoomAvatarEventContent,
|
avatar::RoomAvatarEventContent,
|
||||||
canonical_alias::RoomCanonicalAliasEventContent,
|
canonical_alias::RoomCanonicalAliasEventContent,
|
||||||
create::RoomCreateEventContent,
|
|
||||||
guest_access::{GuestAccess, RoomGuestAccessEventContent},
|
guest_access::{GuestAccess, RoomGuestAccessEventContent},
|
||||||
history_visibility::{HistoryVisibility, RoomHistoryVisibilityEventContent},
|
history_visibility::{HistoryVisibility, RoomHistoryVisibilityEventContent},
|
||||||
join_rules::{JoinRule, RoomJoinRulesEventContent},
|
join_rules::{JoinRule, RoomJoinRulesEventContent},
|
||||||
|
name::RoomNameEventContent,
|
||||||
topic::RoomTopicEventContent,
|
topic::RoomTopicEventContent,
|
||||||
},
|
},
|
||||||
StateEventType,
|
StateEventType,
|
||||||
|
@ -34,7 +37,7 @@ use tracing::{error, info, warn};
|
||||||
///
|
///
|
||||||
/// - Rooms are ordered by the number of joined members
|
/// - Rooms are ordered by the number of joined members
|
||||||
pub async fn get_public_rooms_filtered_route(
|
pub async fn get_public_rooms_filtered_route(
|
||||||
body: Ruma<get_public_rooms_filtered::v3::Request>,
|
body: Ruma<get_public_rooms_filtered::v3::IncomingRequest>,
|
||||||
) -> Result<get_public_rooms_filtered::v3::Response> {
|
) -> Result<get_public_rooms_filtered::v3::Response> {
|
||||||
get_public_rooms_filtered_helper(
|
get_public_rooms_filtered_helper(
|
||||||
body.server.as_deref(),
|
body.server.as_deref(),
|
||||||
|
@ -52,14 +55,14 @@ pub async fn get_public_rooms_filtered_route(
|
||||||
///
|
///
|
||||||
/// - Rooms are ordered by the number of joined members
|
/// - Rooms are ordered by the number of joined members
|
||||||
pub async fn get_public_rooms_route(
|
pub async fn get_public_rooms_route(
|
||||||
body: Ruma<get_public_rooms::v3::Request>,
|
body: Ruma<get_public_rooms::v3::IncomingRequest>,
|
||||||
) -> Result<get_public_rooms::v3::Response> {
|
) -> Result<get_public_rooms::v3::Response> {
|
||||||
let response = get_public_rooms_filtered_helper(
|
let response = get_public_rooms_filtered_helper(
|
||||||
body.server.as_deref(),
|
body.server.as_deref(),
|
||||||
body.limit,
|
body.limit,
|
||||||
body.since.as_deref(),
|
body.since.as_deref(),
|
||||||
&Filter::default(),
|
&IncomingFilter::default(),
|
||||||
&RoomNetwork::Matrix,
|
&IncomingRoomNetwork::Matrix,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
@ -77,15 +80,10 @@ pub async fn get_public_rooms_route(
|
||||||
///
|
///
|
||||||
/// - TODO: Access control checks
|
/// - TODO: Access control checks
|
||||||
pub async fn set_room_visibility_route(
|
pub async fn set_room_visibility_route(
|
||||||
body: Ruma<set_room_visibility::v3::Request>,
|
body: Ruma<set_room_visibility::v3::IncomingRequest>,
|
||||||
) -> Result<set_room_visibility::v3::Response> {
|
) -> Result<set_room_visibility::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
if !services().rooms.metadata.exists(&body.room_id)? {
|
|
||||||
// Return 404 if the room doesn't exist
|
|
||||||
return Err(Error::BadRequest(ErrorKind::NotFound, "Room not found"));
|
|
||||||
}
|
|
||||||
|
|
||||||
match &body.visibility {
|
match &body.visibility {
|
||||||
room::Visibility::Public => {
|
room::Visibility::Public => {
|
||||||
services().rooms.directory.set_public(&body.room_id)?;
|
services().rooms.directory.set_public(&body.room_id)?;
|
||||||
|
@ -107,13 +105,8 @@ pub async fn set_room_visibility_route(
|
||||||
///
|
///
|
||||||
/// Gets the visibility of a given room in the room directory.
|
/// Gets the visibility of a given room in the room directory.
|
||||||
pub async fn get_room_visibility_route(
|
pub async fn get_room_visibility_route(
|
||||||
body: Ruma<get_room_visibility::v3::Request>,
|
body: Ruma<get_room_visibility::v3::IncomingRequest>,
|
||||||
) -> Result<get_room_visibility::v3::Response> {
|
) -> Result<get_room_visibility::v3::Response> {
|
||||||
if !services().rooms.metadata.exists(&body.room_id)? {
|
|
||||||
// Return 404 if the room doesn't exist
|
|
||||||
return Err(Error::BadRequest(ErrorKind::NotFound, "Room not found"));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(get_room_visibility::v3::Response {
|
Ok(get_room_visibility::v3::Response {
|
||||||
visibility: if services().rooms.directory.is_public_room(&body.room_id)? {
|
visibility: if services().rooms.directory.is_public_room(&body.room_id)? {
|
||||||
room::Visibility::Public
|
room::Visibility::Public
|
||||||
|
@ -127,8 +120,8 @@ pub(crate) async fn get_public_rooms_filtered_helper(
|
||||||
server: Option<&ServerName>,
|
server: Option<&ServerName>,
|
||||||
limit: Option<UInt>,
|
limit: Option<UInt>,
|
||||||
since: Option<&str>,
|
since: Option<&str>,
|
||||||
filter: &Filter,
|
filter: &IncomingFilter,
|
||||||
_network: &RoomNetwork,
|
_network: &IncomingRoomNetwork,
|
||||||
) -> Result<get_public_rooms_filtered::v3::Response> {
|
) -> Result<get_public_rooms_filtered::v3::Response> {
|
||||||
if let Some(other_server) =
|
if let Some(other_server) =
|
||||||
server.filter(|server| *server != services().globals.server_name().as_str())
|
server.filter(|server| *server != services().globals.server_name().as_str())
|
||||||
|
@ -139,10 +132,9 @@ pub(crate) async fn get_public_rooms_filtered_helper(
|
||||||
other_server,
|
other_server,
|
||||||
federation::directory::get_public_rooms_filtered::v1::Request {
|
federation::directory::get_public_rooms_filtered::v1::Request {
|
||||||
limit,
|
limit,
|
||||||
since: since.map(ToOwned::to_owned),
|
since,
|
||||||
filter: Filter {
|
filter: Filter {
|
||||||
generic_search_term: filter.generic_search_term.clone(),
|
generic_search_term: filter.generic_search_term.as_deref(),
|
||||||
room_types: filter.room_types.clone(),
|
|
||||||
},
|
},
|
||||||
room_network: RoomNetwork::Matrix,
|
room_network: RoomNetwork::Matrix,
|
||||||
},
|
},
|
||||||
|
@ -202,7 +194,17 @@ pub(crate) async fn get_public_rooms_filtered_helper(
|
||||||
Error::bad_database("Invalid canonical alias event in database.")
|
Error::bad_database("Invalid canonical alias event in database.")
|
||||||
})
|
})
|
||||||
})?,
|
})?,
|
||||||
name: services().rooms.state_accessor.get_name(&room_id)?,
|
name: services()
|
||||||
|
.rooms
|
||||||
|
.state_accessor
|
||||||
|
.room_state_get(&room_id, &StateEventType::RoomName, "")?
|
||||||
|
.map_or(Ok(None), |s| {
|
||||||
|
serde_json::from_str(s.content.get())
|
||||||
|
.map(|c: RoomNameEventContent| c.name)
|
||||||
|
.map_err(|_| {
|
||||||
|
Error::bad_database("Invalid room name event in database.")
|
||||||
|
})
|
||||||
|
})?,
|
||||||
num_joined_members: services()
|
num_joined_members: services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
|
@ -221,7 +223,6 @@ pub(crate) async fn get_public_rooms_filtered_helper(
|
||||||
serde_json::from_str(s.content.get())
|
serde_json::from_str(s.content.get())
|
||||||
.map(|c: RoomTopicEventContent| Some(c.topic))
|
.map(|c: RoomTopicEventContent| Some(c.topic))
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
error!("Invalid room topic event in database for room {}", room_id);
|
|
||||||
Error::bad_database("Invalid room topic event in database.")
|
Error::bad_database("Invalid room topic event in database.")
|
||||||
})
|
})
|
||||||
})?,
|
})?,
|
||||||
|
@ -286,20 +287,6 @@ pub(crate) async fn get_public_rooms_filtered_helper(
|
||||||
.transpose()?
|
.transpose()?
|
||||||
.flatten()
|
.flatten()
|
||||||
.ok_or_else(|| Error::bad_database("Missing room join rule event for room."))?,
|
.ok_or_else(|| Error::bad_database("Missing room join rule event for room."))?,
|
||||||
room_type: services()
|
|
||||||
.rooms
|
|
||||||
.state_accessor
|
|
||||||
.room_state_get(&room_id, &StateEventType::RoomCreate, "")?
|
|
||||||
.map(|s| {
|
|
||||||
serde_json::from_str::<RoomCreateEventContent>(s.content.get()).map_err(
|
|
||||||
|e| {
|
|
||||||
error!("Invalid room create event in database: {}", e);
|
|
||||||
Error::BadDatabase("Invalid room create event in database.")
|
|
||||||
},
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.transpose()?
|
|
||||||
.and_then(|e| e.room_type),
|
|
||||||
room_id,
|
room_id,
|
||||||
};
|
};
|
||||||
Ok(chunk)
|
Ok(chunk)
|
||||||
|
@ -351,7 +338,7 @@ pub(crate) async fn get_public_rooms_filtered_helper(
|
||||||
let prev_batch = if num_since == 0 {
|
let prev_batch = if num_since == 0 {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(format!("p{num_since}"))
|
Some(format!("p{}", num_since))
|
||||||
};
|
};
|
||||||
|
|
||||||
let next_batch = if chunk.len() < limit as usize {
|
let next_batch = if chunk.len() < limit as usize {
|
||||||
|
|
|
@ -10,7 +10,7 @@ use ruma::api::client::{
|
||||||
///
|
///
|
||||||
/// - A user can only access their own filters
|
/// - A user can only access their own filters
|
||||||
pub async fn get_filter_route(
|
pub async fn get_filter_route(
|
||||||
body: Ruma<get_filter::v3::Request>,
|
body: Ruma<get_filter::v3::IncomingRequest>,
|
||||||
) -> Result<get_filter::v3::Response> {
|
) -> Result<get_filter::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
let filter = match services().users.get_filter(sender_user, &body.filter_id)? {
|
let filter = match services().users.get_filter(sender_user, &body.filter_id)? {
|
||||||
|
@ -25,7 +25,7 @@ pub async fn get_filter_route(
|
||||||
///
|
///
|
||||||
/// Creates a new filter to be used by other endpoints.
|
/// Creates a new filter to be used by other endpoints.
|
||||||
pub async fn create_filter_route(
|
pub async fn create_filter_route(
|
||||||
body: Ruma<create_filter::v3::Request>,
|
body: Ruma<create_filter::v3::IncomingRequest>,
|
||||||
) -> Result<create_filter::v3::Response> {
|
) -> Result<create_filter::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
Ok(create_filter::v3::Response::new(
|
Ok(create_filter::v3::Response::new(
|
||||||
|
|
|
@ -14,14 +14,10 @@ use ruma::{
|
||||||
federation,
|
federation,
|
||||||
},
|
},
|
||||||
serde::Raw,
|
serde::Raw,
|
||||||
DeviceKeyAlgorithm, OwnedDeviceId, OwnedUserId, UserId,
|
DeviceId, DeviceKeyAlgorithm, UserId,
|
||||||
};
|
};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use std::{
|
use std::collections::{BTreeMap, HashMap, HashSet};
|
||||||
collections::{hash_map, BTreeMap, HashMap, HashSet},
|
|
||||||
time::{Duration, Instant},
|
|
||||||
};
|
|
||||||
use tracing::debug;
|
|
||||||
|
|
||||||
/// # `POST /_matrix/client/r0/keys/upload`
|
/// # `POST /_matrix/client/r0/keys/upload`
|
||||||
///
|
///
|
||||||
|
@ -69,7 +65,9 @@ pub async fn upload_keys_route(
|
||||||
/// - Always fetches users from other servers over federation
|
/// - Always fetches users from other servers over federation
|
||||||
/// - Gets master keys, self-signing keys, user signing keys and device keys.
|
/// - Gets master keys, self-signing keys, user signing keys and device keys.
|
||||||
/// - The master and self-signing keys contain signatures that the user is allowed to see
|
/// - The master and self-signing keys contain signatures that the user is allowed to see
|
||||||
pub 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::IncomingRequest>,
|
||||||
|
) -> Result<get_keys::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
let response =
|
let response =
|
||||||
|
@ -95,7 +93,7 @@ pub async fn claim_keys_route(
|
||||||
///
|
///
|
||||||
/// - Requires UIAA to verify password
|
/// - Requires UIAA to verify password
|
||||||
pub async fn upload_signing_keys_route(
|
pub async fn upload_signing_keys_route(
|
||||||
body: Ruma<upload_signing_keys::v3::Request>,
|
body: Ruma<upload_signing_keys::v3::IncomingRequest>,
|
||||||
) -> Result<upload_signing_keys::v3::Response> {
|
) -> Result<upload_signing_keys::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
||||||
|
@ -136,7 +134,6 @@ pub async fn upload_signing_keys_route(
|
||||||
master_key,
|
master_key,
|
||||||
&body.self_signing_key,
|
&body.self_signing_key,
|
||||||
&body.user_signing_key,
|
&body.user_signing_key,
|
||||||
true, // notify so that other users see the new keys
|
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,12 +148,11 @@ pub async fn upload_signatures_route(
|
||||||
) -> Result<upload_signatures::v3::Response> {
|
) -> Result<upload_signatures::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
for (user_id, keys) in &body.signed_keys {
|
for (user_id, signed_keys) in &body.signed_keys {
|
||||||
for (key_id, key) in keys {
|
for (key_id, signed_key) in signed_keys {
|
||||||
let key = serde_json::to_value(key)
|
let signed_key = serde_json::to_value(signed_key).unwrap();
|
||||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Invalid key JSON"))?;
|
|
||||||
|
|
||||||
for signature in key
|
for signature in signed_key
|
||||||
.get("signatures")
|
.get("signatures")
|
||||||
.ok_or(Error::BadRequest(
|
.ok_or(Error::BadRequest(
|
||||||
ErrorKind::InvalidParam,
|
ErrorKind::InvalidParam,
|
||||||
|
@ -205,7 +201,7 @@ pub async fn upload_signatures_route(
|
||||||
///
|
///
|
||||||
/// - TODO: left users
|
/// - TODO: left users
|
||||||
pub async fn get_key_changes_route(
|
pub async fn get_key_changes_route(
|
||||||
body: Ruma<get_key_changes::v3::Request>,
|
body: Ruma<get_key_changes::v3::IncomingRequest>,
|
||||||
) -> Result<get_key_changes::v3::Response> {
|
) -> Result<get_key_changes::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
|
@ -238,7 +234,7 @@ pub async fn get_key_changes_route(
|
||||||
services()
|
services()
|
||||||
.users
|
.users
|
||||||
.keys_changed(
|
.keys_changed(
|
||||||
room_id.as_ref(),
|
&room_id.to_string(),
|
||||||
body.from.parse().map_err(|_| {
|
body.from.parse().map_err(|_| {
|
||||||
Error::BadRequest(ErrorKind::InvalidParam, "Invalid `from`.")
|
Error::BadRequest(ErrorKind::InvalidParam, "Invalid `from`.")
|
||||||
})?,
|
})?,
|
||||||
|
@ -257,7 +253,7 @@ pub async fn get_key_changes_route(
|
||||||
|
|
||||||
pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool>(
|
pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool>(
|
||||||
sender_user: Option<&UserId>,
|
sender_user: Option<&UserId>,
|
||||||
device_keys_input: &BTreeMap<OwnedUserId, Vec<OwnedDeviceId>>,
|
device_keys_input: &BTreeMap<Box<UserId>, Vec<Box<DeviceId>>>,
|
||||||
allowed_signatures: F,
|
allowed_signatures: F,
|
||||||
) -> Result<get_keys::v3::Response> {
|
) -> Result<get_keys::v3::Response> {
|
||||||
let mut master_keys = BTreeMap::new();
|
let mut master_keys = BTreeMap::new();
|
||||||
|
@ -268,7 +264,7 @@ pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool>(
|
||||||
let mut get_over_federation = HashMap::new();
|
let mut get_over_federation = HashMap::new();
|
||||||
|
|
||||||
for (user_id, device_ids) in device_keys_input {
|
for (user_id, device_ids) in device_keys_input {
|
||||||
let user_id: &UserId = user_id;
|
let user_id: &UserId = &**user_id;
|
||||||
|
|
||||||
if user_id.server_name() != services().globals.server_name() {
|
if user_id.server_name() != services().globals.server_name() {
|
||||||
get_over_federation
|
get_over_federation
|
||||||
|
@ -316,17 +312,15 @@ pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(master_key) =
|
if let Some(master_key) = services()
|
||||||
services()
|
|
||||||
.users
|
.users
|
||||||
.get_master_key(sender_user, user_id, &allowed_signatures)?
|
.get_master_key(user_id, &allowed_signatures)?
|
||||||
{
|
{
|
||||||
master_keys.insert(user_id.to_owned(), master_key);
|
master_keys.insert(user_id.to_owned(), master_key);
|
||||||
}
|
}
|
||||||
if let Some(self_signing_key) =
|
if let Some(self_signing_key) = services()
|
||||||
services()
|
|
||||||
.users
|
.users
|
||||||
.get_self_signing_key(sender_user, user_id, &allowed_signatures)?
|
.get_self_signing_key(user_id, &allowed_signatures)?
|
||||||
{
|
{
|
||||||
self_signing_keys.insert(user_id.to_owned(), self_signing_key);
|
self_signing_keys.insert(user_id.to_owned(), self_signing_key);
|
||||||
}
|
}
|
||||||
|
@ -339,96 +333,36 @@ pub(crate) async fn get_keys_helper<F: Fn(&UserId) -> bool>(
|
||||||
|
|
||||||
let mut failures = BTreeMap::new();
|
let mut failures = BTreeMap::new();
|
||||||
|
|
||||||
let back_off = |id| match services()
|
|
||||||
.globals
|
|
||||||
.bad_query_ratelimiter
|
|
||||||
.write()
|
|
||||||
.unwrap()
|
|
||||||
.entry(id)
|
|
||||||
{
|
|
||||||
hash_map::Entry::Vacant(e) => {
|
|
||||||
e.insert((Instant::now(), 1));
|
|
||||||
}
|
|
||||||
hash_map::Entry::Occupied(mut e) => *e.get_mut() = (Instant::now(), e.get().1 + 1),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut futures: FuturesUnordered<_> = get_over_federation
|
let mut futures: FuturesUnordered<_> = get_over_federation
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(server, vec)| async move {
|
.map(|(server, vec)| async move {
|
||||||
if let Some((time, tries)) = services()
|
|
||||||
.globals
|
|
||||||
.bad_query_ratelimiter
|
|
||||||
.read()
|
|
||||||
.unwrap()
|
|
||||||
.get(server)
|
|
||||||
{
|
|
||||||
// Exponential backoff
|
|
||||||
let mut min_elapsed_duration = Duration::from_secs(30) * (*tries) * (*tries);
|
|
||||||
if min_elapsed_duration > Duration::from_secs(60 * 60 * 24) {
|
|
||||||
min_elapsed_duration = Duration::from_secs(60 * 60 * 24);
|
|
||||||
}
|
|
||||||
|
|
||||||
if time.elapsed() < min_elapsed_duration {
|
|
||||||
debug!("Backing off query from {:?}", server);
|
|
||||||
return (
|
|
||||||
server,
|
|
||||||
Err(Error::BadServerResponse("bad query, still backing off")),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut device_keys_input_fed = BTreeMap::new();
|
let mut device_keys_input_fed = BTreeMap::new();
|
||||||
for (user_id, keys) in vec {
|
for (user_id, keys) in vec {
|
||||||
device_keys_input_fed.insert(user_id.to_owned(), keys.clone());
|
device_keys_input_fed.insert(user_id.to_owned(), keys.clone());
|
||||||
}
|
}
|
||||||
(
|
(
|
||||||
server,
|
server,
|
||||||
tokio::time::timeout(
|
services()
|
||||||
Duration::from_secs(25),
|
.sending
|
||||||
services().sending.send_federation_request(
|
.send_federation_request(
|
||||||
server,
|
server,
|
||||||
federation::keys::get_keys::v1::Request {
|
federation::keys::get_keys::v1::Request {
|
||||||
device_keys: device_keys_input_fed,
|
device_keys: device_keys_input_fed,
|
||||||
},
|
},
|
||||||
),
|
|
||||||
)
|
)
|
||||||
.await
|
.await,
|
||||||
.map_err(|_e| Error::BadServerResponse("Query took too long")),
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
while let Some((server, response)) = futures.next().await {
|
while let Some((server, response)) = futures.next().await {
|
||||||
match response {
|
match response {
|
||||||
Ok(Ok(response)) => {
|
Ok(response) => {
|
||||||
for (user, masterkey) in response.master_keys {
|
master_keys.extend(response.master_keys);
|
||||||
let (master_key_id, mut master_key) =
|
|
||||||
services().users.parse_master_key(&user, &masterkey)?;
|
|
||||||
|
|
||||||
if let Some(our_master_key) = services().users.get_key(
|
|
||||||
&master_key_id,
|
|
||||||
sender_user,
|
|
||||||
&user,
|
|
||||||
&allowed_signatures,
|
|
||||||
)? {
|
|
||||||
let (_, our_master_key) =
|
|
||||||
services().users.parse_master_key(&user, &our_master_key)?;
|
|
||||||
master_key.signatures.extend(our_master_key.signatures);
|
|
||||||
}
|
|
||||||
let json = serde_json::to_value(master_key).expect("to_value always works");
|
|
||||||
let raw = serde_json::from_value(json).expect("Raw::from_value always works");
|
|
||||||
services().users.add_cross_signing_keys(
|
|
||||||
&user, &raw, &None, &None,
|
|
||||||
false, // Dont notify. A notification would trigger another key request resulting in an endless loop
|
|
||||||
)?;
|
|
||||||
master_keys.insert(user, raw);
|
|
||||||
}
|
|
||||||
|
|
||||||
self_signing_keys.extend(response.self_signing_keys);
|
self_signing_keys.extend(response.self_signing_keys);
|
||||||
device_keys.extend(response.device_keys);
|
device_keys.extend(response.device_keys);
|
||||||
}
|
}
|
||||||
_ => {
|
Err(_e) => {
|
||||||
back_off(server.to_owned());
|
|
||||||
failures.insert(server.to_string(), json!({}));
|
failures.insert(server.to_string(), json!({}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -462,7 +396,7 @@ fn add_unsigned_device_display_name(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn claim_keys_helper(
|
pub(crate) async fn claim_keys_helper(
|
||||||
one_time_keys_input: &BTreeMap<OwnedUserId, BTreeMap<OwnedDeviceId, DeviceKeyAlgorithm>>,
|
one_time_keys_input: &BTreeMap<Box<UserId>, BTreeMap<Box<DeviceId>, DeviceKeyAlgorithm>>,
|
||||||
) -> Result<claim_keys::v3::Response> {
|
) -> Result<claim_keys::v3::Response> {
|
||||||
let mut one_time_keys = BTreeMap::new();
|
let mut one_time_keys = BTreeMap::new();
|
||||||
|
|
||||||
|
@ -493,16 +427,13 @@ pub(crate) async fn claim_keys_helper(
|
||||||
|
|
||||||
let mut failures = BTreeMap::new();
|
let mut failures = BTreeMap::new();
|
||||||
|
|
||||||
let mut futures: FuturesUnordered<_> = get_over_federation
|
for (server, vec) in get_over_federation {
|
||||||
.into_iter()
|
|
||||||
.map(|(server, vec)| async move {
|
|
||||||
let mut one_time_keys_input_fed = BTreeMap::new();
|
let mut one_time_keys_input_fed = BTreeMap::new();
|
||||||
for (user_id, keys) in vec {
|
for (user_id, keys) in vec {
|
||||||
one_time_keys_input_fed.insert(user_id.clone(), keys.clone());
|
one_time_keys_input_fed.insert(user_id.clone(), keys.clone());
|
||||||
}
|
}
|
||||||
(
|
// Ignore failures
|
||||||
server,
|
if let Ok(keys) = services()
|
||||||
services()
|
|
||||||
.sending
|
.sending
|
||||||
.send_federation_request(
|
.send_federation_request(
|
||||||
server,
|
server,
|
||||||
|
@ -510,21 +441,13 @@ pub(crate) async fn claim_keys_helper(
|
||||||
one_time_keys: one_time_keys_input_fed,
|
one_time_keys: one_time_keys_input_fed,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await,
|
.await
|
||||||
)
|
{
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
while let Some((server, response)) = futures.next().await {
|
|
||||||
match response {
|
|
||||||
Ok(keys) => {
|
|
||||||
one_time_keys.extend(keys.one_time_keys);
|
one_time_keys.extend(keys.one_time_keys);
|
||||||
}
|
} else {
|
||||||
Err(_e) => {
|
|
||||||
failures.insert(server.to_string(), json!({}));
|
failures.insert(server.to_string(), json!({}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Ok(claim_keys::v3::Response {
|
Ok(claim_keys::v3::Response {
|
||||||
failures,
|
failures,
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
use crate::{service::media::FileMeta, services, utils, Error, Result, Ruma};
|
use crate::{service::media::FileMeta, services, utils, Error, Result, Ruma};
|
||||||
use ruma::api::client::{
|
use ruma::api::client::{
|
||||||
error::ErrorKind,
|
error::ErrorKind,
|
||||||
|
@ -29,7 +27,7 @@ pub async fn get_media_config_route(
|
||||||
/// - Some metadata will be saved in the database
|
/// - Some metadata will be saved in the database
|
||||||
/// - Media will be saved in the media/ directory
|
/// - Media will be saved in the media/ directory
|
||||||
pub async fn create_content_route(
|
pub async fn create_content_route(
|
||||||
body: Ruma<create_content::v3::Request>,
|
body: Ruma<create_content::v3::IncomingRequest>,
|
||||||
) -> Result<create_content::v3::Response> {
|
) -> Result<create_content::v3::Response> {
|
||||||
let mxc = format!(
|
let mxc = format!(
|
||||||
"mxc://{}/{}",
|
"mxc://{}/{}",
|
||||||
|
@ -51,7 +49,7 @@ pub async fn create_content_route(
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(create_content::v3::Response {
|
Ok(create_content::v3::Response {
|
||||||
content_uri: mxc.into(),
|
content_uri: mxc.try_into().expect("Invalid mxc:// URI"),
|
||||||
blurhash: None,
|
blurhash: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -59,7 +57,7 @@ pub async fn create_content_route(
|
||||||
pub async fn get_remote_content(
|
pub async fn get_remote_content(
|
||||||
mxc: &str,
|
mxc: &str,
|
||||||
server_name: &ruma::ServerName,
|
server_name: &ruma::ServerName,
|
||||||
media_id: String,
|
media_id: &str,
|
||||||
) -> Result<get_content::v3::Response, Error> {
|
) -> Result<get_content::v3::Response, Error> {
|
||||||
let content_response = services()
|
let content_response = services()
|
||||||
.sending
|
.sending
|
||||||
|
@ -67,10 +65,8 @@ pub async fn get_remote_content(
|
||||||
server_name,
|
server_name,
|
||||||
get_content::v3::Request {
|
get_content::v3::Request {
|
||||||
allow_remote: false,
|
allow_remote: false,
|
||||||
server_name: server_name.to_owned(),
|
server_name,
|
||||||
media_id,
|
media_id,
|
||||||
timeout_ms: Duration::from_secs(20),
|
|
||||||
allow_redirect: false,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -78,7 +74,7 @@ pub async fn get_remote_content(
|
||||||
services()
|
services()
|
||||||
.media
|
.media
|
||||||
.create(
|
.create(
|
||||||
mxc.to_owned(),
|
mxc.to_string(),
|
||||||
content_response.content_disposition.as_deref(),
|
content_response.content_disposition.as_deref(),
|
||||||
content_response.content_type.as_deref(),
|
content_response.content_type.as_deref(),
|
||||||
&content_response.file,
|
&content_response.file,
|
||||||
|
@ -94,7 +90,7 @@ pub async fn get_remote_content(
|
||||||
///
|
///
|
||||||
/// - Only allows federation if `allow_remote` is true
|
/// - Only allows federation if `allow_remote` is true
|
||||||
pub async fn get_content_route(
|
pub async fn get_content_route(
|
||||||
body: Ruma<get_content::v3::Request>,
|
body: Ruma<get_content::v3::IncomingRequest>,
|
||||||
) -> Result<get_content::v3::Response> {
|
) -> Result<get_content::v3::Response> {
|
||||||
let mxc = format!("mxc://{}/{}", body.server_name, body.media_id);
|
let mxc = format!("mxc://{}/{}", body.server_name, body.media_id);
|
||||||
|
|
||||||
|
@ -108,11 +104,10 @@ pub async fn get_content_route(
|
||||||
file,
|
file,
|
||||||
content_type,
|
content_type,
|
||||||
content_disposition,
|
content_disposition,
|
||||||
cross_origin_resource_policy: Some("cross-origin".to_owned()),
|
|
||||||
})
|
})
|
||||||
} else if &*body.server_name != services().globals.server_name() && body.allow_remote {
|
} else if &*body.server_name != services().globals.server_name() && body.allow_remote {
|
||||||
let remote_content_response =
|
let remote_content_response =
|
||||||
get_remote_content(&mxc, &body.server_name, body.media_id.clone()).await?;
|
get_remote_content(&mxc, &body.server_name, &body.media_id).await?;
|
||||||
Ok(remote_content_response)
|
Ok(remote_content_response)
|
||||||
} else {
|
} else {
|
||||||
Err(Error::BadRequest(ErrorKind::NotFound, "Media not found."))
|
Err(Error::BadRequest(ErrorKind::NotFound, "Media not found."))
|
||||||
|
@ -125,7 +120,7 @@ pub async fn get_content_route(
|
||||||
///
|
///
|
||||||
/// - Only allows federation if `allow_remote` is true
|
/// - Only allows federation if `allow_remote` is true
|
||||||
pub async fn get_content_as_filename_route(
|
pub async fn get_content_as_filename_route(
|
||||||
body: Ruma<get_content_as_filename::v3::Request>,
|
body: Ruma<get_content_as_filename::v3::IncomingRequest>,
|
||||||
) -> Result<get_content_as_filename::v3::Response> {
|
) -> Result<get_content_as_filename::v3::Response> {
|
||||||
let mxc = format!("mxc://{}/{}", body.server_name, body.media_id);
|
let mxc = format!("mxc://{}/{}", body.server_name, body.media_id);
|
||||||
|
|
||||||
|
@ -139,17 +134,15 @@ pub async fn get_content_as_filename_route(
|
||||||
file,
|
file,
|
||||||
content_type,
|
content_type,
|
||||||
content_disposition: Some(format!("inline; filename={}", body.filename)),
|
content_disposition: Some(format!("inline; filename={}", body.filename)),
|
||||||
cross_origin_resource_policy: Some("cross-origin".to_owned()),
|
|
||||||
})
|
})
|
||||||
} else if &*body.server_name != services().globals.server_name() && body.allow_remote {
|
} else if &*body.server_name != services().globals.server_name() && body.allow_remote {
|
||||||
let remote_content_response =
|
let remote_content_response =
|
||||||
get_remote_content(&mxc, &body.server_name, body.media_id.clone()).await?;
|
get_remote_content(&mxc, &body.server_name, &body.media_id).await?;
|
||||||
|
|
||||||
Ok(get_content_as_filename::v3::Response {
|
Ok(get_content_as_filename::v3::Response {
|
||||||
content_disposition: Some(format!("inline: filename={}", body.filename)),
|
content_disposition: Some(format!("inline: filename={}", body.filename)),
|
||||||
content_type: remote_content_response.content_type,
|
content_type: remote_content_response.content_type,
|
||||||
file: remote_content_response.file,
|
file: remote_content_response.file,
|
||||||
cross_origin_resource_policy: Some("cross-origin".to_owned()),
|
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(Error::BadRequest(ErrorKind::NotFound, "Media not found."))
|
Err(Error::BadRequest(ErrorKind::NotFound, "Media not found."))
|
||||||
|
@ -162,7 +155,7 @@ pub async fn get_content_as_filename_route(
|
||||||
///
|
///
|
||||||
/// - Only allows federation if `allow_remote` is true
|
/// - Only allows federation if `allow_remote` is true
|
||||||
pub async fn get_content_thumbnail_route(
|
pub async fn get_content_thumbnail_route(
|
||||||
body: Ruma<get_content_thumbnail::v3::Request>,
|
body: Ruma<get_content_thumbnail::v3::IncomingRequest>,
|
||||||
) -> Result<get_content_thumbnail::v3::Response> {
|
) -> Result<get_content_thumbnail::v3::Response> {
|
||||||
let mxc = format!("mxc://{}/{}", body.server_name, body.media_id);
|
let mxc = format!("mxc://{}/{}", body.server_name, body.media_id);
|
||||||
|
|
||||||
|
@ -181,11 +174,7 @@ pub async fn get_content_thumbnail_route(
|
||||||
)
|
)
|
||||||
.await?
|
.await?
|
||||||
{
|
{
|
||||||
Ok(get_content_thumbnail::v3::Response {
|
Ok(get_content_thumbnail::v3::Response { file, content_type })
|
||||||
file,
|
|
||||||
content_type,
|
|
||||||
cross_origin_resource_policy: Some("cross-origin".to_owned()),
|
|
||||||
})
|
|
||||||
} else if &*body.server_name != services().globals.server_name() && body.allow_remote {
|
} else if &*body.server_name != services().globals.server_name() && body.allow_remote {
|
||||||
let get_thumbnail_response = services()
|
let get_thumbnail_response = services()
|
||||||
.sending
|
.sending
|
||||||
|
@ -196,10 +185,8 @@ pub async fn get_content_thumbnail_route(
|
||||||
height: body.height,
|
height: body.height,
|
||||||
width: body.width,
|
width: body.width,
|
||||||
method: body.method.clone(),
|
method: body.method.clone(),
|
||||||
server_name: body.server_name.clone(),
|
server_name: &body.server_name,
|
||||||
media_id: body.media_id.clone(),
|
media_id: &body.media_id,
|
||||||
timeout_ms: Duration::from_secs(20),
|
|
||||||
allow_redirect: false,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,13 +1,10 @@
|
||||||
use crate::{
|
use crate::{service::pdu::PduBuilder, services, utils, Error, Result, Ruma};
|
||||||
service::{pdu::PduBuilder, rooms::timeline::PduCount},
|
|
||||||
services, utils, Error, Result, Ruma,
|
|
||||||
};
|
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::{
|
api::client::{
|
||||||
error::ErrorKind,
|
error::ErrorKind,
|
||||||
message::{get_message_events, send_message_event},
|
message::{get_message_events, send_message_event},
|
||||||
},
|
},
|
||||||
events::{StateEventType, TimelineEventType},
|
events::{RoomEventType, StateEventType},
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
collections::{BTreeMap, HashSet},
|
collections::{BTreeMap, HashSet},
|
||||||
|
@ -22,7 +19,7 @@ use std::{
|
||||||
/// - The only requirement for the content is that it has to be valid json
|
/// - 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
|
/// - Tries to send the event into the room, auth rules will determine if it is allowed
|
||||||
pub async fn send_message_event_route(
|
pub async fn send_message_event_route(
|
||||||
body: Ruma<send_message_event::v3::Request>,
|
body: Ruma<send_message_event::v3::IncomingRequest>,
|
||||||
) -> Result<send_message_event::v3::Response> {
|
) -> Result<send_message_event::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
let sender_device = body.sender_device.as_deref();
|
let sender_device = body.sender_device.as_deref();
|
||||||
|
@ -39,7 +36,7 @@ pub async fn send_message_event_route(
|
||||||
let state_lock = mutex_state.lock().await;
|
let state_lock = mutex_state.lock().await;
|
||||||
|
|
||||||
// Forbid m.room.encrypted if encryption is disabled
|
// Forbid m.room.encrypted if encryption is disabled
|
||||||
if TimelineEventType::RoomEncrypted == body.event_type.to_string().into()
|
if RoomEventType::RoomEncrypted == body.event_type.to_string().into()
|
||||||
&& !services().globals.allow_encryption()
|
&& !services().globals.allow_encryption()
|
||||||
{
|
{
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
|
@ -108,23 +105,34 @@ pub async fn send_message_event_route(
|
||||||
/// - Only works if the user is joined (TODO: always allow, but only show events where the user was
|
/// - Only works if the user is joined (TODO: always allow, but only show events where the user was
|
||||||
/// joined, depending on history_visibility)
|
/// joined, depending on history_visibility)
|
||||||
pub async fn get_message_events_route(
|
pub async fn get_message_events_route(
|
||||||
body: Ruma<get_message_events::v3::Request>,
|
body: Ruma<get_message_events::v3::IncomingRequest>,
|
||||||
) -> Result<get_message_events::v3::Response> {
|
) -> Result<get_message_events::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
|
if !services()
|
||||||
|
.rooms
|
||||||
|
.state_cache
|
||||||
|
.is_joined(sender_user, &body.room_id)?
|
||||||
|
{
|
||||||
|
return Err(Error::BadRequest(
|
||||||
|
ErrorKind::Forbidden,
|
||||||
|
"You don't have permission to view this room.",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
let from = match body.from.clone() {
|
let from = match body.from.clone() {
|
||||||
Some(from) => PduCount::try_from_string(&from)?,
|
Some(from) => from
|
||||||
|
.parse()
|
||||||
|
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Invalid `from` value."))?,
|
||||||
|
|
||||||
None => match body.dir {
|
None => match body.dir {
|
||||||
ruma::api::Direction::Forward => PduCount::min(),
|
get_message_events::v3::Direction::Forward => 0,
|
||||||
ruma::api::Direction::Backward => PduCount::max(),
|
get_message_events::v3::Direction::Backward => u64::MAX,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let to = body
|
let to = body.to.as_ref().map(|t| t.parse());
|
||||||
.to
|
|
||||||
.as_ref()
|
|
||||||
.and_then(|t| PduCount::try_from_string(t).ok());
|
|
||||||
|
|
||||||
services().rooms.lazy_loading.lazy_load_confirm_delivery(
|
services().rooms.lazy_loading.lazy_load_confirm_delivery(
|
||||||
sender_user,
|
sender_user,
|
||||||
|
@ -133,7 +141,8 @@ pub async fn get_message_events_route(
|
||||||
from,
|
from,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let limit = u64::from(body.limit).min(100) as usize;
|
// Use limit or else 10
|
||||||
|
let limit = body.limit.try_into().map_or(10_usize, |l: u32| l as usize);
|
||||||
|
|
||||||
let next_token;
|
let next_token;
|
||||||
|
|
||||||
|
@ -142,27 +151,25 @@ pub async fn get_message_events_route(
|
||||||
let mut lazy_loaded = HashSet::new();
|
let mut lazy_loaded = HashSet::new();
|
||||||
|
|
||||||
match body.dir {
|
match body.dir {
|
||||||
ruma::api::Direction::Forward => {
|
get_message_events::v3::Direction::Forward => {
|
||||||
let events_after: Vec<_> = services()
|
let events_after: Vec<_> = services()
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.pdus_after(sender_user, &body.room_id, from)?
|
.pdus_after(sender_user, &body.room_id, from)?
|
||||||
.take(limit)
|
.take(limit)
|
||||||
.filter_map(|r| r.ok()) // Filter out buggy events
|
.filter_map(|r| r.ok()) // Filter out buggy events
|
||||||
.filter(|(_, pdu)| {
|
.filter_map(|(pdu_id, pdu)| {
|
||||||
services()
|
services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.timeline
|
||||||
.user_can_see_event(sender_user, &body.room_id, &pdu.event_id)
|
.pdu_count(&pdu_id)
|
||||||
.unwrap_or(false)
|
.map(|pdu_count| (pdu_count, pdu))
|
||||||
|
.ok()
|
||||||
})
|
})
|
||||||
.take_while(|&(k, _)| Some(k) != to) // Stop at `to`
|
.take_while(|&(k, _)| Some(Ok(k)) != to) // Stop at `to`
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
for (_, event) in &events_after {
|
for (_, event) in &events_after {
|
||||||
/* TODO: Remove this when these are resolved:
|
|
||||||
* https://github.com/vector-im/element-android/issues/3417
|
|
||||||
* https://github.com/vector-im/element-web/issues/21034
|
|
||||||
if !services().rooms.lazy_loading.lazy_load_was_sent_before(
|
if !services().rooms.lazy_loading.lazy_load_was_sent_before(
|
||||||
sender_user,
|
sender_user,
|
||||||
sender_device,
|
sender_device,
|
||||||
|
@ -171,8 +178,6 @@ pub async fn get_message_events_route(
|
||||||
)? {
|
)? {
|
||||||
lazy_loaded.insert(event.sender.clone());
|
lazy_loaded.insert(event.sender.clone());
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
lazy_loaded.insert(event.sender.clone());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
next_token = events_after.last().map(|(count, _)| count).copied();
|
next_token = events_after.last().map(|(count, _)| count).copied();
|
||||||
|
@ -182,36 +187,29 @@ pub async fn get_message_events_route(
|
||||||
.map(|(_, pdu)| pdu.to_room_event())
|
.map(|(_, pdu)| pdu.to_room_event())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
resp.start = from.stringify();
|
resp.start = from.to_string();
|
||||||
resp.end = next_token.map(|count| count.stringify());
|
resp.end = next_token.map(|count| count.to_string());
|
||||||
resp.chunk = events_after;
|
resp.chunk = events_after;
|
||||||
}
|
}
|
||||||
ruma::api::Direction::Backward => {
|
get_message_events::v3::Direction::Backward => {
|
||||||
services()
|
|
||||||
.rooms
|
|
||||||
.timeline
|
|
||||||
.backfill_if_required(&body.room_id, from)
|
|
||||||
.await?;
|
|
||||||
let events_before: Vec<_> = services()
|
let events_before: Vec<_> = services()
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.pdus_until(sender_user, &body.room_id, from)?
|
.pdus_until(sender_user, &body.room_id, from)?
|
||||||
.take(limit)
|
.take(limit)
|
||||||
.filter_map(|r| r.ok()) // Filter out buggy events
|
.filter_map(|r| r.ok()) // Filter out buggy events
|
||||||
.filter(|(_, pdu)| {
|
.filter_map(|(pdu_id, pdu)| {
|
||||||
services()
|
services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.timeline
|
||||||
.user_can_see_event(sender_user, &body.room_id, &pdu.event_id)
|
.pdu_count(&pdu_id)
|
||||||
.unwrap_or(false)
|
.map(|pdu_count| (pdu_count, pdu))
|
||||||
|
.ok()
|
||||||
})
|
})
|
||||||
.take_while(|&(k, _)| Some(k) != to) // Stop at `to`
|
.take_while(|&(k, _)| Some(Ok(k)) != to) // Stop at `to`
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
for (_, event) in &events_before {
|
for (_, event) in &events_before {
|
||||||
/* TODO: Remove this when these are resolved:
|
|
||||||
* https://github.com/vector-im/element-android/issues/3417
|
|
||||||
* https://github.com/vector-im/element-web/issues/21034
|
|
||||||
if !services().rooms.lazy_loading.lazy_load_was_sent_before(
|
if !services().rooms.lazy_loading.lazy_load_was_sent_before(
|
||||||
sender_user,
|
sender_user,
|
||||||
sender_device,
|
sender_device,
|
||||||
|
@ -220,8 +218,6 @@ pub async fn get_message_events_route(
|
||||||
)? {
|
)? {
|
||||||
lazy_loaded.insert(event.sender.clone());
|
lazy_loaded.insert(event.sender.clone());
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
lazy_loaded.insert(event.sender.clone());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
next_token = events_before.last().map(|(count, _)| count).copied();
|
next_token = events_before.last().map(|(count, _)| count).copied();
|
||||||
|
@ -231,8 +227,8 @@ pub async fn get_message_events_route(
|
||||||
.map(|(_, pdu)| pdu.to_room_event())
|
.map(|(_, pdu)| pdu.to_room_event())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
resp.start = from.stringify();
|
resp.start = from.to_string();
|
||||||
resp.end = next_token.map(|count| count.stringify());
|
resp.end = next_token.map(|count| count.to_string());
|
||||||
resp.chunk = events_before;
|
resp.chunk = events_before;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -248,8 +244,6 @@ pub async fn get_message_events_route(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: enable again when we are sure clients can handle it
|
|
||||||
/*
|
|
||||||
if let Some(next_token) = next_token {
|
if let Some(next_token) = next_token {
|
||||||
services().rooms.lazy_loading.lazy_load_mark_sent(
|
services().rooms.lazy_loading.lazy_load_mark_sent(
|
||||||
sender_user,
|
sender_user,
|
||||||
|
@ -259,7 +253,6 @@ pub async fn get_message_events_route(
|
||||||
next_token,
|
next_token,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
Ok(resp)
|
Ok(resp)
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,17 +16,14 @@ mod profile;
|
||||||
mod push;
|
mod push;
|
||||||
mod read_marker;
|
mod read_marker;
|
||||||
mod redact;
|
mod redact;
|
||||||
mod relations;
|
|
||||||
mod report;
|
mod report;
|
||||||
mod room;
|
mod room;
|
||||||
mod search;
|
mod search;
|
||||||
mod session;
|
mod session;
|
||||||
mod space;
|
|
||||||
mod state;
|
mod state;
|
||||||
mod sync;
|
mod sync;
|
||||||
mod tag;
|
mod tag;
|
||||||
mod thirdparty;
|
mod thirdparty;
|
||||||
mod threads;
|
|
||||||
mod to_device;
|
mod to_device;
|
||||||
mod typing;
|
mod typing;
|
||||||
mod unversioned;
|
mod unversioned;
|
||||||
|
@ -51,17 +48,14 @@ pub use profile::*;
|
||||||
pub use push::*;
|
pub use push::*;
|
||||||
pub use read_marker::*;
|
pub use read_marker::*;
|
||||||
pub use redact::*;
|
pub use redact::*;
|
||||||
pub use relations::*;
|
|
||||||
pub use report::*;
|
pub use report::*;
|
||||||
pub use room::*;
|
pub use room::*;
|
||||||
pub use search::*;
|
pub use search::*;
|
||||||
pub use session::*;
|
pub use session::*;
|
||||||
pub use space::*;
|
|
||||||
pub use state::*;
|
pub use state::*;
|
||||||
pub use sync::*;
|
pub use sync::*;
|
||||||
pub use tag::*;
|
pub use tag::*;
|
||||||
pub use thirdparty::*;
|
pub use thirdparty::*;
|
||||||
pub use threads::*;
|
|
||||||
pub use to_device::*;
|
pub use to_device::*;
|
||||||
pub use typing::*;
|
pub use typing::*;
|
||||||
pub use unversioned::*;
|
pub use unversioned::*;
|
||||||
|
@ -69,6 +63,6 @@ pub use user_directory::*;
|
||||||
pub use voip::*;
|
pub use voip::*;
|
||||||
|
|
||||||
pub const DEVICE_ID_LENGTH: usize = 10;
|
pub const DEVICE_ID_LENGTH: usize = 10;
|
||||||
pub const TOKEN_LENGTH: usize = 32;
|
pub const TOKEN_LENGTH: usize = 256;
|
||||||
pub const SESSION_ID_LENGTH: usize = 32;
|
pub const SESSION_ID_LENGTH: usize = 256;
|
||||||
pub const AUTO_GEN_PASSWORD_LENGTH: usize = 15;
|
pub const AUTO_GEN_PASSWORD_LENGTH: usize = 15;
|
||||||
|
|
|
@ -1,15 +1,12 @@
|
||||||
use crate::{services, utils, Error, Result, Ruma};
|
use crate::{services, utils, Result, Ruma};
|
||||||
use ruma::api::client::{
|
use ruma::api::client::presence::{get_presence, set_presence};
|
||||||
error::ErrorKind,
|
|
||||||
presence::{get_presence, set_presence},
|
|
||||||
};
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
/// # `PUT /_matrix/client/r0/presence/{userId}/status`
|
/// # `PUT /_matrix/client/r0/presence/{userId}/status`
|
||||||
///
|
///
|
||||||
/// Sets the presence state of the sender user.
|
/// Sets the presence state of the sender user.
|
||||||
pub async fn set_presence_route(
|
pub async fn set_presence_route(
|
||||||
body: Ruma<set_presence::v3::Request>,
|
body: Ruma<set_presence::v3::IncomingRequest>,
|
||||||
) -> Result<set_presence::v3::Response> {
|
) -> Result<set_presence::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
|
@ -46,7 +43,7 @@ pub async fn set_presence_route(
|
||||||
///
|
///
|
||||||
/// - Only works if you share a room with the user
|
/// - Only works if you share a room with the user
|
||||||
pub async fn get_presence_route(
|
pub async fn get_presence_route(
|
||||||
body: Ruma<get_presence::v3::Request>,
|
body: Ruma<get_presence::v3::IncomingRequest>,
|
||||||
) -> Result<get_presence::v3::Response> {
|
) -> Result<get_presence::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
|
@ -82,9 +79,6 @@ pub async fn get_presence_route(
|
||||||
presence: presence.content.presence,
|
presence: presence.content.presence,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(Error::BadRequest(
|
todo!();
|
||||||
ErrorKind::NotFound,
|
|
||||||
"Presence state for this user was not found",
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ use ruma::{
|
||||||
},
|
},
|
||||||
federation::{self, query::get_profile_information::v1::ProfileField},
|
federation::{self, query::get_profile_information::v1::ProfileField},
|
||||||
},
|
},
|
||||||
events::{room::member::RoomMemberEventContent, StateEventType, TimelineEventType},
|
events::{room::member::RoomMemberEventContent, RoomEventType, StateEventType},
|
||||||
};
|
};
|
||||||
use serde_json::value::to_raw_value;
|
use serde_json::value::to_raw_value;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -20,7 +20,7 @@ use std::sync::Arc;
|
||||||
///
|
///
|
||||||
/// - Also makes sure other users receive the update using presence EDUs
|
/// - Also makes sure other users receive the update using presence EDUs
|
||||||
pub async fn set_displayname_route(
|
pub async fn set_displayname_route(
|
||||||
body: Ruma<set_display_name::v3::Request>,
|
body: Ruma<set_display_name::v3::IncomingRequest>,
|
||||||
) -> Result<set_display_name::v3::Response> {
|
) -> Result<set_display_name::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ pub async fn set_displayname_route(
|
||||||
.map(|room_id| {
|
.map(|room_id| {
|
||||||
Ok::<_, Error>((
|
Ok::<_, Error>((
|
||||||
PduBuilder {
|
PduBuilder {
|
||||||
event_type: TimelineEventType::RoomMember,
|
event_type: RoomEventType::RoomMember,
|
||||||
content: to_raw_value(&RoomMemberEventContent {
|
content: to_raw_value(&RoomMemberEventContent {
|
||||||
displayname: body.displayname.clone(),
|
displayname: body.displayname.clone(),
|
||||||
..serde_json::from_str(
|
..serde_json::from_str(
|
||||||
|
@ -121,7 +121,7 @@ pub async fn set_displayname_route(
|
||||||
///
|
///
|
||||||
/// - If user is on another server: Fetches displayname over federation
|
/// - If user is on another server: Fetches displayname over federation
|
||||||
pub async fn get_displayname_route(
|
pub async fn get_displayname_route(
|
||||||
body: Ruma<get_display_name::v3::Request>,
|
body: Ruma<get_display_name::v3::IncomingRequest>,
|
||||||
) -> Result<get_display_name::v3::Response> {
|
) -> Result<get_display_name::v3::Response> {
|
||||||
if body.user_id.server_name() != services().globals.server_name() {
|
if body.user_id.server_name() != services().globals.server_name() {
|
||||||
let response = services()
|
let response = services()
|
||||||
|
@ -129,8 +129,8 @@ pub async fn get_displayname_route(
|
||||||
.send_federation_request(
|
.send_federation_request(
|
||||||
body.user_id.server_name(),
|
body.user_id.server_name(),
|
||||||
federation::query::get_profile_information::v1::Request {
|
federation::query::get_profile_information::v1::Request {
|
||||||
user_id: body.user_id.clone(),
|
user_id: &body.user_id,
|
||||||
field: Some(ProfileField::DisplayName),
|
field: Some(&ProfileField::DisplayName),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -151,7 +151,7 @@ pub async fn get_displayname_route(
|
||||||
///
|
///
|
||||||
/// - Also makes sure other users receive the update using presence EDUs
|
/// - Also makes sure other users receive the update using presence EDUs
|
||||||
pub async fn set_avatar_url_route(
|
pub async fn set_avatar_url_route(
|
||||||
body: Ruma<set_avatar_url::v3::Request>,
|
body: Ruma<set_avatar_url::v3::IncomingRequest>,
|
||||||
) -> Result<set_avatar_url::v3::Response> {
|
) -> Result<set_avatar_url::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
|
@ -172,7 +172,7 @@ pub async fn set_avatar_url_route(
|
||||||
.map(|room_id| {
|
.map(|room_id| {
|
||||||
Ok::<_, Error>((
|
Ok::<_, Error>((
|
||||||
PduBuilder {
|
PduBuilder {
|
||||||
event_type: TimelineEventType::RoomMember,
|
event_type: RoomEventType::RoomMember,
|
||||||
content: to_raw_value(&RoomMemberEventContent {
|
content: to_raw_value(&RoomMemberEventContent {
|
||||||
avatar_url: body.avatar_url.clone(),
|
avatar_url: body.avatar_url.clone(),
|
||||||
..serde_json::from_str(
|
..serde_json::from_str(
|
||||||
|
@ -256,7 +256,7 @@ pub async fn set_avatar_url_route(
|
||||||
///
|
///
|
||||||
/// - If user is on another server: Fetches avatar_url and blurhash over federation
|
/// - If user is on another server: Fetches avatar_url and blurhash over federation
|
||||||
pub async fn get_avatar_url_route(
|
pub async fn get_avatar_url_route(
|
||||||
body: Ruma<get_avatar_url::v3::Request>,
|
body: Ruma<get_avatar_url::v3::IncomingRequest>,
|
||||||
) -> Result<get_avatar_url::v3::Response> {
|
) -> Result<get_avatar_url::v3::Response> {
|
||||||
if body.user_id.server_name() != services().globals.server_name() {
|
if body.user_id.server_name() != services().globals.server_name() {
|
||||||
let response = services()
|
let response = services()
|
||||||
|
@ -264,8 +264,8 @@ pub async fn get_avatar_url_route(
|
||||||
.send_federation_request(
|
.send_federation_request(
|
||||||
body.user_id.server_name(),
|
body.user_id.server_name(),
|
||||||
federation::query::get_profile_information::v1::Request {
|
federation::query::get_profile_information::v1::Request {
|
||||||
user_id: body.user_id.clone(),
|
user_id: &body.user_id,
|
||||||
field: Some(ProfileField::AvatarUrl),
|
field: Some(&ProfileField::AvatarUrl),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -288,7 +288,7 @@ pub async fn get_avatar_url_route(
|
||||||
///
|
///
|
||||||
/// - If user is on another server: Fetches profile over federation
|
/// - If user is on another server: Fetches profile over federation
|
||||||
pub async fn get_profile_route(
|
pub async fn get_profile_route(
|
||||||
body: Ruma<get_profile::v3::Request>,
|
body: Ruma<get_profile::v3::IncomingRequest>,
|
||||||
) -> Result<get_profile::v3::Response> {
|
) -> Result<get_profile::v3::Response> {
|
||||||
if body.user_id.server_name() != services().globals.server_name() {
|
if body.user_id.server_name() != services().globals.server_name() {
|
||||||
let response = services()
|
let response = services()
|
||||||
|
@ -296,7 +296,7 @@ pub async fn get_profile_route(
|
||||||
.send_federation_request(
|
.send_federation_request(
|
||||||
body.user_id.server_name(),
|
body.user_id.server_name(),
|
||||||
federation::query::get_profile_information::v1::Request {
|
federation::query::get_profile_information::v1::Request {
|
||||||
user_id: body.user_id.clone(),
|
user_id: &body.user_id,
|
||||||
field: None,
|
field: None,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -5,11 +5,11 @@ use ruma::{
|
||||||
push::{
|
push::{
|
||||||
delete_pushrule, get_pushers, get_pushrule, get_pushrule_actions, get_pushrule_enabled,
|
delete_pushrule, get_pushers, get_pushrule, get_pushrule_actions, get_pushrule_enabled,
|
||||||
get_pushrules_all, set_pusher, set_pushrule, set_pushrule_actions,
|
get_pushrules_all, set_pusher, set_pushrule, set_pushrule_actions,
|
||||||
set_pushrule_enabled, RuleScope,
|
set_pushrule_enabled, RuleKind,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
events::{push_rules::PushRulesEvent, GlobalAccountDataEventType},
|
events::{push_rules::PushRulesEvent, GlobalAccountDataEventType},
|
||||||
push::{InsertPushRuleError, RemovePushRuleError},
|
push::{ConditionalPushRuleInit, PatternedPushRuleInit, SimplePushRuleInit},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// # `GET /_matrix/client/r0/pushrules`
|
/// # `GET /_matrix/client/r0/pushrules`
|
||||||
|
@ -45,7 +45,7 @@ pub async fn get_pushrules_all_route(
|
||||||
///
|
///
|
||||||
/// Retrieves a single specified push rule for this user.
|
/// Retrieves a single specified push rule for this user.
|
||||||
pub async fn get_pushrule_route(
|
pub async fn get_pushrule_route(
|
||||||
body: Ruma<get_pushrule::v3::Request>,
|
body: Ruma<get_pushrule::v3::IncomingRequest>,
|
||||||
) -> Result<get_pushrule::v3::Response> {
|
) -> Result<get_pushrule::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
|
@ -65,10 +65,30 @@ pub async fn get_pushrule_route(
|
||||||
.map_err(|_| Error::bad_database("Invalid account data event in db."))?
|
.map_err(|_| Error::bad_database("Invalid account data event in db."))?
|
||||||
.content;
|
.content;
|
||||||
|
|
||||||
let rule = account_data
|
let global = account_data.global;
|
||||||
.global
|
let rule = match body.kind {
|
||||||
.get(body.kind.clone(), &body.rule_id)
|
RuleKind::Override => global
|
||||||
.map(Into::into);
|
.override_
|
||||||
|
.get(body.rule_id.as_str())
|
||||||
|
.map(|rule| rule.clone().into()),
|
||||||
|
RuleKind::Underride => global
|
||||||
|
.underride
|
||||||
|
.get(body.rule_id.as_str())
|
||||||
|
.map(|rule| rule.clone().into()),
|
||||||
|
RuleKind::Sender => global
|
||||||
|
.sender
|
||||||
|
.get(body.rule_id.as_str())
|
||||||
|
.map(|rule| rule.clone().into()),
|
||||||
|
RuleKind::Room => global
|
||||||
|
.room
|
||||||
|
.get(body.rule_id.as_str())
|
||||||
|
.map(|rule| rule.clone().into()),
|
||||||
|
RuleKind::Content => global
|
||||||
|
.content
|
||||||
|
.get(body.rule_id.as_str())
|
||||||
|
.map(|rule| rule.clone().into()),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
if let Some(rule) = rule {
|
if let Some(rule) = rule {
|
||||||
Ok(get_pushrule::v3::Response { rule })
|
Ok(get_pushrule::v3::Response { rule })
|
||||||
|
@ -84,12 +104,12 @@ pub async fn get_pushrule_route(
|
||||||
///
|
///
|
||||||
/// Creates a single specified push rule for this user.
|
/// Creates a single specified push rule for this user.
|
||||||
pub async fn set_pushrule_route(
|
pub async fn set_pushrule_route(
|
||||||
body: Ruma<set_pushrule::v3::Request>,
|
body: Ruma<set_pushrule::v3::IncomingRequest>,
|
||||||
) -> Result<set_pushrule::v3::Response> {
|
) -> Result<set_pushrule::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
let body = body.body;
|
let body = body.body;
|
||||||
|
|
||||||
if body.scope != RuleScope::Global {
|
if body.scope != "global" {
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::InvalidParam,
|
ErrorKind::InvalidParam,
|
||||||
"Scopes other than 'global' are not supported.",
|
"Scopes other than 'global' are not supported.",
|
||||||
|
@ -111,36 +131,67 @@ pub async fn set_pushrule_route(
|
||||||
let mut account_data = serde_json::from_str::<PushRulesEvent>(event.get())
|
let mut account_data = serde_json::from_str::<PushRulesEvent>(event.get())
|
||||||
.map_err(|_| Error::bad_database("Invalid account data event in db."))?;
|
.map_err(|_| Error::bad_database("Invalid account data event in db."))?;
|
||||||
|
|
||||||
if let Err(error) = account_data.content.global.insert(
|
let global = &mut account_data.content.global;
|
||||||
body.rule.clone(),
|
match body.kind {
|
||||||
body.after.as_deref(),
|
RuleKind::Override => {
|
||||||
body.before.as_deref(),
|
global.override_.replace(
|
||||||
) {
|
ConditionalPushRuleInit {
|
||||||
let err = match error {
|
actions: body.actions,
|
||||||
InsertPushRuleError::ServerDefaultRuleId => Error::BadRequest(
|
default: false,
|
||||||
ErrorKind::InvalidParam,
|
enabled: true,
|
||||||
"Rule IDs starting with a dot are reserved for server-default rules.",
|
rule_id: body.rule_id,
|
||||||
),
|
conditions: body.conditions,
|
||||||
InsertPushRuleError::InvalidRuleId => Error::BadRequest(
|
}
|
||||||
ErrorKind::InvalidParam,
|
.into(),
|
||||||
"Rule ID containing invalid characters.",
|
);
|
||||||
),
|
}
|
||||||
InsertPushRuleError::RelativeToServerDefaultRule => Error::BadRequest(
|
RuleKind::Underride => {
|
||||||
ErrorKind::InvalidParam,
|
global.underride.replace(
|
||||||
"Can't place a push rule relatively to a server-default rule.",
|
ConditionalPushRuleInit {
|
||||||
),
|
actions: body.actions,
|
||||||
InsertPushRuleError::UnknownRuleId => Error::BadRequest(
|
default: false,
|
||||||
ErrorKind::NotFound,
|
enabled: true,
|
||||||
"The before or after rule could not be found.",
|
rule_id: body.rule_id,
|
||||||
),
|
conditions: body.conditions,
|
||||||
InsertPushRuleError::BeforeHigherThanAfter => Error::BadRequest(
|
}
|
||||||
ErrorKind::InvalidParam,
|
.into(),
|
||||||
"The before rule has a higher priority than the after rule.",
|
);
|
||||||
),
|
}
|
||||||
_ => Error::BadRequest(ErrorKind::InvalidParam, "Invalid data."),
|
RuleKind::Sender => {
|
||||||
};
|
global.sender.replace(
|
||||||
|
SimplePushRuleInit {
|
||||||
return Err(err);
|
actions: body.actions,
|
||||||
|
default: false,
|
||||||
|
enabled: true,
|
||||||
|
rule_id: body.rule_id,
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
RuleKind::Room => {
|
||||||
|
global.room.replace(
|
||||||
|
SimplePushRuleInit {
|
||||||
|
actions: body.actions,
|
||||||
|
default: false,
|
||||||
|
enabled: true,
|
||||||
|
rule_id: body.rule_id,
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
RuleKind::Content => {
|
||||||
|
global.content.replace(
|
||||||
|
PatternedPushRuleInit {
|
||||||
|
actions: body.actions,
|
||||||
|
default: false,
|
||||||
|
enabled: true,
|
||||||
|
rule_id: body.rule_id,
|
||||||
|
pattern: body.pattern.unwrap_or_default(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
services().account_data.update(
|
services().account_data.update(
|
||||||
|
@ -157,11 +208,11 @@ pub async fn set_pushrule_route(
|
||||||
///
|
///
|
||||||
/// Gets the actions of a single specified push rule for this user.
|
/// Gets the actions of a single specified push rule for this user.
|
||||||
pub async fn get_pushrule_actions_route(
|
pub async fn get_pushrule_actions_route(
|
||||||
body: Ruma<get_pushrule_actions::v3::Request>,
|
body: Ruma<get_pushrule_actions::v3::IncomingRequest>,
|
||||||
) -> Result<get_pushrule_actions::v3::Response> {
|
) -> Result<get_pushrule_actions::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
if body.scope != RuleScope::Global {
|
if body.scope != "global" {
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::InvalidParam,
|
ErrorKind::InvalidParam,
|
||||||
"Scopes other than 'global' are not supported.",
|
"Scopes other than 'global' are not supported.",
|
||||||
|
@ -185,26 +236,44 @@ pub async fn get_pushrule_actions_route(
|
||||||
.content;
|
.content;
|
||||||
|
|
||||||
let global = account_data.global;
|
let global = account_data.global;
|
||||||
let actions = global
|
let actions = match body.kind {
|
||||||
.get(body.kind.clone(), &body.rule_id)
|
RuleKind::Override => global
|
||||||
.map(|rule| rule.actions().to_owned())
|
.override_
|
||||||
.ok_or(Error::BadRequest(
|
.get(body.rule_id.as_str())
|
||||||
ErrorKind::NotFound,
|
.map(|rule| rule.actions.clone()),
|
||||||
"Push rule not found.",
|
RuleKind::Underride => global
|
||||||
))?;
|
.underride
|
||||||
|
.get(body.rule_id.as_str())
|
||||||
|
.map(|rule| rule.actions.clone()),
|
||||||
|
RuleKind::Sender => global
|
||||||
|
.sender
|
||||||
|
.get(body.rule_id.as_str())
|
||||||
|
.map(|rule| rule.actions.clone()),
|
||||||
|
RuleKind::Room => global
|
||||||
|
.room
|
||||||
|
.get(body.rule_id.as_str())
|
||||||
|
.map(|rule| rule.actions.clone()),
|
||||||
|
RuleKind::Content => global
|
||||||
|
.content
|
||||||
|
.get(body.rule_id.as_str())
|
||||||
|
.map(|rule| rule.actions.clone()),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
Ok(get_pushrule_actions::v3::Response { actions })
|
Ok(get_pushrule_actions::v3::Response {
|
||||||
|
actions: actions.unwrap_or_default(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # `PUT /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}/actions`
|
/// # `PUT /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}/actions`
|
||||||
///
|
///
|
||||||
/// Sets the actions of a single specified push rule for this user.
|
/// Sets the actions of a single specified push rule for this user.
|
||||||
pub async fn set_pushrule_actions_route(
|
pub async fn set_pushrule_actions_route(
|
||||||
body: Ruma<set_pushrule_actions::v3::Request>,
|
body: Ruma<set_pushrule_actions::v3::IncomingRequest>,
|
||||||
) -> Result<set_pushrule_actions::v3::Response> {
|
) -> Result<set_pushrule_actions::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
if body.scope != RuleScope::Global {
|
if body.scope != "global" {
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::InvalidParam,
|
ErrorKind::InvalidParam,
|
||||||
"Scopes other than 'global' are not supported.",
|
"Scopes other than 'global' are not supported.",
|
||||||
|
@ -226,17 +295,40 @@ pub async fn set_pushrule_actions_route(
|
||||||
let mut account_data = serde_json::from_str::<PushRulesEvent>(event.get())
|
let mut account_data = serde_json::from_str::<PushRulesEvent>(event.get())
|
||||||
.map_err(|_| Error::bad_database("Invalid account data event in db."))?;
|
.map_err(|_| Error::bad_database("Invalid account data event in db."))?;
|
||||||
|
|
||||||
if account_data
|
let global = &mut account_data.content.global;
|
||||||
.content
|
match body.kind {
|
||||||
.global
|
RuleKind::Override => {
|
||||||
.set_actions(body.kind.clone(), &body.rule_id, body.actions.clone())
|
if let Some(mut rule) = global.override_.get(body.rule_id.as_str()).cloned() {
|
||||||
.is_err()
|
rule.actions = body.actions.clone();
|
||||||
{
|
global.override_.replace(rule);
|
||||||
return Err(Error::BadRequest(
|
|
||||||
ErrorKind::NotFound,
|
|
||||||
"Push rule not found.",
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
RuleKind::Underride => {
|
||||||
|
if let Some(mut rule) = global.underride.get(body.rule_id.as_str()).cloned() {
|
||||||
|
rule.actions = body.actions.clone();
|
||||||
|
global.underride.replace(rule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RuleKind::Sender => {
|
||||||
|
if let Some(mut rule) = global.sender.get(body.rule_id.as_str()).cloned() {
|
||||||
|
rule.actions = body.actions.clone();
|
||||||
|
global.sender.replace(rule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RuleKind::Room => {
|
||||||
|
if let Some(mut rule) = global.room.get(body.rule_id.as_str()).cloned() {
|
||||||
|
rule.actions = body.actions.clone();
|
||||||
|
global.room.replace(rule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RuleKind::Content => {
|
||||||
|
if let Some(mut rule) = global.content.get(body.rule_id.as_str()).cloned() {
|
||||||
|
rule.actions = body.actions.clone();
|
||||||
|
global.content.replace(rule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
|
||||||
services().account_data.update(
|
services().account_data.update(
|
||||||
None,
|
None,
|
||||||
|
@ -252,11 +344,11 @@ pub async fn set_pushrule_actions_route(
|
||||||
///
|
///
|
||||||
/// Gets the enabled status of a single specified push rule for this user.
|
/// Gets the enabled status of a single specified push rule for this user.
|
||||||
pub async fn get_pushrule_enabled_route(
|
pub async fn get_pushrule_enabled_route(
|
||||||
body: Ruma<get_pushrule_enabled::v3::Request>,
|
body: Ruma<get_pushrule_enabled::v3::IncomingRequest>,
|
||||||
) -> Result<get_pushrule_enabled::v3::Response> {
|
) -> Result<get_pushrule_enabled::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
if body.scope != RuleScope::Global {
|
if body.scope != "global" {
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::InvalidParam,
|
ErrorKind::InvalidParam,
|
||||||
"Scopes other than 'global' are not supported.",
|
"Scopes other than 'global' are not supported.",
|
||||||
|
@ -279,13 +371,34 @@ pub async fn get_pushrule_enabled_route(
|
||||||
.map_err(|_| Error::bad_database("Invalid account data event in db."))?;
|
.map_err(|_| Error::bad_database("Invalid account data event in db."))?;
|
||||||
|
|
||||||
let global = account_data.content.global;
|
let global = account_data.content.global;
|
||||||
let enabled = global
|
let enabled = match body.kind {
|
||||||
.get(body.kind.clone(), &body.rule_id)
|
RuleKind::Override => global
|
||||||
.map(|r| r.enabled())
|
.override_
|
||||||
.ok_or(Error::BadRequest(
|
.iter()
|
||||||
ErrorKind::NotFound,
|
.find(|rule| rule.rule_id == body.rule_id)
|
||||||
"Push rule not found.",
|
.map_or(false, |rule| rule.enabled),
|
||||||
))?;
|
RuleKind::Underride => global
|
||||||
|
.underride
|
||||||
|
.iter()
|
||||||
|
.find(|rule| rule.rule_id == body.rule_id)
|
||||||
|
.map_or(false, |rule| rule.enabled),
|
||||||
|
RuleKind::Sender => global
|
||||||
|
.sender
|
||||||
|
.iter()
|
||||||
|
.find(|rule| rule.rule_id == body.rule_id)
|
||||||
|
.map_or(false, |rule| rule.enabled),
|
||||||
|
RuleKind::Room => global
|
||||||
|
.room
|
||||||
|
.iter()
|
||||||
|
.find(|rule| rule.rule_id == body.rule_id)
|
||||||
|
.map_or(false, |rule| rule.enabled),
|
||||||
|
RuleKind::Content => global
|
||||||
|
.content
|
||||||
|
.iter()
|
||||||
|
.find(|rule| rule.rule_id == body.rule_id)
|
||||||
|
.map_or(false, |rule| rule.enabled),
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
Ok(get_pushrule_enabled::v3::Response { enabled })
|
Ok(get_pushrule_enabled::v3::Response { enabled })
|
||||||
}
|
}
|
||||||
|
@ -294,11 +407,11 @@ pub async fn get_pushrule_enabled_route(
|
||||||
///
|
///
|
||||||
/// Sets the enabled status of a single specified push rule for this user.
|
/// Sets the enabled status of a single specified push rule for this user.
|
||||||
pub async fn set_pushrule_enabled_route(
|
pub async fn set_pushrule_enabled_route(
|
||||||
body: Ruma<set_pushrule_enabled::v3::Request>,
|
body: Ruma<set_pushrule_enabled::v3::IncomingRequest>,
|
||||||
) -> Result<set_pushrule_enabled::v3::Response> {
|
) -> Result<set_pushrule_enabled::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
if body.scope != RuleScope::Global {
|
if body.scope != "global" {
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::InvalidParam,
|
ErrorKind::InvalidParam,
|
||||||
"Scopes other than 'global' are not supported.",
|
"Scopes other than 'global' are not supported.",
|
||||||
|
@ -320,16 +433,44 @@ pub async fn set_pushrule_enabled_route(
|
||||||
let mut account_data = serde_json::from_str::<PushRulesEvent>(event.get())
|
let mut account_data = serde_json::from_str::<PushRulesEvent>(event.get())
|
||||||
.map_err(|_| Error::bad_database("Invalid account data event in db."))?;
|
.map_err(|_| Error::bad_database("Invalid account data event in db."))?;
|
||||||
|
|
||||||
if account_data
|
let global = &mut account_data.content.global;
|
||||||
.content
|
match body.kind {
|
||||||
.global
|
RuleKind::Override => {
|
||||||
.set_enabled(body.kind.clone(), &body.rule_id, body.enabled)
|
if let Some(mut rule) = global.override_.get(body.rule_id.as_str()).cloned() {
|
||||||
.is_err()
|
global.override_.remove(&rule);
|
||||||
{
|
rule.enabled = body.enabled;
|
||||||
return Err(Error::BadRequest(
|
global.override_.insert(rule);
|
||||||
ErrorKind::NotFound,
|
}
|
||||||
"Push rule not found.",
|
}
|
||||||
));
|
RuleKind::Underride => {
|
||||||
|
if let Some(mut rule) = global.underride.get(body.rule_id.as_str()).cloned() {
|
||||||
|
global.underride.remove(&rule);
|
||||||
|
rule.enabled = body.enabled;
|
||||||
|
global.underride.insert(rule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RuleKind::Sender => {
|
||||||
|
if let Some(mut rule) = global.sender.get(body.rule_id.as_str()).cloned() {
|
||||||
|
global.sender.remove(&rule);
|
||||||
|
rule.enabled = body.enabled;
|
||||||
|
global.sender.insert(rule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RuleKind::Room => {
|
||||||
|
if let Some(mut rule) = global.room.get(body.rule_id.as_str()).cloned() {
|
||||||
|
global.room.remove(&rule);
|
||||||
|
rule.enabled = body.enabled;
|
||||||
|
global.room.insert(rule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RuleKind::Content => {
|
||||||
|
if let Some(mut rule) = global.content.get(body.rule_id.as_str()).cloned() {
|
||||||
|
global.content.remove(&rule);
|
||||||
|
rule.enabled = body.enabled;
|
||||||
|
global.content.insert(rule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
services().account_data.update(
|
services().account_data.update(
|
||||||
|
@ -346,11 +487,11 @@ pub async fn set_pushrule_enabled_route(
|
||||||
///
|
///
|
||||||
/// Deletes a single specified push rule for this user.
|
/// Deletes a single specified push rule for this user.
|
||||||
pub async fn delete_pushrule_route(
|
pub async fn delete_pushrule_route(
|
||||||
body: Ruma<delete_pushrule::v3::Request>,
|
body: Ruma<delete_pushrule::v3::IncomingRequest>,
|
||||||
) -> Result<delete_pushrule::v3::Response> {
|
) -> Result<delete_pushrule::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
if body.scope != RuleScope::Global {
|
if body.scope != "global" {
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::InvalidParam,
|
ErrorKind::InvalidParam,
|
||||||
"Scopes other than 'global' are not supported.",
|
"Scopes other than 'global' are not supported.",
|
||||||
|
@ -372,23 +513,34 @@ pub async fn delete_pushrule_route(
|
||||||
let mut account_data = serde_json::from_str::<PushRulesEvent>(event.get())
|
let mut account_data = serde_json::from_str::<PushRulesEvent>(event.get())
|
||||||
.map_err(|_| Error::bad_database("Invalid account data event in db."))?;
|
.map_err(|_| Error::bad_database("Invalid account data event in db."))?;
|
||||||
|
|
||||||
if let Err(error) = account_data
|
let global = &mut account_data.content.global;
|
||||||
.content
|
match body.kind {
|
||||||
.global
|
RuleKind::Override => {
|
||||||
.remove(body.kind.clone(), &body.rule_id)
|
if let Some(rule) = global.override_.get(body.rule_id.as_str()).cloned() {
|
||||||
{
|
global.override_.remove(&rule);
|
||||||
let err = match error {
|
|
||||||
RemovePushRuleError::ServerDefault => Error::BadRequest(
|
|
||||||
ErrorKind::InvalidParam,
|
|
||||||
"Cannot delete a server-default pushrule.",
|
|
||||||
),
|
|
||||||
RemovePushRuleError::NotFound => {
|
|
||||||
Error::BadRequest(ErrorKind::NotFound, "Push rule not found.")
|
|
||||||
}
|
}
|
||||||
_ => Error::BadRequest(ErrorKind::InvalidParam, "Invalid data."),
|
}
|
||||||
};
|
RuleKind::Underride => {
|
||||||
|
if let Some(rule) = global.underride.get(body.rule_id.as_str()).cloned() {
|
||||||
return Err(err);
|
global.underride.remove(&rule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RuleKind::Sender => {
|
||||||
|
if let Some(rule) = global.sender.get(body.rule_id.as_str()).cloned() {
|
||||||
|
global.sender.remove(&rule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RuleKind::Room => {
|
||||||
|
if let Some(rule) = global.room.get(body.rule_id.as_str()).cloned() {
|
||||||
|
global.room.remove(&rule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RuleKind::Content => {
|
||||||
|
if let Some(rule) = global.content.get(body.rule_id.as_str()).cloned() {
|
||||||
|
global.content.remove(&rule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
services().account_data.update(
|
services().account_data.update(
|
||||||
|
@ -423,10 +575,9 @@ pub async fn set_pushers_route(
|
||||||
body: Ruma<set_pusher::v3::Request>,
|
body: Ruma<set_pusher::v3::Request>,
|
||||||
) -> Result<set_pusher::v3::Response> {
|
) -> Result<set_pusher::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
let pusher = body.pusher.clone();
|
||||||
|
|
||||||
services()
|
services().pusher.set_pusher(sender_user, pusher)?;
|
||||||
.pusher
|
|
||||||
.set_pusher(sender_user, body.action.clone())?;
|
|
||||||
|
|
||||||
Ok(set_pusher::v3::Response::default())
|
Ok(set_pusher::v3::Response::default())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
use crate::{service::rooms::timeline::PduCount, services, Error, Result, Ruma};
|
use crate::{services, Error, Result, Ruma};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::{error::ErrorKind, read_marker::set_read_marker, receipt::create_receipt},
|
api::client::{error::ErrorKind, read_marker::set_read_marker, receipt::create_receipt},
|
||||||
events::{
|
events::RoomAccountDataEventType,
|
||||||
receipt::{ReceiptThread, ReceiptType},
|
receipt::ReceiptType,
|
||||||
RoomAccountDataEventType,
|
|
||||||
},
|
|
||||||
MilliSecondsSinceUnixEpoch,
|
MilliSecondsSinceUnixEpoch,
|
||||||
};
|
};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
@ -16,14 +14,13 @@ use std::collections::BTreeMap;
|
||||||
/// - Updates fully-read account data event to `fully_read`
|
/// - Updates fully-read account data event to `fully_read`
|
||||||
/// - If `read_receipt` is set: Update private marker and public read receipt EDU
|
/// - If `read_receipt` is set: Update private marker and public read receipt EDU
|
||||||
pub async fn set_read_marker_route(
|
pub async fn set_read_marker_route(
|
||||||
body: Ruma<set_read_marker::v3::Request>,
|
body: Ruma<set_read_marker::v3::IncomingRequest>,
|
||||||
) -> Result<set_read_marker::v3::Response> {
|
) -> Result<set_read_marker::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
if let Some(fully_read) = &body.fully_read {
|
|
||||||
let fully_read_event = ruma::events::fully_read::FullyReadEvent {
|
let fully_read_event = ruma::events::fully_read::FullyReadEvent {
|
||||||
content: ruma::events::fully_read::FullyReadEventContent {
|
content: ruma::events::fully_read::FullyReadEventContent {
|
||||||
event_id: fully_read.clone(),
|
event_id: body.fully_read.clone(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
services().account_data.update(
|
services().account_data.update(
|
||||||
|
@ -32,47 +29,30 @@ pub async fn set_read_marker_route(
|
||||||
RoomAccountDataEventType::FullyRead,
|
RoomAccountDataEventType::FullyRead,
|
||||||
&serde_json::to_value(fully_read_event).expect("to json value always works"),
|
&serde_json::to_value(fully_read_event).expect("to json value always works"),
|
||||||
)?;
|
)?;
|
||||||
}
|
|
||||||
|
|
||||||
if body.private_read_receipt.is_some() || body.read_receipt.is_some() {
|
if let Some(event) = &body.read_receipt {
|
||||||
|
services().rooms.edus.read_receipt.private_read_set(
|
||||||
|
&body.room_id,
|
||||||
|
sender_user,
|
||||||
services()
|
services()
|
||||||
.rooms
|
|
||||||
.user
|
|
||||||
.reset_notification_counts(sender_user, &body.room_id)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(event) = &body.private_read_receipt {
|
|
||||||
let count = services()
|
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.get_pdu_count(event)?
|
.get_pdu_count(event)?
|
||||||
.ok_or(Error::BadRequest(
|
.ok_or(Error::BadRequest(
|
||||||
ErrorKind::InvalidParam,
|
ErrorKind::InvalidParam,
|
||||||
"Event does not exist.",
|
"Event does not exist.",
|
||||||
))?;
|
))?,
|
||||||
let count = match count {
|
)?;
|
||||||
PduCount::Backfilled(_) => {
|
|
||||||
return Err(Error::BadRequest(
|
|
||||||
ErrorKind::InvalidParam,
|
|
||||||
"Read receipt is in backfilled timeline",
|
|
||||||
))
|
|
||||||
}
|
|
||||||
PduCount::Normal(c) => c,
|
|
||||||
};
|
|
||||||
services()
|
services()
|
||||||
.rooms
|
.rooms
|
||||||
.edus
|
.user
|
||||||
.read_receipt
|
.reset_notification_counts(sender_user, &body.room_id)?;
|
||||||
.private_read_set(&body.room_id, sender_user, count)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(event) = &body.read_receipt {
|
|
||||||
let mut user_receipts = BTreeMap::new();
|
let mut user_receipts = BTreeMap::new();
|
||||||
user_receipts.insert(
|
user_receipts.insert(
|
||||||
sender_user.clone(),
|
sender_user.clone(),
|
||||||
ruma::events::receipt::Receipt {
|
ruma::events::receipt::Receipt {
|
||||||
ts: Some(MilliSecondsSinceUnixEpoch::now()),
|
ts: Some(MilliSecondsSinceUnixEpoch::now()),
|
||||||
thread: ReceiptThread::Unthreaded,
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -99,41 +79,32 @@ pub async fn set_read_marker_route(
|
||||||
///
|
///
|
||||||
/// Sets private read marker and public read receipt EDU.
|
/// Sets private read marker and public read receipt EDU.
|
||||||
pub async fn create_receipt_route(
|
pub async fn create_receipt_route(
|
||||||
body: Ruma<create_receipt::v3::Request>,
|
body: Ruma<create_receipt::v3::IncomingRequest>,
|
||||||
) -> Result<create_receipt::v3::Response> {
|
) -> Result<create_receipt::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
if matches!(
|
services().rooms.edus.read_receipt.private_read_set(
|
||||||
&body.receipt_type,
|
&body.room_id,
|
||||||
create_receipt::v3::ReceiptType::Read | create_receipt::v3::ReceiptType::ReadPrivate
|
sender_user,
|
||||||
) {
|
services()
|
||||||
|
.rooms
|
||||||
|
.timeline
|
||||||
|
.get_pdu_count(&body.event_id)?
|
||||||
|
.ok_or(Error::BadRequest(
|
||||||
|
ErrorKind::InvalidParam,
|
||||||
|
"Event does not exist.",
|
||||||
|
))?,
|
||||||
|
)?;
|
||||||
services()
|
services()
|
||||||
.rooms
|
.rooms
|
||||||
.user
|
.user
|
||||||
.reset_notification_counts(sender_user, &body.room_id)?;
|
.reset_notification_counts(sender_user, &body.room_id)?;
|
||||||
}
|
|
||||||
|
|
||||||
match body.receipt_type {
|
|
||||||
create_receipt::v3::ReceiptType::FullyRead => {
|
|
||||||
let fully_read_event = ruma::events::fully_read::FullyReadEvent {
|
|
||||||
content: ruma::events::fully_read::FullyReadEventContent {
|
|
||||||
event_id: body.event_id.clone(),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
services().account_data.update(
|
|
||||||
Some(&body.room_id),
|
|
||||||
sender_user,
|
|
||||||
RoomAccountDataEventType::FullyRead,
|
|
||||||
&serde_json::to_value(fully_read_event).expect("to json value always works"),
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
create_receipt::v3::ReceiptType::Read => {
|
|
||||||
let mut user_receipts = BTreeMap::new();
|
let mut user_receipts = BTreeMap::new();
|
||||||
user_receipts.insert(
|
user_receipts.insert(
|
||||||
sender_user.clone(),
|
sender_user.clone(),
|
||||||
ruma::events::receipt::Receipt {
|
ruma::events::receipt::Receipt {
|
||||||
ts: Some(MilliSecondsSinceUnixEpoch::now()),
|
ts: Some(MilliSecondsSinceUnixEpoch::now()),
|
||||||
thread: ReceiptThread::Unthreaded,
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
let mut receipts = BTreeMap::new();
|
let mut receipts = BTreeMap::new();
|
||||||
|
@ -150,33 +121,6 @@ pub async fn create_receipt_route(
|
||||||
room_id: body.room_id.clone(),
|
room_id: body.room_id.clone(),
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
}
|
|
||||||
create_receipt::v3::ReceiptType::ReadPrivate => {
|
|
||||||
let count = services()
|
|
||||||
.rooms
|
|
||||||
.timeline
|
|
||||||
.get_pdu_count(&body.event_id)?
|
|
||||||
.ok_or(Error::BadRequest(
|
|
||||||
ErrorKind::InvalidParam,
|
|
||||||
"Event does not exist.",
|
|
||||||
))?;
|
|
||||||
let count = match count {
|
|
||||||
PduCount::Backfilled(_) => {
|
|
||||||
return Err(Error::BadRequest(
|
|
||||||
ErrorKind::InvalidParam,
|
|
||||||
"Read receipt is in backfilled timeline",
|
|
||||||
))
|
|
||||||
}
|
|
||||||
PduCount::Normal(c) => c,
|
|
||||||
};
|
|
||||||
services().rooms.edus.read_receipt.private_read_set(
|
|
||||||
&body.room_id,
|
|
||||||
sender_user,
|
|
||||||
count,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
_ => return Err(Error::bad_database("Unsupported receipt type")),
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(create_receipt::v3::Response {})
|
Ok(create_receipt::v3::Response {})
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ use std::sync::Arc;
|
||||||
use crate::{service::pdu::PduBuilder, services, Result, Ruma};
|
use crate::{service::pdu::PduBuilder, services, Result, Ruma};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::redact::redact_event,
|
api::client::redact::redact_event,
|
||||||
events::{room::redaction::RoomRedactionEventContent, TimelineEventType},
|
events::{room::redaction::RoomRedactionEventContent, RoomEventType},
|
||||||
};
|
};
|
||||||
|
|
||||||
use serde_json::value::to_raw_value;
|
use serde_json::value::to_raw_value;
|
||||||
|
@ -14,7 +14,7 @@ use serde_json::value::to_raw_value;
|
||||||
///
|
///
|
||||||
/// - TODO: Handle txn id
|
/// - TODO: Handle txn id
|
||||||
pub async fn redact_event_route(
|
pub async fn redact_event_route(
|
||||||
body: Ruma<redact_event::v3::Request>,
|
body: Ruma<redact_event::v3::IncomingRequest>,
|
||||||
) -> Result<redact_event::v3::Response> {
|
) -> Result<redact_event::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
let body = body.body;
|
let body = body.body;
|
||||||
|
@ -32,9 +32,8 @@ pub async fn redact_event_route(
|
||||||
|
|
||||||
let event_id = services().rooms.timeline.build_and_append_pdu(
|
let event_id = services().rooms.timeline.build_and_append_pdu(
|
||||||
PduBuilder {
|
PduBuilder {
|
||||||
event_type: TimelineEventType::RoomRedaction,
|
event_type: RoomEventType::RoomRedaction,
|
||||||
content: to_raw_value(&RoomRedactionEventContent {
|
content: to_raw_value(&RoomRedactionEventContent {
|
||||||
redacts: Some(body.event_id.clone()),
|
|
||||||
reason: body.reason.clone(),
|
reason: body.reason.clone(),
|
||||||
})
|
})
|
||||||
.expect("event is valid, we just created it"),
|
.expect("event is valid, we just created it"),
|
||||||
|
|
|
@ -1,146 +0,0 @@
|
||||||
use ruma::api::client::relations::{
|
|
||||||
get_relating_events, get_relating_events_with_rel_type,
|
|
||||||
get_relating_events_with_rel_type_and_event_type,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{service::rooms::timeline::PduCount, services, Result, Ruma};
|
|
||||||
|
|
||||||
/// # `GET /_matrix/client/r0/rooms/{roomId}/relations/{eventId}/{relType}/{eventType}`
|
|
||||||
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");
|
|
||||||
|
|
||||||
let from = match body.from.clone() {
|
|
||||||
Some(from) => PduCount::try_from_string(&from)?,
|
|
||||||
None => match ruma::api::Direction::Backward {
|
|
||||||
// TODO: fix ruma so `body.dir` exists
|
|
||||||
ruma::api::Direction::Forward => PduCount::min(),
|
|
||||||
ruma::api::Direction::Backward => PduCount::max(),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
let to = body
|
|
||||||
.to
|
|
||||||
.as_ref()
|
|
||||||
.and_then(|t| PduCount::try_from_string(t).ok());
|
|
||||||
|
|
||||||
// Use limit or else 10, with maximum 100
|
|
||||||
let limit = body
|
|
||||||
.limit
|
|
||||||
.and_then(|u| u32::try_from(u).ok())
|
|
||||||
.map_or(10_usize, |u| u as usize)
|
|
||||||
.min(100);
|
|
||||||
|
|
||||||
let res = services()
|
|
||||||
.rooms
|
|
||||||
.pdu_metadata
|
|
||||||
.paginate_relations_with_filter(
|
|
||||||
sender_user,
|
|
||||||
&body.room_id,
|
|
||||||
&body.event_id,
|
|
||||||
Some(body.event_type.clone()),
|
|
||||||
Some(body.rel_type.clone()),
|
|
||||||
from,
|
|
||||||
to,
|
|
||||||
limit,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(
|
|
||||||
get_relating_events_with_rel_type_and_event_type::v1::Response {
|
|
||||||
chunk: res.chunk,
|
|
||||||
next_batch: res.next_batch,
|
|
||||||
prev_batch: res.prev_batch,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # `GET /_matrix/client/r0/rooms/{roomId}/relations/{eventId}/{relType}`
|
|
||||||
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");
|
|
||||||
|
|
||||||
let from = match body.from.clone() {
|
|
||||||
Some(from) => PduCount::try_from_string(&from)?,
|
|
||||||
None => match ruma::api::Direction::Backward {
|
|
||||||
// TODO: fix ruma so `body.dir` exists
|
|
||||||
ruma::api::Direction::Forward => PduCount::min(),
|
|
||||||
ruma::api::Direction::Backward => PduCount::max(),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
let to = body
|
|
||||||
.to
|
|
||||||
.as_ref()
|
|
||||||
.and_then(|t| PduCount::try_from_string(t).ok());
|
|
||||||
|
|
||||||
// Use limit or else 10, with maximum 100
|
|
||||||
let limit = body
|
|
||||||
.limit
|
|
||||||
.and_then(|u| u32::try_from(u).ok())
|
|
||||||
.map_or(10_usize, |u| u as usize)
|
|
||||||
.min(100);
|
|
||||||
|
|
||||||
let res = services()
|
|
||||||
.rooms
|
|
||||||
.pdu_metadata
|
|
||||||
.paginate_relations_with_filter(
|
|
||||||
sender_user,
|
|
||||||
&body.room_id,
|
|
||||||
&body.event_id,
|
|
||||||
None,
|
|
||||||
Some(body.rel_type.clone()),
|
|
||||||
from,
|
|
||||||
to,
|
|
||||||
limit,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(get_relating_events_with_rel_type::v1::Response {
|
|
||||||
chunk: res.chunk,
|
|
||||||
next_batch: res.next_batch,
|
|
||||||
prev_batch: res.prev_batch,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # `GET /_matrix/client/r0/rooms/{roomId}/relations/{eventId}`
|
|
||||||
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");
|
|
||||||
|
|
||||||
let from = match body.from.clone() {
|
|
||||||
Some(from) => PduCount::try_from_string(&from)?,
|
|
||||||
None => match ruma::api::Direction::Backward {
|
|
||||||
// TODO: fix ruma so `body.dir` exists
|
|
||||||
ruma::api::Direction::Forward => PduCount::min(),
|
|
||||||
ruma::api::Direction::Backward => PduCount::max(),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
let to = body
|
|
||||||
.to
|
|
||||||
.as_ref()
|
|
||||||
.and_then(|t| PduCount::try_from_string(t).ok());
|
|
||||||
|
|
||||||
// Use limit or else 10, with maximum 100
|
|
||||||
let limit = body
|
|
||||||
.limit
|
|
||||||
.and_then(|u| u32::try_from(u).ok())
|
|
||||||
.map_or(10_usize, |u| u as usize)
|
|
||||||
.min(100);
|
|
||||||
|
|
||||||
services()
|
|
||||||
.rooms
|
|
||||||
.pdu_metadata
|
|
||||||
.paginate_relations_with_filter(
|
|
||||||
sender_user,
|
|
||||||
&body.room_id,
|
|
||||||
&body.event_id,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
from,
|
|
||||||
to,
|
|
||||||
limit,
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -10,7 +10,7 @@ use ruma::{
|
||||||
/// Reports an inappropriate event to homeserver admins
|
/// Reports an inappropriate event to homeserver admins
|
||||||
///
|
///
|
||||||
pub async fn report_event_route(
|
pub async fn report_event_route(
|
||||||
body: Ruma<report_content::v3::Request>,
|
body: Ruma<report_content::v3::IncomingRequest>,
|
||||||
) -> Result<report_content::v3::Response> {
|
) -> Result<report_content::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
|
|
|
@ -19,11 +19,11 @@ use ruma::{
|
||||||
tombstone::RoomTombstoneEventContent,
|
tombstone::RoomTombstoneEventContent,
|
||||||
topic::RoomTopicEventContent,
|
topic::RoomTopicEventContent,
|
||||||
},
|
},
|
||||||
StateEventType, TimelineEventType,
|
RoomEventType, StateEventType,
|
||||||
},
|
},
|
||||||
int,
|
int,
|
||||||
serde::JsonObject,
|
serde::{CanonicalJsonObject, JsonObject},
|
||||||
CanonicalJsonObject, OwnedRoomAliasId, RoomAliasId, RoomId,
|
RoomAliasId, RoomId,
|
||||||
};
|
};
|
||||||
use serde_json::{json, value::to_raw_value};
|
use serde_json::{json, value::to_raw_value};
|
||||||
use std::{cmp::max, collections::BTreeMap, sync::Arc};
|
use std::{cmp::max, collections::BTreeMap, sync::Arc};
|
||||||
|
@ -46,7 +46,7 @@ use tracing::{info, warn};
|
||||||
/// - Send events implied by `name` and `topic`
|
/// - Send events implied by `name` and `topic`
|
||||||
/// - Send invite events
|
/// - Send invite events
|
||||||
pub async fn create_room_route(
|
pub async fn create_room_route(
|
||||||
body: Ruma<create_room::v3::Request>,
|
body: Ruma<create_room::v3::IncomingRequest>,
|
||||||
) -> Result<create_room::v3::Response> {
|
) -> Result<create_room::v3::Response> {
|
||||||
use create_room::v3::RoomPreset;
|
use create_room::v3::RoomPreset;
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ pub async fn create_room_route(
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let alias: Option<OwnedRoomAliasId> =
|
let alias: Option<Box<RoomAliasId>> =
|
||||||
body.room_alias_name
|
body.room_alias_name
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or(Ok(None), |localpart| {
|
.map_or(Ok(None), |localpart| {
|
||||||
|
@ -142,9 +142,8 @@ pub async fn create_room_route(
|
||||||
content
|
content
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
// TODO: Add correct value for v11
|
|
||||||
let mut content = serde_json::from_str::<CanonicalJsonObject>(
|
let mut content = serde_json::from_str::<CanonicalJsonObject>(
|
||||||
to_raw_value(&RoomCreateEventContent::new_v1(sender_user.clone()))
|
to_raw_value(&RoomCreateEventContent::new(sender_user.clone()))
|
||||||
.map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Invalid creation content"))?
|
.map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Invalid creation content"))?
|
||||||
.get(),
|
.get(),
|
||||||
)
|
)
|
||||||
|
@ -176,7 +175,7 @@ pub async fn create_room_route(
|
||||||
// 1. The room create event
|
// 1. The room create event
|
||||||
services().rooms.timeline.build_and_append_pdu(
|
services().rooms.timeline.build_and_append_pdu(
|
||||||
PduBuilder {
|
PduBuilder {
|
||||||
event_type: TimelineEventType::RoomCreate,
|
event_type: RoomEventType::RoomCreate,
|
||||||
content: to_raw_value(&content).expect("event is valid, we just created it"),
|
content: to_raw_value(&content).expect("event is valid, we just created it"),
|
||||||
unsigned: None,
|
unsigned: None,
|
||||||
state_key: Some("".to_owned()),
|
state_key: Some("".to_owned()),
|
||||||
|
@ -190,7 +189,7 @@ pub async fn create_room_route(
|
||||||
// 2. Let the room creator join
|
// 2. Let the room creator join
|
||||||
services().rooms.timeline.build_and_append_pdu(
|
services().rooms.timeline.build_and_append_pdu(
|
||||||
PduBuilder {
|
PduBuilder {
|
||||||
event_type: TimelineEventType::RoomMember,
|
event_type: RoomEventType::RoomMember,
|
||||||
content: to_raw_value(&RoomMemberEventContent {
|
content: to_raw_value(&RoomMemberEventContent {
|
||||||
membership: MembershipState::Join,
|
membership: MembershipState::Join,
|
||||||
displayname: services().users.displayname(sender_user)?,
|
displayname: services().users.displayname(sender_user)?,
|
||||||
|
@ -214,7 +213,10 @@ pub async fn create_room_route(
|
||||||
// 3. Power levels
|
// 3. Power levels
|
||||||
|
|
||||||
// Figure out preset. We need it for preset specific events
|
// Figure out preset. We need it for preset specific events
|
||||||
let preset = body.preset.clone().unwrap_or(match &body.visibility {
|
let preset = body
|
||||||
|
.preset
|
||||||
|
.clone()
|
||||||
|
.unwrap_or_else(|| match &body.visibility {
|
||||||
room::Visibility::Private => RoomPreset::PrivateChat,
|
room::Visibility::Private => RoomPreset::PrivateChat,
|
||||||
room::Visibility::Public => RoomPreset::PublicChat,
|
room::Visibility::Public => RoomPreset::PublicChat,
|
||||||
_ => RoomPreset::PrivateChat, // Room visibility should not be custom
|
_ => RoomPreset::PrivateChat, // Room visibility should not be custom
|
||||||
|
@ -248,7 +250,7 @@ pub async fn create_room_route(
|
||||||
|
|
||||||
services().rooms.timeline.build_and_append_pdu(
|
services().rooms.timeline.build_and_append_pdu(
|
||||||
PduBuilder {
|
PduBuilder {
|
||||||
event_type: TimelineEventType::RoomPowerLevels,
|
event_type: RoomEventType::RoomPowerLevels,
|
||||||
content: to_raw_value(&power_levels_content)
|
content: to_raw_value(&power_levels_content)
|
||||||
.expect("to_raw_value always works on serde_json::Value"),
|
.expect("to_raw_value always works on serde_json::Value"),
|
||||||
unsigned: None,
|
unsigned: None,
|
||||||
|
@ -264,7 +266,7 @@ pub async fn create_room_route(
|
||||||
if let Some(room_alias_id) = &alias {
|
if let Some(room_alias_id) = &alias {
|
||||||
services().rooms.timeline.build_and_append_pdu(
|
services().rooms.timeline.build_and_append_pdu(
|
||||||
PduBuilder {
|
PduBuilder {
|
||||||
event_type: TimelineEventType::RoomCanonicalAlias,
|
event_type: RoomEventType::RoomCanonicalAlias,
|
||||||
content: to_raw_value(&RoomCanonicalAliasEventContent {
|
content: to_raw_value(&RoomCanonicalAliasEventContent {
|
||||||
alias: Some(room_alias_id.to_owned()),
|
alias: Some(room_alias_id.to_owned()),
|
||||||
alt_aliases: vec![],
|
alt_aliases: vec![],
|
||||||
|
@ -285,7 +287,7 @@ pub async fn create_room_route(
|
||||||
// 5.1 Join Rules
|
// 5.1 Join Rules
|
||||||
services().rooms.timeline.build_and_append_pdu(
|
services().rooms.timeline.build_and_append_pdu(
|
||||||
PduBuilder {
|
PduBuilder {
|
||||||
event_type: TimelineEventType::RoomJoinRules,
|
event_type: RoomEventType::RoomJoinRules,
|
||||||
content: to_raw_value(&RoomJoinRulesEventContent::new(match preset {
|
content: to_raw_value(&RoomJoinRulesEventContent::new(match preset {
|
||||||
RoomPreset::PublicChat => JoinRule::Public,
|
RoomPreset::PublicChat => JoinRule::Public,
|
||||||
// according to spec "invite" is the default
|
// according to spec "invite" is the default
|
||||||
|
@ -304,7 +306,7 @@ pub async fn create_room_route(
|
||||||
// 5.2 History Visibility
|
// 5.2 History Visibility
|
||||||
services().rooms.timeline.build_and_append_pdu(
|
services().rooms.timeline.build_and_append_pdu(
|
||||||
PduBuilder {
|
PduBuilder {
|
||||||
event_type: TimelineEventType::RoomHistoryVisibility,
|
event_type: RoomEventType::RoomHistoryVisibility,
|
||||||
content: to_raw_value(&RoomHistoryVisibilityEventContent::new(
|
content: to_raw_value(&RoomHistoryVisibilityEventContent::new(
|
||||||
HistoryVisibility::Shared,
|
HistoryVisibility::Shared,
|
||||||
))
|
))
|
||||||
|
@ -321,7 +323,7 @@ pub async fn create_room_route(
|
||||||
// 5.3 Guest Access
|
// 5.3 Guest Access
|
||||||
services().rooms.timeline.build_and_append_pdu(
|
services().rooms.timeline.build_and_append_pdu(
|
||||||
PduBuilder {
|
PduBuilder {
|
||||||
event_type: TimelineEventType::RoomGuestAccess,
|
event_type: RoomEventType::RoomGuestAccess,
|
||||||
content: to_raw_value(&RoomGuestAccessEventContent::new(match preset {
|
content: to_raw_value(&RoomGuestAccessEventContent::new(match preset {
|
||||||
RoomPreset::PublicChat => GuestAccess::Forbidden,
|
RoomPreset::PublicChat => GuestAccess::Forbidden,
|
||||||
_ => GuestAccess::CanJoin,
|
_ => GuestAccess::CanJoin,
|
||||||
|
@ -347,7 +349,7 @@ pub async fn create_room_route(
|
||||||
pdu_builder.state_key.get_or_insert_with(|| "".to_owned());
|
pdu_builder.state_key.get_or_insert_with(|| "".to_owned());
|
||||||
|
|
||||||
// Silently skip encryption events if they are not allowed
|
// Silently skip encryption events if they are not allowed
|
||||||
if pdu_builder.event_type == TimelineEventType::RoomEncryption
|
if pdu_builder.event_type == RoomEventType::RoomEncryption
|
||||||
&& !services().globals.allow_encryption()
|
&& !services().globals.allow_encryption()
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
|
@ -365,8 +367,8 @@ pub async fn create_room_route(
|
||||||
if let Some(name) = &body.name {
|
if let Some(name) = &body.name {
|
||||||
services().rooms.timeline.build_and_append_pdu(
|
services().rooms.timeline.build_and_append_pdu(
|
||||||
PduBuilder {
|
PduBuilder {
|
||||||
event_type: TimelineEventType::RoomName,
|
event_type: RoomEventType::RoomName,
|
||||||
content: to_raw_value(&RoomNameEventContent::new(name.clone()))
|
content: to_raw_value(&RoomNameEventContent::new(Some(name.clone())))
|
||||||
.expect("event is valid, we just created it"),
|
.expect("event is valid, we just created it"),
|
||||||
unsigned: None,
|
unsigned: None,
|
||||||
state_key: Some("".to_owned()),
|
state_key: Some("".to_owned()),
|
||||||
|
@ -381,7 +383,7 @@ pub async fn create_room_route(
|
||||||
if let Some(topic) = &body.topic {
|
if let Some(topic) = &body.topic {
|
||||||
services().rooms.timeline.build_and_append_pdu(
|
services().rooms.timeline.build_and_append_pdu(
|
||||||
PduBuilder {
|
PduBuilder {
|
||||||
event_type: TimelineEventType::RoomTopic,
|
event_type: RoomEventType::RoomTopic,
|
||||||
content: to_raw_value(&RoomTopicEventContent {
|
content: to_raw_value(&RoomTopicEventContent {
|
||||||
topic: topic.clone(),
|
topic: topic.clone(),
|
||||||
})
|
})
|
||||||
|
@ -399,7 +401,7 @@ pub async fn create_room_route(
|
||||||
// 8. Events implied by invite (and TODO: invite_3pid)
|
// 8. Events implied by invite (and TODO: invite_3pid)
|
||||||
drop(state_lock);
|
drop(state_lock);
|
||||||
for user_id in &body.invite {
|
for user_id in &body.invite {
|
||||||
let _ = invite_helper(sender_user, user_id, &room_id, None, body.is_direct).await;
|
let _ = invite_helper(sender_user, user_id, &room_id, body.is_direct).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Homeserver specific stuff
|
// Homeserver specific stuff
|
||||||
|
@ -422,35 +424,28 @@ pub async fn create_room_route(
|
||||||
///
|
///
|
||||||
/// - You have to currently be joined to the room (TODO: Respect history visibility)
|
/// - You have to currently be joined to the room (TODO: Respect history visibility)
|
||||||
pub async fn get_room_event_route(
|
pub async fn get_room_event_route(
|
||||||
body: Ruma<get_room_event::v3::Request>,
|
body: Ruma<get_room_event::v3::IncomingRequest>,
|
||||||
) -> Result<get_room_event::v3::Response> {
|
) -> Result<get_room_event::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
let event = services()
|
if !services()
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.state_cache
|
||||||
.get_pdu(&body.event_id)?
|
.is_joined(sender_user, &body.room_id)?
|
||||||
.ok_or_else(|| {
|
{
|
||||||
warn!("Event not found, event ID: {:?}", &body.event_id);
|
|
||||||
Error::BadRequest(ErrorKind::NotFound, "Event not found.")
|
|
||||||
})?;
|
|
||||||
|
|
||||||
if !services().rooms.state_accessor.user_can_see_event(
|
|
||||||
sender_user,
|
|
||||||
&event.room_id,
|
|
||||||
&body.event_id,
|
|
||||||
)? {
|
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::Forbidden,
|
ErrorKind::Forbidden,
|
||||||
"You don't have permission to view this event.",
|
"You don't have permission to view this room.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut event = (*event).clone();
|
|
||||||
event.add_age()?;
|
|
||||||
|
|
||||||
Ok(get_room_event::v3::Response {
|
Ok(get_room_event::v3::Response {
|
||||||
event: event.to_room_event(),
|
event: services()
|
||||||
|
.rooms
|
||||||
|
.timeline
|
||||||
|
.get_pdu(&body.event_id)?
|
||||||
|
.ok_or(Error::BadRequest(ErrorKind::NotFound, "Event not found."))?
|
||||||
|
.to_room_event(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -460,7 +455,7 @@ pub async fn get_room_event_route(
|
||||||
///
|
///
|
||||||
/// - Only users joined to the room are allowed to call this TODO: Allow any user to call it if history_visibility is world readable
|
/// - Only users joined to the room are allowed to call this TODO: Allow any user to call it if history_visibility is world readable
|
||||||
pub async fn get_room_aliases_route(
|
pub async fn get_room_aliases_route(
|
||||||
body: Ruma<aliases::v3::Request>,
|
body: Ruma<aliases::v3::IncomingRequest>,
|
||||||
) -> Result<aliases::v3::Response> {
|
) -> Result<aliases::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
|
@ -496,7 +491,7 @@ pub async fn get_room_aliases_route(
|
||||||
/// - Moves local aliases
|
/// - Moves local aliases
|
||||||
/// - Modifies old room power levels to prevent users from speaking
|
/// - Modifies old room power levels to prevent users from speaking
|
||||||
pub async fn upgrade_room_route(
|
pub async fn upgrade_room_route(
|
||||||
body: Ruma<upgrade_room::v3::Request>,
|
body: Ruma<upgrade_room::v3::IncomingRequest>,
|
||||||
) -> Result<upgrade_room::v3::Response> {
|
) -> Result<upgrade_room::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
|
@ -533,7 +528,7 @@ pub async fn upgrade_room_route(
|
||||||
// Fail if the sender does not have the required permissions
|
// Fail if the sender does not have the required permissions
|
||||||
let tombstone_event_id = services().rooms.timeline.build_and_append_pdu(
|
let tombstone_event_id = services().rooms.timeline.build_and_append_pdu(
|
||||||
PduBuilder {
|
PduBuilder {
|
||||||
event_type: TimelineEventType::RoomTombstone,
|
event_type: RoomEventType::RoomTombstone,
|
||||||
content: to_raw_value(&RoomTombstoneEventContent {
|
content: to_raw_value(&RoomTombstoneEventContent {
|
||||||
body: "This room has been replaced".to_owned(),
|
body: "This room has been replaced".to_owned(),
|
||||||
replacement_room: replacement_room.clone(),
|
replacement_room: replacement_room.clone(),
|
||||||
|
@ -615,7 +610,7 @@ pub async fn upgrade_room_route(
|
||||||
|
|
||||||
services().rooms.timeline.build_and_append_pdu(
|
services().rooms.timeline.build_and_append_pdu(
|
||||||
PduBuilder {
|
PduBuilder {
|
||||||
event_type: TimelineEventType::RoomCreate,
|
event_type: RoomEventType::RoomCreate,
|
||||||
content: to_raw_value(&create_event_content)
|
content: to_raw_value(&create_event_content)
|
||||||
.expect("event is valid, we just created it"),
|
.expect("event is valid, we just created it"),
|
||||||
unsigned: None,
|
unsigned: None,
|
||||||
|
@ -630,7 +625,7 @@ pub async fn upgrade_room_route(
|
||||||
// Join the new room
|
// Join the new room
|
||||||
services().rooms.timeline.build_and_append_pdu(
|
services().rooms.timeline.build_and_append_pdu(
|
||||||
PduBuilder {
|
PduBuilder {
|
||||||
event_type: TimelineEventType::RoomMember,
|
event_type: RoomEventType::RoomMember,
|
||||||
content: to_raw_value(&RoomMemberEventContent {
|
content: to_raw_value(&RoomMemberEventContent {
|
||||||
membership: MembershipState::Join,
|
membership: MembershipState::Join,
|
||||||
displayname: services().users.displayname(sender_user)?,
|
displayname: services().users.displayname(sender_user)?,
|
||||||
|
@ -723,7 +718,7 @@ pub async fn upgrade_room_route(
|
||||||
// Modify the power levels in the old room to prevent sending of events and inviting new users
|
// Modify the power levels in the old room to prevent sending of events and inviting new users
|
||||||
let _ = services().rooms.timeline.build_and_append_pdu(
|
let _ = services().rooms.timeline.build_and_append_pdu(
|
||||||
PduBuilder {
|
PduBuilder {
|
||||||
event_type: TimelineEventType::RoomPowerLevels,
|
event_type: RoomEventType::RoomPowerLevels,
|
||||||
content: to_raw_value(&power_levels_event_content)
|
content: to_raw_value(&power_levels_event_content)
|
||||||
.expect("event is valid, we just created it"),
|
.expect("event is valid, we just created it"),
|
||||||
unsigned: None,
|
unsigned: None,
|
||||||
|
|
|
@ -15,7 +15,7 @@ use std::collections::BTreeMap;
|
||||||
///
|
///
|
||||||
/// - Only works if the user is currently joined to the room (TODO: Respect history visibility)
|
/// - Only works if the user is currently joined to the room (TODO: Respect history visibility)
|
||||||
pub async fn search_events_route(
|
pub async fn search_events_route(
|
||||||
body: Ruma<search_events::v3::Request>,
|
body: Ruma<search_events::v3::IncomingRequest>,
|
||||||
) -> Result<search_events::v3::Response> {
|
) -> Result<search_events::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
|
@ -31,8 +31,7 @@ pub async fn search_events_route(
|
||||||
.collect()
|
.collect()
|
||||||
});
|
});
|
||||||
|
|
||||||
// Use limit or else 10, with maximum 100
|
let limit = filter.limit.map_or(10, |l| u64::from(l) as usize);
|
||||||
let limit = filter.limit.map_or(10, u64::from).min(100) as usize;
|
|
||||||
|
|
||||||
let mut searches = Vec::new();
|
let mut searches = Vec::new();
|
||||||
|
|
||||||
|
@ -82,21 +81,6 @@ pub async fn search_events_route(
|
||||||
|
|
||||||
let results: Vec<_> = results
|
let results: Vec<_> = results
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|result| {
|
|
||||||
services()
|
|
||||||
.rooms
|
|
||||||
.timeline
|
|
||||||
.get_pdu_from_id(result)
|
|
||||||
.ok()?
|
|
||||||
.filter(|pdu| {
|
|
||||||
services()
|
|
||||||
.rooms
|
|
||||||
.state_accessor
|
|
||||||
.user_can_see_event(sender_user, &pdu.room_id, &pdu.event_id)
|
|
||||||
.unwrap_or(false)
|
|
||||||
})
|
|
||||||
.map(|pdu| pdu.to_room_event())
|
|
||||||
})
|
|
||||||
.map(|result| {
|
.map(|result| {
|
||||||
Ok::<_, Error>(SearchResult {
|
Ok::<_, Error>(SearchResult {
|
||||||
context: EventContextResult {
|
context: EventContextResult {
|
||||||
|
@ -107,7 +91,11 @@ pub async fn search_events_route(
|
||||||
start: None,
|
start: None,
|
||||||
},
|
},
|
||||||
rank: None,
|
rank: None,
|
||||||
result: Some(result),
|
result: services()
|
||||||
|
.rooms
|
||||||
|
.timeline
|
||||||
|
.get_pdu_from_id(result)?
|
||||||
|
.map(|pdu| pdu.to_room_event()),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.filter_map(|r| r.ok())
|
.filter_map(|r| r.ok())
|
||||||
|
@ -115,7 +103,7 @@ pub async fn search_events_route(
|
||||||
.take(limit)
|
.take(limit)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let next_batch = if results.len() < limit {
|
let next_batch = if results.len() < limit as usize {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some((skip + limit).to_string())
|
Some((skip + limit).to_string())
|
||||||
|
|
|
@ -4,12 +4,12 @@ use ruma::{
|
||||||
api::client::{
|
api::client::{
|
||||||
error::ErrorKind,
|
error::ErrorKind,
|
||||||
session::{get_login_types, login, logout, logout_all},
|
session::{get_login_types, login, logout, logout_all},
|
||||||
uiaa::UserIdentifier,
|
uiaa::IncomingUserIdentifier,
|
||||||
},
|
},
|
||||||
UserId,
|
UserId,
|
||||||
};
|
};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use tracing::{info, warn};
|
use tracing::info;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
struct Claims {
|
struct Claims {
|
||||||
|
@ -22,11 +22,10 @@ struct Claims {
|
||||||
/// Get the supported login types of this server. One of these should be used as the `type` field
|
/// Get the supported login types of this server. One of these should be used as the `type` field
|
||||||
/// when logging in.
|
/// when logging in.
|
||||||
pub async fn get_login_types_route(
|
pub async fn get_login_types_route(
|
||||||
_body: Ruma<get_login_types::v3::Request>,
|
_body: Ruma<get_login_types::v3::IncomingRequest>,
|
||||||
) -> Result<get_login_types::v3::Response> {
|
) -> Result<get_login_types::v3::Response> {
|
||||||
Ok(get_login_types::v3::Response::new(vec![
|
Ok(get_login_types::v3::Response::new(vec![
|
||||||
get_login_types::v3::LoginType::Password(Default::default()),
|
get_login_types::v3::LoginType::Password(Default::default()),
|
||||||
get_login_types::v3::LoginType::ApplicationService(Default::default()),
|
|
||||||
]))
|
]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,32 +40,24 @@ pub async fn get_login_types_route(
|
||||||
///
|
///
|
||||||
/// Note: You can use [`GET /_matrix/client/r0/login`](fn.get_supported_versions_route.html) to see
|
/// Note: You can use [`GET /_matrix/client/r0/login`](fn.get_supported_versions_route.html) to see
|
||||||
/// supported login types.
|
/// supported login types.
|
||||||
pub async fn login_route(body: Ruma<login::v3::Request>) -> Result<login::v3::Response> {
|
pub async fn login_route(body: Ruma<login::v3::IncomingRequest>) -> Result<login::v3::Response> {
|
||||||
// To allow deprecated login methods
|
|
||||||
#![allow(deprecated)]
|
|
||||||
// Validate login method
|
// Validate login method
|
||||||
// TODO: Other login methods
|
// TODO: Other login methods
|
||||||
let user_id = match &body.login_info {
|
let user_id = match &body.login_info {
|
||||||
login::v3::LoginInfo::Password(login::v3::Password {
|
login::v3::IncomingLoginInfo::Password(login::v3::IncomingPassword {
|
||||||
identifier,
|
identifier,
|
||||||
password,
|
password,
|
||||||
user,
|
|
||||||
address: _,
|
|
||||||
medium: _,
|
|
||||||
}) => {
|
}) => {
|
||||||
let user_id = if let Some(UserIdentifier::UserIdOrLocalpart(user_id)) = identifier {
|
let username = if let IncomingUserIdentifier::UserIdOrLocalpart(user_id) = identifier {
|
||||||
UserId::parse_with_server_name(
|
user_id.to_lowercase()
|
||||||
user_id.to_lowercase(),
|
} else {
|
||||||
|
return Err(Error::BadRequest(ErrorKind::Forbidden, "Bad login type."));
|
||||||
|
};
|
||||||
|
let user_id = UserId::parse_with_server_name(
|
||||||
|
username.to_owned(),
|
||||||
services().globals.server_name(),
|
services().globals.server_name(),
|
||||||
)
|
)
|
||||||
} else if let Some(user) = user {
|
|
||||||
UserId::parse(user)
|
|
||||||
} else {
|
|
||||||
warn!("Bad login type: {:?}", &body.login_info);
|
|
||||||
return Err(Error::BadRequest(ErrorKind::Forbidden, "Bad login type."));
|
|
||||||
}
|
|
||||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidUsername, "Username is invalid."))?;
|
.map_err(|_| Error::BadRequest(ErrorKind::InvalidUsername, "Username is invalid."))?;
|
||||||
|
|
||||||
let hash = services()
|
let hash = services()
|
||||||
.users
|
.users
|
||||||
.password_hash(&user_id)?
|
.password_hash(&user_id)?
|
||||||
|
@ -93,7 +84,7 @@ pub async fn login_route(body: Ruma<login::v3::Request>) -> Result<login::v3::Re
|
||||||
|
|
||||||
user_id
|
user_id
|
||||||
}
|
}
|
||||||
login::v3::LoginInfo::Token(login::v3::Token { token }) => {
|
login::v3::IncomingLoginInfo::Token(login::v3::IncomingToken { token }) => {
|
||||||
if let Some(jwt_decoding_key) = services().globals.jwt_decoding_key() {
|
if let Some(jwt_decoding_key) = services().globals.jwt_decoding_key() {
|
||||||
let token = jsonwebtoken::decode::<Claims>(
|
let token = jsonwebtoken::decode::<Claims>(
|
||||||
token,
|
token,
|
||||||
|
@ -101,7 +92,7 @@ pub async fn login_route(body: Ruma<login::v3::Request>) -> Result<login::v3::Re
|
||||||
&jsonwebtoken::Validation::default(),
|
&jsonwebtoken::Validation::default(),
|
||||||
)
|
)
|
||||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidUsername, "Token is invalid."))?;
|
.map_err(|_| Error::BadRequest(ErrorKind::InvalidUsername, "Token is invalid."))?;
|
||||||
let username = token.claims.sub.to_lowercase();
|
let username = token.claims.sub;
|
||||||
UserId::parse_with_server_name(username, services().globals.server_name()).map_err(
|
UserId::parse_with_server_name(username, services().globals.server_name()).map_err(
|
||||||
|_| Error::BadRequest(ErrorKind::InvalidUsername, "Username is invalid."),
|
|_| Error::BadRequest(ErrorKind::InvalidUsername, "Username is invalid."),
|
||||||
)?
|
)?
|
||||||
|
@ -112,31 +103,7 @@ pub async fn login_route(body: Ruma<login::v3::Request>) -> Result<login::v3::Re
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
login::v3::LoginInfo::ApplicationService(login::v3::ApplicationService {
|
|
||||||
identifier,
|
|
||||||
user,
|
|
||||||
}) => {
|
|
||||||
if !body.from_appservice {
|
|
||||||
return Err(Error::BadRequest(
|
|
||||||
ErrorKind::Forbidden,
|
|
||||||
"Forbidden login type.",
|
|
||||||
));
|
|
||||||
};
|
|
||||||
if let Some(UserIdentifier::UserIdOrLocalpart(user_id)) = identifier {
|
|
||||||
UserId::parse_with_server_name(
|
|
||||||
user_id.to_lowercase(),
|
|
||||||
services().globals.server_name(),
|
|
||||||
)
|
|
||||||
} else if let Some(user) = user {
|
|
||||||
UserId::parse(user)
|
|
||||||
} else {
|
|
||||||
warn!("Bad login type: {:?}", &body.login_info);
|
|
||||||
return Err(Error::BadRequest(ErrorKind::Forbidden, "Bad login type."));
|
|
||||||
}
|
|
||||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidUsername, "Username is invalid."))?
|
|
||||||
}
|
|
||||||
_ => {
|
_ => {
|
||||||
warn!("Unsupported or unknown login type: {:?}", &body.login_info);
|
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::Unknown,
|
ErrorKind::Unknown,
|
||||||
"Unsupported login type.",
|
"Unsupported login type.",
|
||||||
|
@ -174,16 +141,12 @@ pub async fn login_route(body: Ruma<login::v3::Request>) -> Result<login::v3::Re
|
||||||
|
|
||||||
info!("{} logged in", user_id);
|
info!("{} logged in", user_id);
|
||||||
|
|
||||||
// Homeservers are still required to send the `home_server` field
|
|
||||||
#[allow(deprecated)]
|
|
||||||
Ok(login::v3::Response {
|
Ok(login::v3::Response {
|
||||||
user_id,
|
user_id,
|
||||||
access_token: token,
|
access_token: token,
|
||||||
home_server: Some(services().globals.server_name().to_owned()),
|
home_server: Some(services().globals.server_name().to_owned()),
|
||||||
device_id,
|
device_id,
|
||||||
well_known: None,
|
well_known: None,
|
||||||
refresh_token: None,
|
|
||||||
expires_in: None,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
use crate::{services, Result, Ruma};
|
|
||||||
use ruma::api::client::space::get_hierarchy;
|
|
||||||
|
|
||||||
/// # `GET /_matrix/client/v1/rooms/{room_id}/hierarchy``
|
|
||||||
///
|
|
||||||
/// Paginates over the space tree in a depth-first manner to locate child rooms of a given space.
|
|
||||||
pub 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 skip = body
|
|
||||||
.from
|
|
||||||
.as_ref()
|
|
||||||
.and_then(|s| s.parse::<usize>().ok())
|
|
||||||
.unwrap_or(0);
|
|
||||||
|
|
||||||
let limit = body.limit.map_or(10, u64::from).min(100) as usize;
|
|
||||||
|
|
||||||
let max_depth = body.max_depth.map_or(3, u64::from).min(10) as usize + 1; // +1 to skip the space room itself
|
|
||||||
|
|
||||||
services()
|
|
||||||
.rooms
|
|
||||||
.spaces
|
|
||||||
.get_hierarchy(
|
|
||||||
sender_user,
|
|
||||||
&body.room_id,
|
|
||||||
limit,
|
|
||||||
skip,
|
|
||||||
max_depth,
|
|
||||||
body.suggested_only,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
|
@ -7,12 +7,15 @@ use ruma::{
|
||||||
state::{get_state_events, get_state_events_for_key, send_state_event},
|
state::{get_state_events, get_state_events_for_key, send_state_event},
|
||||||
},
|
},
|
||||||
events::{
|
events::{
|
||||||
room::canonical_alias::RoomCanonicalAliasEventContent, AnyStateEventContent, StateEventType,
|
room::{
|
||||||
|
canonical_alias::RoomCanonicalAliasEventContent,
|
||||||
|
history_visibility::{HistoryVisibility, RoomHistoryVisibilityEventContent},
|
||||||
|
},
|
||||||
|
AnyStateEventContent, StateEventType,
|
||||||
},
|
},
|
||||||
serde::Raw,
|
serde::Raw,
|
||||||
EventId, RoomId, UserId,
|
EventId, RoomId, UserId,
|
||||||
};
|
};
|
||||||
use tracing::log::warn;
|
|
||||||
|
|
||||||
/// # `PUT /_matrix/client/r0/rooms/{roomId}/state/{eventType}/{stateKey}`
|
/// # `PUT /_matrix/client/r0/rooms/{roomId}/state/{eventType}/{stateKey}`
|
||||||
///
|
///
|
||||||
|
@ -22,7 +25,7 @@ use tracing::log::warn;
|
||||||
/// - Tries to send the event into the room, auth rules will determine if it is allowed
|
/// - 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
|
/// - If event is new canonical_alias: Rejects if alias is incorrect
|
||||||
pub async fn send_state_event_for_key_route(
|
pub async fn send_state_event_for_key_route(
|
||||||
body: Ruma<send_state_event::v3::Request>,
|
body: Ruma<send_state_event::v3::IncomingRequest>,
|
||||||
) -> Result<send_state_event::v3::Response> {
|
) -> Result<send_state_event::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
|
@ -47,7 +50,7 @@ pub async fn send_state_event_for_key_route(
|
||||||
/// - Tries to send the event into the room, auth rules will determine if it is allowed
|
/// - 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
|
/// - If event is new canonical_alias: Rejects if alias is incorrect
|
||||||
pub 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>,
|
body: Ruma<send_state_event::v3::IncomingRequest>,
|
||||||
) -> Result<RumaResponse<send_state_event::v3::Response>> {
|
) -> Result<RumaResponse<send_state_event::v3::Response>> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
|
@ -78,14 +81,33 @@ pub async fn send_state_event_for_empty_key_route(
|
||||||
///
|
///
|
||||||
/// - If not joined: Only works if current room history visibility is world readable
|
/// - If not joined: Only works if current room history visibility is world readable
|
||||||
pub async fn get_state_events_route(
|
pub async fn get_state_events_route(
|
||||||
body: Ruma<get_state_events::v3::Request>,
|
body: Ruma<get_state_events::v3::IncomingRequest>,
|
||||||
) -> Result<get_state_events::v3::Response> {
|
) -> Result<get_state_events::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
|
#[allow(clippy::blocks_in_if_conditions)]
|
||||||
|
// Users not in the room should not be able to access the state unless history_visibility is
|
||||||
|
// WorldReadable
|
||||||
if !services()
|
if !services()
|
||||||
|
.rooms
|
||||||
|
.state_cache
|
||||||
|
.is_joined(sender_user, &body.room_id)?
|
||||||
|
&& !matches!(
|
||||||
|
services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.user_can_see_state_events(sender_user, &body.room_id)?
|
.room_state_get(&body.room_id, &StateEventType::RoomHistoryVisibility, "")?
|
||||||
|
.map(|event| {
|
||||||
|
serde_json::from_str(event.content.get())
|
||||||
|
.map(|e: RoomHistoryVisibilityEventContent| e.history_visibility)
|
||||||
|
.map_err(|_| {
|
||||||
|
Error::bad_database(
|
||||||
|
"Invalid room history visibility event in database.",
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
Some(Ok(HistoryVisibility::WorldReadable))
|
||||||
|
)
|
||||||
{
|
{
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::Forbidden,
|
ErrorKind::Forbidden,
|
||||||
|
@ -111,14 +133,33 @@ pub async fn get_state_events_route(
|
||||||
///
|
///
|
||||||
/// - If not joined: Only works if current room history visibility is world readable
|
/// - If not joined: Only works if current room history visibility is world readable
|
||||||
pub 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>,
|
body: Ruma<get_state_events_for_key::v3::IncomingRequest>,
|
||||||
) -> Result<get_state_events_for_key::v3::Response> {
|
) -> Result<get_state_events_for_key::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
|
#[allow(clippy::blocks_in_if_conditions)]
|
||||||
|
// Users not in the room should not be able to access the state unless history_visibility is
|
||||||
|
// WorldReadable
|
||||||
if !services()
|
if !services()
|
||||||
|
.rooms
|
||||||
|
.state_cache
|
||||||
|
.is_joined(sender_user, &body.room_id)?
|
||||||
|
&& !matches!(
|
||||||
|
services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.user_can_see_state_events(sender_user, &body.room_id)?
|
.room_state_get(&body.room_id, &StateEventType::RoomHistoryVisibility, "")?
|
||||||
|
.map(|event| {
|
||||||
|
serde_json::from_str(event.content.get())
|
||||||
|
.map(|e: RoomHistoryVisibilityEventContent| e.history_visibility)
|
||||||
|
.map_err(|_| {
|
||||||
|
Error::bad_database(
|
||||||
|
"Invalid room history visibility event in database.",
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
Some(Ok(HistoryVisibility::WorldReadable))
|
||||||
|
)
|
||||||
{
|
{
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::Forbidden,
|
ErrorKind::Forbidden,
|
||||||
|
@ -130,13 +171,10 @@ pub async fn get_state_events_for_key_route(
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.room_state_get(&body.room_id, &body.event_type, &body.state_key)?
|
.room_state_get(&body.room_id, &body.event_type, &body.state_key)?
|
||||||
.ok_or_else(|| {
|
.ok_or(Error::BadRequest(
|
||||||
warn!(
|
ErrorKind::NotFound,
|
||||||
"State event {:?} not found in room {:?}",
|
"State event not found.",
|
||||||
&body.event_type, &body.room_id
|
))?;
|
||||||
);
|
|
||||||
Error::BadRequest(ErrorKind::NotFound, "State event not found.")
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(get_state_events_for_key::v3::Response {
|
Ok(get_state_events_for_key::v3::Response {
|
||||||
content: serde_json::from_str(event.content.get())
|
content: serde_json::from_str(event.content.get())
|
||||||
|
@ -150,14 +188,33 @@ pub async fn get_state_events_for_key_route(
|
||||||
///
|
///
|
||||||
/// - If not joined: Only works if current room history visibility is world readable
|
/// - If not joined: Only works if current room history visibility is world readable
|
||||||
pub 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>,
|
body: Ruma<get_state_events_for_key::v3::IncomingRequest>,
|
||||||
) -> Result<RumaResponse<get_state_events_for_key::v3::Response>> {
|
) -> Result<RumaResponse<get_state_events_for_key::v3::Response>> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
|
#[allow(clippy::blocks_in_if_conditions)]
|
||||||
|
// Users not in the room should not be able to access the state unless history_visibility is
|
||||||
|
// WorldReadable
|
||||||
if !services()
|
if !services()
|
||||||
|
.rooms
|
||||||
|
.state_cache
|
||||||
|
.is_joined(sender_user, &body.room_id)?
|
||||||
|
&& !matches!(
|
||||||
|
services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.user_can_see_state_events(sender_user, &body.room_id)?
|
.room_state_get(&body.room_id, &StateEventType::RoomHistoryVisibility, "")?
|
||||||
|
.map(|event| {
|
||||||
|
serde_json::from_str(event.content.get())
|
||||||
|
.map(|e: RoomHistoryVisibilityEventContent| e.history_visibility)
|
||||||
|
.map_err(|_| {
|
||||||
|
Error::bad_database(
|
||||||
|
"Invalid room history visibility event in database.",
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
Some(Ok(HistoryVisibility::WorldReadable))
|
||||||
|
)
|
||||||
{
|
{
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::Forbidden,
|
ErrorKind::Forbidden,
|
||||||
|
@ -169,13 +226,10 @@ pub async fn get_state_events_for_empty_key_route(
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.room_state_get(&body.room_id, &body.event_type, "")?
|
.room_state_get(&body.room_id, &body.event_type, "")?
|
||||||
.ok_or_else(|| {
|
.ok_or(Error::BadRequest(
|
||||||
warn!(
|
ErrorKind::NotFound,
|
||||||
"State event {:?} not found in room {:?}",
|
"State event not found.",
|
||||||
&body.event_type, &body.room_id
|
))?;
|
||||||
);
|
|
||||||
Error::BadRequest(ErrorKind::NotFound, "State event not found.")
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(get_state_events_for_key::v3::Response {
|
Ok(get_state_events_for_key::v3::Response {
|
||||||
content: serde_json::from_str(event.content.get())
|
content: serde_json::from_str(event.content.get())
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -14,7 +14,7 @@ use std::collections::BTreeMap;
|
||||||
///
|
///
|
||||||
/// - Inserts the tag into the tag event of the room account data.
|
/// - Inserts the tag into the tag event of the room account data.
|
||||||
pub async fn update_tag_route(
|
pub async fn update_tag_route(
|
||||||
body: Ruma<create_tag::v3::Request>,
|
body: Ruma<create_tag::v3::IncomingRequest>,
|
||||||
) -> Result<create_tag::v3::Response> {
|
) -> Result<create_tag::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ pub async fn update_tag_route(
|
||||||
///
|
///
|
||||||
/// - Removes the tag from the tag event of the room account data.
|
/// - Removes the tag from the tag event of the room account data.
|
||||||
pub async fn delete_tag_route(
|
pub async fn delete_tag_route(
|
||||||
body: Ruma<delete_tag::v3::Request>,
|
body: Ruma<delete_tag::v3::IncomingRequest>,
|
||||||
) -> Result<delete_tag::v3::Response> {
|
) -> Result<delete_tag::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
|
@ -98,7 +98,9 @@ pub async fn delete_tag_route(
|
||||||
/// Returns tags on the room.
|
/// Returns tags on the room.
|
||||||
///
|
///
|
||||||
/// - Gets the tag event of the room account data.
|
/// - Gets the tag event of the room account data.
|
||||||
pub 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::IncomingRequest>,
|
||||||
|
) -> Result<get_tags::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
let event = services().account_data.get(
|
let event = services().account_data.get(
|
||||||
|
|
|
@ -7,7 +7,7 @@ use std::collections::BTreeMap;
|
||||||
///
|
///
|
||||||
/// TODO: Fetches all metadata about protocols supported by the homeserver.
|
/// TODO: Fetches all metadata about protocols supported by the homeserver.
|
||||||
pub async fn get_protocols_route(
|
pub async fn get_protocols_route(
|
||||||
_body: Ruma<get_protocols::v3::Request>,
|
_body: Ruma<get_protocols::v3::IncomingRequest>,
|
||||||
) -> Result<get_protocols::v3::Response> {
|
) -> Result<get_protocols::v3::Response> {
|
||||||
// TODO
|
// TODO
|
||||||
Ok(get_protocols::v3::Response {
|
Ok(get_protocols::v3::Response {
|
||||||
|
|
|
@ -1,49 +0,0 @@
|
||||||
use ruma::api::client::{error::ErrorKind, threads::get_threads};
|
|
||||||
|
|
||||||
use crate::{services, Error, Result, Ruma};
|
|
||||||
|
|
||||||
/// # `GET /_matrix/client/r0/rooms/{roomId}/threads`
|
|
||||||
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
|
|
||||||
let limit = body
|
|
||||||
.limit
|
|
||||||
.and_then(|l| l.try_into().ok())
|
|
||||||
.unwrap_or(10)
|
|
||||||
.min(100);
|
|
||||||
|
|
||||||
let from = if let Some(from) = &body.from {
|
|
||||||
from.parse()
|
|
||||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, ""))?
|
|
||||||
} else {
|
|
||||||
u64::MAX
|
|
||||||
};
|
|
||||||
|
|
||||||
let threads = services()
|
|
||||||
.rooms
|
|
||||||
.threads
|
|
||||||
.threads_until(sender_user, &body.room_id, from, &body.include)?
|
|
||||||
.take(limit)
|
|
||||||
.filter_map(|r| r.ok())
|
|
||||||
.filter(|(_, pdu)| {
|
|
||||||
services()
|
|
||||||
.rooms
|
|
||||||
.state_accessor
|
|
||||||
.user_can_see_event(sender_user, &body.room_id, &pdu.event_id)
|
|
||||||
.unwrap_or(false)
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let next_batch = threads.last().map(|(count, _)| count.to_string());
|
|
||||||
|
|
||||||
Ok(get_threads::v1::Response {
|
|
||||||
chunk: threads
|
|
||||||
.into_iter()
|
|
||||||
.map(|(_, pdu)| pdu.to_room_event())
|
|
||||||
.collect(),
|
|
||||||
next_batch,
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use ruma::events::ToDeviceEventType;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use crate::{services, Error, Result, Ruma};
|
use crate::{services, Error, Result, Ruma};
|
||||||
|
@ -13,7 +14,7 @@ use ruma::{
|
||||||
///
|
///
|
||||||
/// Send a to-device event to a set of client devices.
|
/// Send a to-device event to a set of client devices.
|
||||||
pub async fn send_event_to_device_route(
|
pub async fn send_event_to_device_route(
|
||||||
body: Ruma<send_event_to_device::v3::Request>,
|
body: Ruma<send_event_to_device::v3::IncomingRequest>,
|
||||||
) -> Result<send_event_to_device::v3::Response> {
|
) -> Result<send_event_to_device::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
let sender_device = body.sender_device.as_deref();
|
let sender_device = body.sender_device.as_deref();
|
||||||
|
@ -34,20 +35,19 @@ pub async fn send_event_to_device_route(
|
||||||
map.insert(target_device_id_maybe.clone(), event.clone());
|
map.insert(target_device_id_maybe.clone(), event.clone());
|
||||||
let mut messages = BTreeMap::new();
|
let mut messages = BTreeMap::new();
|
||||||
messages.insert(target_user_id.clone(), map);
|
messages.insert(target_user_id.clone(), map);
|
||||||
let count = services().globals.next_count()?;
|
|
||||||
|
|
||||||
services().sending.send_reliable_edu(
|
services().sending.send_reliable_edu(
|
||||||
target_user_id.server_name(),
|
target_user_id.server_name(),
|
||||||
serde_json::to_vec(&federation::transactions::edu::Edu::DirectToDevice(
|
serde_json::to_vec(&federation::transactions::edu::Edu::DirectToDevice(
|
||||||
DirectDeviceContent {
|
DirectDeviceContent {
|
||||||
sender: sender_user.clone(),
|
sender: sender_user.clone(),
|
||||||
ev_type: body.event_type.clone(),
|
ev_type: ToDeviceEventType::from(&*body.event_type),
|
||||||
message_id: count.to_string().into(),
|
message_id: body.txn_id.to_owned(),
|
||||||
messages,
|
messages,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
.expect("DirectToDevice EDU can be serialized"),
|
.expect("DirectToDevice EDU can be serialized"),
|
||||||
count,
|
services().globals.next_count()?,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
|
@ -58,8 +58,8 @@ pub async fn send_event_to_device_route(
|
||||||
services().users.add_to_device_event(
|
services().users.add_to_device_event(
|
||||||
sender_user,
|
sender_user,
|
||||||
target_user_id,
|
target_user_id,
|
||||||
target_device_id,
|
&target_device_id,
|
||||||
&body.event_type.to_string(),
|
&body.event_type,
|
||||||
event.deserialize_as().map_err(|_| {
|
event.deserialize_as().map_err(|_| {
|
||||||
Error::BadRequest(ErrorKind::InvalidParam, "Event is invalid")
|
Error::BadRequest(ErrorKind::InvalidParam, "Event is invalid")
|
||||||
})?,
|
})?,
|
||||||
|
@ -72,7 +72,7 @@ pub async fn send_event_to_device_route(
|
||||||
sender_user,
|
sender_user,
|
||||||
target_user_id,
|
target_user_id,
|
||||||
&target_device_id?,
|
&target_device_id?,
|
||||||
&body.event_type.to_string(),
|
&body.event_type,
|
||||||
event.deserialize_as().map_err(|_| {
|
event.deserialize_as().map_err(|_| {
|
||||||
Error::BadRequest(ErrorKind::InvalidParam, "Event is invalid")
|
Error::BadRequest(ErrorKind::InvalidParam, "Event is invalid")
|
||||||
})?,
|
})?,
|
||||||
|
|
|
@ -5,7 +5,7 @@ use ruma::api::client::{error::ErrorKind, typing::create_typing_event};
|
||||||
///
|
///
|
||||||
/// Sets the typing state of the sender user.
|
/// Sets the typing state of the sender user.
|
||||||
pub async fn create_typing_event_route(
|
pub async fn create_typing_event_route(
|
||||||
body: Ruma<create_typing_event::v3::Request>,
|
body: Ruma<create_typing_event::v3::IncomingRequest>,
|
||||||
) -> Result<create_typing_event::v3::Response> {
|
) -> Result<create_typing_event::v3::Response> {
|
||||||
use create_typing_event::v3::Typing;
|
use create_typing_event::v3::Typing;
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
use std::{collections::BTreeMap, iter::FromIterator};
|
use std::{collections::BTreeMap, iter::FromIterator};
|
||||||
|
|
||||||
use axum::{response::IntoResponse, Json};
|
use ruma::api::client::discovery::get_supported_versions;
|
||||||
use ruma::api::client::{discovery::get_supported_versions, error::ErrorKind};
|
|
||||||
|
|
||||||
use crate::{services, Error, Result, Ruma};
|
use crate::{Result, Ruma};
|
||||||
|
|
||||||
/// # `GET /_matrix/client/versions`
|
/// # `GET /_matrix/client/versions`
|
||||||
///
|
///
|
||||||
|
@ -16,7 +15,7 @@ use crate::{services, Error, Result, Ruma};
|
||||||
/// Note: Unstable features are used while developing new features. Clients should avoid using
|
/// Note: Unstable features are used while developing new features. Clients should avoid using
|
||||||
/// unstable features in their stable releases
|
/// unstable features in their stable releases
|
||||||
pub async fn get_supported_versions_route(
|
pub async fn get_supported_versions_route(
|
||||||
_body: Ruma<get_supported_versions::Request>,
|
_body: Ruma<get_supported_versions::IncomingRequest>,
|
||||||
) -> Result<get_supported_versions::Response> {
|
) -> Result<get_supported_versions::Response> {
|
||||||
let resp = get_supported_versions::Response {
|
let resp = get_supported_versions::Response {
|
||||||
versions: vec![
|
versions: vec![
|
||||||
|
@ -24,27 +23,9 @@ pub async fn get_supported_versions_route(
|
||||||
"r0.6.0".to_owned(),
|
"r0.6.0".to_owned(),
|
||||||
"v1.1".to_owned(),
|
"v1.1".to_owned(),
|
||||||
"v1.2".to_owned(),
|
"v1.2".to_owned(),
|
||||||
"v1.3".to_owned(),
|
|
||||||
"v1.4".to_owned(),
|
|
||||||
"v1.5".to_owned(),
|
|
||||||
],
|
],
|
||||||
unstable_features: BTreeMap::from_iter([("org.matrix.e2e_cross_signing".to_owned(), true)]),
|
unstable_features: BTreeMap::from_iter([("org.matrix.e2e_cross_signing".to_owned(), true)]),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(resp)
|
Ok(resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # `GET /.well-known/matrix/client`
|
|
||||||
pub async fn well_known_client_route(
|
|
||||||
_body: Ruma<get_supported_versions::Request>,
|
|
||||||
) -> Result<impl IntoResponse> {
|
|
||||||
let client_url = match services().globals.well_known_client() {
|
|
||||||
Some(url) => url.clone(),
|
|
||||||
None => return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Json(serde_json::json!({
|
|
||||||
"m.homeserver": {"base_url": client_url},
|
|
||||||
"org.matrix.msc3575.proxy": {"url": client_url}
|
|
||||||
})))
|
|
||||||
}
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ use ruma::{
|
||||||
/// - Hides any local users that aren't in any public rooms (i.e. those that have the join rule set to public)
|
/// - 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
|
/// and don't share a room with the sender
|
||||||
pub async fn search_users_route(
|
pub async fn search_users_route(
|
||||||
body: Ruma<search_users::v3::Request>,
|
body: Ruma<search_users::v3::IncomingRequest>,
|
||||||
) -> Result<search_users::v3::Response> {
|
) -> Result<search_users::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
let limit = u64::from(body.limit) as usize;
|
let limit = u64::from(body.limit) as usize;
|
||||||
|
@ -75,7 +75,7 @@ pub async fn search_users_route(
|
||||||
let user_is_in_shared_rooms = services()
|
let user_is_in_shared_rooms = services()
|
||||||
.rooms
|
.rooms
|
||||||
.user
|
.user
|
||||||
.get_shared_rooms(vec![sender_user.clone(), user_id])
|
.get_shared_rooms(vec![sender_user.clone(), user_id.clone()])
|
||||||
.ok()?
|
.ok()?
|
||||||
.next()
|
.next()
|
||||||
.is_some();
|
.is_some();
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use crate::{services, Result, Ruma};
|
use crate::{services, Result, Ruma};
|
||||||
use base64::{engine::general_purpose, Engine as _};
|
use hmac::{Hmac, Mac, NewMac};
|
||||||
use hmac::{Hmac, Mac};
|
|
||||||
use ruma::{api::client::voip::get_turn_server_info, SecondsSinceUnixEpoch};
|
use ruma::{api::client::voip::get_turn_server_info, SecondsSinceUnixEpoch};
|
||||||
use sha1::Sha1;
|
use sha1::Sha1;
|
||||||
use std::time::{Duration, SystemTime};
|
use std::time::{Duration, SystemTime};
|
||||||
|
@ -11,7 +10,7 @@ type HmacSha1 = Hmac<Sha1>;
|
||||||
///
|
///
|
||||||
/// TODO: Returns information about the recommended turn server.
|
/// TODO: Returns information about the recommended turn server.
|
||||||
pub async fn turn_server_route(
|
pub async fn turn_server_route(
|
||||||
body: Ruma<get_turn_server_info::v3::Request>,
|
body: Ruma<get_turn_server_info::v3::IncomingRequest>,
|
||||||
) -> Result<get_turn_server_info::v3::Response> {
|
) -> Result<get_turn_server_info::v3::Response> {
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
|
@ -29,7 +28,7 @@ pub async fn turn_server_route(
|
||||||
.expect("HMAC can take key of any size");
|
.expect("HMAC can take key of any size");
|
||||||
mac.update(username.as_bytes());
|
mac.update(username.as_bytes());
|
||||||
|
|
||||||
let password: String = general_purpose::STANDARD.encode(mac.finalize().into_bytes());
|
let password: String = base64::encode_config(mac.finalize().into_bytes(), base64::STANDARD);
|
||||||
|
|
||||||
(username, password)
|
(username, password)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -3,19 +3,22 @@ use std::{collections::BTreeMap, iter::FromIterator, str};
|
||||||
use axum::{
|
use axum::{
|
||||||
async_trait,
|
async_trait,
|
||||||
body::{Full, HttpBody},
|
body::{Full, HttpBody},
|
||||||
extract::{rejection::TypedHeaderRejectionReason, FromRequest, Path, TypedHeader},
|
extract::{
|
||||||
|
rejection::TypedHeaderRejectionReason, FromRequest, Path, RequestParts, TypedHeader,
|
||||||
|
},
|
||||||
headers::{
|
headers::{
|
||||||
authorization::{Bearer, Credentials},
|
authorization::{Bearer, Credentials},
|
||||||
Authorization,
|
Authorization,
|
||||||
},
|
},
|
||||||
response::{IntoResponse, Response},
|
response::{IntoResponse, Response},
|
||||||
BoxError, RequestExt, RequestPartsExt,
|
BoxError,
|
||||||
};
|
};
|
||||||
use bytes::{Buf, BufMut, Bytes, BytesMut};
|
use bytes::{BufMut, Bytes, BytesMut};
|
||||||
use http::{Request, StatusCode};
|
use http::StatusCode;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::{client::error::ErrorKind, AuthScheme, IncomingRequest, OutgoingResponse},
|
api::{client::error::ErrorKind, AuthScheme, IncomingRequest, OutgoingResponse},
|
||||||
CanonicalJsonValue, OwnedDeviceId, OwnedServerName, UserId,
|
signatures::CanonicalJsonValue,
|
||||||
|
DeviceId, ServerName, UserId,
|
||||||
};
|
};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use tracing::{debug, error, warn};
|
use tracing::{debug, error, warn};
|
||||||
|
@ -24,45 +27,28 @@ use super::{Ruma, RumaResponse};
|
||||||
use crate::{services, Error, Result};
|
use crate::{services, Error, Result};
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl<T, S, B> FromRequest<S, B> for Ruma<T>
|
impl<T, B> FromRequest<B> for Ruma<T>
|
||||||
where
|
where
|
||||||
T: IncomingRequest,
|
T: IncomingRequest,
|
||||||
B: HttpBody + Send + 'static,
|
B: HttpBody + Send,
|
||||||
B::Data: Send,
|
B::Data: Send,
|
||||||
B::Error: Into<BoxError>,
|
B::Error: Into<BoxError>,
|
||||||
{
|
{
|
||||||
type Rejection = Error;
|
type Rejection = Error;
|
||||||
|
|
||||||
async fn from_request(req: Request<B>, _state: &S) -> Result<Self, Self::Rejection> {
|
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct QueryParams {
|
struct QueryParams {
|
||||||
access_token: Option<String>,
|
access_token: Option<String>,
|
||||||
user_id: Option<String>,
|
user_id: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
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 metadata = T::METADATA;
|
||||||
let auth_header: Option<TypedHeader<Authorization<Bearer>>> = parts.extract().await?;
|
let auth_header = Option::<TypedHeader<Authorization<Bearer>>>::from_request(req).await?;
|
||||||
let path_params: Path<Vec<String>> = parts.extract().await?;
|
let path_params = Path::<Vec<String>>::from_request(req).await?;
|
||||||
|
|
||||||
let query = parts.uri.query().unwrap_or_default();
|
let query = req.uri().query().unwrap_or_default();
|
||||||
let query_params: QueryParams = match serde_html_form::from_str(query) {
|
let query_params: QueryParams = match ruma::serde::urlencoded::from_str(query) {
|
||||||
Ok(params) => params,
|
Ok(params) => params,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!(%query, "Failed to deserialize query parameters: {}", e);
|
error!(%query, "Failed to deserialize query parameters: {}", e);
|
||||||
|
@ -78,6 +64,10 @@ where
|
||||||
None => query_params.access_token.as_deref(),
|
None => query_params.access_token.as_deref(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut body = Bytes::from_request(req)
|
||||||
|
.await
|
||||||
|
.map_err(|_| Error::BadRequest(ErrorKind::MissingToken, "Missing token."))?;
|
||||||
|
|
||||||
let mut json_body = serde_json::from_slice::<CanonicalJsonValue>(&body).ok();
|
let mut json_body = serde_json::from_slice::<CanonicalJsonValue>(&body).ok();
|
||||||
|
|
||||||
let appservices = services().appservice.all().unwrap();
|
let appservices = services().appservice.all().unwrap();
|
||||||
|
@ -91,7 +81,7 @@ where
|
||||||
let (sender_user, sender_device, sender_servername, from_appservice) =
|
let (sender_user, sender_device, sender_servername, from_appservice) =
|
||||||
if let Some((_id, registration)) = appservice_registration {
|
if let Some((_id, registration)) = appservice_registration {
|
||||||
match metadata.authentication {
|
match metadata.authentication {
|
||||||
AuthScheme::AccessToken => {
|
AuthScheme::AccessToken | AuthScheme::QueryOnlyAccessToken => {
|
||||||
let user_id = query_params.user_id.map_or_else(
|
let user_id = query_params.user_id.map_or_else(
|
||||||
|| {
|
|| {
|
||||||
UserId::parse_with_server_name(
|
UserId::parse_with_server_name(
|
||||||
|
@ -122,7 +112,7 @@ where
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
match metadata.authentication {
|
match metadata.authentication {
|
||||||
AuthScheme::AccessToken => {
|
AuthScheme::AccessToken | AuthScheme::QueryOnlyAccessToken => {
|
||||||
let token = match token {
|
let token = match token {
|
||||||
Some(token) => token,
|
Some(token) => token,
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -142,15 +132,15 @@ where
|
||||||
}
|
}
|
||||||
Some((user_id, device_id)) => (
|
Some((user_id, device_id)) => (
|
||||||
Some(user_id),
|
Some(user_id),
|
||||||
Some(OwnedDeviceId::from(device_id)),
|
Some(Box::<DeviceId>::from(device_id)),
|
||||||
None,
|
None,
|
||||||
false,
|
false,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AuthScheme::ServerSignatures => {
|
AuthScheme::ServerSignatures => {
|
||||||
let TypedHeader(Authorization(x_matrix)) = parts
|
let TypedHeader(Authorization(x_matrix)) =
|
||||||
.extract::<TypedHeader<Authorization<XMatrix>>>()
|
TypedHeader::<Authorization<XMatrix>>::from_request(req)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
warn!("Missing or invalid Authorization header: {}", e);
|
warn!("Missing or invalid Authorization header: {}", e);
|
||||||
|
@ -181,11 +171,11 @@ where
|
||||||
let mut request_map = BTreeMap::from_iter([
|
let mut request_map = BTreeMap::from_iter([
|
||||||
(
|
(
|
||||||
"method".to_owned(),
|
"method".to_owned(),
|
||||||
CanonicalJsonValue::String(parts.method.to_string()),
|
CanonicalJsonValue::String(req.method().to_string()),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"uri".to_owned(),
|
"uri".to_owned(),
|
||||||
CanonicalJsonValue::String(parts.uri.to_string()),
|
CanonicalJsonValue::String(req.uri().to_string()),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"origin".to_owned(),
|
"origin".to_owned(),
|
||||||
|
@ -235,7 +225,7 @@ where
|
||||||
x_matrix.origin, e, request_map
|
x_matrix.origin, e, request_map
|
||||||
);
|
);
|
||||||
|
|
||||||
if parts.uri.to_string().contains('@') {
|
if req.uri().to_string().contains('@') {
|
||||||
warn!(
|
warn!(
|
||||||
"Request uri contained '@' character. Make sure your \
|
"Request uri contained '@' character. Make sure your \
|
||||||
reverse proxy gives Conduit the raw uri (apache: use \
|
reverse proxy gives Conduit the raw uri (apache: use \
|
||||||
|
@ -254,8 +244,8 @@ where
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut http_request = http::Request::builder().uri(parts.uri).method(parts.method);
|
let mut http_request = http::Request::builder().uri(req.uri()).method(req.method());
|
||||||
*http_request.headers_mut().unwrap() = parts.headers;
|
*http_request.headers_mut().unwrap() = req.headers().clone();
|
||||||
|
|
||||||
if let Some(CanonicalJsonValue::Object(json_body)) = &mut json_body {
|
if let Some(CanonicalJsonValue::Object(json_body)) = &mut json_body {
|
||||||
let user_id = sender_user.clone().unwrap_or_else(|| {
|
let user_id = sender_user.clone().unwrap_or_else(|| {
|
||||||
|
@ -292,8 +282,7 @@ where
|
||||||
debug!("{:?}", http_request);
|
debug!("{:?}", http_request);
|
||||||
|
|
||||||
let body = T::try_from_http_request(http_request, &path_params).map_err(|e| {
|
let body = T::try_from_http_request(http_request, &path_params).map_err(|e| {
|
||||||
warn!("try_from_http_request failed: {:?}", e);
|
warn!("{:?}", e);
|
||||||
debug!("JSON body: {:?}", json_body);
|
|
||||||
Error::BadRequest(ErrorKind::BadJson, "Failed to deserialize request.")
|
Error::BadRequest(ErrorKind::BadJson, "Failed to deserialize request.")
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
@ -309,7 +298,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
struct XMatrix {
|
struct XMatrix {
|
||||||
origin: OwnedServerName,
|
origin: Box<ServerName>,
|
||||||
key: String, // KeyName?
|
key: String, // KeyName?
|
||||||
sig: String,
|
sig: String,
|
||||||
}
|
}
|
||||||
|
@ -320,7 +309,8 @@ impl Credentials for XMatrix {
|
||||||
fn decode(value: &http::HeaderValue) -> Option<Self> {
|
fn decode(value: &http::HeaderValue) -> Option<Self> {
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
value.as_bytes().starts_with(b"X-Matrix "),
|
value.as_bytes().starts_with(b"X-Matrix "),
|
||||||
"HeaderValue to decode should start with \"X-Matrix ..\", received = {value:?}",
|
"HeaderValue to decode should start with \"X-Matrix ..\", received = {:?}",
|
||||||
|
value,
|
||||||
);
|
);
|
||||||
|
|
||||||
let parameters = str::from_utf8(&value.as_bytes()["X-Matrix ".len()..])
|
let parameters = str::from_utf8(&value.as_bytes()["X-Matrix ".len()..])
|
||||||
|
@ -373,55 +363,3 @@ 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())
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::uiaa::UiaaResponse, CanonicalJsonValue, OwnedDeviceId, OwnedServerName,
|
api::client::uiaa::UiaaResponse, signatures::CanonicalJsonValue, DeviceId, ServerName, UserId,
|
||||||
OwnedUserId,
|
|
||||||
};
|
};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
|
@ -11,9 +10,9 @@ mod axum;
|
||||||
/// Extractor for Ruma request structs
|
/// Extractor for Ruma request structs
|
||||||
pub struct Ruma<T> {
|
pub struct Ruma<T> {
|
||||||
pub body: T,
|
pub body: T,
|
||||||
pub sender_user: Option<OwnedUserId>,
|
pub sender_user: Option<Box<UserId>>,
|
||||||
pub sender_device: Option<OwnedDeviceId>,
|
pub sender_device: Option<Box<DeviceId>>,
|
||||||
pub sender_servername: Option<OwnedServerName>,
|
pub sender_servername: Option<Box<ServerName>>,
|
||||||
// This is None when body is not a valid string
|
// This is None when body is not a valid string
|
||||||
pub json_body: Option<CanonicalJsonValue>,
|
pub json_body: Option<CanonicalJsonValue>,
|
||||||
pub from_appservice: bool,
|
pub from_appservice: bool,
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -4,7 +4,7 @@ use std::{
|
||||||
net::{IpAddr, Ipv4Addr},
|
net::{IpAddr, Ipv4Addr},
|
||||||
};
|
};
|
||||||
|
|
||||||
use ruma::{OwnedServerName, RoomVersionId};
|
use ruma::{RoomVersionId, ServerName};
|
||||||
use serde::{de::IgnoredAny, Deserialize};
|
use serde::{de::IgnoredAny, Deserialize};
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
|
||||||
|
@ -20,16 +20,12 @@ pub struct Config {
|
||||||
pub port: u16,
|
pub port: u16,
|
||||||
pub tls: Option<TlsConfig>,
|
pub tls: Option<TlsConfig>,
|
||||||
|
|
||||||
pub server_name: OwnedServerName,
|
pub server_name: Box<ServerName>,
|
||||||
#[serde(default = "default_database_backend")]
|
#[serde(default = "default_database_backend")]
|
||||||
pub database_backend: String,
|
pub database_backend: String,
|
||||||
pub database_path: String,
|
pub database_path: String,
|
||||||
#[serde(default = "default_db_cache_capacity_mb")]
|
#[serde(default = "default_db_cache_capacity_mb")]
|
||||||
pub db_cache_capacity_mb: f64,
|
pub db_cache_capacity_mb: f64,
|
||||||
#[serde(default = "true_fn")]
|
|
||||||
pub enable_lightning_bolt: bool,
|
|
||||||
#[serde(default = "true_fn")]
|
|
||||||
pub allow_check_for_updates: bool,
|
|
||||||
#[serde(default = "default_conduit_cache_capacity_modifier")]
|
#[serde(default = "default_conduit_cache_capacity_modifier")]
|
||||||
pub conduit_cache_capacity_modifier: f64,
|
pub conduit_cache_capacity_modifier: f64,
|
||||||
#[serde(default = "default_rocksdb_max_open_files")]
|
#[serde(default = "default_rocksdb_max_open_files")]
|
||||||
|
@ -42,11 +38,8 @@ pub struct Config {
|
||||||
pub max_request_size: u32,
|
pub max_request_size: u32,
|
||||||
#[serde(default = "default_max_concurrent_requests")]
|
#[serde(default = "default_max_concurrent_requests")]
|
||||||
pub max_concurrent_requests: u16,
|
pub max_concurrent_requests: u16,
|
||||||
#[serde(default = "default_max_fetch_prev_events")]
|
|
||||||
pub max_fetch_prev_events: u16,
|
|
||||||
#[serde(default = "false_fn")]
|
#[serde(default = "false_fn")]
|
||||||
pub allow_registration: bool,
|
pub allow_registration: bool,
|
||||||
pub registration_token: Option<String>,
|
|
||||||
#[serde(default = "true_fn")]
|
#[serde(default = "true_fn")]
|
||||||
pub allow_encryption: bool,
|
pub allow_encryption: bool,
|
||||||
#[serde(default = "false_fn")]
|
#[serde(default = "false_fn")]
|
||||||
|
@ -57,7 +50,6 @@ pub struct Config {
|
||||||
pub allow_unstable_room_versions: bool,
|
pub allow_unstable_room_versions: bool,
|
||||||
#[serde(default = "default_default_room_version")]
|
#[serde(default = "default_default_room_version")]
|
||||||
pub default_room_version: RoomVersionId,
|
pub default_room_version: RoomVersionId,
|
||||||
pub well_known_client: Option<String>,
|
|
||||||
#[serde(default = "false_fn")]
|
#[serde(default = "false_fn")]
|
||||||
pub allow_jaeger: bool,
|
pub allow_jaeger: bool,
|
||||||
#[serde(default = "false_fn")]
|
#[serde(default = "false_fn")]
|
||||||
|
@ -65,8 +57,8 @@ pub struct Config {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub proxy: ProxyConfig,
|
pub proxy: ProxyConfig,
|
||||||
pub jwt_secret: Option<String>,
|
pub jwt_secret: Option<String>,
|
||||||
#[serde(default = "default_trusted_servers")]
|
#[serde(default = "Vec::new")]
|
||||||
pub trusted_servers: Vec<OwnedServerName>,
|
pub trusted_servers: Vec<Box<ServerName>>,
|
||||||
#[serde(default = "default_log")]
|
#[serde(default = "default_log")]
|
||||||
pub log: String,
|
pub log: String,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
@ -143,10 +135,6 @@ impl fmt::Display for Config {
|
||||||
&self.max_concurrent_requests.to_string(),
|
&self.max_concurrent_requests.to_string(),
|
||||||
),
|
),
|
||||||
("Allow registration", &self.allow_registration.to_string()),
|
("Allow registration", &self.allow_registration.to_string()),
|
||||||
(
|
|
||||||
"Enabled lightning bolt",
|
|
||||||
&self.enable_lightning_bolt.to_string(),
|
|
||||||
),
|
|
||||||
("Allow encryption", &self.allow_encryption.to_string()),
|
("Allow encryption", &self.allow_encryption.to_string()),
|
||||||
("Allow federation", &self.allow_federation.to_string()),
|
("Allow federation", &self.allow_federation.to_string()),
|
||||||
("Allow room creation", &self.allow_room_creation.to_string()),
|
("Allow room creation", &self.allow_room_creation.to_string()),
|
||||||
|
@ -189,7 +177,7 @@ impl fmt::Display for Config {
|
||||||
("Turn TTL", &self.turn_ttl.to_string()),
|
("Turn TTL", &self.turn_ttl.to_string()),
|
||||||
("Turn URIs", {
|
("Turn URIs", {
|
||||||
let mut lst = vec![];
|
let mut lst = vec![];
|
||||||
for item in self.turn_uris.iter().cloned().enumerate() {
|
for item in self.turn_uris.to_vec().into_iter().enumerate() {
|
||||||
let (_, uri): (usize, String) = item;
|
let (_, uri): (usize, String) = item;
|
||||||
lst.push(uri);
|
lst.push(uri);
|
||||||
}
|
}
|
||||||
|
@ -197,13 +185,13 @@ impl fmt::Display for Config {
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut msg: String = "Active config values:\n\n".to_owned();
|
let mut msg: String = "Active config values:\n\n".to_string();
|
||||||
|
|
||||||
for line in lines.into_iter().enumerate() {
|
for line in lines.into_iter().enumerate() {
|
||||||
msg += &format!("{}: {}\n", line.1 .0, line.1 .1);
|
msg += &format!("{}: {}\n", line.1 .0, line.1 .1);
|
||||||
}
|
}
|
||||||
|
|
||||||
write!(f, "{msg}")
|
write!(f, "{}", msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,7 +216,7 @@ fn default_database_backend() -> String {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_db_cache_capacity_mb() -> f64 {
|
fn default_db_cache_capacity_mb() -> f64 {
|
||||||
300.0
|
10.0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_conduit_cache_capacity_modifier() -> f64 {
|
fn default_conduit_cache_capacity_modifier() -> f64 {
|
||||||
|
@ -236,7 +224,7 @@ fn default_conduit_cache_capacity_modifier() -> f64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_rocksdb_max_open_files() -> i32 {
|
fn default_rocksdb_max_open_files() -> i32 {
|
||||||
1000
|
20
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_pdu_cache_capacity() -> u32 {
|
fn default_pdu_cache_capacity() -> u32 {
|
||||||
|
@ -244,7 +232,7 @@ fn default_pdu_cache_capacity() -> u32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_cleanup_second_interval() -> u32 {
|
fn default_cleanup_second_interval() -> u32 {
|
||||||
60 // every minute
|
1 * 60 // every minute
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_max_request_size() -> u32 {
|
fn default_max_request_size() -> u32 {
|
||||||
|
@ -255,16 +243,8 @@ fn default_max_concurrent_requests() -> u16 {
|
||||||
100
|
100
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_max_fetch_prev_events() -> u16 {
|
|
||||||
100_u16
|
|
||||||
}
|
|
||||||
|
|
||||||
fn default_trusted_servers() -> Vec<OwnedServerName> {
|
|
||||||
vec![OwnedServerName::try_from("matrix.org").unwrap()]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn default_log() -> String {
|
fn default_log() -> String {
|
||||||
"warn,state_res=warn,_=off,sled=off".to_owned()
|
"info,state_res=warn,_=off,sled=off".to_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_turn_ttl() -> u64 {
|
fn default_turn_ttl() -> u64 {
|
||||||
|
@ -272,6 +252,6 @@ fn default_turn_ttl() -> u64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// I know, it's a great name
|
// I know, it's a great name
|
||||||
pub fn default_default_room_version() -> RoomVersionId {
|
fn default_default_room_version() -> RoomVersionId {
|
||||||
RoomVersionId::V9
|
RoomVersionId::V6
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,9 +29,7 @@ use crate::Result;
|
||||||
/// would be used for `ordinary.onion`, `matrix.myspecial.onion`, but not `hello.myspecial.onion`.
|
/// would be used for `ordinary.onion`, `matrix.myspecial.onion`, but not `hello.myspecial.onion`.
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
#[derive(Default)]
|
|
||||||
pub enum ProxyConfig {
|
pub enum ProxyConfig {
|
||||||
#[default]
|
|
||||||
None,
|
None,
|
||||||
Global {
|
Global {
|
||||||
#[serde(deserialize_with = "crate::utils::deserialize_from_str")]
|
#[serde(deserialize_with = "crate::utils::deserialize_from_str")]
|
||||||
|
@ -50,6 +48,11 @@ impl ProxyConfig {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl Default for ProxyConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
ProxyConfig::None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
pub struct PartialProxyConfig {
|
pub struct PartialProxyConfig {
|
||||||
|
|
|
@ -38,7 +38,6 @@ pub trait KeyValueDatabaseEngine: Send + Sync {
|
||||||
fn memory_usage(&self) -> Result<String> {
|
fn memory_usage(&self) -> Result<String> {
|
||||||
Ok("Current database engine does not support memory usage reporting.".to_owned())
|
Ok("Current database engine does not support memory usage reporting.".to_owned())
|
||||||
}
|
}
|
||||||
fn clear_caches(&self) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait KvTree: Send + Sync {
|
pub trait KvTree: Send + Sync {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
database::{
|
database::{
|
||||||
abstraction::{watchers::Watchers, KeyValueDatabaseEngine, KvTree},
|
abstraction::{watchers::Watchers, DatabaseEngine, Tree},
|
||||||
Config,
|
Config,
|
||||||
},
|
},
|
||||||
Result,
|
Result,
|
||||||
|
@ -15,7 +15,7 @@ pub struct Engine {
|
||||||
persy: Persy,
|
persy: Persy,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KeyValueDatabaseEngine for Arc<Engine> {
|
impl DatabaseEngine for Arc<Engine> {
|
||||||
fn open(config: &Config) -> Result<Self> {
|
fn open(config: &Config) -> Result<Self> {
|
||||||
let mut cfg = persy::Config::new();
|
let mut cfg = persy::Config::new();
|
||||||
cfg.change_cache_size((config.db_cache_capacity_mb * 1024.0 * 1024.0) as u64);
|
cfg.change_cache_size((config.db_cache_capacity_mb * 1024.0 * 1024.0) as u64);
|
||||||
|
@ -27,7 +27,7 @@ impl KeyValueDatabaseEngine for Arc<Engine> {
|
||||||
Ok(Arc::new(Engine { persy }))
|
Ok(Arc::new(Engine { persy }))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn open_tree(&self, name: &'static str) -> Result<Arc<dyn KvTree>> {
|
fn open_tree(&self, name: &'static str) -> Result<Arc<dyn Tree>> {
|
||||||
// Create if it doesn't exist
|
// Create if it doesn't exist
|
||||||
if !self.persy.exists_index(name)? {
|
if !self.persy.exists_index(name)? {
|
||||||
let mut tx = self.persy.begin()?;
|
let mut tx = self.persy.begin()?;
|
||||||
|
@ -61,7 +61,7 @@ impl PersyTree {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KvTree for PersyTree {
|
impl Tree for PersyTree {
|
||||||
fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>> {
|
fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>> {
|
||||||
let result = self
|
let result = self
|
||||||
.persy
|
.persy
|
||||||
|
@ -116,7 +116,7 @@ impl KvTree for PersyTree {
|
||||||
match iter {
|
match iter {
|
||||||
Ok(iter) => Box::new(iter.filter_map(|(k, v)| {
|
Ok(iter) => Box::new(iter.filter_map(|(k, v)| {
|
||||||
v.into_iter()
|
v.into_iter()
|
||||||
.map(|val| ((*k).to_owned(), (*val).to_owned()))
|
.map(|val| ((*k).to_owned().into(), (*val).to_owned().into()))
|
||||||
.next()
|
.next()
|
||||||
})),
|
})),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
@ -142,7 +142,7 @@ impl KvTree for PersyTree {
|
||||||
Ok(iter) => {
|
Ok(iter) => {
|
||||||
let map = iter.filter_map(|(k, v)| {
|
let map = iter.filter_map(|(k, v)| {
|
||||||
v.into_iter()
|
v.into_iter()
|
||||||
.map(|val| ((*k).to_owned(), (*val).to_owned()))
|
.map(|val| ((*k).to_owned().into(), (*val).to_owned().into()))
|
||||||
.next()
|
.next()
|
||||||
});
|
});
|
||||||
if backwards {
|
if backwards {
|
||||||
|
@ -179,7 +179,7 @@ impl KvTree for PersyTree {
|
||||||
iter.take_while(move |(k, _)| (*k).starts_with(&owned_prefix))
|
iter.take_while(move |(k, _)| (*k).starts_with(&owned_prefix))
|
||||||
.filter_map(|(k, v)| {
|
.filter_map(|(k, v)| {
|
||||||
v.into_iter()
|
v.into_iter()
|
||||||
.map(|val| ((*k).to_owned(), (*val).to_owned()))
|
.map(|val| ((*k).to_owned().into(), (*val).to_owned().into()))
|
||||||
.next()
|
.next()
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
|
@ -45,17 +45,6 @@ fn db_options(max_open_files: i32, rocksdb_cache: &rocksdb::Cache) -> rocksdb::O
|
||||||
db_opts.set_compaction_style(rocksdb::DBCompactionStyle::Level);
|
db_opts.set_compaction_style(rocksdb::DBCompactionStyle::Level);
|
||||||
db_opts.optimize_level_style_compaction(10 * 1024 * 1024);
|
db_opts.optimize_level_style_compaction(10 * 1024 * 1024);
|
||||||
|
|
||||||
// https://github.com/facebook/rocksdb/wiki/Setup-Options-and-Basic-Tuning
|
|
||||||
db_opts.set_max_background_jobs(6);
|
|
||||||
db_opts.set_bytes_per_sync(1048576);
|
|
||||||
|
|
||||||
// https://github.com/facebook/rocksdb/wiki/WAL-Recovery-Modes#ktoleratecorruptedtailrecords
|
|
||||||
//
|
|
||||||
// Unclean shutdowns of a Matrix homeserver are likely to be fine when
|
|
||||||
// recovered in this manner as it's likely any lost information will be
|
|
||||||
// restored via federation.
|
|
||||||
db_opts.set_wal_recovery_mode(rocksdb::DBRecoveryMode::TolerateCorruptedTailRecords);
|
|
||||||
|
|
||||||
let prefix_extractor = rocksdb::SliceTransform::create_fixed_prefix(1);
|
let prefix_extractor = rocksdb::SliceTransform::create_fixed_prefix(1);
|
||||||
db_opts.set_prefix_extractor(prefix_extractor);
|
db_opts.set_prefix_extractor(prefix_extractor);
|
||||||
|
|
||||||
|
@ -65,7 +54,7 @@ fn db_options(max_open_files: i32, rocksdb_cache: &rocksdb::Cache) -> rocksdb::O
|
||||||
impl KeyValueDatabaseEngine for Arc<Engine> {
|
impl KeyValueDatabaseEngine for Arc<Engine> {
|
||||||
fn open(config: &Config) -> Result<Self> {
|
fn open(config: &Config) -> Result<Self> {
|
||||||
let cache_capacity_bytes = (config.db_cache_capacity_mb * 1024.0 * 1024.0) as usize;
|
let cache_capacity_bytes = (config.db_cache_capacity_mb * 1024.0 * 1024.0) as usize;
|
||||||
let rocksdb_cache = rocksdb::Cache::new_lru_cache(cache_capacity_bytes);
|
let rocksdb_cache = rocksdb::Cache::new_lru_cache(cache_capacity_bytes).unwrap();
|
||||||
|
|
||||||
let db_opts = db_options(config.rocksdb_max_open_files, &rocksdb_cache);
|
let db_opts = db_options(config.rocksdb_max_open_files, &rocksdb_cache);
|
||||||
|
|
||||||
|
@ -132,8 +121,6 @@ impl KeyValueDatabaseEngine for Arc<Engine> {
|
||||||
self.cache.get_pinned_usage() as f64 / 1024.0 / 1024.0,
|
self.cache.get_pinned_usage() as f64 / 1024.0 / 1024.0,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear_caches(&self) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RocksDbEngineTree<'_> {
|
impl RocksDbEngineTree<'_> {
|
||||||
|
@ -174,7 +161,6 @@ impl KvTree for RocksDbEngineTree<'_> {
|
||||||
self.db
|
self.db
|
||||||
.rocks
|
.rocks
|
||||||
.iterator_cf(&self.cf(), rocksdb::IteratorMode::Start)
|
.iterator_cf(&self.cf(), rocksdb::IteratorMode::Start)
|
||||||
.map(|r| r.unwrap())
|
|
||||||
.map(|(k, v)| (Vec::from(k), Vec::from(v))),
|
.map(|(k, v)| (Vec::from(k), Vec::from(v))),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -198,7 +184,6 @@ impl KvTree for RocksDbEngineTree<'_> {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.map(|r| r.unwrap())
|
|
||||||
.map(|(k, v)| (Vec::from(k), Vec::from(v))),
|
.map(|(k, v)| (Vec::from(k), Vec::from(v))),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -206,7 +191,7 @@ impl KvTree for RocksDbEngineTree<'_> {
|
||||||
fn increment(&self, key: &[u8]) -> Result<Vec<u8>> {
|
fn increment(&self, key: &[u8]) -> Result<Vec<u8>> {
|
||||||
let lock = self.write_lock.write().unwrap();
|
let lock = self.write_lock.write().unwrap();
|
||||||
|
|
||||||
let old = self.db.rocks.get_cf(&self.cf(), key)?;
|
let old = self.db.rocks.get_cf(&self.cf(), &key)?;
|
||||||
let new = utils::increment(old.as_deref()).unwrap();
|
let new = utils::increment(old.as_deref()).unwrap();
|
||||||
self.db.rocks.put_cf(&self.cf(), key, &new)?;
|
self.db.rocks.put_cf(&self.cf(), key, &new)?;
|
||||||
|
|
||||||
|
@ -239,7 +224,6 @@ impl KvTree for RocksDbEngineTree<'_> {
|
||||||
&self.cf(),
|
&self.cf(),
|
||||||
rocksdb::IteratorMode::From(&prefix, rocksdb::Direction::Forward),
|
rocksdb::IteratorMode::From(&prefix, rocksdb::Direction::Forward),
|
||||||
)
|
)
|
||||||
.map(|r| r.unwrap())
|
|
||||||
.map(|(k, v)| (Vec::from(k), Vec::from(v)))
|
.map(|(k, v)| (Vec::from(k), Vec::from(v)))
|
||||||
.take_while(move |(k, _)| k.starts_with(&prefix)),
|
.take_while(move |(k, _)| k.starts_with(&prefix)),
|
||||||
)
|
)
|
||||||
|
|
|
@ -33,7 +33,7 @@ impl Iterator for PreparedStatementIterator<'_> {
|
||||||
struct NonAliasingBox<T>(*mut T);
|
struct NonAliasingBox<T>(*mut T);
|
||||||
impl<T> Drop for NonAliasingBox<T> {
|
impl<T> Drop for NonAliasingBox<T> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
drop(unsafe { Box::from_raw(self.0) });
|
unsafe { Box::from_raw(self.0) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,13 +48,13 @@ pub struct Engine {
|
||||||
|
|
||||||
impl Engine {
|
impl Engine {
|
||||||
fn prepare_conn(path: &Path, cache_size_kb: u32) -> Result<Connection> {
|
fn prepare_conn(path: &Path, cache_size_kb: u32) -> Result<Connection> {
|
||||||
let conn = Connection::open(path)?;
|
let conn = Connection::open(&path)?;
|
||||||
|
|
||||||
conn.pragma_update(Some(Main), "page_size", 2048)?;
|
conn.pragma_update(Some(Main), "page_size", &2048)?;
|
||||||
conn.pragma_update(Some(Main), "journal_mode", "WAL")?;
|
conn.pragma_update(Some(Main), "journal_mode", &"WAL")?;
|
||||||
conn.pragma_update(Some(Main), "synchronous", "NORMAL")?;
|
conn.pragma_update(Some(Main), "synchronous", &"NORMAL")?;
|
||||||
conn.pragma_update(Some(Main), "cache_size", -i64::from(cache_size_kb))?;
|
conn.pragma_update(Some(Main), "cache_size", &(-i64::from(cache_size_kb)))?;
|
||||||
conn.pragma_update(Some(Main), "wal_autocheckpoint", 0)?;
|
conn.pragma_update(Some(Main), "wal_autocheckpoint", &0)?;
|
||||||
|
|
||||||
Ok(conn)
|
Ok(conn)
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ impl Engine {
|
||||||
|
|
||||||
pub fn flush_wal(self: &Arc<Self>) -> Result<()> {
|
pub fn flush_wal(self: &Arc<Self>) -> Result<()> {
|
||||||
self.write_lock()
|
self.write_lock()
|
||||||
.pragma_update(Some(Main), "wal_checkpoint", "RESTART")?;
|
.pragma_update(Some(Main), "wal_checkpoint", &"RESTART")?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -106,7 +106,7 @@ impl KeyValueDatabaseEngine for Arc<Engine> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn open_tree(&self, name: &str) -> Result<Arc<dyn KvTree>> {
|
fn open_tree(&self, name: &str) -> Result<Arc<dyn KvTree>> {
|
||||||
self.write_lock().execute(&format!("CREATE TABLE IF NOT EXISTS {name} ( \"key\" BLOB PRIMARY KEY, \"value\" BLOB NOT NULL )"), [])?;
|
self.write_lock().execute(&format!("CREATE TABLE IF NOT EXISTS {} ( \"key\" BLOB PRIMARY KEY, \"value\" BLOB NOT NULL )", name), [])?;
|
||||||
|
|
||||||
Ok(Arc::new(SqliteTable {
|
Ok(Arc::new(SqliteTable {
|
||||||
engine: Arc::clone(self),
|
engine: Arc::clone(self),
|
||||||
|
@ -135,6 +135,7 @@ type TupleOfBytes = (Vec<u8>, Vec<u8>);
|
||||||
|
|
||||||
impl SqliteTable {
|
impl SqliteTable {
|
||||||
fn get_with_guard(&self, guard: &Connection, key: &[u8]) -> Result<Option<Vec<u8>>> {
|
fn get_with_guard(&self, guard: &Connection, key: &[u8]) -> Result<Option<Vec<u8>>> {
|
||||||
|
//dbg!(&self.name);
|
||||||
Ok(guard
|
Ok(guard
|
||||||
.prepare(format!("SELECT value FROM {} WHERE key = ?", self.name).as_str())?
|
.prepare(format!("SELECT value FROM {} WHERE key = ?", self.name).as_str())?
|
||||||
.query_row([key], |row| row.get(0))
|
.query_row([key], |row| row.get(0))
|
||||||
|
@ -142,6 +143,7 @@ impl SqliteTable {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert_with_guard(&self, guard: &Connection, key: &[u8], value: &[u8]) -> Result<()> {
|
fn insert_with_guard(&self, guard: &Connection, key: &[u8], value: &[u8]) -> Result<()> {
|
||||||
|
//dbg!(&self.name);
|
||||||
guard.execute(
|
guard.execute(
|
||||||
format!(
|
format!(
|
||||||
"INSERT OR REPLACE INTO {} (key, value) VALUES (?, ?)",
|
"INSERT OR REPLACE INTO {} (key, value) VALUES (?, ?)",
|
||||||
|
@ -174,7 +176,10 @@ impl SqliteTable {
|
||||||
statement
|
statement
|
||||||
.query_map([], |row| Ok((row.get_unwrap(0), row.get_unwrap(1))))
|
.query_map([], |row| Ok((row.get_unwrap(0), row.get_unwrap(1))))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.map(move |r| r.unwrap()),
|
.map(move |r| {
|
||||||
|
//dbg!(&name);
|
||||||
|
r.unwrap()
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
Box::new(PreparedStatementIterator {
|
Box::new(PreparedStatementIterator {
|
||||||
|
@ -271,7 +276,10 @@ impl KvTree for SqliteTable {
|
||||||
statement
|
statement
|
||||||
.query_map([from], |row| Ok((row.get_unwrap(0), row.get_unwrap(1))))
|
.query_map([from], |row| Ok((row.get_unwrap(0), row.get_unwrap(1))))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.map(move |r| r.unwrap()),
|
.map(move |r| {
|
||||||
|
//dbg!(&name);
|
||||||
|
r.unwrap()
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
Box::new(PreparedStatementIterator {
|
Box::new(PreparedStatementIterator {
|
||||||
iterator,
|
iterator,
|
||||||
|
@ -293,7 +301,10 @@ impl KvTree for SqliteTable {
|
||||||
statement
|
statement
|
||||||
.query_map([from], |row| Ok((row.get_unwrap(0), row.get_unwrap(1))))
|
.query_map([from], |row| Ok((row.get_unwrap(0), row.get_unwrap(1))))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.map(move |r| r.unwrap()),
|
.map(move |r| {
|
||||||
|
//dbg!(&name);
|
||||||
|
r.unwrap()
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
Box::new(PreparedStatementIterator {
|
Box::new(PreparedStatementIterator {
|
||||||
|
|
|
@ -8,7 +8,6 @@ use tokio::sync::watch;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub(super) struct Watchers {
|
pub(super) struct Watchers {
|
||||||
#[allow(clippy::type_complexity)]
|
|
||||||
watchers: RwLock<HashMap<Vec<u8>, (watch::Sender<()>, watch::Receiver<()>)>>,
|
watchers: RwLock<HashMap<Vec<u8>, (watch::Sender<()>, watch::Receiver<()>)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -123,12 +123,13 @@ impl service::account_data::Data for KeyValueDatabase {
|
||||||
.take_while(move |(k, _)| k.starts_with(&prefix))
|
.take_while(move |(k, _)| k.starts_with(&prefix))
|
||||||
.map(|(k, v)| {
|
.map(|(k, v)| {
|
||||||
Ok::<_, Error>((
|
Ok::<_, Error>((
|
||||||
RoomAccountDataEventType::from(
|
RoomAccountDataEventType::try_from(
|
||||||
utils::string_from_bytes(k.rsplit(|&b| b == 0xff).next().ok_or_else(
|
utils::string_from_bytes(k.rsplit(|&b| b == 0xff).next().ok_or_else(
|
||||||
|| Error::bad_database("RoomUserData ID in db is invalid."),
|
|| Error::bad_database("RoomUserData ID in db is invalid."),
|
||||||
)?)
|
)?)
|
||||||
.map_err(|_| Error::bad_database("RoomUserData ID in db is invalid."))?,
|
.map_err(|_| Error::bad_database("RoomUserData ID in db is invalid."))?,
|
||||||
),
|
)
|
||||||
|
.map_err(|_| Error::bad_database("RoomUserData ID in db is invalid."))?,
|
||||||
serde_json::from_slice::<Raw<AnyEphemeralRoomEvent>>(&v).map_err(|_| {
|
serde_json::from_slice::<Raw<AnyEphemeralRoomEvent>>(&v).map_err(|_| {
|
||||||
Error::bad_database("Database contains invalid account data.")
|
Error::bad_database("Database contains invalid account data.")
|
||||||
})?,
|
})?,
|
||||||
|
|
|
@ -1,18 +1,16 @@
|
||||||
use std::collections::{BTreeMap, HashMap};
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use futures_util::{stream::FuturesUnordered, StreamExt};
|
use futures_util::{stream::FuturesUnordered, StreamExt};
|
||||||
use lru_cache::LruCache;
|
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::federation::discovery::{ServerSigningKeys, VerifyKey},
|
api::federation::discovery::{ServerSigningKeys, VerifyKey},
|
||||||
signatures::Ed25519KeyPair,
|
signatures::Ed25519KeyPair,
|
||||||
DeviceId, MilliSecondsSinceUnixEpoch, OwnedServerSigningKeyId, ServerName, UserId,
|
DeviceId, MilliSecondsSinceUnixEpoch, ServerName, ServerSigningKeyId, UserId,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{database::KeyValueDatabase, service, services, utils, Error, Result};
|
use crate::{database::KeyValueDatabase, service, services, utils, Error, Result};
|
||||||
|
|
||||||
pub const COUNTER: &[u8] = b"c";
|
pub const COUNTER: &[u8] = b"c";
|
||||||
pub const LAST_CHECK_FOR_UPDATES_COUNT: &[u8] = b"u";
|
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl service::globals::Data for KeyValueDatabase {
|
impl service::globals::Data for KeyValueDatabase {
|
||||||
|
@ -28,23 +26,6 @@ impl service::globals::Data for KeyValueDatabase {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn last_check_for_updates_id(&self) -> Result<u64> {
|
|
||||||
self.global
|
|
||||||
.get(LAST_CHECK_FOR_UPDATES_COUNT)?
|
|
||||||
.map_or(Ok(0_u64), |bytes| {
|
|
||||||
utils::u64_from_bytes(&bytes).map_err(|_| {
|
|
||||||
Error::bad_database("last check for updates count has invalid bytes.")
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_check_for_updates_id(&self, id: u64) -> Result<()> {
|
|
||||||
self.global
|
|
||||||
.insert(LAST_CHECK_FOR_UPDATES_COUNT, &id.to_be_bytes())?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn watch(&self, user_id: &UserId, device_id: &DeviceId) -> Result<()> {
|
async fn watch(&self, user_id: &UserId, device_id: &DeviceId) -> Result<()> {
|
||||||
let userid_bytes = user_id.as_bytes().to_vec();
|
let userid_bytes = user_id.as_bytes().to_vec();
|
||||||
let mut userid_prefix = userid_bytes.clone();
|
let mut userid_prefix = userid_bytes.clone();
|
||||||
|
@ -137,67 +118,8 @@ impl service::globals::Data for KeyValueDatabase {
|
||||||
self._db.cleanup()
|
self._db.cleanup()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn memory_usage(&self) -> String {
|
fn memory_usage(&self) -> Result<String> {
|
||||||
let pdu_cache = self.pdu_cache.lock().unwrap().len();
|
self._db.memory_usage()
|
||||||
let shorteventid_cache = self.shorteventid_cache.lock().unwrap().len();
|
|
||||||
let auth_chain_cache = self.auth_chain_cache.lock().unwrap().len();
|
|
||||||
let eventidshort_cache = self.eventidshort_cache.lock().unwrap().len();
|
|
||||||
let statekeyshort_cache = self.statekeyshort_cache.lock().unwrap().len();
|
|
||||||
let our_real_users_cache = self.our_real_users_cache.read().unwrap().len();
|
|
||||||
let appservice_in_room_cache = self.appservice_in_room_cache.read().unwrap().len();
|
|
||||||
let lasttimelinecount_cache = self.lasttimelinecount_cache.lock().unwrap().len();
|
|
||||||
|
|
||||||
let mut response = format!(
|
|
||||||
"\
|
|
||||||
pdu_cache: {pdu_cache}
|
|
||||||
shorteventid_cache: {shorteventid_cache}
|
|
||||||
auth_chain_cache: {auth_chain_cache}
|
|
||||||
eventidshort_cache: {eventidshort_cache}
|
|
||||||
statekeyshort_cache: {statekeyshort_cache}
|
|
||||||
our_real_users_cache: {our_real_users_cache}
|
|
||||||
appservice_in_room_cache: {appservice_in_room_cache}
|
|
||||||
lasttimelinecount_cache: {lasttimelinecount_cache}\n"
|
|
||||||
);
|
|
||||||
if let Ok(db_stats) = self._db.memory_usage() {
|
|
||||||
response += &db_stats;
|
|
||||||
}
|
|
||||||
|
|
||||||
response
|
|
||||||
}
|
|
||||||
|
|
||||||
fn clear_caches(&self, amount: u32) {
|
|
||||||
if amount > 0 {
|
|
||||||
let c = &mut *self.pdu_cache.lock().unwrap();
|
|
||||||
*c = LruCache::new(c.capacity());
|
|
||||||
}
|
|
||||||
if amount > 1 {
|
|
||||||
let c = &mut *self.shorteventid_cache.lock().unwrap();
|
|
||||||
*c = LruCache::new(c.capacity());
|
|
||||||
}
|
|
||||||
if amount > 2 {
|
|
||||||
let c = &mut *self.auth_chain_cache.lock().unwrap();
|
|
||||||
*c = LruCache::new(c.capacity());
|
|
||||||
}
|
|
||||||
if amount > 3 {
|
|
||||||
let c = &mut *self.eventidshort_cache.lock().unwrap();
|
|
||||||
*c = LruCache::new(c.capacity());
|
|
||||||
}
|
|
||||||
if amount > 4 {
|
|
||||||
let c = &mut *self.statekeyshort_cache.lock().unwrap();
|
|
||||||
*c = LruCache::new(c.capacity());
|
|
||||||
}
|
|
||||||
if amount > 5 {
|
|
||||||
let c = &mut *self.our_real_users_cache.write().unwrap();
|
|
||||||
*c = HashMap::new();
|
|
||||||
}
|
|
||||||
if amount > 6 {
|
|
||||||
let c = &mut *self.appservice_in_room_cache.write().unwrap();
|
|
||||||
*c = HashMap::new();
|
|
||||||
}
|
|
||||||
if amount > 7 {
|
|
||||||
let c = &mut *self.lasttimelinecount_cache.lock().unwrap();
|
|
||||||
*c = HashMap::new();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_keypair(&self) -> Result<Ed25519KeyPair> {
|
fn load_keypair(&self) -> Result<Ed25519KeyPair> {
|
||||||
|
@ -212,7 +134,7 @@ lasttimelinecount_cache: {lasttimelinecount_cache}\n"
|
||||||
|
|
||||||
let mut parts = keypair_bytes.splitn(2, |&b| b == 0xff);
|
let mut parts = keypair_bytes.splitn(2, |&b| b == 0xff);
|
||||||
|
|
||||||
utils::string_from_bytes(
|
let keypair = utils::string_from_bytes(
|
||||||
// 1. version
|
// 1. version
|
||||||
parts
|
parts
|
||||||
.next()
|
.next()
|
||||||
|
@ -229,7 +151,9 @@ lasttimelinecount_cache: {lasttimelinecount_cache}\n"
|
||||||
.and_then(|(version, key)| {
|
.and_then(|(version, key)| {
|
||||||
Ed25519KeyPair::from_der(key, version)
|
Ed25519KeyPair::from_der(key, version)
|
||||||
.map_err(|_| Error::bad_database("Private or public keys are invalid."))
|
.map_err(|_| Error::bad_database("Private or public keys are invalid."))
|
||||||
})
|
});
|
||||||
|
|
||||||
|
keypair
|
||||||
}
|
}
|
||||||
fn remove_keypair(&self) -> Result<()> {
|
fn remove_keypair(&self) -> Result<()> {
|
||||||
self.global.remove(b"keypair")
|
self.global.remove(b"keypair")
|
||||||
|
@ -239,7 +163,7 @@ lasttimelinecount_cache: {lasttimelinecount_cache}\n"
|
||||||
&self,
|
&self,
|
||||||
origin: &ServerName,
|
origin: &ServerName,
|
||||||
new_keys: ServerSigningKeys,
|
new_keys: ServerSigningKeys,
|
||||||
) -> Result<BTreeMap<OwnedServerSigningKeyId, VerifyKey>> {
|
) -> Result<BTreeMap<Box<ServerSigningKeyId>, VerifyKey>> {
|
||||||
// Not atomic, but this is not critical
|
// Not atomic, but this is not critical
|
||||||
let signingkeys = self.server_signingkeys.get(origin.as_bytes())?;
|
let signingkeys = self.server_signingkeys.get(origin.as_bytes())?;
|
||||||
|
|
||||||
|
@ -256,8 +180,8 @@ lasttimelinecount_cache: {lasttimelinecount_cache}\n"
|
||||||
..
|
..
|
||||||
} = new_keys;
|
} = new_keys;
|
||||||
|
|
||||||
keys.verify_keys.extend(verify_keys);
|
keys.verify_keys.extend(verify_keys.into_iter());
|
||||||
keys.old_verify_keys.extend(old_verify_keys);
|
keys.old_verify_keys.extend(old_verify_keys.into_iter());
|
||||||
|
|
||||||
self.server_signingkeys.insert(
|
self.server_signingkeys.insert(
|
||||||
origin.as_bytes(),
|
origin.as_bytes(),
|
||||||
|
@ -278,7 +202,7 @@ lasttimelinecount_cache: {lasttimelinecount_cache}\n"
|
||||||
fn signing_keys_for(
|
fn signing_keys_for(
|
||||||
&self,
|
&self,
|
||||||
origin: &ServerName,
|
origin: &ServerName,
|
||||||
) -> Result<BTreeMap<OwnedServerSigningKeyId, VerifyKey>> {
|
) -> Result<BTreeMap<Box<ServerSigningKeyId>, VerifyKey>> {
|
||||||
let signingkeys = self
|
let signingkeys = self
|
||||||
.server_signingkeys
|
.server_signingkeys
|
||||||
.get(origin.as_bytes())?
|
.get(origin.as_bytes())?
|
||||||
|
|
|
@ -6,7 +6,7 @@ use ruma::{
|
||||||
error::ErrorKind,
|
error::ErrorKind,
|
||||||
},
|
},
|
||||||
serde::Raw,
|
serde::Raw,
|
||||||
OwnedRoomId, RoomId, UserId,
|
RoomId, UserId,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{database::KeyValueDatabase, service, services, utils, Error, Result};
|
use crate::{database::KeyValueDatabase, service, services, utils, Error, Result};
|
||||||
|
@ -198,13 +198,13 @@ impl service::key_backups::Data for KeyValueDatabase {
|
||||||
&self,
|
&self,
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
version: &str,
|
version: &str,
|
||||||
) -> Result<BTreeMap<OwnedRoomId, RoomKeyBackup>> {
|
) -> Result<BTreeMap<Box<RoomId>, RoomKeyBackup>> {
|
||||||
let mut prefix = user_id.as_bytes().to_vec();
|
let mut prefix = user_id.as_bytes().to_vec();
|
||||||
prefix.push(0xff);
|
prefix.push(0xff);
|
||||||
prefix.extend_from_slice(version.as_bytes());
|
prefix.extend_from_slice(version.as_bytes());
|
||||||
prefix.push(0xff);
|
prefix.push(0xff);
|
||||||
|
|
||||||
let mut rooms = BTreeMap::<OwnedRoomId, RoomKeyBackup>::new();
|
let mut rooms = BTreeMap::<Box<RoomId>, RoomKeyBackup>::new();
|
||||||
|
|
||||||
for result in self
|
for result in self
|
||||||
.backupkeyid_backup
|
.backupkeyid_backup
|
||||||
|
|
|
@ -1,36 +1,38 @@
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::push::{set_pusher, Pusher},
|
api::client::push::{get_pushers, set_pusher},
|
||||||
UserId,
|
UserId,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{database::KeyValueDatabase, service, utils, Error, Result};
|
use crate::{database::KeyValueDatabase, service, utils, Error, Result};
|
||||||
|
|
||||||
impl service::pusher::Data for KeyValueDatabase {
|
impl service::pusher::Data for KeyValueDatabase {
|
||||||
fn set_pusher(&self, sender: &UserId, pusher: set_pusher::v3::PusherAction) -> Result<()> {
|
fn set_pusher(&self, sender: &UserId, pusher: set_pusher::v3::Pusher) -> Result<()> {
|
||||||
match &pusher {
|
|
||||||
set_pusher::v3::PusherAction::Post(data) => {
|
|
||||||
let mut key = sender.as_bytes().to_vec();
|
let mut key = sender.as_bytes().to_vec();
|
||||||
key.push(0xff);
|
key.push(0xff);
|
||||||
key.extend_from_slice(data.pusher.ids.pushkey.as_bytes());
|
key.extend_from_slice(pusher.pushkey.as_bytes());
|
||||||
|
|
||||||
|
// There are 2 kinds of pushers but the spec says: null deletes the pusher.
|
||||||
|
if pusher.kind.is_none() {
|
||||||
|
return self
|
||||||
|
.senderkey_pusher
|
||||||
|
.remove(&key)
|
||||||
|
.map(|_| ())
|
||||||
|
.map_err(Into::into);
|
||||||
|
}
|
||||||
|
|
||||||
self.senderkey_pusher.insert(
|
self.senderkey_pusher.insert(
|
||||||
&key,
|
&key,
|
||||||
&serde_json::to_vec(&pusher).expect("Pusher is valid JSON value"),
|
&serde_json::to_vec(&pusher).expect("Pusher is valid JSON value"),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
set_pusher::v3::PusherAction::Delete(ids) => {
|
|
||||||
let mut key = sender.as_bytes().to_vec();
|
|
||||||
key.push(0xff);
|
|
||||||
key.extend_from_slice(ids.pushkey.as_bytes());
|
|
||||||
self.senderkey_pusher
|
|
||||||
.remove(&key)
|
|
||||||
.map(|_| ())
|
|
||||||
.map_err(Into::into)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_pusher(&self, sender: &UserId, pushkey: &str) -> Result<Option<Pusher>> {
|
fn get_pusher(
|
||||||
|
&self,
|
||||||
|
sender: &UserId,
|
||||||
|
pushkey: &str,
|
||||||
|
) -> Result<Option<get_pushers::v3::Pusher>> {
|
||||||
let mut senderkey = sender.as_bytes().to_vec();
|
let mut senderkey = sender.as_bytes().to_vec();
|
||||||
senderkey.push(0xff);
|
senderkey.push(0xff);
|
||||||
senderkey.extend_from_slice(pushkey.as_bytes());
|
senderkey.extend_from_slice(pushkey.as_bytes());
|
||||||
|
@ -38,20 +40,20 @@ impl service::pusher::Data for KeyValueDatabase {
|
||||||
self.senderkey_pusher
|
self.senderkey_pusher
|
||||||
.get(&senderkey)?
|
.get(&senderkey)?
|
||||||
.map(|push| {
|
.map(|push| {
|
||||||
serde_json::from_slice(&push)
|
serde_json::from_slice(&*push)
|
||||||
.map_err(|_| Error::bad_database("Invalid Pusher in db."))
|
.map_err(|_| Error::bad_database("Invalid Pusher in db."))
|
||||||
})
|
})
|
||||||
.transpose()
|
.transpose()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_pushers(&self, sender: &UserId) -> Result<Vec<Pusher>> {
|
fn get_pushers(&self, sender: &UserId) -> Result<Vec<get_pushers::v3::Pusher>> {
|
||||||
let mut prefix = sender.as_bytes().to_vec();
|
let mut prefix = sender.as_bytes().to_vec();
|
||||||
prefix.push(0xff);
|
prefix.push(0xff);
|
||||||
|
|
||||||
self.senderkey_pusher
|
self.senderkey_pusher
|
||||||
.scan_prefix(prefix)
|
.scan_prefix(prefix)
|
||||||
.map(|(_, push)| {
|
.map(|(_, push)| {
|
||||||
serde_json::from_slice(&push)
|
serde_json::from_slice(&*push)
|
||||||
.map_err(|_| Error::bad_database("Invalid Pusher in db."))
|
.map_err(|_| Error::bad_database("Invalid Pusher in db."))
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use ruma::{api::client::error::ErrorKind, OwnedRoomAliasId, OwnedRoomId, RoomAliasId, RoomId};
|
use ruma::{api::client::error::ErrorKind, RoomAliasId, RoomId};
|
||||||
|
|
||||||
use crate::{database::KeyValueDatabase, service, services, utils, Error, Result};
|
use crate::{database::KeyValueDatabase, service, services, utils, Error, Result};
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ impl service::rooms::alias::Data for KeyValueDatabase {
|
||||||
let mut aliasid = room_id.as_bytes().to_vec();
|
let mut aliasid = room_id.as_bytes().to_vec();
|
||||||
aliasid.push(0xff);
|
aliasid.push(0xff);
|
||||||
aliasid.extend_from_slice(&services().globals.next_count()?.to_be_bytes());
|
aliasid.extend_from_slice(&services().globals.next_count()?.to_be_bytes());
|
||||||
self.aliasid_alias.insert(&aliasid, alias.as_bytes())?;
|
self.aliasid_alias.insert(&aliasid, &*alias.as_bytes())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ impl service::rooms::alias::Data for KeyValueDatabase {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_local_alias(&self, alias: &RoomAliasId) -> Result<Option<OwnedRoomId>> {
|
fn resolve_local_alias(&self, alias: &RoomAliasId) -> Result<Option<Box<RoomId>>> {
|
||||||
self.alias_roomid
|
self.alias_roomid
|
||||||
.get(alias.alias().as_bytes())?
|
.get(alias.alias().as_bytes())?
|
||||||
.map(|bytes| {
|
.map(|bytes| {
|
||||||
|
@ -46,7 +46,7 @@ impl service::rooms::alias::Data for KeyValueDatabase {
|
||||||
fn local_aliases_for_room<'a>(
|
fn local_aliases_for_room<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
) -> Box<dyn Iterator<Item = Result<OwnedRoomAliasId>> + 'a> {
|
) -> Box<dyn Iterator<Item = Result<Box<RoomAliasId>>> + 'a> {
|
||||||
let mut prefix = room_id.as_bytes().to_vec();
|
let mut prefix = room_id.as_bytes().to_vec();
|
||||||
prefix.push(0xff);
|
prefix.push(0xff);
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use ruma::{OwnedRoomId, RoomId};
|
use ruma::RoomId;
|
||||||
|
|
||||||
use crate::{database::KeyValueDatabase, service, utils, Error, Result};
|
use crate::{database::KeyValueDatabase, service, utils, Error, Result};
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ impl service::rooms::directory::Data for KeyValueDatabase {
|
||||||
Ok(self.publicroomids.get(room_id.as_bytes())?.is_some())
|
Ok(self.publicroomids.get(room_id.as_bytes())?.is_some())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn public_rooms<'a>(&'a self) -> Box<dyn Iterator<Item = Result<OwnedRoomId>> + 'a> {
|
fn public_rooms<'a>(&'a self) -> Box<dyn Iterator<Item = Result<Box<RoomId>>> + 'a> {
|
||||||
Box::new(self.publicroomids.iter().map(|(bytes, _)| {
|
Box::new(self.publicroomids.iter().map(|(bytes, _)| {
|
||||||
RoomId::parse(
|
RoomId::parse(
|
||||||
utils::string_from_bytes(&bytes).map_err(|_| {
|
utils::string_from_bytes(&bytes).map_err(|_| {
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use ruma::{
|
use ruma::{events::presence::PresenceEvent, presence::PresenceState, RoomId, UInt, UserId};
|
||||||
events::presence::PresenceEvent, presence::PresenceState, OwnedUserId, RoomId, UInt, UserId,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{database::KeyValueDatabase, service, services, utils, Error, Result};
|
use crate::{database::KeyValueDatabase, service, services, utils, Error, Result};
|
||||||
|
|
||||||
|
@ -78,7 +76,7 @@ impl service::rooms::edus::presence::Data for KeyValueDatabase {
|
||||||
&self,
|
&self,
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
since: u64,
|
since: u64,
|
||||||
) -> Result<HashMap<OwnedUserId, PresenceEvent>> {
|
) -> Result<HashMap<Box<UserId>, PresenceEvent>> {
|
||||||
let mut prefix = room_id.as_bytes().to_vec();
|
let mut prefix = room_id.as_bytes().to_vec();
|
||||||
prefix.push(0xff);
|
prefix.push(0xff);
|
||||||
|
|
||||||
|
@ -88,7 +86,7 @@ impl service::rooms::edus::presence::Data for KeyValueDatabase {
|
||||||
|
|
||||||
for (key, value) in self
|
for (key, value) in self
|
||||||
.presenceid_presence
|
.presenceid_presence
|
||||||
.iter_from(&first_possible_edu, false)
|
.iter_from(&*first_possible_edu, false)
|
||||||
.take_while(|(key, _)| key.starts_with(&prefix))
|
.take_while(|(key, _)| key.starts_with(&prefix))
|
||||||
{
|
{
|
||||||
let user_id = UserId::parse(
|
let user_id = UserId::parse(
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
use ruma::{
|
use ruma::{
|
||||||
events::receipt::ReceiptEvent, serde::Raw, CanonicalJsonObject, OwnedUserId, RoomId, UserId,
|
events::receipt::ReceiptEvent, serde::Raw, signatures::CanonicalJsonObject, RoomId, UserId,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{database::KeyValueDatabase, service, services, utils, Error, Result};
|
use crate::{database::KeyValueDatabase, service, services, utils, Error, Result};
|
||||||
|
@ -55,7 +55,7 @@ impl service::rooms::edus::read_receipt::Data for KeyValueDatabase {
|
||||||
) -> Box<
|
) -> Box<
|
||||||
dyn Iterator<
|
dyn Iterator<
|
||||||
Item = Result<(
|
Item = Result<(
|
||||||
OwnedUserId,
|
Box<UserId>,
|
||||||
u64,
|
u64,
|
||||||
Raw<ruma::events::AnySyncEphemeralRoomEvent>,
|
Raw<ruma::events::AnySyncEphemeralRoomEvent>,
|
||||||
)>,
|
)>,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{collections::HashSet, mem};
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use ruma::{OwnedUserId, RoomId, UserId};
|
use ruma::{RoomId, UserId};
|
||||||
|
|
||||||
use crate::{database::KeyValueDatabase, service, services, utils, Error, Result};
|
use crate::{database::KeyValueDatabase, service, services, utils, Error, Result};
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ impl service::rooms::edus::typing::Data for KeyValueDatabase {
|
||||||
room_typing_id.extend_from_slice(&count);
|
room_typing_id.extend_from_slice(&count);
|
||||||
|
|
||||||
self.typingid_userid
|
self.typingid_userid
|
||||||
.insert(&room_typing_id, user_id.as_bytes())?;
|
.insert(&room_typing_id, &*user_id.as_bytes())?;
|
||||||
|
|
||||||
self.roomid_lasttypingupdate
|
self.roomid_lasttypingupdate
|
||||||
.insert(room_id.as_bytes(), &count)?;
|
.insert(room_id.as_bytes(), &count)?;
|
||||||
|
@ -53,47 +53,6 @@ impl service::rooms::edus::typing::Data for KeyValueDatabase {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn typings_maintain(&self, room_id: &RoomId) -> Result<()> {
|
|
||||||
let mut prefix = room_id.as_bytes().to_vec();
|
|
||||||
prefix.push(0xff);
|
|
||||||
|
|
||||||
let current_timestamp = utils::millis_since_unix_epoch();
|
|
||||||
|
|
||||||
let mut found_outdated = false;
|
|
||||||
|
|
||||||
// Find all outdated edus before inserting a new one
|
|
||||||
for outdated_edu in self
|
|
||||||
.typingid_userid
|
|
||||||
.scan_prefix(prefix)
|
|
||||||
.map(|(key, _)| {
|
|
||||||
Ok::<_, Error>((
|
|
||||||
key.clone(),
|
|
||||||
utils::u64_from_bytes(
|
|
||||||
&key.splitn(2, |&b| b == 0xff).nth(1).ok_or_else(|| {
|
|
||||||
Error::bad_database("RoomTyping has invalid timestamp or delimiters.")
|
|
||||||
})?[0..mem::size_of::<u64>()],
|
|
||||||
)
|
|
||||||
.map_err(|_| Error::bad_database("RoomTyping has invalid timestamp bytes."))?,
|
|
||||||
))
|
|
||||||
})
|
|
||||||
.filter_map(|r| r.ok())
|
|
||||||
.take_while(|&(_, timestamp)| timestamp < current_timestamp)
|
|
||||||
{
|
|
||||||
// This is an outdated edu (time > timestamp)
|
|
||||||
self.typingid_userid.remove(&outdated_edu.0)?;
|
|
||||||
found_outdated = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if found_outdated {
|
|
||||||
self.roomid_lasttypingupdate.insert(
|
|
||||||
room_id.as_bytes(),
|
|
||||||
&services().globals.next_count()?.to_be_bytes(),
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn last_typing_update(&self, room_id: &RoomId) -> Result<u64> {
|
fn last_typing_update(&self, room_id: &RoomId) -> Result<u64> {
|
||||||
Ok(self
|
Ok(self
|
||||||
.roomid_lasttypingupdate
|
.roomid_lasttypingupdate
|
||||||
|
@ -107,7 +66,7 @@ impl service::rooms::edus::typing::Data for KeyValueDatabase {
|
||||||
.unwrap_or(0))
|
.unwrap_or(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn typings_all(&self, room_id: &RoomId) -> Result<HashSet<OwnedUserId>> {
|
fn typings_all(&self, room_id: &RoomId) -> Result<HashSet<Box<UserId>>> {
|
||||||
let mut prefix = room_id.as_bytes().to_vec();
|
let mut prefix = room_id.as_bytes().to_vec();
|
||||||
prefix.push(0xff);
|
prefix.push(0xff);
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use ruma::{OwnedRoomId, RoomId};
|
use ruma::RoomId;
|
||||||
|
|
||||||
use crate::{database::KeyValueDatabase, service, services, utils, Error, Result};
|
use crate::{database::KeyValueDatabase, service, services, utils, Error, Result};
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ impl service::rooms::metadata::Data for KeyValueDatabase {
|
||||||
.is_some())
|
.is_some())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn iter_ids<'a>(&'a self) -> Box<dyn Iterator<Item = Result<OwnedRoomId>> + 'a> {
|
fn iter_ids<'a>(&'a self) -> Box<dyn Iterator<Item = Result<Box<RoomId>>> + 'a> {
|
||||||
Box::new(self.roomid_shortroomid.iter().map(|(bytes, _)| {
|
Box::new(self.roomid_shortroomid.iter().map(|(bytes, _)| {
|
||||||
RoomId::parse(
|
RoomId::parse(
|
||||||
utils::string_from_bytes(&bytes).map_err(|_| {
|
utils::string_from_bytes(&bytes).map_err(|_| {
|
||||||
|
|
|
@ -12,7 +12,6 @@ mod state;
|
||||||
mod state_accessor;
|
mod state_accessor;
|
||||||
mod state_cache;
|
mod state_cache;
|
||||||
mod state_compressor;
|
mod state_compressor;
|
||||||
mod threads;
|
|
||||||
mod timeline;
|
mod timeline;
|
||||||
mod user;
|
mod user;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use ruma::{CanonicalJsonObject, EventId};
|
use ruma::{signatures::CanonicalJsonObject, EventId};
|
||||||
|
|
||||||
use crate::{database::KeyValueDatabase, service, Error, PduEvent, Result};
|
use crate::{database::KeyValueDatabase, service, Error, PduEvent, Result};
|
||||||
|
|
||||||
|
|
|
@ -1,64 +1,10 @@
|
||||||
use std::{mem, sync::Arc};
|
use std::sync::Arc;
|
||||||
|
|
||||||
use ruma::{EventId, RoomId, UserId};
|
use ruma::{EventId, RoomId};
|
||||||
|
|
||||||
use crate::{
|
use crate::{database::KeyValueDatabase, service, Result};
|
||||||
database::KeyValueDatabase,
|
|
||||||
service::{self, rooms::timeline::PduCount},
|
|
||||||
services, utils, Error, PduEvent, Result,
|
|
||||||
};
|
|
||||||
|
|
||||||
impl service::rooms::pdu_metadata::Data for KeyValueDatabase {
|
impl service::rooms::pdu_metadata::Data for KeyValueDatabase {
|
||||||
fn add_relation(&self, from: u64, to: u64) -> Result<()> {
|
|
||||||
let mut key = to.to_be_bytes().to_vec();
|
|
||||||
key.extend_from_slice(&from.to_be_bytes());
|
|
||||||
self.tofrom_relation.insert(&key, &[])?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn relations_until<'a>(
|
|
||||||
&'a self,
|
|
||||||
user_id: &'a UserId,
|
|
||||||
shortroomid: u64,
|
|
||||||
target: u64,
|
|
||||||
until: PduCount,
|
|
||||||
) -> Result<Box<dyn Iterator<Item = Result<(PduCount, PduEvent)>> + 'a>> {
|
|
||||||
let prefix = target.to_be_bytes().to_vec();
|
|
||||||
let mut current = prefix.clone();
|
|
||||||
|
|
||||||
let count_raw = match until {
|
|
||||||
PduCount::Normal(x) => x - 1,
|
|
||||||
PduCount::Backfilled(x) => {
|
|
||||||
current.extend_from_slice(&0_u64.to_be_bytes());
|
|
||||||
u64::MAX - x - 1
|
|
||||||
}
|
|
||||||
};
|
|
||||||
current.extend_from_slice(&count_raw.to_be_bytes());
|
|
||||||
|
|
||||||
Ok(Box::new(
|
|
||||||
self.tofrom_relation
|
|
||||||
.iter_from(¤t, true)
|
|
||||||
.take_while(move |(k, _)| k.starts_with(&prefix))
|
|
||||||
.map(move |(tofrom, _data)| {
|
|
||||||
let from = utils::u64_from_bytes(&tofrom[(mem::size_of::<u64>())..])
|
|
||||||
.map_err(|_| Error::bad_database("Invalid count in tofrom_relation."))?;
|
|
||||||
|
|
||||||
let mut pduid = shortroomid.to_be_bytes().to_vec();
|
|
||||||
pduid.extend_from_slice(&from.to_be_bytes());
|
|
||||||
|
|
||||||
let mut pdu = services()
|
|
||||||
.rooms
|
|
||||||
.timeline
|
|
||||||
.get_pdu_from_id(&pduid)?
|
|
||||||
.ok_or_else(|| Error::bad_database("Pdu in tofrom_relation is invalid."))?;
|
|
||||||
if pdu.sender != user_id {
|
|
||||||
pdu.remove_transaction_id()?;
|
|
||||||
}
|
|
||||||
Ok((PduCount::Normal(from), pdu))
|
|
||||||
}),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mark_as_referenced(&self, room_id: &RoomId, event_ids: &[Arc<EventId>]) -> Result<()> {
|
fn mark_as_referenced(&self, room_id: &RoomId, event_ids: &[Arc<EventId>]) -> Result<()> {
|
||||||
for prev in event_ids {
|
for prev in event_ids {
|
||||||
let mut key = room_id.as_bytes().to_vec();
|
let mut key = room_id.as_bytes().to_vec();
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::mem::size_of;
|
||||||
|
|
||||||
use ruma::RoomId;
|
use ruma::RoomId;
|
||||||
|
|
||||||
use crate::{database::KeyValueDatabase, service, services, utils, Result};
|
use crate::{database::KeyValueDatabase, service, services, utils, Result};
|
||||||
|
@ -13,7 +15,7 @@ impl service::rooms::search::Data for KeyValueDatabase {
|
||||||
let mut key = shortroomid.to_be_bytes().to_vec();
|
let mut key = shortroomid.to_be_bytes().to_vec();
|
||||||
key.extend_from_slice(word.as_bytes());
|
key.extend_from_slice(word.as_bytes());
|
||||||
key.push(0xff);
|
key.push(0xff);
|
||||||
key.extend_from_slice(pdu_id); // TODO: currently we save the room id a second time here
|
key.extend_from_slice(&pdu_id);
|
||||||
(key, Vec::new())
|
(key, Vec::new())
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -32,6 +34,7 @@ impl service::rooms::search::Data for KeyValueDatabase {
|
||||||
.expect("room exists")
|
.expect("room exists")
|
||||||
.to_be_bytes()
|
.to_be_bytes()
|
||||||
.to_vec();
|
.to_vec();
|
||||||
|
let prefix_clone = prefix.clone();
|
||||||
|
|
||||||
let words: Vec<_> = search_string
|
let words: Vec<_> = search_string
|
||||||
.split_terminator(|c: char| !c.is_alphanumeric())
|
.split_terminator(|c: char| !c.is_alphanumeric())
|
||||||
|
@ -43,7 +46,6 @@ impl service::rooms::search::Data for KeyValueDatabase {
|
||||||
let mut prefix2 = prefix.clone();
|
let mut prefix2 = prefix.clone();
|
||||||
prefix2.extend_from_slice(word.as_bytes());
|
prefix2.extend_from_slice(word.as_bytes());
|
||||||
prefix2.push(0xff);
|
prefix2.push(0xff);
|
||||||
let prefix3 = prefix2.clone();
|
|
||||||
|
|
||||||
let mut last_possible_id = prefix2.clone();
|
let mut last_possible_id = prefix2.clone();
|
||||||
last_possible_id.extend_from_slice(&u64::MAX.to_be_bytes());
|
last_possible_id.extend_from_slice(&u64::MAX.to_be_bytes());
|
||||||
|
@ -51,7 +53,7 @@ impl service::rooms::search::Data for KeyValueDatabase {
|
||||||
self.tokenids
|
self.tokenids
|
||||||
.iter_from(&last_possible_id, true) // Newest pdus first
|
.iter_from(&last_possible_id, true) // Newest pdus first
|
||||||
.take_while(move |(k, _)| k.starts_with(&prefix2))
|
.take_while(move |(k, _)| k.starts_with(&prefix2))
|
||||||
.map(move |(key, _)| key[prefix3.len()..].to_vec())
|
.map(|(key, _)| key[key.len() - size_of::<u64>()..].to_vec())
|
||||||
});
|
});
|
||||||
|
|
||||||
let common_elements = match utils::common_elements(iterators, |a, b| {
|
let common_elements = match utils::common_elements(iterators, |a, b| {
|
||||||
|
@ -62,6 +64,12 @@ impl service::rooms::search::Data for KeyValueDatabase {
|
||||||
None => return Ok(None),
|
None => return Ok(None),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Some((Box::new(common_elements), words)))
|
let mapped = common_elements.map(move |id| {
|
||||||
|
let mut pduid = prefix_clone.clone();
|
||||||
|
pduid.extend_from_slice(&id);
|
||||||
|
pduid
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(Some((Box::new(mapped), words)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -157,9 +157,10 @@ impl service::rooms::short::Data for KeyValueDatabase {
|
||||||
.ok_or_else(|| Error::bad_database("Invalid statekey in shortstatekey_statekey."))?;
|
.ok_or_else(|| Error::bad_database("Invalid statekey in shortstatekey_statekey."))?;
|
||||||
|
|
||||||
let event_type =
|
let event_type =
|
||||||
StateEventType::from(utils::string_from_bytes(eventtype_bytes).map_err(|_| {
|
StateEventType::try_from(utils::string_from_bytes(eventtype_bytes).map_err(|_| {
|
||||||
Error::bad_database("Event type in shortstatekey_statekey is invalid unicode.")
|
Error::bad_database("Event type in shortstatekey_statekey is invalid unicode.")
|
||||||
})?);
|
})?)
|
||||||
|
.map_err(|_| Error::bad_database("Event type in shortstatekey_statekey is invalid."))?;
|
||||||
|
|
||||||
let state_key = utils::string_from_bytes(statekey_bytes).map_err(|_| {
|
let state_key = utils::string_from_bytes(statekey_bytes).map_err(|_| {
|
||||||
Error::bad_database("Statekey in shortstatekey_statekey is invalid unicode.")
|
Error::bad_database("Statekey in shortstatekey_statekey is invalid unicode.")
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use ruma::{EventId, OwnedEventId, RoomId};
|
use ruma::{EventId, RoomId};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -52,7 +52,7 @@ impl service::rooms::state::Data for KeyValueDatabase {
|
||||||
fn set_forward_extremities<'a>(
|
fn set_forward_extremities<'a>(
|
||||||
&self,
|
&self,
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
event_ids: Vec<OwnedEventId>,
|
event_ids: Vec<Box<EventId>>,
|
||||||
_mutex_lock: &MutexGuard<'_, ()>, // Take mutex guard to make sure users get the room state mutex
|
_mutex_lock: &MutexGuard<'_, ()>, // Take mutex guard to make sure users get the room state mutex
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let mut prefix = room_id.as_bytes().to_vec();
|
let mut prefix = room_id.as_bytes().to_vec();
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
use std::{collections::HashMap, sync::Arc};
|
use std::{
|
||||||
|
collections::{BTreeMap, HashMap},
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{database::KeyValueDatabase, service, services, utils, Error, PduEvent, Result};
|
use crate::{database::KeyValueDatabase, service, services, utils, Error, PduEvent, Result};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
@ -6,7 +9,7 @@ use ruma::{events::StateEventType, EventId, RoomId};
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl service::rooms::state_accessor::Data for KeyValueDatabase {
|
impl service::rooms::state_accessor::Data for KeyValueDatabase {
|
||||||
async fn state_full_ids(&self, shortstatehash: u64) -> Result<HashMap<u64, Arc<EventId>>> {
|
async fn state_full_ids(&self, shortstatehash: u64) -> Result<BTreeMap<u64, Arc<EventId>>> {
|
||||||
let full_state = services()
|
let full_state = services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_compressor
|
.state_compressor
|
||||||
|
@ -14,9 +17,9 @@ impl service::rooms::state_accessor::Data for KeyValueDatabase {
|
||||||
.pop()
|
.pop()
|
||||||
.expect("there is always one layer")
|
.expect("there is always one layer")
|
||||||
.1;
|
.1;
|
||||||
let mut result = HashMap::new();
|
let mut result = BTreeMap::new();
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
for compressed in full_state.iter() {
|
for compressed in full_state.into_iter() {
|
||||||
let parsed = services()
|
let parsed = services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_compressor
|
.state_compressor
|
||||||
|
@ -45,7 +48,7 @@ impl service::rooms::state_accessor::Data for KeyValueDatabase {
|
||||||
|
|
||||||
let mut result = HashMap::new();
|
let mut result = HashMap::new();
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
for compressed in full_state.iter() {
|
for compressed in full_state {
|
||||||
let (_, eventid) = services()
|
let (_, eventid) = services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_compressor
|
.state_compressor
|
||||||
|
@ -95,7 +98,7 @@ impl service::rooms::state_accessor::Data for KeyValueDatabase {
|
||||||
.expect("there is always one layer")
|
.expect("there is always one layer")
|
||||||
.1;
|
.1;
|
||||||
Ok(full_state
|
Ok(full_state
|
||||||
.iter()
|
.into_iter()
|
||||||
.find(|bytes| bytes.starts_with(&shortstatekey.to_be_bytes()))
|
.find(|bytes| bytes.starts_with(&shortstatekey.to_be_bytes()))
|
||||||
.and_then(|compressed| {
|
.and_then(|compressed| {
|
||||||
services()
|
services()
|
||||||
|
|
|
@ -4,7 +4,7 @@ use regex::Regex;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
events::{AnyStrippedStateEvent, AnySyncStateEvent},
|
events::{AnyStrippedStateEvent, AnySyncStateEvent},
|
||||||
serde::Raw,
|
serde::Raw,
|
||||||
OwnedRoomId, OwnedServerName, OwnedUserId, RoomId, ServerName, UserId,
|
RoomId, ServerName, UserId,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{database::KeyValueDatabase, service, services, utils, Error, Result};
|
use crate::{database::KeyValueDatabase, service, services, utils, Error, Result};
|
||||||
|
@ -108,7 +108,8 @@ impl service::rooms::state_cache::Data for KeyValueDatabase {
|
||||||
joinedcount += 1;
|
joinedcount += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for _invited in self.room_members_invited(room_id).filter_map(|r| r.ok()) {
|
for invited in self.room_members_invited(room_id).filter_map(|r| r.ok()) {
|
||||||
|
joined_servers.insert(invited.server_name().to_owned());
|
||||||
invitedcount += 1;
|
invitedcount += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,36 +124,6 @@ impl service::rooms::state_cache::Data for KeyValueDatabase {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.insert(room_id.to_owned(), Arc::new(real_users));
|
.insert(room_id.to_owned(), Arc::new(real_users));
|
||||||
|
|
||||||
for old_joined_server in self.room_servers(room_id).filter_map(|r| r.ok()) {
|
|
||||||
if !joined_servers.remove(&old_joined_server) {
|
|
||||||
// Server not in room anymore
|
|
||||||
let mut roomserver_id = room_id.as_bytes().to_vec();
|
|
||||||
roomserver_id.push(0xff);
|
|
||||||
roomserver_id.extend_from_slice(old_joined_server.as_bytes());
|
|
||||||
|
|
||||||
let mut serverroom_id = old_joined_server.as_bytes().to_vec();
|
|
||||||
serverroom_id.push(0xff);
|
|
||||||
serverroom_id.extend_from_slice(room_id.as_bytes());
|
|
||||||
|
|
||||||
self.roomserverids.remove(&roomserver_id)?;
|
|
||||||
self.serverroomids.remove(&serverroom_id)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now only new servers are in joined_servers anymore
|
|
||||||
for server in joined_servers {
|
|
||||||
let mut roomserver_id = room_id.as_bytes().to_vec();
|
|
||||||
roomserver_id.push(0xff);
|
|
||||||
roomserver_id.extend_from_slice(server.as_bytes());
|
|
||||||
|
|
||||||
let mut serverroom_id = server.as_bytes().to_vec();
|
|
||||||
serverroom_id.push(0xff);
|
|
||||||
serverroom_id.extend_from_slice(room_id.as_bytes());
|
|
||||||
|
|
||||||
self.roomserverids.insert(&roomserver_id, &[])?;
|
|
||||||
self.serverroomids.insert(&serverroom_id, &[])?;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.appservice_in_room_cache
|
self.appservice_in_room_cache
|
||||||
.write()
|
.write()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
@ -162,7 +133,7 @@ impl service::rooms::state_cache::Data for KeyValueDatabase {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip(self, room_id))]
|
#[tracing::instrument(skip(self, room_id))]
|
||||||
fn get_our_real_users(&self, room_id: &RoomId) -> Result<Arc<HashSet<OwnedUserId>>> {
|
fn get_our_real_users(&self, room_id: &RoomId) -> Result<Arc<HashSet<Box<UserId>>>> {
|
||||||
let maybe = self
|
let maybe = self
|
||||||
.our_real_users_cache
|
.our_real_users_cache
|
||||||
.read()
|
.read()
|
||||||
|
@ -261,7 +232,7 @@ impl service::rooms::state_cache::Data for KeyValueDatabase {
|
||||||
fn room_servers<'a>(
|
fn room_servers<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
) -> Box<dyn Iterator<Item = Result<OwnedServerName>> + 'a> {
|
) -> Box<dyn Iterator<Item = Result<Box<ServerName>>> + 'a> {
|
||||||
let mut prefix = room_id.as_bytes().to_vec();
|
let mut prefix = room_id.as_bytes().to_vec();
|
||||||
prefix.push(0xff);
|
prefix.push(0xff);
|
||||||
|
|
||||||
|
@ -294,7 +265,7 @@ impl service::rooms::state_cache::Data for KeyValueDatabase {
|
||||||
fn server_rooms<'a>(
|
fn server_rooms<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
server: &ServerName,
|
server: &ServerName,
|
||||||
) -> Box<dyn Iterator<Item = Result<OwnedRoomId>> + 'a> {
|
) -> Box<dyn Iterator<Item = Result<Box<RoomId>>> + 'a> {
|
||||||
let mut prefix = server.as_bytes().to_vec();
|
let mut prefix = server.as_bytes().to_vec();
|
||||||
prefix.push(0xff);
|
prefix.push(0xff);
|
||||||
|
|
||||||
|
@ -316,7 +287,7 @@ impl service::rooms::state_cache::Data for KeyValueDatabase {
|
||||||
fn room_members<'a>(
|
fn room_members<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
) -> Box<dyn Iterator<Item = Result<OwnedUserId>> + 'a> {
|
) -> Box<dyn Iterator<Item = Result<Box<UserId>>> + 'a> {
|
||||||
let mut prefix = room_id.as_bytes().to_vec();
|
let mut prefix = room_id.as_bytes().to_vec();
|
||||||
prefix.push(0xff);
|
prefix.push(0xff);
|
||||||
|
|
||||||
|
@ -362,7 +333,7 @@ impl service::rooms::state_cache::Data for KeyValueDatabase {
|
||||||
fn room_useroncejoined<'a>(
|
fn room_useroncejoined<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
) -> Box<dyn Iterator<Item = Result<OwnedUserId>> + 'a> {
|
) -> Box<dyn Iterator<Item = Result<Box<UserId>>> + 'a> {
|
||||||
let mut prefix = room_id.as_bytes().to_vec();
|
let mut prefix = room_id.as_bytes().to_vec();
|
||||||
prefix.push(0xff);
|
prefix.push(0xff);
|
||||||
|
|
||||||
|
@ -392,7 +363,7 @@ impl service::rooms::state_cache::Data for KeyValueDatabase {
|
||||||
fn room_members_invited<'a>(
|
fn room_members_invited<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
) -> Box<dyn Iterator<Item = Result<OwnedUserId>> + 'a> {
|
) -> Box<dyn Iterator<Item = Result<Box<UserId>>> + 'a> {
|
||||||
let mut prefix = room_id.as_bytes().to_vec();
|
let mut prefix = room_id.as_bytes().to_vec();
|
||||||
prefix.push(0xff);
|
prefix.push(0xff);
|
||||||
|
|
||||||
|
@ -450,7 +421,7 @@ impl service::rooms::state_cache::Data for KeyValueDatabase {
|
||||||
fn rooms_joined<'a>(
|
fn rooms_joined<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
) -> Box<dyn Iterator<Item = Result<OwnedRoomId>> + 'a> {
|
) -> Box<dyn Iterator<Item = Result<Box<RoomId>>> + 'a> {
|
||||||
Box::new(
|
Box::new(
|
||||||
self.userroomid_joined
|
self.userroomid_joined
|
||||||
.scan_prefix(user_id.as_bytes().to_vec())
|
.scan_prefix(user_id.as_bytes().to_vec())
|
||||||
|
@ -471,12 +442,11 @@ impl service::rooms::state_cache::Data for KeyValueDatabase {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an iterator over all rooms a user was invited to.
|
/// Returns an iterator over all rooms a user was invited to.
|
||||||
#[allow(clippy::type_complexity)]
|
|
||||||
#[tracing::instrument(skip(self))]
|
#[tracing::instrument(skip(self))]
|
||||||
fn rooms_invited<'a>(
|
fn rooms_invited<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
) -> Box<dyn Iterator<Item = Result<(OwnedRoomId, Vec<Raw<AnyStrippedStateEvent>>)>> + 'a> {
|
) -> Box<dyn Iterator<Item = Result<(Box<RoomId>, Vec<Raw<AnyStrippedStateEvent>>)>> + 'a> {
|
||||||
let mut prefix = user_id.as_bytes().to_vec();
|
let mut prefix = user_id.as_bytes().to_vec();
|
||||||
prefix.push(0xff);
|
prefix.push(0xff);
|
||||||
|
|
||||||
|
@ -550,12 +520,11 @@ impl service::rooms::state_cache::Data for KeyValueDatabase {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an iterator over all rooms a user left.
|
/// Returns an iterator over all rooms a user left.
|
||||||
#[allow(clippy::type_complexity)]
|
|
||||||
#[tracing::instrument(skip(self))]
|
#[tracing::instrument(skip(self))]
|
||||||
fn rooms_left<'a>(
|
fn rooms_left<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
) -> Box<dyn Iterator<Item = Result<(OwnedRoomId, Vec<Raw<AnySyncStateEvent>>)>> + 'a> {
|
) -> Box<dyn Iterator<Item = Result<(Box<RoomId>, Vec<Raw<AnySyncStateEvent>>)>> + 'a> {
|
||||||
let mut prefix = user_id.as_bytes().to_vec();
|
let mut prefix = user_id.as_bytes().to_vec();
|
||||||
prefix.push(0xff);
|
prefix.push(0xff);
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{collections::HashSet, mem::size_of, sync::Arc};
|
use std::{collections::HashSet, mem::size_of};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
database::KeyValueDatabase,
|
database::KeyValueDatabase,
|
||||||
|
@ -37,20 +37,20 @@ impl service::rooms::state_compressor::Data for KeyValueDatabase {
|
||||||
|
|
||||||
Ok(StateDiff {
|
Ok(StateDiff {
|
||||||
parent,
|
parent,
|
||||||
added: Arc::new(added),
|
added,
|
||||||
removed: Arc::new(removed),
|
removed,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn save_statediff(&self, shortstatehash: u64, diff: StateDiff) -> Result<()> {
|
fn save_statediff(&self, shortstatehash: u64, diff: StateDiff) -> Result<()> {
|
||||||
let mut value = diff.parent.unwrap_or(0).to_be_bytes().to_vec();
|
let mut value = diff.parent.unwrap_or(0).to_be_bytes().to_vec();
|
||||||
for new in diff.added.iter() {
|
for new in &diff.added {
|
||||||
value.extend_from_slice(&new[..]);
|
value.extend_from_slice(&new[..]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !diff.removed.is_empty() {
|
if !diff.removed.is_empty() {
|
||||||
value.extend_from_slice(&0_u64.to_be_bytes());
|
value.extend_from_slice(&0_u64.to_be_bytes());
|
||||||
for removed in diff.removed.iter() {
|
for removed in &diff.removed {
|
||||||
value.extend_from_slice(&removed[..]);
|
value.extend_from_slice(&removed[..]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,78 +0,0 @@
|
||||||
use std::mem;
|
|
||||||
|
|
||||||
use ruma::{api::client::threads::get_threads::v1::IncludeThreads, OwnedUserId, RoomId, UserId};
|
|
||||||
|
|
||||||
use crate::{database::KeyValueDatabase, service, services, utils, Error, PduEvent, Result};
|
|
||||||
|
|
||||||
impl service::rooms::threads::Data for KeyValueDatabase {
|
|
||||||
fn threads_until<'a>(
|
|
||||||
&'a self,
|
|
||||||
user_id: &'a UserId,
|
|
||||||
room_id: &'a RoomId,
|
|
||||||
until: u64,
|
|
||||||
_include: &'a IncludeThreads,
|
|
||||||
) -> Result<Box<dyn Iterator<Item = Result<(u64, PduEvent)>> + 'a>> {
|
|
||||||
let prefix = services()
|
|
||||||
.rooms
|
|
||||||
.short
|
|
||||||
.get_shortroomid(room_id)?
|
|
||||||
.expect("room exists")
|
|
||||||
.to_be_bytes()
|
|
||||||
.to_vec();
|
|
||||||
|
|
||||||
let mut current = prefix.clone();
|
|
||||||
current.extend_from_slice(&(until - 1).to_be_bytes());
|
|
||||||
|
|
||||||
Ok(Box::new(
|
|
||||||
self.threadid_userids
|
|
||||||
.iter_from(¤t, true)
|
|
||||||
.take_while(move |(k, _)| k.starts_with(&prefix))
|
|
||||||
.map(move |(pduid, _users)| {
|
|
||||||
let count = utils::u64_from_bytes(&pduid[(mem::size_of::<u64>())..])
|
|
||||||
.map_err(|_| Error::bad_database("Invalid pduid in threadid_userids."))?;
|
|
||||||
let mut pdu = services()
|
|
||||||
.rooms
|
|
||||||
.timeline
|
|
||||||
.get_pdu_from_id(&pduid)?
|
|
||||||
.ok_or_else(|| {
|
|
||||||
Error::bad_database("Invalid pduid reference in threadid_userids")
|
|
||||||
})?;
|
|
||||||
if pdu.sender != user_id {
|
|
||||||
pdu.remove_transaction_id()?;
|
|
||||||
}
|
|
||||||
Ok((count, pdu))
|
|
||||||
}),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_participants(&self, root_id: &[u8], participants: &[OwnedUserId]) -> Result<()> {
|
|
||||||
let users = participants
|
|
||||||
.iter()
|
|
||||||
.map(|user| user.as_bytes())
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.join(&[0xff][..]);
|
|
||||||
|
|
||||||
self.threadid_userids.insert(root_id, &users)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_participants(&self, root_id: &[u8]) -> Result<Option<Vec<OwnedUserId>>> {
|
|
||||||
if let Some(users) = self.threadid_userids.get(root_id)? {
|
|
||||||
Ok(Some(
|
|
||||||
users
|
|
||||||
.split(|b| *b == 0xff)
|
|
||||||
.map(|bytes| {
|
|
||||||
UserId::parse(utils::string_from_bytes(bytes).map_err(|_| {
|
|
||||||
Error::bad_database("Invalid UserId bytes in threadid_userids.")
|
|
||||||
})?)
|
|
||||||
.map_err(|_| Error::bad_database("Invalid UserId in threadid_userids."))
|
|
||||||
})
|
|
||||||
.filter_map(|r| r.ok())
|
|
||||||
.collect(),
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +1,36 @@
|
||||||
use std::{collections::hash_map, mem::size_of, sync::Arc};
|
use std::{collections::hash_map, mem::size_of, sync::Arc};
|
||||||
|
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::error::ErrorKind, CanonicalJsonObject, EventId, OwnedUserId, RoomId, UserId,
|
api::client::error::ErrorKind, signatures::CanonicalJsonObject, EventId, RoomId, UserId,
|
||||||
};
|
};
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
|
|
||||||
use crate::{database::KeyValueDatabase, service, services, utils, Error, PduEvent, Result};
|
use crate::{database::KeyValueDatabase, service, services, utils, Error, PduEvent, Result};
|
||||||
|
|
||||||
use service::rooms::timeline::PduCount;
|
|
||||||
|
|
||||||
impl service::rooms::timeline::Data for KeyValueDatabase {
|
impl service::rooms::timeline::Data for KeyValueDatabase {
|
||||||
fn last_timeline_count(&self, sender_user: &UserId, room_id: &RoomId) -> Result<PduCount> {
|
fn first_pdu_in_room(&self, room_id: &RoomId) -> Result<Option<Arc<PduEvent>>> {
|
||||||
|
let prefix = services()
|
||||||
|
.rooms
|
||||||
|
.short
|
||||||
|
.get_shortroomid(room_id)?
|
||||||
|
.expect("room exists")
|
||||||
|
.to_be_bytes()
|
||||||
|
.to_vec();
|
||||||
|
|
||||||
|
// Look for PDUs in that room.
|
||||||
|
self.pduid_pdu
|
||||||
|
.iter_from(&prefix, false)
|
||||||
|
.filter(|(k, _)| k.starts_with(&prefix))
|
||||||
|
.map(|(_, pdu)| {
|
||||||
|
serde_json::from_slice(&pdu)
|
||||||
|
.map_err(|_| Error::bad_database("Invalid first PDU in db."))
|
||||||
|
.map(Arc::new)
|
||||||
|
})
|
||||||
|
.next()
|
||||||
|
.transpose()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn last_timeline_count(&self, sender_user: &UserId, room_id: &RoomId) -> Result<u64> {
|
||||||
match self
|
match self
|
||||||
.lasttimelinecount_cache
|
.lasttimelinecount_cache
|
||||||
.lock()
|
.lock()
|
||||||
|
@ -19,18 +39,20 @@ impl service::rooms::timeline::Data for KeyValueDatabase {
|
||||||
{
|
{
|
||||||
hash_map::Entry::Vacant(v) => {
|
hash_map::Entry::Vacant(v) => {
|
||||||
if let Some(last_count) = self
|
if let Some(last_count) = self
|
||||||
.pdus_until(sender_user, room_id, PduCount::max())?
|
.pdus_until(&sender_user, &room_id, u64::MAX)?
|
||||||
.find_map(|r| {
|
.filter_map(|r| {
|
||||||
// Filter out buggy events
|
// Filter out buggy events
|
||||||
if r.is_err() {
|
if r.is_err() {
|
||||||
error!("Bad pdu in pdus_since: {:?}", r);
|
error!("Bad pdu in pdus_since: {:?}", r);
|
||||||
}
|
}
|
||||||
r.ok()
|
r.ok()
|
||||||
})
|
})
|
||||||
|
.map(|(pduid, _)| self.pdu_count(&pduid))
|
||||||
|
.next()
|
||||||
{
|
{
|
||||||
Ok(*v.insert(last_count.0))
|
Ok(*v.insert(last_count?))
|
||||||
} else {
|
} else {
|
||||||
Ok(PduCount::Normal(0))
|
Ok(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hash_map::Entry::Occupied(o) => Ok(*o.get()),
|
hash_map::Entry::Occupied(o) => Ok(*o.get()),
|
||||||
|
@ -38,27 +60,29 @@ impl service::rooms::timeline::Data for KeyValueDatabase {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the `count` of this pdu's id.
|
/// Returns the `count` of this pdu's id.
|
||||||
fn get_pdu_count(&self, event_id: &EventId) -> Result<Option<PduCount>> {
|
fn get_pdu_count(&self, event_id: &EventId) -> Result<Option<u64>> {
|
||||||
self.eventid_pduid
|
self.eventid_pduid
|
||||||
.get(event_id.as_bytes())?
|
.get(event_id.as_bytes())?
|
||||||
.map(|pdu_id| pdu_count(&pdu_id))
|
.map(|pdu_id| self.pdu_count(&pdu_id))
|
||||||
.transpose()
|
.transpose()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the json of a pdu.
|
/// Returns the json of a pdu.
|
||||||
fn get_pdu_json(&self, event_id: &EventId) -> Result<Option<CanonicalJsonObject>> {
|
fn get_pdu_json(&self, event_id: &EventId) -> Result<Option<CanonicalJsonObject>> {
|
||||||
self.get_non_outlier_pdu_json(event_id)?.map_or_else(
|
self.eventid_pduid
|
||||||
|| {
|
|
||||||
self.eventid_outlierpdu
|
|
||||||
.get(event_id.as_bytes())?
|
.get(event_id.as_bytes())?
|
||||||
|
.map_or_else(
|
||||||
|
|| self.eventid_outlierpdu.get(event_id.as_bytes()),
|
||||||
|
|pduid| {
|
||||||
|
Ok(Some(self.pduid_pdu.get(&pduid)?.ok_or_else(|| {
|
||||||
|
Error::bad_database("Invalid pduid in eventid_pduid.")
|
||||||
|
})?))
|
||||||
|
},
|
||||||
|
)?
|
||||||
.map(|pdu| {
|
.map(|pdu| {
|
||||||
serde_json::from_slice(&pdu)
|
serde_json::from_slice(&pdu).map_err(|_| Error::bad_database("Invalid PDU in db."))
|
||||||
.map_err(|_| Error::bad_database("Invalid PDU in db."))
|
|
||||||
})
|
})
|
||||||
.transpose()
|
.transpose()
|
||||||
},
|
|
||||||
|x| Ok(Some(x)),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the json of a pdu.
|
/// Returns the json of a pdu.
|
||||||
|
@ -83,6 +107,8 @@ impl service::rooms::timeline::Data for KeyValueDatabase {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the pdu.
|
/// Returns the pdu.
|
||||||
|
///
|
||||||
|
/// Checks the `eventid_outlierpdu` Tree if not found in the timeline.
|
||||||
fn get_non_outlier_pdu(&self, event_id: &EventId) -> Result<Option<PduEvent>> {
|
fn get_non_outlier_pdu(&self, event_id: &EventId) -> Result<Option<PduEvent>> {
|
||||||
self.eventid_pduid
|
self.eventid_pduid
|
||||||
.get(event_id.as_bytes())?
|
.get(event_id.as_bytes())?
|
||||||
|
@ -107,20 +133,22 @@ impl service::rooms::timeline::Data for KeyValueDatabase {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(pdu) = self
|
if let Some(pdu) = self
|
||||||
.get_non_outlier_pdu(event_id)?
|
.eventid_pduid
|
||||||
.map_or_else(
|
|
||||||
|| {
|
|
||||||
self.eventid_outlierpdu
|
|
||||||
.get(event_id.as_bytes())?
|
.get(event_id.as_bytes())?
|
||||||
|
.map_or_else(
|
||||||
|
|| self.eventid_outlierpdu.get(event_id.as_bytes()),
|
||||||
|
|pduid| {
|
||||||
|
Ok(Some(self.pduid_pdu.get(&pduid)?.ok_or_else(|| {
|
||||||
|
Error::bad_database("Invalid pduid in eventid_pduid.")
|
||||||
|
})?))
|
||||||
|
},
|
||||||
|
)?
|
||||||
.map(|pdu| {
|
.map(|pdu| {
|
||||||
serde_json::from_slice(&pdu)
|
serde_json::from_slice(&pdu)
|
||||||
.map_err(|_| Error::bad_database("Invalid PDU in db."))
|
.map_err(|_| Error::bad_database("Invalid PDU in db."))
|
||||||
})
|
|
||||||
.transpose()
|
|
||||||
},
|
|
||||||
|x| Ok(Some(x)),
|
|
||||||
)?
|
|
||||||
.map(Arc::new)
|
.map(Arc::new)
|
||||||
|
})
|
||||||
|
.transpose()?
|
||||||
{
|
{
|
||||||
self.pdu_cache
|
self.pdu_cache
|
||||||
.lock()
|
.lock()
|
||||||
|
@ -154,6 +182,12 @@ impl service::rooms::timeline::Data for KeyValueDatabase {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the `count` of this pdu's id.
|
||||||
|
fn pdu_count(&self, pdu_id: &[u8]) -> Result<u64> {
|
||||||
|
utils::u64_from_bytes(&pdu_id[pdu_id.len() - size_of::<u64>()..])
|
||||||
|
.map_err(|_| Error::bad_database("PDU has invalid count bytes."))
|
||||||
|
}
|
||||||
|
|
||||||
fn append_pdu(
|
fn append_pdu(
|
||||||
&self,
|
&self,
|
||||||
pdu_id: &[u8],
|
pdu_id: &[u8],
|
||||||
|
@ -169,56 +203,66 @@ impl service::rooms::timeline::Data for KeyValueDatabase {
|
||||||
self.lasttimelinecount_cache
|
self.lasttimelinecount_cache
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.insert(pdu.room_id.clone(), PduCount::Normal(count));
|
.insert(pdu.room_id.clone(), count);
|
||||||
|
|
||||||
self.eventid_pduid.insert(pdu.event_id.as_bytes(), pdu_id)?;
|
self.eventid_pduid
|
||||||
|
.insert(pdu.event_id.as_bytes(), &pdu_id)?;
|
||||||
self.eventid_outlierpdu.remove(pdu.event_id.as_bytes())?;
|
self.eventid_outlierpdu.remove(pdu.event_id.as_bytes())?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepend_backfill_pdu(
|
|
||||||
&self,
|
|
||||||
pdu_id: &[u8],
|
|
||||||
event_id: &EventId,
|
|
||||||
json: &CanonicalJsonObject,
|
|
||||||
) -> Result<()> {
|
|
||||||
self.pduid_pdu.insert(
|
|
||||||
pdu_id,
|
|
||||||
&serde_json::to_vec(json).expect("CanonicalJsonObject is always a valid"),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
self.eventid_pduid.insert(event_id.as_bytes(), pdu_id)?;
|
|
||||||
self.eventid_outlierpdu.remove(event_id.as_bytes())?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Removes a pdu and creates a new one with the same id.
|
/// Removes a pdu and creates a new one with the same id.
|
||||||
fn replace_pdu(
|
fn replace_pdu(&self, pdu_id: &[u8], pdu: &PduEvent) -> Result<()> {
|
||||||
&self,
|
|
||||||
pdu_id: &[u8],
|
|
||||||
pdu_json: &CanonicalJsonObject,
|
|
||||||
pdu: &PduEvent,
|
|
||||||
) -> Result<()> {
|
|
||||||
if self.pduid_pdu.get(pdu_id)?.is_some() {
|
if self.pduid_pdu.get(pdu_id)?.is_some() {
|
||||||
self.pduid_pdu.insert(
|
self.pduid_pdu.insert(
|
||||||
pdu_id,
|
pdu_id,
|
||||||
&serde_json::to_vec(pdu_json).expect("CanonicalJsonObject is always a valid"),
|
&serde_json::to_vec(pdu).expect("CanonicalJsonObject is always a valid"),
|
||||||
)?;
|
)?;
|
||||||
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
return Err(Error::BadRequest(
|
Err(Error::BadRequest(
|
||||||
ErrorKind::NotFound,
|
ErrorKind::NotFound,
|
||||||
"PDU does not exist.",
|
"PDU does not exist.",
|
||||||
));
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.pdu_cache
|
/// Returns an iterator over all events in a room that happened after the event with id `since`
|
||||||
.lock()
|
/// in chronological order.
|
||||||
.unwrap()
|
fn pdus_since<'a>(
|
||||||
.remove(&(*pdu.event_id).to_owned());
|
&'a self,
|
||||||
|
user_id: &UserId,
|
||||||
|
room_id: &RoomId,
|
||||||
|
since: u64,
|
||||||
|
) -> Result<Box<dyn Iterator<Item = Result<(Vec<u8>, PduEvent)>> + 'a>> {
|
||||||
|
let prefix = services()
|
||||||
|
.rooms
|
||||||
|
.short
|
||||||
|
.get_shortroomid(room_id)?
|
||||||
|
.expect("room exists")
|
||||||
|
.to_be_bytes()
|
||||||
|
.to_vec();
|
||||||
|
|
||||||
Ok(())
|
// Skip the first pdu if it's exactly at since, because we sent that last time
|
||||||
|
let mut first_pdu_id = prefix.clone();
|
||||||
|
first_pdu_id.extend_from_slice(&(since + 1).to_be_bytes());
|
||||||
|
|
||||||
|
let user_id = user_id.to_owned();
|
||||||
|
|
||||||
|
Ok(Box::new(
|
||||||
|
self.pduid_pdu
|
||||||
|
.iter_from(&first_pdu_id, false)
|
||||||
|
.take_while(move |(k, _)| k.starts_with(&prefix))
|
||||||
|
.map(move |(pdu_id, v)| {
|
||||||
|
let mut pdu = serde_json::from_slice::<PduEvent>(&v)
|
||||||
|
.map_err(|_| Error::bad_database("PDU in db is invalid."))?;
|
||||||
|
if pdu.sender != user_id {
|
||||||
|
pdu.remove_transaction_id()?;
|
||||||
|
}
|
||||||
|
Ok((pdu_id, pdu))
|
||||||
|
}),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an iterator over all events and their tokens in a room that happened before the
|
/// Returns an iterator over all events and their tokens in a room that happened before the
|
||||||
|
@ -227,15 +271,27 @@ impl service::rooms::timeline::Data for KeyValueDatabase {
|
||||||
&'a self,
|
&'a self,
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
until: PduCount,
|
until: u64,
|
||||||
) -> Result<Box<dyn Iterator<Item = Result<(PduCount, PduEvent)>> + 'a>> {
|
) -> Result<Box<dyn Iterator<Item = Result<(Vec<u8>, PduEvent)>> + 'a>> {
|
||||||
let (prefix, current) = count_to_id(room_id, until, 1, true)?;
|
// Create the first part of the full pdu id
|
||||||
|
let prefix = services()
|
||||||
|
.rooms
|
||||||
|
.short
|
||||||
|
.get_shortroomid(room_id)?
|
||||||
|
.expect("room exists")
|
||||||
|
.to_be_bytes()
|
||||||
|
.to_vec();
|
||||||
|
|
||||||
|
let mut current = prefix.clone();
|
||||||
|
current.extend_from_slice(&(until.saturating_sub(1)).to_be_bytes()); // -1 because we don't want event at `until`
|
||||||
|
|
||||||
|
let current: &[u8] = ¤t;
|
||||||
|
|
||||||
let user_id = user_id.to_owned();
|
let user_id = user_id.to_owned();
|
||||||
|
|
||||||
Ok(Box::new(
|
Ok(Box::new(
|
||||||
self.pduid_pdu
|
self.pduid_pdu
|
||||||
.iter_from(¤t, true)
|
.iter_from(current, true)
|
||||||
.take_while(move |(k, _)| k.starts_with(&prefix))
|
.take_while(move |(k, _)| k.starts_with(&prefix))
|
||||||
.map(move |(pdu_id, v)| {
|
.map(move |(pdu_id, v)| {
|
||||||
let mut pdu = serde_json::from_slice::<PduEvent>(&v)
|
let mut pdu = serde_json::from_slice::<PduEvent>(&v)
|
||||||
|
@ -243,9 +299,7 @@ impl service::rooms::timeline::Data for KeyValueDatabase {
|
||||||
if pdu.sender != user_id {
|
if pdu.sender != user_id {
|
||||||
pdu.remove_transaction_id()?;
|
pdu.remove_transaction_id()?;
|
||||||
}
|
}
|
||||||
pdu.add_age()?;
|
Ok((pdu_id, pdu))
|
||||||
let count = pdu_count(&pdu_id)?;
|
|
||||||
Ok((count, pdu))
|
|
||||||
}),
|
}),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -254,15 +308,27 @@ impl service::rooms::timeline::Data for KeyValueDatabase {
|
||||||
&'a self,
|
&'a self,
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
from: PduCount,
|
from: u64,
|
||||||
) -> Result<Box<dyn Iterator<Item = Result<(PduCount, PduEvent)>> + 'a>> {
|
) -> Result<Box<dyn Iterator<Item = Result<(Vec<u8>, PduEvent)>> + 'a>> {
|
||||||
let (prefix, current) = count_to_id(room_id, from, 1, false)?;
|
// Create the first part of the full pdu id
|
||||||
|
let prefix = services()
|
||||||
|
.rooms
|
||||||
|
.short
|
||||||
|
.get_shortroomid(room_id)?
|
||||||
|
.expect("room exists")
|
||||||
|
.to_be_bytes()
|
||||||
|
.to_vec();
|
||||||
|
|
||||||
|
let mut current = prefix.clone();
|
||||||
|
current.extend_from_slice(&(from + 1).to_be_bytes()); // +1 so we don't send the base event
|
||||||
|
|
||||||
|
let current: &[u8] = ¤t;
|
||||||
|
|
||||||
let user_id = user_id.to_owned();
|
let user_id = user_id.to_owned();
|
||||||
|
|
||||||
Ok(Box::new(
|
Ok(Box::new(
|
||||||
self.pduid_pdu
|
self.pduid_pdu
|
||||||
.iter_from(¤t, false)
|
.iter_from(current, false)
|
||||||
.take_while(move |(k, _)| k.starts_with(&prefix))
|
.take_while(move |(k, _)| k.starts_with(&prefix))
|
||||||
.map(move |(pdu_id, v)| {
|
.map(move |(pdu_id, v)| {
|
||||||
let mut pdu = serde_json::from_slice::<PduEvent>(&v)
|
let mut pdu = serde_json::from_slice::<PduEvent>(&v)
|
||||||
|
@ -270,9 +336,7 @@ impl service::rooms::timeline::Data for KeyValueDatabase {
|
||||||
if pdu.sender != user_id {
|
if pdu.sender != user_id {
|
||||||
pdu.remove_transaction_id()?;
|
pdu.remove_transaction_id()?;
|
||||||
}
|
}
|
||||||
pdu.add_age()?;
|
Ok((pdu_id, pdu))
|
||||||
let count = pdu_count(&pdu_id)?;
|
|
||||||
Ok((count, pdu))
|
|
||||||
}),
|
}),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -280,8 +344,8 @@ impl service::rooms::timeline::Data for KeyValueDatabase {
|
||||||
fn increment_notification_counts(
|
fn increment_notification_counts(
|
||||||
&self,
|
&self,
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
notifies: Vec<OwnedUserId>,
|
notifies: Vec<Box<UserId>>,
|
||||||
highlights: Vec<OwnedUserId>,
|
highlights: Vec<Box<UserId>>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let mut notifies_batch = Vec::new();
|
let mut notifies_batch = Vec::new();
|
||||||
let mut highlights_batch = Vec::new();
|
let mut highlights_batch = Vec::new();
|
||||||
|
@ -305,60 +369,3 @@ impl service::rooms::timeline::Data for KeyValueDatabase {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the `count` of this pdu's id.
|
|
||||||
fn pdu_count(pdu_id: &[u8]) -> Result<PduCount> {
|
|
||||||
let last_u64 = utils::u64_from_bytes(&pdu_id[pdu_id.len() - size_of::<u64>()..])
|
|
||||||
.map_err(|_| Error::bad_database("PDU has invalid count bytes."))?;
|
|
||||||
let second_last_u64 = utils::u64_from_bytes(
|
|
||||||
&pdu_id[pdu_id.len() - 2 * size_of::<u64>()..pdu_id.len() - size_of::<u64>()],
|
|
||||||
);
|
|
||||||
|
|
||||||
if matches!(second_last_u64, Ok(0)) {
|
|
||||||
Ok(PduCount::Backfilled(u64::MAX - last_u64))
|
|
||||||
} else {
|
|
||||||
Ok(PduCount::Normal(last_u64))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn count_to_id(
|
|
||||||
room_id: &RoomId,
|
|
||||||
count: PduCount,
|
|
||||||
offset: u64,
|
|
||||||
subtract: bool,
|
|
||||||
) -> Result<(Vec<u8>, Vec<u8>)> {
|
|
||||||
let prefix = services()
|
|
||||||
.rooms
|
|
||||||
.short
|
|
||||||
.get_shortroomid(room_id)?
|
|
||||||
.ok_or_else(|| Error::bad_database("Looked for bad shortroomid in timeline"))?
|
|
||||||
.to_be_bytes()
|
|
||||||
.to_vec();
|
|
||||||
let mut pdu_id = prefix.clone();
|
|
||||||
// +1 so we don't send the base event
|
|
||||||
let count_raw = match count {
|
|
||||||
PduCount::Normal(x) => {
|
|
||||||
if subtract {
|
|
||||||
x - offset
|
|
||||||
} else {
|
|
||||||
x + offset
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PduCount::Backfilled(x) => {
|
|
||||||
pdu_id.extend_from_slice(&0_u64.to_be_bytes());
|
|
||||||
let num = u64::MAX - x;
|
|
||||||
if subtract {
|
|
||||||
if num > 0 {
|
|
||||||
num - offset
|
|
||||||
} else {
|
|
||||||
num
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
num + offset
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
pdu_id.extend_from_slice(&count_raw.to_be_bytes());
|
|
||||||
|
|
||||||
Ok((prefix, pdu_id))
|
|
||||||
}
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue