mirror of
https://forgejo.ellis.link/continuwuation/continuwuity.git
synced 2026-05-26 20:49:55 +00:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e524590860 | |||
| d5217566d9 | |||
| 099f98978b | |||
| 43e70fe7c1 | |||
| 721659f22a | |||
| 099c9fb22f |
@@ -0,0 +1,8 @@
|
||||
|
||||
<!-- Please describe your changes here -->
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
- [ ] I ran `cargo fmt`, `cargo clippy`, and `cargo test`
|
||||
- [ ] I agree to release my code and all other changes of this MR under the Apache-2.0 license
|
||||
|
||||
@@ -0,0 +1,264 @@
|
||||
name: CI and Artifacts
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
# documentation workflow deals with this or is not relevant for this workflow
|
||||
paths-ignore:
|
||||
- '*.md'
|
||||
- 'conduwuit-example.toml'
|
||||
- 'book.toml'
|
||||
- '.gitlab-ci.yml'
|
||||
- '.gitignore'
|
||||
- 'renovate.json'
|
||||
- 'docs/**'
|
||||
- 'debian/**'
|
||||
- 'docker/**'
|
||||
branches:
|
||||
- main
|
||||
tags:
|
||||
- '*'
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
#workflow_dispatch:
|
||||
|
||||
#concurrency:
|
||||
# group: ${{ gitea.head_ref || gitea.ref_name }}
|
||||
# cancel-in-progress: true
|
||||
|
||||
env:
|
||||
# Required to make some things output color
|
||||
TERM: ansi
|
||||
# Publishing to my nix binary cache
|
||||
ATTIC_TOKEN: ${{ secrets.ATTIC_TOKEN }}
|
||||
# conduwuit.cachix.org
|
||||
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
# Just in case incremental is still being set to true, speeds up CI
|
||||
CARGO_INCREMENTAL: 0
|
||||
# Custom nix binary cache if fork is being used
|
||||
ATTIC_ENDPOINT: ${{ vars.ATTIC_ENDPOINT }}
|
||||
ATTIC_PUBLIC_KEY: ${{ vars.ATTIC_PUBLIC_KEY }}
|
||||
# Get error output from nix that we can actually use
|
||||
NIX_CONFIG: show-trace = true
|
||||
|
||||
#permissions:
|
||||
# packages: write
|
||||
# contents: read
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
name: Test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Sync repository
|
||||
uses: https://github.com/actions/checkout@v4
|
||||
|
||||
- name: Tag comparison check
|
||||
if: startsWith(gitea.ref, 'refs/tags/v')
|
||||
run: |
|
||||
# Tag mismatch with latest repo tag check to prevent potential downgrades
|
||||
LATEST_TAG=$(git describe --tags `git rev-list --tags --max-count=1`)
|
||||
|
||||
if [ $LATEST_TAG != ${{ gitea.ref_name }} ]; then
|
||||
echo '# WARNING: Attempting to run this workflow for a tag that is not the latest repo tag. Aborting.'
|
||||
echo '# WARNING: Attempting to run this workflow for a tag that is not the latest repo tag. Aborting.' >> $GITHUB_STEP_SUMMARY
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Install Nix
|
||||
uses: https://github.com/DeterminateSystems/nix-installer-action@main
|
||||
with:
|
||||
diagnostic-endpoint: ""
|
||||
extra-conf: |
|
||||
experimental-features = nix-command flakes
|
||||
accept-flake-config = true
|
||||
|
||||
- name: Enable Cachix binary cache
|
||||
run: |
|
||||
nix profile install nixpkgs#cachix
|
||||
cachix use crane
|
||||
cachix use nix-community
|
||||
|
||||
- name: Configure Magic Nix Cache
|
||||
uses: https://github.com/DeterminateSystems/magic-nix-cache-action@main
|
||||
with:
|
||||
diagnostic-endpoint: ""
|
||||
upstream-cache: "https://attic.kennel.juneis.dog/conduwuit"
|
||||
|
||||
- name: Apply Nix binary cache configuration
|
||||
run: |
|
||||
sudo tee -a /etc/nix/nix.conf > /dev/null <<EOF
|
||||
extra-substituters = https://attic.kennel.juneis.dog/conduit https://attic.kennel.juneis.dog/conduwuit https://cache.lix.systems https://conduwuit.cachix.org
|
||||
extra-trusted-public-keys = conduit:eEKoUwlQGDdYmAI/Q/0slVlegqh/QmAvQd7HBSm21Wk= conduwuit:BbycGUgTISsltcmH0qNjFR9dbrQNYgdIAcmViSGoVTE= cache.lix.systems:aBnZUw8zA7H35Cz2RyKFVs3H4PlGTLawyY5KRbvJR8o= conduwuit.cachix.org-1:MFRm6jcnfTf0jSAbmvLfhO3KBMt4px+1xaereWXp8Xg=
|
||||
EOF
|
||||
|
||||
- name: Use alternative Nix binary caches if specified
|
||||
if: ${{ (env.ATTIC_ENDPOINT != '') && (env.ATTIC_PUBLIC_KEY != '') }}
|
||||
run: |
|
||||
sudo tee -a /etc/nix/nix.conf > /dev/null <<EOF
|
||||
extra-substituters = ${{ env.ATTIC_ENDPOINT }}
|
||||
extra-trusted-public-keys = ${{ env.ATTIC_PUBLIC_KEY }}
|
||||
EOF
|
||||
|
||||
- name: Prepare build environment
|
||||
run: |
|
||||
echo 'source $HOME/.nix-profile/share/nix-direnv/direnvrc' > "$HOME/.direnvrc"
|
||||
nix profile install --impure --inputs-from . nixpkgs#direnv nixpkgs#nix-direnv
|
||||
direnv allow
|
||||
nix develop .#all-features --command true
|
||||
|
||||
- name: Cache CI dependencies
|
||||
run: |
|
||||
bin/nix-build-and-cache ci
|
||||
|
||||
- name: Run CI tests
|
||||
run: |
|
||||
direnv exec . engage > >(tee -a test_output.log)
|
||||
|
||||
- name: Sync Complement repository
|
||||
uses: https://github.com/actions/checkout@v4
|
||||
with:
|
||||
repository: 'matrix-org/complement'
|
||||
path: complement_src
|
||||
|
||||
- name: Run Complement tests
|
||||
run: |
|
||||
direnv exec . bin/complement 'complement_src' 'complement_test_logs.jsonl' 'complement_test_results.jsonl'
|
||||
cp -v -f result complement_oci_image.tar.gz
|
||||
|
||||
- name: Upload Complement OCI image
|
||||
uses: https://github.com/actions/upload-artifact@v4
|
||||
with:
|
||||
name: complement_oci_image.tar.gz
|
||||
path: complement_oci_image.tar.gz
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload Complement logs
|
||||
uses: https://github.com/actions/upload-artifact@v4
|
||||
with:
|
||||
name: complement_test_logs.jsonl
|
||||
path: complement_test_logs.jsonl
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload Complement results
|
||||
uses: https://github.com/actions/upload-artifact@v4
|
||||
with:
|
||||
name: complement_test_results.jsonl
|
||||
path: complement_test_results.jsonl
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Diff Complement results with checked-in repo results
|
||||
run: |
|
||||
diff -u --color=always tests/test_results/complement/test_results.jsonl complement_test_results.jsonl > >(tee -a complement_test_output.log)
|
||||
echo '# Complement diff results' >> $GITHUB_STEP_SUMMARY
|
||||
echo '```diff' >> $GITHUB_STEP_SUMMARY
|
||||
tail -n 100 complement_test_output.log | sed 's/\x1b\[[0-9;]*m//g' >> $GITHUB_STEP_SUMMARY
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
- name: Update Job Summary
|
||||
if: success() || failure()
|
||||
run: |
|
||||
if [ ${{ job.status }} == 'success' ]; then
|
||||
echo '# ✅ completed suwuccessfully' >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
tail -n 40 test_output.log | sed 's/\x1b\[[0-9;]*m//g' >> $GITHUB_STEP_SUMMARY
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
needs: tests
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- target: aarch64-unknown-linux-musl
|
||||
- target: x86_64-unknown-linux-musl
|
||||
steps:
|
||||
- name: Sync repository
|
||||
uses: https://github.com/actions/checkout@v4
|
||||
|
||||
- name: Install Nix
|
||||
uses: https://github.com/DeterminateSystems/nix-installer-action@main
|
||||
with:
|
||||
diagnostic-endpoint: ""
|
||||
extra-conf: |
|
||||
experimental-features = nix-command flakes
|
||||
accept-flake-config = true
|
||||
|
||||
- name: Install and enable Cachix binary cache
|
||||
run: |
|
||||
nix profile install nixpkgs#cachix
|
||||
cachix use crane
|
||||
cachix use nix-community
|
||||
|
||||
- name: Configure Magic Nix Cache
|
||||
uses: https://github.com/DeterminateSystems/magic-nix-cache-action@main
|
||||
with:
|
||||
diagnostic-endpoint: ""
|
||||
upstream-cache: "https://attic.kennel.juneis.dog/conduwuit"
|
||||
|
||||
- name: Apply Nix binary cache configuration
|
||||
run: |
|
||||
sudo tee -a /etc/nix/nix.conf > /dev/null <<EOF
|
||||
extra-substituters = https://attic.kennel.juneis.dog/conduit https://attic.kennel.juneis.dog/conduwuit https://cache.lix.systems https://conduwuit.cachix.org
|
||||
extra-trusted-public-keys = conduit:eEKoUwlQGDdYmAI/Q/0slVlegqh/QmAvQd7HBSm21Wk= conduwuit:BbycGUgTISsltcmH0qNjFR9dbrQNYgdIAcmViSGoVTE= cache.lix.systems:aBnZUw8zA7H35Cz2RyKFVs3H4PlGTLawyY5KRbvJR8o= conduwuit.cachix.org-1:MFRm6jcnfTf0jSAbmvLfhO3KBMt4px+1xaereWXp8Xg=
|
||||
EOF
|
||||
|
||||
- name: Use alternative Nix binary caches if specified
|
||||
if: ${{ (env.ATTIC_ENDPOINT != '') && (env.ATTIC_PUBLIC_KEY != '') }}
|
||||
run: |
|
||||
sudo tee -a /etc/nix/nix.conf > /dev/null <<EOF
|
||||
extra-substituters = ${{ env.ATTIC_ENDPOINT }}
|
||||
extra-trusted-public-keys = ${{ env.ATTIC_PUBLIC_KEY }}
|
||||
EOF
|
||||
|
||||
- name: Prepare build environment
|
||||
run: |
|
||||
echo 'source $HOME/.nix-profile/share/nix-direnv/direnvrc' > "$HOME/.direnvrc"
|
||||
nix profile install --impure --inputs-from . nixpkgs#direnv nixpkgs#nix-direnv
|
||||
direnv allow
|
||||
nix develop .#all-features --command true
|
||||
|
||||
- name: Build static ${{ matrix.target }}
|
||||
run: |
|
||||
CARGO_DEB_TARGET_TUPLE=$(echo ${{ matrix.target }} | grep -o -E '^([^-]*-){3}[^-]*')
|
||||
SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)
|
||||
|
||||
bin/nix-build-and-cache just .#static-${{ matrix.target }}
|
||||
mkdir -v -p target/release/
|
||||
mkdir -v -p target/$CARGO_DEB_TARGET_TUPLE/release/
|
||||
cp -v -f result/bin/conduit target/release/conduwuit
|
||||
cp -v -f result/bin/conduit target/$CARGO_DEB_TARGET_TUPLE/release/conduwuit
|
||||
# -p conduit is the main crate name
|
||||
direnv exec . cargo deb --verbose --no-build --no-strip -p conduit --target=$CARGO_DEB_TARGET_TUPLE --output target/release/${{ matrix.target }}.deb
|
||||
mv -v target/release/conduwuit static-${{ matrix.target }}
|
||||
mv -v target/release/${{ matrix.target }}.deb ${{ matrix.target }}.deb
|
||||
|
||||
- name: Upload static-${{ matrix.target }}
|
||||
uses: https://github.com/actions/upload-artifact@v4
|
||||
with:
|
||||
name: static-${{ matrix.target }}
|
||||
path: static-${{ matrix.target }}
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload deb ${{ matrix.target }}
|
||||
uses: https://github.com/actions/upload-artifact@v4
|
||||
with:
|
||||
name: deb-${{ matrix.target }}
|
||||
path: ${{ matrix.target }}.deb
|
||||
if-no-files-found: error
|
||||
compression-level: 0
|
||||
|
||||
- name: Build OCI image ${{ matrix.target }}
|
||||
run: |
|
||||
bin/nix-build-and-cache just .#oci-image-${{ matrix.target }}
|
||||
cp -v -f result oci-image-${{ matrix.target }}.tar.gz
|
||||
|
||||
- name: Upload OCI image ${{ matrix.target }}
|
||||
uses: https://github.com/actions/upload-artifact@v4
|
||||
with:
|
||||
name: oci-image-${{ matrix.target }}
|
||||
path: oci-image-${{ matrix.target }}.tar.gz
|
||||
if-no-files-found: error
|
||||
compression-level: 0
|
||||
+113
-180
@@ -22,8 +22,8 @@ concurrency:
|
||||
|
||||
env:
|
||||
# sccache only on main repo
|
||||
SCCACHE_GHA_ENABLED: "${{ !startsWith(github.ref, 'refs/tags/') && (github.event.pull_request.draft != true) && (vars.DOCKER_USERNAME != '') && (vars.GITLAB_USERNAME != '') && (vars.SCCACHE_ENDPOINT != '') && (github.event.pull_request.user.login != 'renovate[bot]') && 'true' || 'false' }}"
|
||||
RUSTC_WRAPPER: "${{ !startsWith(github.ref, 'refs/tags/') && (github.event.pull_request.draft != true) && (vars.DOCKER_USERNAME != '') && (vars.GITLAB_USERNAME != '') && (vars.SCCACHE_ENDPOINT != '') && (github.event.pull_request.user.login != 'renovate[bot]') && 'sccache' || '' }}"
|
||||
SCCACHE_GHA_ENABLED: "${{ (github.event.pull_request.draft != true) && (vars.DOCKER_USERNAME != '') && (vars.GITLAB_USERNAME != '') && (vars.SCCACHE_ENDPOINT != '') && (github.event.pull_request.user.login != 'renovate[bot]') && 'true' || 'false' }}"
|
||||
RUSTC_WRAPPER: "${{ (github.event.pull_request.draft != true) && (vars.DOCKER_USERNAME != '') && (vars.GITLAB_USERNAME != '') && (vars.SCCACHE_ENDPOINT != '') && (github.event.pull_request.user.login != 'renovate[bot]') && 'sccache' || '' }}"
|
||||
SCCACHE_BUCKET: "${{ (github.event.pull_request.draft != true) && (vars.DOCKER_USERNAME != '') && (vars.GITLAB_USERNAME != '') && (vars.SCCACHE_ENDPOINT != '') && (github.event.pull_request.user.login != 'renovate[bot]') && 'sccache' || '' }}"
|
||||
SCCACHE_S3_USE_SSL: ${{ vars.SCCACHE_S3_USE_SSL }}
|
||||
SCCACHE_REGION: ${{ vars.SCCACHE_REGION }}
|
||||
@@ -45,14 +45,16 @@ env:
|
||||
# Get error output from nix that we can actually use, and use our binary caches for the earlier CI steps
|
||||
NIX_CONFIG: |
|
||||
show-trace = true
|
||||
extra-substituters = https://attic.kennel.juneis.dog/conduwuit https://attic.kennel.juneis.dog/conduit https://conduwuit.cachix.org https://aseipp-nix-cache.freetls.fastly.net
|
||||
extra-trusted-public-keys = conduit:eEKoUwlQGDdYmAI/Q/0slVlegqh/QmAvQd7HBSm21Wk= conduwuit:BbycGUgTISsltcmH0qNjFR9dbrQNYgdIAcmViSGoVTE= conduwuit.cachix.org-1:MFRm6jcnfTf0jSAbmvLfhO3KBMt4px+1xaereWXp8Xg=
|
||||
extra-substituters = https://attic.kennel.juneis.dog/conduwuit https://attic.kennel.juneis.dog/conduit https://cache.lix.systems https://conduwuit.cachix.org https://aseipp-nix-cache.freetls.fastly.net
|
||||
extra-trusted-public-keys = conduit:eEKoUwlQGDdYmAI/Q/0slVlegqh/QmAvQd7HBSm21Wk= conduwuit:BbycGUgTISsltcmH0qNjFR9dbrQNYgdIAcmViSGoVTE= cache.lix.systems:aBnZUw8zA7H35Cz2RyKFVs3H4PlGTLawyY5KRbvJR8o= conduwuit.cachix.org-1:MFRm6jcnfTf0jSAbmvLfhO3KBMt4px+1xaereWXp8Xg=
|
||||
experimental-features = nix-command flakes
|
||||
extra-experimental-features = nix-command flakes
|
||||
accept-flake-config = true
|
||||
# complement uses libolm
|
||||
NIXPKGS_ALLOW_INSECURE: 1
|
||||
WEB_UPLOAD_SSH_USERNAME: ${{ secrets.WEB_UPLOAD_SSH_USERNAME }}
|
||||
GH_SHA: ${{ github.sha }}
|
||||
GH_REF_NAME: ${{ github.ref_name }}
|
||||
WEBSERVER_DIR_NAME: ${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}
|
||||
|
||||
permissions: {}
|
||||
|
||||
@@ -85,13 +87,11 @@ jobs:
|
||||
END
|
||||
|
||||
echo "Checking connection"
|
||||
ssh -q website "echo test" || ssh -q website "echo test"
|
||||
ssh -q website "echo test"
|
||||
|
||||
echo "Creating commit rev directory on web server"
|
||||
ssh -q website "rm -rf /var/www/girlboss.ceo/~strawberry/conduwuit/ci-bins/${WEBSERVER_DIR_NAME}/" || ssh -q website "rm -rf /var/www/girlboss.ceo/~strawberry/conduwuit/ci-bins/${WEBSERVER_DIR_NAME}/"
|
||||
ssh -q website "mkdir -v /var/www/girlboss.ceo/~strawberry/conduwuit/ci-bins/${WEBSERVER_DIR_NAME}/" || ssh -q website "mkdir -v /var/www/girlboss.ceo/~strawberry/conduwuit/ci-bins/${WEBSERVER_DIR_NAME}/"
|
||||
|
||||
echo "SSH_WEBSITE=1" >> "$GITHUB_ENV"
|
||||
ssh -q website "rm -rf /var/www/girlboss.ceo/~strawberry/conduwuit/ci-bins/$GITHUB_SHA/"
|
||||
ssh -q website "mkdir -v /var/www/girlboss.ceo/~strawberry/conduwuit/ci-bins/$GITHUB_SHA/"
|
||||
|
||||
- name: Install liburing
|
||||
run: |
|
||||
@@ -126,9 +126,6 @@ jobs:
|
||||
- uses: nixbuild/nix-quick-install-action@master
|
||||
|
||||
- name: Restore and cache Nix store
|
||||
# we want a fresh-state when we do releases/tags to avoid potential cache poisoning attacks impacting
|
||||
# releases and tags
|
||||
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
|
||||
uses: nix-community/cache-nix-action@v5.1.0
|
||||
with:
|
||||
# restore and save a cache using this key
|
||||
@@ -158,7 +155,7 @@ jobs:
|
||||
- name: Apply Nix binary cache configuration
|
||||
run: |
|
||||
sudo tee -a "${XDG_CONFIG_HOME:-$HOME/.config}/nix/nix.conf" > /dev/null <<EOF
|
||||
extra-substituters = https://attic.kennel.juneis.dog/conduwuit https://attic.kennel.juneis.dog/conduit https://conduwuit.cachix.org https://aseipp-nix-cache.freetls.fastly.net
|
||||
extra-substituters = https://attic.kennel.juneis.dog/conduwuit https://attic.kennel.juneis.dog/conduit https://cache.lix.systems https://conduwuit.cachix.org https://aseipp-nix-cache.freetls.fastly.net
|
||||
extra-trusted-public-keys = conduit:eEKoUwlQGDdYmAI/Q/0slVlegqh/QmAvQd7HBSm21Wk= conduwuit:BbycGUgTISsltcmH0qNjFR9dbrQNYgdIAcmViSGoVTE= cache.lix.systems:aBnZUw8zA7H35Cz2RyKFVs3H4PlGTLawyY5KRbvJR8o= conduwuit.cachix.org-1:MFRm6jcnfTf0jSAbmvLfhO3KBMt4px+1xaereWXp8Xg=
|
||||
experimental-features = nix-command flakes
|
||||
extra-experimental-features = nix-command flakes
|
||||
@@ -189,16 +186,11 @@ jobs:
|
||||
|
||||
# use sccache for Rust
|
||||
- name: Run sccache-cache
|
||||
# we want a fresh-state when we do releases/tags to avoid potential cache poisoning attacks impacting
|
||||
# releases and tags
|
||||
if: ${{ (env.SCCACHE_GHA_ENABLED == 'true') && !startsWith(github.ref, 'refs/tags/') }}
|
||||
if: (env.SCCACHE_GHA_ENABLED == 'true')
|
||||
uses: mozilla-actions/sccache-action@main
|
||||
|
||||
# use rust-cache
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
# we want a fresh-state when we do releases/tags to avoid potential cache poisoning attacks impacting
|
||||
# releases and tags
|
||||
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
|
||||
with:
|
||||
cache-all-crates: "true"
|
||||
cache-on-failure: "true"
|
||||
@@ -314,16 +306,11 @@ jobs:
|
||||
END
|
||||
|
||||
echo "Checking connection"
|
||||
ssh -q website "echo test" || ssh -q website "echo test"
|
||||
|
||||
echo "SSH_WEBSITE=1" >> "$GITHUB_ENV"
|
||||
ssh -q website "echo test"
|
||||
|
||||
- uses: nixbuild/nix-quick-install-action@master
|
||||
|
||||
- name: Restore and cache Nix store
|
||||
# we want a fresh-state when we do releases/tags to avoid potential cache poisoning attacks impacting
|
||||
# releases and tags
|
||||
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
|
||||
uses: nix-community/cache-nix-action@v5.1.0
|
||||
with:
|
||||
# restore and save a cache using this key
|
||||
@@ -353,7 +340,7 @@ jobs:
|
||||
- name: Apply Nix binary cache configuration
|
||||
run: |
|
||||
sudo tee -a "${XDG_CONFIG_HOME:-$HOME/.config}/nix/nix.conf" > /dev/null <<EOF
|
||||
extra-substituters = https://attic.kennel.juneis.dog/conduwuit https://attic.kennel.juneis.dog/conduit https://conduwuit.cachix.org https://aseipp-nix-cache.freetls.fastly.net
|
||||
extra-substituters = https://attic.kennel.juneis.dog/conduwuit https://attic.kennel.juneis.dog/conduit https://cache.lix.systems https://conduwuit.cachix.org https://aseipp-nix-cache.freetls.fastly.net
|
||||
extra-trusted-public-keys = conduit:eEKoUwlQGDdYmAI/Q/0slVlegqh/QmAvQd7HBSm21Wk= conduwuit:BbycGUgTISsltcmH0qNjFR9dbrQNYgdIAcmViSGoVTE= cache.lix.systems:aBnZUw8zA7H35Cz2RyKFVs3H4PlGTLawyY5KRbvJR8o= conduwuit.cachix.org-1:MFRm6jcnfTf0jSAbmvLfhO3KBMt4px+1xaereWXp8Xg=
|
||||
experimental-features = nix-command flakes
|
||||
extra-experimental-features = nix-command flakes
|
||||
@@ -377,16 +364,11 @@ jobs:
|
||||
|
||||
# use sccache for Rust
|
||||
- name: Run sccache-cache
|
||||
# we want a fresh-state when we do releases/tags to avoid potential cache poisoning attacks impacting
|
||||
# releases and tags
|
||||
if: ${{ (env.SCCACHE_GHA_ENABLED == 'true') && !startsWith(github.ref, 'refs/tags/') }}
|
||||
if: (env.SCCACHE_GHA_ENABLED == 'true')
|
||||
uses: mozilla-actions/sccache-action@main
|
||||
|
||||
# use rust-cache
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
# we want a fresh-state when we do releases/tags to avoid potential cache poisoning attacks impacting
|
||||
# releases and tags
|
||||
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
|
||||
with:
|
||||
cache-all-crates: "true"
|
||||
cache-on-failure: "true"
|
||||
@@ -509,29 +491,29 @@ jobs:
|
||||
- name: Upload static-x86_64-linux-musl-all-features-x86_64-haswell-optimised to webserver
|
||||
if: ${{ matrix.target == 'x86_64-linux-musl' }}
|
||||
run: |
|
||||
if [ ! -z $SSH_WEBSITE ]; then
|
||||
chmod +x static-x86_64-linux-musl-x86_64-haswell-optimised
|
||||
scp static-x86_64-linux-musl-x86_64-haswell-optimised website:/var/www/girlboss.ceo/~strawberry/conduwuit/ci-bins/${WEBSERVER_DIR_NAME}/static-x86_64-linux-musl-x86_64-haswell-optimised
|
||||
if [ ! -z $WEB_UPLOAD_SSH_USERNAME ]; then
|
||||
scp static-x86_64-linux-musl-x86_64-haswell-optimised website:/var/www/girlboss.ceo/~strawberry/conduwuit/ci-bins/${GH_SHA}/static-x86_64-linux-musl-x86_64-haswell-optimised
|
||||
fi
|
||||
|
||||
- name: Upload static-${{ matrix.target }}-all-features to webserver
|
||||
if: (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main' || (github.event.pull_request.draft != true)) && (env.web_upload_ssh_private_key != '') && github.event.pull_request.user.login != 'renovate[bot]'
|
||||
run: |
|
||||
if [ ! -z $SSH_WEBSITE ]; then
|
||||
chmod +x static-${{ matrix.target }}
|
||||
scp static-${{ matrix.target }} website:/var/www/girlboss.ceo/~strawberry/conduwuit/ci-bins/${WEBSERVER_DIR_NAME}/static-${{ matrix.target }}
|
||||
if [ ! -z $WEB_UPLOAD_SSH_USERNAME ]; then
|
||||
scp static-${{ matrix.target }} website:/var/www/girlboss.ceo/~strawberry/conduwuit/ci-bins/${GH_SHA}/static-${{ matrix.target }}
|
||||
fi
|
||||
|
||||
- name: Upload static deb x86_64-linux-musl-all-features-x86_64-haswell-optimised to webserver
|
||||
if: ${{ matrix.target == 'x86_64-linux-musl' }}
|
||||
run: |
|
||||
if [ ! -z $SSH_WEBSITE ]; then
|
||||
scp x86_64-linux-musl-x86_64-haswell-optimised.deb website:/var/www/girlboss.ceo/~strawberry/conduwuit/ci-bins/${WEBSERVER_DIR_NAME}/x86_64-linux-musl-x86_64-haswell-optimised.deb
|
||||
if [ ! -z $WEB_UPLOAD_SSH_USERNAME ]; then
|
||||
scp x86_64-linux-musl-x86_64-haswell-optimised.deb website:/var/www/girlboss.ceo/~strawberry/conduwuit/ci-bins/${GH_SHA}/x86_64-linux-musl-x86_64-haswell-optimised.deb
|
||||
fi
|
||||
|
||||
- name: Upload static deb ${{ matrix.target }}-all-features to webserver
|
||||
if: (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main' || (github.event.pull_request.draft != true)) && (env.web_upload_ssh_private_key != '') && github.event.pull_request.user.login != 'renovate[bot]'
|
||||
run: |
|
||||
if [ ! -z $SSH_WEBSITE ]; then
|
||||
scp ${{ matrix.target }}.deb website:/var/www/girlboss.ceo/~strawberry/conduwuit/ci-bins/${WEBSERVER_DIR_NAME}/${{ matrix.target }}.deb
|
||||
if [ ! -z $WEB_UPLOAD_SSH_USERNAME ]; then
|
||||
scp ${{ matrix.target }}.deb website:/var/www/girlboss.ceo/~strawberry/conduwuit/ci-bins/${GH_SHA}/${{ matrix.target }}.deb
|
||||
fi
|
||||
|
||||
- name: Upload static-${{ matrix.target }}-debug-all-features to GitHub
|
||||
@@ -550,15 +532,17 @@ jobs:
|
||||
compression-level: 0
|
||||
|
||||
- name: Upload static-${{ matrix.target }}-debug-all-features to webserver
|
||||
if: (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main' || (github.event.pull_request.draft != true)) && (env.web_upload_ssh_private_key != '') && github.event.pull_request.user.login != 'renovate[bot]'
|
||||
run: |
|
||||
if [ ! -z $SSH_WEBSITE ]; then
|
||||
scp static-${{ matrix.target }}-debug website:/var/www/girlboss.ceo/~strawberry/conduwuit/ci-bins/${WEBSERVER_DIR_NAME}/static-${{ matrix.target }}-debug
|
||||
if [ ! -z $WEB_UPLOAD_SSH_USERNAME ]; then
|
||||
scp static-${{ matrix.target }}-debug website:/var/www/girlboss.ceo/~strawberry/conduwuit/ci-bins/${GH_SHA}/static-${{ matrix.target }}-debug
|
||||
fi
|
||||
|
||||
- name: Upload static deb ${{ matrix.target }}-debug-all-features to webserver
|
||||
if: (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main' || (github.event.pull_request.draft != true)) && (env.web_upload_ssh_private_key != '') && github.event.pull_request.user.login != 'renovate[bot]'
|
||||
run: |
|
||||
if [ ! -z $SSH_WEBSITE ]; then
|
||||
scp ${{ matrix.target }}-debug.deb website:/var/www/girlboss.ceo/~strawberry/conduwuit/ci-bins/${WEBSERVER_DIR_NAME}/${{ matrix.target }}-debug.deb
|
||||
if [ ! -z $WEB_UPLOAD_SSH_USERNAME ]; then
|
||||
scp ${{ matrix.target }}-debug.deb website:/var/www/girlboss.ceo/~strawberry/conduwuit/ci-bins/${GH_SHA}/${{ matrix.target }}-debug.deb
|
||||
fi
|
||||
|
||||
- name: Build OCI image ${{ matrix.target }}-all-features
|
||||
@@ -580,14 +564,6 @@ jobs:
|
||||
|
||||
cp -v -f result oci-image-${{ matrix.target }}-debug.tar.gz
|
||||
|
||||
- name: Upload OCI image x86_64-linux-musl-all-features-x86_64-haswell-optimised to GitHub
|
||||
if: ${{ matrix.target == 'x86_64-linux-musl' }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: oci-image-x86_64-linux-musl-all-features-x86_64-haswell-optimised
|
||||
path: oci-image-x86_64-linux-musl-all-features-x86_64-haswell-optimised.tar.gz
|
||||
if-no-files-found: error
|
||||
compression-level: 0
|
||||
- name: Upload OCI image ${{ matrix.target }}-all-features to GitHub
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
@@ -607,20 +583,22 @@ jobs:
|
||||
- name: Upload OCI image x86_64-linux-musl-all-features-x86_64-haswell-optimised.tar.gz to webserver
|
||||
if: ${{ matrix.target == 'x86_64-linux-musl' }}
|
||||
run: |
|
||||
if [ ! -z $SSH_WEBSITE ]; then
|
||||
scp oci-image-x86_64-linux-musl-all-features-x86_64-haswell-optimised.tar.gz website:/var/www/girlboss.ceo/~strawberry/conduwuit/ci-bins/${WEBSERVER_DIR_NAME}/oci-image-x86_64-linux-musl-all-features-x86_64-haswell-optimised.tar.gz
|
||||
if [ ! -z $WEB_UPLOAD_SSH_USERNAME ]; then
|
||||
scp oci-image-x86_64-linux-musl-all-features-x86_64-haswell-optimised.tar.gz website:/var/www/girlboss.ceo/~strawberry/conduwuit/ci-bins/${GH_SHA}/oci-image-x86_64-linux-musl-all-features-x86_64-haswell-optimised.tar.gz
|
||||
fi
|
||||
|
||||
- name: Upload OCI image ${{ matrix.target }}-all-features to webserver
|
||||
if: (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main' || (github.event.pull_request.draft != true)) && (env.web_upload_ssh_private_key != '') && github.event.pull_request.user.login != 'renovate[bot]'
|
||||
run: |
|
||||
if [ ! -z $SSH_WEBSITE ]; then
|
||||
scp oci-image-${{ matrix.target }}.tar.gz website:/var/www/girlboss.ceo/~strawberry/conduwuit/ci-bins/${WEBSERVER_DIR_NAME}/oci-image-${{ matrix.target }}.tar.gz
|
||||
if [ ! -z $WEB_UPLOAD_SSH_USERNAME ]; then
|
||||
scp oci-image-${{ matrix.target }}.tar.gz website:/var/www/girlboss.ceo/~strawberry/conduwuit/ci-bins/${GH_SHA}/oci-image-${{ matrix.target }}.tar.gz
|
||||
fi
|
||||
|
||||
- name: Upload OCI image ${{ matrix.target }}-debug-all-features to webserver
|
||||
if: (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main' || (github.event.pull_request.draft != true)) && (env.web_upload_ssh_private_key != '') && github.event.pull_request.user.login != 'renovate[bot]'
|
||||
run: |
|
||||
if [ ! -z $SSH_WEBSITE ]; then
|
||||
scp oci-image-${{ matrix.target }}-debug.tar.gz website:/var/www/girlboss.ceo/~strawberry/conduwuit/ci-bins/${WEBSERVER_DIR_NAME}/oci-image-${{ matrix.target }}-debug.tar.gz
|
||||
if [ ! -z $WEB_UPLOAD_SSH_USERNAME ]; then
|
||||
scp oci-image-${{ matrix.target }}-debug.tar.gz website:/var/www/girlboss.ceo/~strawberry/conduwuit/ci-bins/${GH_SHA}/oci-image-${{ matrix.target }}-debug.tar.gz
|
||||
fi
|
||||
|
||||
build_mac_binaries:
|
||||
@@ -659,9 +637,7 @@ jobs:
|
||||
END
|
||||
|
||||
echo "Checking connection"
|
||||
ssh -q website "echo test" || ssh -q website "echo test"
|
||||
|
||||
echo "SSH_WEBSITE=1" >> "$GITHUB_ENV"
|
||||
ssh -q website "echo test"
|
||||
|
||||
- name: Tag comparison check
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/v') && !endsWith(github.ref, '-rc') }}
|
||||
@@ -677,9 +653,7 @@ jobs:
|
||||
|
||||
# use sccache for Rust
|
||||
- name: Run sccache-cache
|
||||
# we want a fresh-state when we do releases/tags to avoid potential cache poisoning attacks impacting
|
||||
# releases and tags
|
||||
if: ${{ (env.SCCACHE_GHA_ENABLED == 'true') && !startsWith(github.ref, 'refs/tags/') }}
|
||||
if: (env.SCCACHE_GHA_ENABLED == 'true')
|
||||
uses: mozilla-actions/sccache-action@main
|
||||
|
||||
# use rust-cache
|
||||
@@ -693,7 +667,7 @@ jobs:
|
||||
- name: Build macOS x86_64 binary
|
||||
if: ${{ matrix.os == 'macos-13' }}
|
||||
run: |
|
||||
CONDUWUIT_VERSION_EXTRA="$(git rev-parse --short ${{ github.sha }})" cargo build --release --locked --features=perf_measurements,sentry_telemetry,direct_tls
|
||||
CONDUWUIT_VERSION_EXTRA="$(git rev-parse --short ${{ github.sha }})" cargo build --release
|
||||
cp -v -f target/release/conduwuit conduwuit-macos-x86_64
|
||||
otool -L conduwuit-macos-x86_64
|
||||
|
||||
@@ -701,13 +675,12 @@ jobs:
|
||||
- name: Run x86_64 macOS release binary
|
||||
if: ${{ matrix.os == 'macos-13' }}
|
||||
run: |
|
||||
./conduwuit-macos-x86_64 --help
|
||||
./conduwuit-macos-x86_64 --version
|
||||
|
||||
- name: Build macOS arm64 binary
|
||||
if: ${{ matrix.os == 'macos-latest' }}
|
||||
run: |
|
||||
CONDUWUIT_VERSION_EXTRA="$(git rev-parse --short ${{ github.sha }})" cargo build --release --locked --features=perf_measurements,sentry_telemetry,direct_tls
|
||||
CONDUWUIT_VERSION_EXTRA="$(git rev-parse --short ${{ github.sha }})" cargo build --release
|
||||
cp -v -f target/release/conduwuit conduwuit-macos-arm64
|
||||
otool -L conduwuit-macos-arm64
|
||||
|
||||
@@ -715,23 +688,20 @@ jobs:
|
||||
- name: Run arm64 macOS release binary
|
||||
if: ${{ matrix.os == 'macos-latest' }}
|
||||
run: |
|
||||
./conduwuit-macos-arm64 --help
|
||||
./conduwuit-macos-arm64 --version
|
||||
|
||||
- name: Upload macOS x86_64 binary to webserver
|
||||
if: ${{ matrix.os == 'macos-13' }}
|
||||
run: |
|
||||
if [ ! -z $SSH_WEBSITE ]; then
|
||||
chmod +x conduwuit-macos-x86_64
|
||||
scp conduwuit-macos-x86_64 website:/var/www/girlboss.ceo/~strawberry/conduwuit/ci-bins/${WEBSERVER_DIR_NAME}/conduwuit-macos-x86_64
|
||||
if [ ! -z $WEB_UPLOAD_SSH_USERNAME ]; then
|
||||
scp conduwuit-macos-x86_64 website:/var/www/girlboss.ceo/~strawberry/conduwuit/ci-bins/${GH_SHA}/conduwuit-macos-x86_64
|
||||
fi
|
||||
|
||||
- name: Upload macOS arm64 binary to webserver
|
||||
if: ${{ matrix.os == 'macos-latest' }}
|
||||
run: |
|
||||
if [ ! -z $SSH_WEBSITE ]; then
|
||||
chmod +x conduwuit-macos-arm64
|
||||
scp conduwuit-macos-arm64 website:/var/www/girlboss.ceo/~strawberry/conduwuit/ci-bins/${WEBSERVER_DIR_NAME}/conduwuit-macos-arm64
|
||||
if [ ! -z $WEB_UPLOAD_SSH_USERNAME ]; then
|
||||
scp conduwuit-macos-arm64 website:/var/www/girlboss.ceo/~strawberry/conduwuit/ci-bins/${GH_SHA}/conduwuit-macos-arm64
|
||||
fi
|
||||
|
||||
- name: Upload macOS x86_64 binary
|
||||
@@ -769,11 +739,18 @@ jobs:
|
||||
contents: read
|
||||
if: (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main' || (github.event.pull_request.draft != true)) && github.event.pull_request.user.login != 'renovate[bot]'
|
||||
env:
|
||||
DOCKER_HUB_REPO: docker.io/${{ needs.variables.outputs.github_repository }}
|
||||
GHCR_REPO: ghcr.io/${{ needs.variables.outputs.github_repository }}
|
||||
GLCR_REPO: registry.gitlab.com/conduwuit/conduwuit
|
||||
UNIQUE_TAG: ${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}
|
||||
BRANCH_TAG: ${{ (startsWith(github.ref, 'refs/tags/v') && !endsWith(github.ref, '-rc') && 'latest') || (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}
|
||||
DOCKER_ARM64: docker.io/${{ needs.variables.outputs.github_repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-arm64v8
|
||||
DOCKER_AMD64: docker.io/${{ needs.variables.outputs.github_repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-amd64
|
||||
DOCKER_TAG: docker.io/${{ needs.variables.outputs.github_repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}
|
||||
DOCKER_BRANCH: docker.io/${{ needs.variables.outputs.github_repository }}:${{ (startsWith(github.ref, 'refs/tags/v') && !endsWith(github.ref, '-rc') && 'latest') || (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}
|
||||
GHCR_ARM64: ghcr.io/${{ needs.variables.outputs.github_repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-arm64v8
|
||||
GHCR_AMD64: ghcr.io/${{ needs.variables.outputs.github_repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-amd64
|
||||
GHCR_TAG: ghcr.io/${{ needs.variables.outputs.github_repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}
|
||||
GHCR_BRANCH: ghcr.io/${{ needs.variables.outputs.github_repository }}:${{ (startsWith(github.ref, 'refs/tags/v') && !endsWith(github.ref, '-rc') && 'latest') || (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}
|
||||
GLCR_ARM64: registry.gitlab.com/conduwuit/conduwuit:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-arm64v8
|
||||
GLCR_AMD64: registry.gitlab.com/conduwuit/conduwuit:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-amd64
|
||||
GLCR_TAG: registry.gitlab.com/conduwuit/conduwuit:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}
|
||||
GLCR_BRANCH: registry.gitlab.com/conduwuit/conduwuit:${{ (startsWith(github.ref, 'refs/tags/v') && !endsWith(github.ref, '-rc') && 'latest') || (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}
|
||||
|
||||
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
GITLAB_TOKEN: ${{ secrets.GITLAB_TOKEN }}
|
||||
@@ -807,187 +784,143 @@ jobs:
|
||||
|
||||
- name: Move OCI images into position
|
||||
run: |
|
||||
mv -v oci-image-x86_64-linux-musl-all-features-x86_64-haswell-optimised/*.tar.gz oci-image-amd64-haswell-optimised.tar.gz
|
||||
mv -v oci-image-x86_64-linux-musl/*.tar.gz oci-image-amd64.tar.gz
|
||||
mv -v oci-image-aarch64-linux-musl/*.tar.gz oci-image-arm64v8.tar.gz
|
||||
mv -v oci-image-x86_64-linux-musl-debug/*.tar.gz oci-image-amd64-debug.tar.gz
|
||||
mv -v oci-image-aarch64-linux-musl-debug/*.tar.gz oci-image-arm64v8-debug.tar.gz
|
||||
|
||||
- name: Load and push amd64 haswell image
|
||||
run: |
|
||||
docker load -i oci-image-amd64-haswell-optimised.tar.gz
|
||||
if [ ! -z $DOCKERHUB_TOKEN ]; then
|
||||
docker tag $(docker images -q conduwuit:main) ${DOCKER_HUB_REPO}:${UNIQUE_TAG}-haswell
|
||||
docker push ${DOCKER_HUB_REPO}:${UNIQUE_TAG}-haswell
|
||||
fi
|
||||
if [ $GHCR_ENABLED = "true" ]; then
|
||||
docker tag $(docker images -q conduwuit:main) ${GHCR_REPO}:${UNIQUE_TAG}-haswell
|
||||
docker push ${GHCR_REPO}:${UNIQUE_TAG}-haswell
|
||||
fi
|
||||
if [ ! -z $GITLAB_TOKEN ]; then
|
||||
docker tag $(docker images -q conduwuit:main) ${GLCR_REPO}:${UNIQUE_TAG}-haswell
|
||||
docker push ${GLCR_REPO}:${UNIQUE_TAG}-haswell
|
||||
fi
|
||||
|
||||
- name: Load and push amd64 image
|
||||
run: |
|
||||
docker load -i oci-image-amd64.tar.gz
|
||||
if [ ! -z $DOCKERHUB_TOKEN ]; then
|
||||
docker tag $(docker images -q conduwuit:main) ${DOCKER_HUB_REPO}:${UNIQUE_TAG}-amd64
|
||||
docker push ${DOCKER_HUB_REPO}:${UNIQUE_TAG}-amd64
|
||||
docker tag $(docker images -q conduwuit:main) ${DOCKER_AMD64}
|
||||
docker push ${DOCKER_AMD64}
|
||||
fi
|
||||
if [ $GHCR_ENABLED = "true" ]; then
|
||||
docker tag $(docker images -q conduwuit:main) ${GHCR_REPO}:${UNIQUE_TAG}-amd64
|
||||
docker push ${GHCR_REPO}:${UNIQUE_TAG}-amd64
|
||||
docker tag $(docker images -q conduwuit:main) ${GHCR_AMD64}
|
||||
docker push ${GHCR_AMD64}
|
||||
fi
|
||||
if [ ! -z $GITLAB_TOKEN ]; then
|
||||
docker tag $(docker images -q conduwuit:main) ${GLCR_REPO}:${UNIQUE_TAG}-amd64
|
||||
docker push ${GLCR_REPO}:${UNIQUE_TAG}-amd64
|
||||
docker tag $(docker images -q conduwuit:main) ${GLCR_AMD64}
|
||||
docker push ${GLCR_AMD64}
|
||||
fi
|
||||
|
||||
- name: Load and push arm64 image
|
||||
run: |
|
||||
docker load -i oci-image-arm64v8.tar.gz
|
||||
if [ ! -z $DOCKERHUB_TOKEN ]; then
|
||||
docker tag $(docker images -q conduwuit:main) ${DOCKER_HUB_REPO}:${UNIQUE_TAG}-arm64v8
|
||||
docker push ${DOCKER_HUB_REPO}:${UNIQUE_TAG}-arm64v8
|
||||
docker tag $(docker images -q conduwuit:main) ${DOCKER_ARM64}
|
||||
docker push ${DOCKER_ARM64}
|
||||
fi
|
||||
if [ $GHCR_ENABLED = "true" ]; then
|
||||
docker tag $(docker images -q conduwuit:main) ${GHCR_REPO}:${UNIQUE_TAG}-arm64v8
|
||||
docker push ${GHCR_REPO}:${UNIQUE_TAG}-arm64v8
|
||||
docker tag $(docker images -q conduwuit:main) ${GHCR_ARM64}
|
||||
docker push ${GHCR_ARM64}
|
||||
fi
|
||||
if [ ! -z $GITLAB_TOKEN ]; then
|
||||
docker tag $(docker images -q conduwuit:main) ${GLCR_REPO}:${UNIQUE_TAG}-arm64v8
|
||||
docker push ${GLCR_REPO}:${UNIQUE_TAG}-arm64v8
|
||||
docker tag $(docker images -q conduwuit:main) ${GLCR_ARM64}
|
||||
docker push ${GLCR_ARM64}
|
||||
fi
|
||||
|
||||
- name: Load and push amd64 debug image
|
||||
run: |
|
||||
docker load -i oci-image-amd64-debug.tar.gz
|
||||
if [ ! -z $DOCKERHUB_TOKEN ]; then
|
||||
docker tag $(docker images -q conduwuit:main) ${DOCKER_HUB_REPO}:${UNIQUE_TAG}-amd64-debug
|
||||
docker push ${DOCKER_HUB_REPO}:${UNIQUE_TAG}-amd64-debug
|
||||
docker tag $(docker images -q conduwuit:main) ${DOCKER_AMD64}-debug
|
||||
docker push ${DOCKER_AMD64}-debug
|
||||
fi
|
||||
if [ $GHCR_ENABLED = "true" ]; then
|
||||
docker tag $(docker images -q conduwuit:main) ${GHCR_REPO}:${UNIQUE_TAG}-amd64-debug
|
||||
docker push ${GHCR_REPO}:${UNIQUE_TAG}-amd64-debug
|
||||
docker tag $(docker images -q conduwuit:main) ${GHCR_AMD64}-debug
|
||||
docker push ${GHCR_AMD64}-debug
|
||||
fi
|
||||
if [ ! -z $GITLAB_TOKEN ]; then
|
||||
docker tag $(docker images -q conduwuit:main) ${GLCR_REPO}:${UNIQUE_TAG}-amd64-debug
|
||||
docker push ${GLCR_REPO}:${UNIQUE_TAG}-amd64-debug
|
||||
docker tag $(docker images -q conduwuit:main) ${GLCR_AMD64}-debug
|
||||
docker push ${GLCR_AMD64}-debug
|
||||
fi
|
||||
|
||||
- name: Load and push arm64 debug image
|
||||
run: |
|
||||
docker load -i oci-image-arm64v8-debug.tar.gz
|
||||
if [ ! -z $DOCKERHUB_TOKEN ]; then
|
||||
docker tag $(docker images -q conduwuit:main) ${DOCKER_HUB_REPO}:${UNIQUE_TAG}-arm64v8-debug
|
||||
docker push ${DOCKER_HUB_REPO}:${UNIQUE_TAG}-arm64v8-debug
|
||||
docker tag $(docker images -q conduwuit:main) ${DOCKER_ARM64}-debug
|
||||
docker push ${DOCKER_ARM64}-debug
|
||||
fi
|
||||
if [ $GHCR_ENABLED = "true" ]; then
|
||||
docker tag $(docker images -q conduwuit:main) ${GHCR_REPO}:${UNIQUE_TAG}-arm64v8-debug
|
||||
docker push ${GHCR_REPO}:${UNIQUE_TAG}-arm64v8-debug
|
||||
docker tag $(docker images -q conduwuit:main) ${GHCR_ARM64}-debug
|
||||
docker push ${GHCR_ARM64}-debug
|
||||
fi
|
||||
if [ ! -z $GITLAB_TOKEN ]; then
|
||||
docker tag $(docker images -q conduwuit:main) ${GLCR_REPO}:${UNIQUE_TAG}-arm64v8-debug
|
||||
docker push ${GLCR_REPO}:${UNIQUE_TAG}-arm64v8-debug
|
||||
fi
|
||||
|
||||
- name: Create Docker haswell manifests
|
||||
run: |
|
||||
# Dockerhub Container Registry
|
||||
if [ ! -z $DOCKERHUB_TOKEN ]; then
|
||||
docker manifest create ${DOCKER_HUB_REPO}:${UNIQUE_TAG}-haswell --amend ${DOCKER_HUB_REPO}:${UNIQUE_TAG}-haswell
|
||||
docker manifest create ${DOCKER_HUB_REPO}:${BRANCH_TAG}-haswell --amend ${DOCKER_HUB_REPO}:${UNIQUE_TAG}-haswell
|
||||
fi
|
||||
# GitHub Container Registry
|
||||
if [ $GHCR_ENABLED = "true" ]; then
|
||||
docker manifest create ${GHCR_REPO}:${UNIQUE_TAG}-haswell --amend ${GHCR_REPO}:${UNIQUE_TAG}-haswell
|
||||
docker manifest create ${GHCR_REPO}:${BRANCH_TAG}-haswell --amend ${GHCR_REPO}:${UNIQUE_TAG}-haswell
|
||||
fi
|
||||
# GitLab Container Registry
|
||||
if [ ! -z $GITLAB_TOKEN ]; then
|
||||
docker manifest create ${GLCR_REPO}:${UNIQUE_TAG}-haswell --amend ${GLCR_REPO}:${UNIQUE_TAG}-haswell
|
||||
docker manifest create ${GLCR_REPO}:${BRANCH_TAG}-haswell --amend ${GLCR_REPO}:${UNIQUE_TAG}-haswell
|
||||
docker tag $(docker images -q conduwuit:main) ${GLCR_ARM64}-debug
|
||||
docker push ${GLCR_ARM64}-debug
|
||||
fi
|
||||
|
||||
- name: Create Docker combined manifests
|
||||
run: |
|
||||
# Dockerhub Container Registry
|
||||
if [ ! -z $DOCKERHUB_TOKEN ]; then
|
||||
docker manifest create ${DOCKER_HUB_REPO}:${UNIQUE_TAG} --amend ${DOCKER_HUB_REPO}:${UNIQUE_TAG}-arm64v8 --amend ${DOCKER_HUB_REPO}:${UNIQUE_TAG}-amd64
|
||||
docker manifest create ${DOCKER_HUB_REPO}:${BRANCH_TAG} --amend ${DOCKER_HUB_REPO}:${UNIQUE_TAG}-arm64v8 --amend ${DOCKER_HUB_REPO}:${UNIQUE_TAG}-amd64
|
||||
docker manifest create ${DOCKER_TAG} --amend ${DOCKER_ARM64} --amend ${DOCKER_AMD64}
|
||||
docker manifest create ${DOCKER_BRANCH} --amend ${DOCKER_ARM64} --amend ${DOCKER_AMD64}
|
||||
fi
|
||||
# GitHub Container Registry
|
||||
if [ $GHCR_ENABLED = "true" ]; then
|
||||
docker manifest create ${GHCR_REPO}:${UNIQUE_TAG} --amend ${GHCR_REPO}:${UNIQUE_TAG}-arm64v8 --amend ${GHCR_REPO}:${UNIQUE_TAG}-amd64
|
||||
docker manifest create ${GHCR_REPO}:${BRANCH_TAG} --amend ${GHCR_REPO}:${UNIQUE_TAG}-arm64v8 --amend ${GHCR_REPO}:${UNIQUE_TAG}-amd64
|
||||
docker manifest create ${GHCR_TAG} --amend ${GHCR_ARM64} --amend ${GHCR_AMD64}
|
||||
docker manifest create ${GHCR_BRANCH} --amend ${GHCR_ARM64} --amend ${GHCR_AMD64}
|
||||
fi
|
||||
# GitLab Container Registry
|
||||
if [ ! -z $GITLAB_TOKEN ]; then
|
||||
docker manifest create ${GLCR_REPO}:${UNIQUE_TAG} --amend ${GLCR_REPO}:${UNIQUE_TAG}-arm64v8 --amend ${GLCR_REPO}:${UNIQUE_TAG}-amd64
|
||||
docker manifest create ${GLCR_REPO}:${BRANCH_TAG} --amend ${GLCR_REPO}:${UNIQUE_TAG}-arm64v8 --amend ${GLCR_REPO}:${UNIQUE_TAG}-amd64
|
||||
docker manifest create ${GLCR_TAG} --amend ${GLCR_ARM64} --amend ${GLCR_AMD64}
|
||||
docker manifest create ${GLCR_BRANCH} --amend ${GLCR_ARM64} --amend ${GLCR_AMD64}
|
||||
fi
|
||||
|
||||
- name: Create Docker combined debug manifests
|
||||
run: |
|
||||
# Dockerhub Container Registry
|
||||
if [ ! -z $DOCKERHUB_TOKEN ]; then
|
||||
docker manifest create ${DOCKER_HUB_REPO}:${UNIQUE_TAG}-debug --amend ${DOCKER_HUB_REPO}:${UNIQUE_TAG}-arm64v8-debug --amend ${DOCKER_HUB_REPO}:${UNIQUE_TAG}-amd64-debug
|
||||
docker manifest create ${DOCKER_HUB_REPO}:${BRANCH_TAG}-debug --amend ${DOCKER_HUB_REPO}:${UNIQUE_TAG}-arm64v8-debug --amend ${DOCKER_HUB_REPO}:${UNIQUE_TAG}-amd64-debug
|
||||
docker manifest create ${DOCKER_TAG}-debug --amend ${DOCKER_ARM64}-debug --amend ${DOCKER_AMD64}-debug
|
||||
docker manifest create ${DOCKER_BRANCH}-debug --amend ${DOCKER_ARM64}-debug --amend ${DOCKER_AMD64}-debug
|
||||
fi
|
||||
# GitHub Container Registry
|
||||
if [ $GHCR_ENABLED = "true" ]; then
|
||||
docker manifest create ${GHCR_REPO}:${UNIQUE_TAG}-debug --amend ${GHCR_REPO}:${UNIQUE_TAG}-arm64v8-debug --amend ${GHCR_REPO}:${UNIQUE_TAG}-amd64-debug
|
||||
docker manifest create ${GHCR_REPO}:${BRANCH_TAG}-debug --amend ${GHCR_REPO}:${UNIQUE_TAG}-arm64v8-debug --amend ${GHCR_REPO}:${UNIQUE_TAG}-amd64-debug
|
||||
docker manifest create ${GHCR_TAG}-debug --amend ${GHCR_ARM64}-debug --amend ${GHCR_AMD64}-debug
|
||||
docker manifest create ${GHCR_BRANCH}-debug --amend ${GHCR_ARM64}-debug --amend ${GHCR_AMD64}-debug
|
||||
fi
|
||||
# GitLab Container Registry
|
||||
if [ ! -z $GITLAB_TOKEN ]; then
|
||||
docker manifest create ${GLCR_REPO}:${UNIQUE_TAG}-debug --amend ${GLCR_REPO}:${UNIQUE_TAG}-arm64v8-debug --amend ${GLCR_REPO}:${UNIQUE_TAG}-amd64-debug
|
||||
docker manifest create ${GLCR_REPO}:${BRANCH_TAG}-debug --amend ${GLCR_REPO}:${UNIQUE_TAG}-arm64v8-debug --amend ${GLCR_REPO}:${UNIQUE_TAG}-amd64-debug
|
||||
docker manifest create ${GLCR_TAG}-debug --amend ${GLCR_ARM64}-debug --amend ${GLCR_AMD64}-debug
|
||||
docker manifest create ${GLCR_BRANCH}-debug --amend ${GLCR_ARM64}-debug --amend ${GLCR_AMD64}-debug
|
||||
fi
|
||||
|
||||
- name: Push manifests to Docker registries
|
||||
run: |
|
||||
if [ ! -z $DOCKERHUB_TOKEN ]; then
|
||||
docker manifest push ${DOCKER_HUB_REPO}:${UNIQUE_TAG}
|
||||
docker manifest push ${DOCKER_HUB_REPO}:${BRANCH_TAG}
|
||||
docker manifest push ${DOCKER_HUB_REPO}:${UNIQUE_TAG}-debug
|
||||
docker manifest push ${DOCKER_HUB_REPO}:${BRANCH_TAG}-debug
|
||||
docker manifest push ${DOCKER_HUB_REPO}:${UNIQUE_TAG}-haswell
|
||||
docker manifest push ${DOCKER_HUB_REPO}:${BRANCH_TAG}-haswell
|
||||
docker manifest push ${DOCKER_TAG}
|
||||
docker manifest push ${DOCKER_BRANCH}
|
||||
docker manifest push ${DOCKER_TAG}-debug
|
||||
docker manifest push ${DOCKER_BRANCH}-debug
|
||||
fi
|
||||
if [ $GHCR_ENABLED = "true" ]; then
|
||||
docker manifest push ${GHCR_REPO}:${UNIQUE_TAG}
|
||||
docker manifest push ${GHCR_REPO}:${BRANCH_TAG}
|
||||
docker manifest push ${GHCR_REPO}:${UNIQUE_TAG}-debug
|
||||
docker manifest push ${GHCR_REPO}:${BRANCH_TAG}-debug
|
||||
docker manifest push ${GHCR_REPO}:${UNIQUE_TAG}-haswell
|
||||
docker manifest push ${GHCR_REPO}:${BRANCH_TAG}-haswell
|
||||
docker manifest push ${GHCR_TAG}
|
||||
docker manifest push ${GHCR_BRANCH}
|
||||
docker manifest push ${GHCR_TAG}-debug
|
||||
docker manifest push ${GHCR_BRANCH}-debug
|
||||
fi
|
||||
if [ ! -z $GITLAB_TOKEN ]; then
|
||||
docker manifest push ${GLCR_REPO}:${UNIQUE_TAG}
|
||||
docker manifest push ${GLCR_REPO}:${BRANCH_TAG}
|
||||
docker manifest push ${GLCR_REPO}:${UNIQUE_TAG}-debug
|
||||
docker manifest push ${GLCR_REPO}:${BRANCH_TAG}-debug
|
||||
docker manifest push ${GLCR_REPO}:${UNIQUE_TAG}-haswell
|
||||
docker manifest push ${GLCR_REPO}:${BRANCH_TAG}-haswell
|
||||
docker manifest push ${GLCR_TAG}
|
||||
docker manifest push ${GLCR_BRANCH}
|
||||
docker manifest push ${GLCR_TAG}-debug
|
||||
docker manifest push ${GLCR_BRANCH}-debug
|
||||
fi
|
||||
|
||||
- name: Add Image Links to Job Summary
|
||||
run: |
|
||||
if [ ! -z $DOCKERHUB_TOKEN ]; then
|
||||
echo "- \`docker pull ${DOCKER_HUB_REPO}:${UNIQUE_TAG}\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- \`docker pull ${DOCKER_HUB_REPO}:${UNIQUE_TAG}-debug\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- \`docker pull ${DOCKER_HUB_REPO}:${UNIQUE_TAG}-haswell\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- \`docker pull ${DOCKER_TAG}\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- \`docker pull ${DOCKER_TAG}-debug\`" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
if [ $GHCR_ENABLED = "true" ]; then
|
||||
echo "- \`docker pull ${GHCR_REPO}:${UNIQUE_TAG}\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- \`docker pull ${GHCR_REPO}:${UNIQUE_TAG}-debug\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- \`docker pull ${GHCR_REPO}:${UNIQUE_TAG}-haswell\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- \`docker pull ${GHCR_TAG}\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- \`docker pull ${GHCR_TAG}-debug\`" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
if [ ! -z $GITLAB_TOKEN ]; then
|
||||
echo "- \`docker pull ${GLCR_REPO}:${UNIQUE_TAG}\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- \`docker pull ${GLCR_REPO}:${UNIQUE_TAG}-debug\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- \`docker pull ${GLCR_REPO}:${UNIQUE_TAG}-haswell\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- \`docker pull ${GLCR_TAG}\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- \`docker pull ${GLCR_TAG}-debug\`" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
name: Update Docker Hub Description
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- README.md
|
||||
- .github/workflows/docker-hub-description.yml
|
||||
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
dockerHubDescription:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main' || (github.event.pull_request.draft != true)) && github.event.pull_request.user.login != 'renovate[bot]' && (vars.DOCKER_USERNAME != '') }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setting variables
|
||||
uses: actions/github-script@v7
|
||||
id: var
|
||||
with:
|
||||
script: |
|
||||
const githubRepo = '${{ github.repository }}'.toLowerCase()
|
||||
const repoId = githubRepo.split('/')[1]
|
||||
|
||||
core.setOutput('github_repository', githubRepo)
|
||||
const dockerRepo = '${{ vars.DOCKER_USERNAME }}'.toLowerCase() + '/' + repoId
|
||||
core.setOutput('docker_repo', dockerRepo)
|
||||
|
||||
- name: Docker Hub Description
|
||||
uses: peter-evans/dockerhub-description@v4
|
||||
with:
|
||||
username: ${{ vars.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
repository: ${{ steps.var.outputs.docker_repo }}
|
||||
short-description: ${{ github.event.repository.description }}
|
||||
enable-url-completion: true
|
||||
@@ -24,7 +24,7 @@ env:
|
||||
# Get error output from nix that we can actually use, and use our binary caches for the earlier CI steps
|
||||
NIX_CONFIG: |
|
||||
show-trace = true
|
||||
extra-substituters = extra-substituters = https://attic.kennel.juneis.dog/conduwuit https://attic.kennel.juneis.dog/conduit https://conduwuit.cachix.org https://aseipp-nix-cache.freetls.fastly.net
|
||||
extra-substituters = extra-substituters = https://attic.kennel.juneis.dog/conduwuit https://attic.kennel.juneis.dog/conduit https://cache.lix.systems https://conduwuit.cachix.org https://aseipp-nix-cache.freetls.fastly.net
|
||||
extra-trusted-public-keys = conduit:eEKoUwlQGDdYmAI/Q/0slVlegqh/QmAvQd7HBSm21Wk= conduwuit:BbycGUgTISsltcmH0qNjFR9dbrQNYgdIAcmViSGoVTE= cache.lix.systems:aBnZUw8zA7H35Cz2RyKFVs3H4PlGTLawyY5KRbvJR8o= conduwuit.cachix.org-1:MFRm6jcnfTf0jSAbmvLfhO3KBMt4px+1xaereWXp8Xg=
|
||||
experimental-features = nix-command flakes
|
||||
extra-experimental-features = nix-command flakes
|
||||
@@ -73,9 +73,6 @@ jobs:
|
||||
- uses: nixbuild/nix-quick-install-action@master
|
||||
|
||||
- name: Restore and cache Nix store
|
||||
# we want a fresh-state when we do releases/tags to avoid potential cache poisoning attacks impacting
|
||||
# releases and tags
|
||||
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
|
||||
uses: nix-community/cache-nix-action@v5.1.0
|
||||
with:
|
||||
# restore and save a cache using this key
|
||||
@@ -105,7 +102,7 @@ jobs:
|
||||
- name: Apply Nix binary cache configuration
|
||||
run: |
|
||||
sudo tee -a "${XDG_CONFIG_HOME:-$HOME/.config}/nix/nix.conf" > /dev/null <<EOF
|
||||
extra-substituters = https://attic.kennel.juneis.dog/conduwuit https://attic.kennel.juneis.dog/conduit https://conduwuit.cachix.org https://aseipp-nix-cache.freetls.fastly.net
|
||||
extra-substituters = https://attic.kennel.juneis.dog/conduwuit https://attic.kennel.juneis.dog/conduit https://cache.lix.systems https://conduwuit.cachix.org https://aseipp-nix-cache.freetls.fastly.net
|
||||
extra-trusted-public-keys = conduit:eEKoUwlQGDdYmAI/Q/0slVlegqh/QmAvQd7HBSm21Wk= conduwuit:BbycGUgTISsltcmH0qNjFR9dbrQNYgdIAcmViSGoVTE= cache.lix.systems:aBnZUw8zA7H35Cz2RyKFVs3H4PlGTLawyY5KRbvJR8o= conduwuit.cachix.org-1:MFRm6jcnfTf0jSAbmvLfhO3KBMt4px+1xaereWXp8Xg=
|
||||
experimental-features = nix-command flakes
|
||||
extra-experimental-features = nix-command flakes
|
||||
|
||||
+6
-2
@@ -12,8 +12,8 @@ variables:
|
||||
TRANSFER_METER_FREQUENCY: 5s
|
||||
NIX_CONFIG: |
|
||||
show-trace = true
|
||||
extra-substituters = https://attic.kennel.juneis.dog/conduit https://attic.kennel.juneis.dog/conduwuit https://conduwuit.cachix.org
|
||||
extra-trusted-public-keys = conduit:eEKoUwlQGDdYmAI/Q/0slVlegqh/QmAvQd7HBSm21Wk= conduwuit:BbycGUgTISsltcmH0qNjFR9dbrQNYgdIAcmViSGoVTE= conduwuit.cachix.org-1:MFRm6jcnfTf0jSAbmvLfhO3KBMt4px+1xaereWXp8Xg=
|
||||
extra-substituters = https://attic.kennel.juneis.dog/conduit https://attic.kennel.juneis.dog/conduwuit https://cache.lix.systems https://conduwuit.cachix.org
|
||||
extra-trusted-public-keys = conduit:eEKoUwlQGDdYmAI/Q/0slVlegqh/QmAvQd7HBSm21Wk= conduwuit:BbycGUgTISsltcmH0qNjFR9dbrQNYgdIAcmViSGoVTE= cache.lix.systems:aBnZUw8zA7H35Cz2RyKFVs3H4PlGTLawyY5KRbvJR8o= conduwuit.cachix.org-1:MFRm6jcnfTf0jSAbmvLfhO3KBMt4px+1xaereWXp8Xg=
|
||||
experimental-features = nix-command flakes
|
||||
extra-experimental-features = nix-command flakes
|
||||
accept-flake-config = true
|
||||
@@ -45,6 +45,10 @@ before_script:
|
||||
- if command -v nix > /dev/null && [ -n "$ATTIC_ENDPOINT" ]; then echo "extra-substituters = $ATTIC_ENDPOINT" >> /etc/nix/nix.conf; fi
|
||||
- if command -v nix > /dev/null && [ -n "$ATTIC_PUBLIC_KEY" ]; then echo "extra-trusted-public-keys = $ATTIC_PUBLIC_KEY" >> /etc/nix/nix.conf; fi
|
||||
|
||||
# Add Lix binary cache
|
||||
- if command -v nix > /dev/null; then echo "extra-substituters = https://cache.lix.systems" >> /etc/nix/nix.conf; fi
|
||||
- if command -v nix > /dev/null; then echo "extra-trusted-public-keys = cache.lix.systems:aBnZUw8zA7H35Cz2RyKFVs3H4PlGTLawyY5KRbvJR8o=" >> /etc/nix/nix.conf; fi
|
||||
|
||||
# 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
|
||||
|
||||
Generated
+273
-296
File diff suppressed because it is too large
Load Diff
+19
-31
@@ -7,20 +7,19 @@ default-members = ["src/*"]
|
||||
|
||||
[workspace.package]
|
||||
authors = [
|
||||
"June Clementine Strawberry <june@girlboss.ceo>",
|
||||
"strawberry <strawberry@puppygock.gay>", # woof
|
||||
"Jason Volk <jason@zemos.net>",
|
||||
"strawberry <strawberry@puppygock.gay>",
|
||||
"timokoesters <timo@koesters.xyz>",
|
||||
]
|
||||
categories = ["network-programming"]
|
||||
description = "a very cool Matrix chat homeserver written in Rust"
|
||||
description = "a very cool fork of Conduit, a Matrix homeserver written in Rust"
|
||||
edition = "2021"
|
||||
homepage = "https://conduwuit.puppyirl.gay/"
|
||||
keywords = ["chat", "matrix", "networking", "server", "uwu"]
|
||||
keywords = ["chat", "matrix", "server", "uwu"]
|
||||
license = "Apache-2.0"
|
||||
# See also `rust-toolchain.toml`
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/girlbossceo/conduwuit"
|
||||
rust-version = "1.84.0"
|
||||
rust-version = "1.83.0"
|
||||
version = "0.5.0"
|
||||
|
||||
[workspace.metadata.crane]
|
||||
@@ -59,6 +58,10 @@ features = ["parse"]
|
||||
[workspace.dependencies.sanitize-filename]
|
||||
version = "0.6.0"
|
||||
|
||||
[workspace.dependencies.jsonwebtoken]
|
||||
version = "9.3.0"
|
||||
default-features = false
|
||||
|
||||
[workspace.dependencies.base64]
|
||||
version = "0.22.1"
|
||||
default-features = false
|
||||
@@ -333,7 +336,7 @@ version = "0.1.2"
|
||||
[workspace.dependencies.ruma]
|
||||
git = "https://github.com/girlbossceo/ruwuma"
|
||||
#branch = "conduwuit-changes"
|
||||
rev = "b560338b2a50dbf61ecfe80808b9b095ad4cec00"
|
||||
rev = "c4f55b39900b33b2d443dd12a6a2dab50961fdfb"
|
||||
features = [
|
||||
"compat",
|
||||
"rand",
|
||||
@@ -432,23 +435,17 @@ version = "0.35.0"
|
||||
# jemalloc usage
|
||||
[workspace.dependencies.tikv-jemalloc-sys]
|
||||
git = "https://github.com/girlbossceo/jemallocator"
|
||||
rev = "82af58d6a13ddd5dcdc7d4e91eae3b63292995b8"
|
||||
rev = "d87938bfddc26377dd7fdf14bbcd345f3ab19442"
|
||||
default-features = false
|
||||
features = [
|
||||
"background_threads_runtime_support",
|
||||
"unprefixed_malloc_on_supported_platforms",
|
||||
]
|
||||
features = ["unprefixed_malloc_on_supported_platforms"]
|
||||
[workspace.dependencies.tikv-jemallocator]
|
||||
git = "https://github.com/girlbossceo/jemallocator"
|
||||
rev = "82af58d6a13ddd5dcdc7d4e91eae3b63292995b8"
|
||||
rev = "d87938bfddc26377dd7fdf14bbcd345f3ab19442"
|
||||
default-features = false
|
||||
features = [
|
||||
"background_threads_runtime_support",
|
||||
"unprefixed_malloc_on_supported_platforms",
|
||||
]
|
||||
features = ["unprefixed_malloc_on_supported_platforms"]
|
||||
[workspace.dependencies.tikv-jemalloc-ctl]
|
||||
git = "https://github.com/girlbossceo/jemallocator"
|
||||
rev = "82af58d6a13ddd5dcdc7d4e91eae3b63292995b8"
|
||||
rev = "d87938bfddc26377dd7fdf14bbcd345f3ab19442"
|
||||
default-features = false
|
||||
features = ["use_std"]
|
||||
|
||||
@@ -507,14 +504,6 @@ version = "0.2"
|
||||
[workspace.dependencies.num-traits]
|
||||
version = "0.2"
|
||||
|
||||
[workspace.dependencies.minicbor]
|
||||
version = "0.25.1"
|
||||
features = ["std"]
|
||||
|
||||
[workspace.dependencies.minicbor-serde]
|
||||
version = "0.3.2"
|
||||
features = ["std"]
|
||||
|
||||
#
|
||||
# Patches
|
||||
#
|
||||
@@ -524,16 +513,16 @@ features = ["std"]
|
||||
# https://github.com/girlbossceo/tracing/commit/b348dca742af641c47bc390261f60711c2af573c
|
||||
[patch.crates-io.tracing-subscriber]
|
||||
git = "https://github.com/girlbossceo/tracing"
|
||||
rev = "05825066a6d0e9ad6b80dcf29457eb179ff4768c"
|
||||
rev = "ccc4fbd8238c2d5ba354e61ec17ac610af11401d"
|
||||
[patch.crates-io.tracing]
|
||||
git = "https://github.com/girlbossceo/tracing"
|
||||
rev = "05825066a6d0e9ad6b80dcf29457eb179ff4768c"
|
||||
rev = "ccc4fbd8238c2d5ba354e61ec17ac610af11401d"
|
||||
[patch.crates-io.tracing-core]
|
||||
git = "https://github.com/girlbossceo/tracing"
|
||||
rev = "05825066a6d0e9ad6b80dcf29457eb179ff4768c"
|
||||
rev = "ccc4fbd8238c2d5ba354e61ec17ac610af11401d"
|
||||
[patch.crates-io.tracing-log]
|
||||
git = "https://github.com/girlbossceo/tracing"
|
||||
rev = "05825066a6d0e9ad6b80dcf29457eb179ff4768c"
|
||||
rev = "ccc4fbd8238c2d5ba354e61ec17ac610af11401d"
|
||||
|
||||
# adds a tab completion callback: https://github.com/girlbossceo/rustyline-async/commit/de26100b0db03e419a3d8e1dd26895d170d1fe50
|
||||
# adds event for CTRL+\: https://github.com/girlbossceo/rustyline-async/commit/67d8c49aeac03a5ef4e818f663eaa94dd7bf339b
|
||||
@@ -884,7 +873,6 @@ enum_glob_use = { level = "allow", priority = 1 }
|
||||
if_not_else = { level = "allow", priority = 1 }
|
||||
if_then_some_else_none = { level = "allow", priority = 1 }
|
||||
inline_always = { level = "allow", priority = 1 }
|
||||
match_bool = { level = "allow", priority = 1 }
|
||||
missing_docs_in_private_items = { level = "allow", priority = 1 }
|
||||
missing_errors_doc = { level = "allow", priority = 1 }
|
||||
missing_panics_doc = { level = "allow", priority = 1 }
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
<!-- ANCHOR: catchphrase -->
|
||||
|
||||
### a very cool [Matrix](https://matrix.org/) chat homeserver written in Rust
|
||||
### a very cool, featureful fork of [Conduit](https://conduit.rs/)
|
||||
|
||||
<!-- ANCHOR_END: catchphrase -->
|
||||
|
||||
@@ -15,15 +15,16 @@ information and how to deploy/setup conduwuit.
|
||||
|
||||
#### What is Matrix?
|
||||
|
||||
[Matrix](https://matrix.org) is an open, federated, and extensible network for
|
||||
decentralised communication. Users from any Matrix homeserver can chat with users from all
|
||||
other homeservers over federation. Matrix is designed to be extensible and built on top of.
|
||||
You can even use bridges such as Matrix Appservices to communicate with users outside of Matrix, like a community on Discord.
|
||||
[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?
|
||||
|
||||
A high-performance, efficient, low-cost, and featureful Matrix homeserver that's
|
||||
easy to set up and just works with minimal configuration needed.
|
||||
A high-performance and efficient Matrix homeserver that's easy to set up and
|
||||
just works. You can install it on a mini-computer like the Raspberry Pi to
|
||||
host Matrix for your family, friends or company.
|
||||
|
||||
#### Can I try it out?
|
||||
|
||||
@@ -36,22 +37,17 @@ homeserver". This means there are rules, so please read the rules:
|
||||
[https://transfem.dev/homeserver_rules.txt](https://transfem.dev/homeserver_rules.txt)
|
||||
|
||||
transfem.dev is also listed at
|
||||
[servers.joinmatrix.org](https://servers.joinmatrix.org/), which is a list of
|
||||
popular public Matrix homeservers, including some others that run conduwuit.
|
||||
[servers.joinmatrix.org](https://servers.joinmatrix.org/)
|
||||
|
||||
#### What is the current status?
|
||||
|
||||
conduwuit is technically a hard fork of [Conduit](https://conduit.rs/), which is in beta.
|
||||
The beta status initially was inherited from Conduit, however the huge amount of
|
||||
codebase divergance, changes, fixes, and improvements have effectively made this
|
||||
beta status not entirely applicable to us anymore.
|
||||
conduwuit is technically a hard fork of Conduit, which is in Beta. The Beta status
|
||||
initially was inherited from Conduit, however overtime this Beta status is rapidly
|
||||
becoming less and less relevant as our codebase significantly diverges more and more.
|
||||
|
||||
conduwuit is very stable based on our rapidly growing userbase, has lots of features that users
|
||||
expect, and very usable as a daily driver for small, medium, and upper-end medium sized homeservers.
|
||||
|
||||
A lot of critical stability and performance issues have been fixed, and a lot of
|
||||
necessary groundwork has finished; making this project way better than it was
|
||||
back in the start at ~early 2024.
|
||||
conduwuit is quite stable and very usable as a daily driver and for a low-medium
|
||||
sized homeserver. There is still a lot of more work to be done, but it is in a far
|
||||
better place than the project was in early 2024.
|
||||
|
||||
#### How is conduwuit funded? Is conduwuit sustainable?
|
||||
|
||||
@@ -76,37 +72,16 @@ Conduit like before. If you are truly finding yourself wanting to migrate back
|
||||
to Conduit, we would appreciate all your feedback and if we can assist with
|
||||
any issues or concerns.
|
||||
|
||||
#### Can I migrate from Synapse or Dendrite?
|
||||
|
||||
Currently there is no known way to seamlessly migrate all user data from the old
|
||||
homeserver to conduwuit. However it is perfectly acceptable to replace the old
|
||||
homeserver software with conduwuit using the same server name and there will not
|
||||
be any issues with federation.
|
||||
|
||||
There is an interest in developing a built-in seamless user data migration
|
||||
method into conduwuit, however there is no concrete ETA or timeline for this.
|
||||
|
||||
|
||||
<!-- ANCHOR_END: body -->
|
||||
|
||||
<!-- ANCHOR: footer -->
|
||||
|
||||
#### Contact
|
||||
|
||||
[`#conduwuit:puppygock.gay`](https://matrix.to/#/#conduwuit:puppygock.gay)
|
||||
is the official project Matrix room. You can get support here, ask questions or
|
||||
concerns, get assistance setting up conduwuit, etc.
|
||||
If you run into any question, feel free to
|
||||
|
||||
This room should stay relevant and focused on conduwuit. An offtopic general
|
||||
chatter room can be found there as well.
|
||||
|
||||
Please keep the issue trackers focused on bug reports and enhancement requests.
|
||||
General support is extremely difficult to be offered over an issue tracker, and
|
||||
simple questions should be asked directly in an interactive platform like our
|
||||
Matrix room above as they can turn into a relevant discussion and/or may not be
|
||||
simple to answer. If you're not sure, just ask in the Matrix room.
|
||||
|
||||
If you have a bug or feature to request: [Open an issue on GitHub](https://github.com/girlbossceo/conduwuit/issues/new)
|
||||
- Ask us in `#conduwuit:puppygock.gay` on Matrix
|
||||
- [Open an issue on GitHub](https://github.com/girlbossceo/conduwuit/issues/new)
|
||||
|
||||
#### Donate
|
||||
|
||||
@@ -114,11 +89,9 @@ conduwuit development is purely made possible by myself and contributors. I do
|
||||
not get paid to work on this, and I work on it in my free time. Donations are
|
||||
heavily appreciated! 💜🥺
|
||||
|
||||
- Liberapay (preferred): <https://liberapay.com/girlbossceo>
|
||||
- GitHub Sponsors (preferred): <https://github.com/sponsors/girlbossceo>
|
||||
- Ko-fi: <https://ko-fi.com/puppygock>
|
||||
|
||||
I do not and will not accept cryptocurrency donations, including things related.
|
||||
- Liberapay: <https://liberapay.com/girlbossceo>
|
||||
- Ko-fi (note they take a fee): <https://ko-fi.com/puppygock>
|
||||
- GitHub Sponsors: <https://github.com/sponsors/girlbossceo>
|
||||
|
||||
#### Logo
|
||||
|
||||
@@ -132,15 +105,11 @@ Both, but I prefer conduwuit.
|
||||
|
||||
#### Mirrors of conduwuit
|
||||
|
||||
If GitHub is unavailable in your country, or has poor connectivity, conduwuit's
|
||||
source code is mirrored onto the following additional platforms I maintain:
|
||||
|
||||
- GitHub: <https://github.com/girlbossceo/conduwuit>
|
||||
- GitLab: <https://gitlab.com/conduwuit/conduwuit>
|
||||
- git.girlcock.ceo: <https://git.girlcock.ceo/strawberry/conduwuit>
|
||||
- git.gay: <https://git.gay/june/conduwuit>
|
||||
- mau.dev: <https://mau.dev/june/conduwuit>
|
||||
- Codeberg: <https://codeberg.org/arf/conduwuit>
|
||||
- Codeberg: <https://codeberg.org/girlbossceo/conduwuit>
|
||||
- sourcehut: <https://git.sr.ht/~girlbossceo/conduwuit>
|
||||
|
||||
<!-- ANCHOR_END: footer -->
|
||||
|
||||
+1
-3
@@ -34,9 +34,7 @@ toplevel="$(git rev-parse --show-toplevel)"
|
||||
|
||||
pushd "$toplevel" > /dev/null
|
||||
|
||||
#bin/nix-build-and-cache just .#linux-complement
|
||||
bin/nix-build-and-cache just .#complement
|
||||
#nom build .#complement
|
||||
bin/nix-build-and-cache just .#linux-complement
|
||||
|
||||
docker load < result
|
||||
popd > /dev/null
|
||||
|
||||
+7
-33
@@ -389,16 +389,13 @@
|
||||
#
|
||||
#allow_registration = false
|
||||
|
||||
# Enabling this setting opens registration to anyone without restrictions.
|
||||
# This makes your server vulnerable to abuse
|
||||
# This item is undocumented. Please contribute documentation for it.
|
||||
#
|
||||
#yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse = false
|
||||
|
||||
# A static registration token that new users will have to provide when
|
||||
# creating an account. If unset and `allow_registration` is true,
|
||||
# you must set
|
||||
# `yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse`
|
||||
# to true to allow open registration without any conditions.
|
||||
# registration is open without any condition.
|
||||
#
|
||||
# YOU NEED TO EDIT THIS OR USE registration_token_file.
|
||||
#
|
||||
@@ -566,6 +563,10 @@
|
||||
#
|
||||
#proxy = "none"
|
||||
|
||||
# This item is undocumented. Please contribute documentation for it.
|
||||
#
|
||||
#jwt_secret =
|
||||
|
||||
# Servers listed here will be used to gather public keys of other servers
|
||||
# (notary trusted key servers).
|
||||
#
|
||||
@@ -648,22 +649,6 @@
|
||||
#
|
||||
#openid_token_ttl = 3600
|
||||
|
||||
# Allow an existing session to mint a login token for another client.
|
||||
# This requires interactive authentication, but has security ramifications
|
||||
# as a malicious client could use the mechanism to spawn more than one
|
||||
# session.
|
||||
# Enabled by default.
|
||||
#
|
||||
#login_via_existing_session = true
|
||||
|
||||
# Login token expiration/TTL in milliseconds.
|
||||
#
|
||||
# These are short-lived tokens for the m.login.token endpoint.
|
||||
# This is used to allow existing sessions to create new sessions.
|
||||
# see login_via_existing_session.
|
||||
#
|
||||
#login_token_ttl = 120000
|
||||
|
||||
# Static TURN username to provide the client if not using a shared secret
|
||||
# ("turn_secret"), It is recommended to use a shared secret over static
|
||||
# credentials.
|
||||
@@ -821,9 +806,6 @@
|
||||
# magic number and translated to the library's default compression level
|
||||
# as they all differ. See their `kDefaultCompressionLevel`.
|
||||
#
|
||||
# Note when using the default value we may override it with a setting
|
||||
# tailored specifically conduwuit.
|
||||
#
|
||||
#rocksdb_compression_level = 32767
|
||||
|
||||
# Level of compression the specified compression algorithm for the
|
||||
@@ -837,9 +819,6 @@
|
||||
# less likely for this data to be used. Research your chosen compression
|
||||
# algorithm.
|
||||
#
|
||||
# Note when using the default value we may override it with a setting
|
||||
# tailored specifically conduwuit.
|
||||
#
|
||||
#rocksdb_bottommost_compression_level = 32767
|
||||
|
||||
# Whether to enable RocksDB's "bottommost_compression".
|
||||
@@ -851,7 +830,7 @@
|
||||
#
|
||||
# See https://github.com/facebook/rocksdb/wiki/Compression for more details.
|
||||
#
|
||||
#rocksdb_bottommost_compression = true
|
||||
#rocksdb_bottommost_compression = false
|
||||
|
||||
# Database recovery mode (for RocksDB WAL corruption).
|
||||
#
|
||||
@@ -1512,11 +1491,6 @@
|
||||
#
|
||||
#sender_workers = 0
|
||||
|
||||
# Enables listener sockets; can be set to false to disable listening. This
|
||||
# option is intended for developer/diagnostic purposes only.
|
||||
#
|
||||
#listening = true
|
||||
|
||||
[global.tls]
|
||||
|
||||
# Path to a valid TLS certificate file.
|
||||
|
||||
Vendored
+4
-16
@@ -10,33 +10,21 @@ CONDUWUIT_DATABASE_PATH_SYMLINK=/var/lib/matrix-conduit
|
||||
case $1 in
|
||||
purge)
|
||||
# Remove debconf changes from the db
|
||||
#db_purge
|
||||
db_purge
|
||||
|
||||
# Per https://www.debian.org/doc/debian-policy/ch-files.html#behavior
|
||||
# "configuration files must be preserved when the package is removed, and
|
||||
# only deleted when the package is purged."
|
||||
|
||||
#
|
||||
|
||||
if [ -d "$CONDUWUIT_CONFIG_PATH" ]; then
|
||||
if test -L "$CONDUWUIT_CONFIG_PATH"; then
|
||||
echo "Deleting conduwuit configuration files"
|
||||
rm -v -r "$CONDUWUIT_CONFIG_PATH"
|
||||
fi
|
||||
rm -v -r "$CONDUWUIT_CONFIG_PATH"
|
||||
fi
|
||||
|
||||
if [ -d "$CONDUWUIT_DATABASE_PATH" ]; then
|
||||
if test -L "$CONDUWUIT_DATABASE_PATH"; then
|
||||
echo "Deleting conduwuit database directory"
|
||||
rm -r "$CONDUWUIT_DATABASE_PATH"
|
||||
fi
|
||||
rm -v -r "$CONDUWUIT_DATABASE_PATH"
|
||||
fi
|
||||
|
||||
if [ -d "$CONDUWUIT_DATABASE_PATH_SYMLINK" ]; then
|
||||
if test -L "$CONDUWUIT_DATABASE_SYMLINK"; then
|
||||
echo "Removing matrix-conduit symlink"
|
||||
rm -r "$CONDUWUIT_DATABASE_PATH_SYMLINK"
|
||||
fi
|
||||
rm -v -r "$CONDUWUIT_DATABASE_PATH_SYMLINK"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
Vendored
+1
-1
@@ -27,7 +27,7 @@ malloc-usable-size = ["rust-rocksdb/malloc-usable-size"]
|
||||
|
||||
[dependencies.rust-rocksdb]
|
||||
git = "https://github.com/girlbossceo/rust-rocksdb-zaidoon1"
|
||||
rev = "1f032427d3a0e7b0f13c04b4e34712bd8610291b"
|
||||
rev = "123d6302fed23fc706344becb2f19623265a83f8"
|
||||
#branch = "master"
|
||||
default-features = false
|
||||
|
||||
|
||||
@@ -17,8 +17,6 @@ services:
|
||||
CONDUWUIT_PORT: 6167 # should match the loadbalancer traefik label
|
||||
CONDUWUIT_MAX_REQUEST_SIZE: 20000000 # in bytes, ~20 MB
|
||||
CONDUWUIT_ALLOW_REGISTRATION: 'true'
|
||||
CONDUWUIT_REGISTRATION_TOKEN: 'YOUR_TOKEN' # A registration token is required when registration is allowed.
|
||||
#CONDUWUIT_YES_I_AM_VERY_VERY_SURE_I_WANT_AN_OPEN_REGISTRATION_SERVER_PRONE_TO_ABUSE: 'true'
|
||||
CONDUWUIT_ALLOW_FEDERATION: 'true'
|
||||
CONDUWUIT_ALLOW_CHECK_FOR_UPDATES: 'true'
|
||||
CONDUWUIT_TRUSTED_SERVERS: '["matrix.org"]'
|
||||
|
||||
@@ -33,8 +33,6 @@ services:
|
||||
CONDUWUIT_PORT: 6167
|
||||
CONDUWUIT_MAX_REQUEST_SIZE: 20000000 # in bytes, ~20 MB
|
||||
CONDUWUIT_ALLOW_REGISTRATION: 'true'
|
||||
CONDUWUIT_REGISTRATION_TOKEN: 'YOUR_TOKEN' # A registration token is required when registration is allowed.
|
||||
#CONDUWUIT_YES_I_AM_VERY_VERY_SURE_I_WANT_AN_OPEN_REGISTRATION_SERVER_PRONE_TO_ABUSE: 'true'
|
||||
CONDUWUIT_ALLOW_FEDERATION: 'true'
|
||||
CONDUWUIT_ALLOW_CHECK_FOR_UPDATES: 'true'
|
||||
CONDUWUIT_TRUSTED_SERVERS: '["matrix.org"]'
|
||||
|
||||
@@ -17,8 +17,6 @@ services:
|
||||
CONDUWUIT_PORT: 6167
|
||||
CONDUWUIT_MAX_REQUEST_SIZE: 20000000 # in bytes, ~20 MB
|
||||
CONDUWUIT_ALLOW_REGISTRATION: 'true'
|
||||
CONDUWUIT_REGISTRATION_TOKEN: 'YOUR_TOKEN' # A registration token is required when registration is allowed.
|
||||
#CONDUWUIT_YES_I_AM_VERY_VERY_SURE_I_WANT_AN_OPEN_REGISTRATION_SERVER_PRONE_TO_ABUSE: 'true'
|
||||
CONDUWUIT_ALLOW_FEDERATION: 'true'
|
||||
CONDUWUIT_ALLOW_CHECK_FOR_UPDATES: 'true'
|
||||
CONDUWUIT_TRUSTED_SERVERS: '["matrix.org"]'
|
||||
|
||||
+7
-11
@@ -97,7 +97,6 @@ env DIRENV_DEVSHELL=all-features \
|
||||
name = "clippy/default"
|
||||
group = "lints"
|
||||
script = """
|
||||
direnv exec . \
|
||||
cargo clippy \
|
||||
--workspace \
|
||||
--profile test \
|
||||
@@ -127,7 +126,6 @@ env DIRENV_DEVSHELL=all-features \
|
||||
name = "clippy/jemalloc"
|
||||
group = "lints"
|
||||
script = """
|
||||
direnv exec . \
|
||||
cargo clippy \
|
||||
--workspace \
|
||||
--profile test \
|
||||
@@ -181,15 +179,13 @@ env DIRENV_DEVSHELL=all-features \
|
||||
name = "cargo/default"
|
||||
group = "tests"
|
||||
script = """
|
||||
env DIRENV_DEVSHELL=default \
|
||||
direnv exec . \
|
||||
cargo test \
|
||||
--workspace \
|
||||
--profile test \
|
||||
--all-targets \
|
||||
--color=always \
|
||||
-- \
|
||||
--color=always
|
||||
cargo test \
|
||||
--workspace \
|
||||
--profile test \
|
||||
--all-targets \
|
||||
--color=always \
|
||||
-- \
|
||||
--color=always
|
||||
"""
|
||||
|
||||
# Checks if the generated example config differs from the checked in repo's
|
||||
|
||||
Generated
+21
-21
@@ -32,11 +32,11 @@
|
||||
"nixpkgs": "nixpkgs_4"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1737621947,
|
||||
"narHash": "sha256-8HFvG7fvIFbgtaYAY2628Tb89fA55nPm2jSiNs0/Cws=",
|
||||
"lastModified": 1733424942,
|
||||
"narHash": "sha256-5t7Sl6EkOaoP4FvzLmH7HFDbdl9SizmLh53RjDQCbWQ=",
|
||||
"owner": "cachix",
|
||||
"repo": "cachix",
|
||||
"rev": "f65a3cd5e339c223471e64c051434616e18cc4f5",
|
||||
"rev": "8b6b0e4694b9aa78b2ea4c93bff6e1a222dc7e4a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -117,11 +117,11 @@
|
||||
},
|
||||
"crane_2": {
|
||||
"locked": {
|
||||
"lastModified": 1737689766,
|
||||
"narHash": "sha256-ivVXYaYlShxYoKfSo5+y5930qMKKJ8CLcAoIBPQfJ6s=",
|
||||
"lastModified": 1734808813,
|
||||
"narHash": "sha256-3aH/0Y6ajIlfy7j52FGZ+s4icVX0oHhqBzRdlOeztqg=",
|
||||
"owner": "ipetkov",
|
||||
"repo": "crane",
|
||||
"rev": "6fe74265bbb6d016d663b1091f015e2976c4a527",
|
||||
"rev": "72e2d02dbac80c8c86bf6bf3e785536acf8ee926",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -170,11 +170,11 @@
|
||||
"rust-analyzer-src": "rust-analyzer-src"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1737786656,
|
||||
"narHash": "sha256-ubCW9Jy7ZUOF354bWxTgLDpVnTvIpNr6qR4H/j7I0oo=",
|
||||
"lastModified": 1735799625,
|
||||
"narHash": "sha256-lFadwWDvVIub11bwfZhsh2WUByf9LOi6yjsSUMmE0xk=",
|
||||
"owner": "nix-community",
|
||||
"repo": "fenix",
|
||||
"rev": "2f721f527886f801403f389a9cabafda8f1e3b7f",
|
||||
"rev": "a9d84a1545814910cb4ab0515ed6921e8b07ee95",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -364,11 +364,11 @@
|
||||
"liburing": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1737600516,
|
||||
"narHash": "sha256-EKyLQ3pbcjoU5jH5atge59F4fzuhTsb6yalUj6Ve2t8=",
|
||||
"lastModified": 1733603756,
|
||||
"narHash": "sha256-eTKnZDZ1Ex++v+BI0DBcUBmCXAO/tE8hxK9MiyztZkU=",
|
||||
"owner": "axboe",
|
||||
"repo": "liburing",
|
||||
"rev": "6c509e2b0c881a13b83b259a221bf15fc9b3f681",
|
||||
"rev": "c3d5d6270cd5ed48d817fc1e8e95f7c8b222f2ff",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -550,11 +550,11 @@
|
||||
},
|
||||
"nixpkgs_5": {
|
||||
"locked": {
|
||||
"lastModified": 1737717945,
|
||||
"narHash": "sha256-ET91TMkab3PmOZnqiJQYOtSGvSTvGeHoegAv4zcTefM=",
|
||||
"lastModified": 1735685343,
|
||||
"narHash": "sha256-h1CpBzdJDNtSUb5QMyfFHKHocTTky+4McgQEBQBM+xA=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "ecd26a469ac56357fd333946a99086e992452b6a",
|
||||
"rev": "81934660d6e9ea54d2f0cdee821e8533b10c221a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -567,11 +567,11 @@
|
||||
"rocksdb": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1737828695,
|
||||
"narHash": "sha256-8Ev6zzhNPU798JNvU27a7gj5X+6SDG3jBweUkQ59DbA=",
|
||||
"lastModified": 1734469478,
|
||||
"narHash": "sha256-IcQ4N8xADYal79K+ONmNq4RLlIwdgUqgrVzgNgiIaG8=",
|
||||
"owner": "girlbossceo",
|
||||
"repo": "rocksdb",
|
||||
"rev": "a4d9230dcc9d03be428b9a728133f8f646c0065c",
|
||||
"rev": "8b4808e7de2fbb5d119d8d72cdca76d8ab84bc47",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -599,11 +599,11 @@
|
||||
"rust-analyzer-src": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1737728869,
|
||||
"narHash": "sha256-U4pl3Hi0lT6GP4ecN3q9wdD2sdaKMbmD/5NJ1NdJ9AM=",
|
||||
"lastModified": 1735742096,
|
||||
"narHash": "sha256-q3a80h8Jf8wfmPURUgRR46nQCB3I5fhZ+/swulTF5HY=",
|
||||
"owner": "rust-lang",
|
||||
"repo": "rust-analyzer",
|
||||
"rev": "6e4c29f7ce18cea7d3d31237a4661ab932eab636",
|
||||
"rev": "7e639ee3dda6ed9cecc79d41f6d38235121e483d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
file = ./rust-toolchain.toml;
|
||||
|
||||
# See also `rust-toolchain.toml`
|
||||
sha256 = "sha256-lMLAupxng4Fd9F1oDw8gx+qA0RuF7ou7xhNU8wgs0PU=";
|
||||
sha256 = "sha256-s1RPtyvDGJaX/BisLT+ifVfuhDT1nZkZ1NcK8sbwELM=";
|
||||
};
|
||||
|
||||
mkScope = pkgs: pkgs.lib.makeScope pkgs.newScope (self: {
|
||||
@@ -169,10 +169,10 @@
|
||||
|
||||
# used for rust caching in CI to speed it up
|
||||
sccache
|
||||
|
||||
# needed so we can get rid of gcc and other unused deps that bloat OCI images
|
||||
removeReferencesTo
|
||||
]
|
||||
# valgrind is unavailable in static contexts
|
||||
# used for CI and complement
|
||||
++ (if !stdenv.hostPlatform.isStatic then [ "valgrind" ] else [])
|
||||
# liburing is Linux-exclusive
|
||||
++ lib.optional stdenv.hostPlatform.isLinux liburing
|
||||
# needed to build Rust applications on macOS
|
||||
|
||||
@@ -17,30 +17,19 @@ ip_range_denylist = []
|
||||
url_preview_domain_contains_allowlist = ["*"]
|
||||
url_preview_domain_explicit_denylist = ["*"]
|
||||
media_compat_file_link = false
|
||||
media_startup_check = true
|
||||
prune_missing_media = true
|
||||
media_startup_check = false
|
||||
prune_missing_media = false
|
||||
log_colors = false
|
||||
admin_room_notices = false
|
||||
allow_check_for_updates = false
|
||||
allow_unstable_room_versions = true
|
||||
rocksdb_log_level = "debug"
|
||||
rocksdb_max_log_files = 1
|
||||
rocksdb_recovery_mode = 0
|
||||
rocksdb_paranoid_file_checks = true
|
||||
log_guest_registrations = false
|
||||
allow_legacy_media = true
|
||||
startup_netburst = true
|
||||
|
||||
# valgrind makes things so slow
|
||||
dns_timeout = 60
|
||||
dns_attempts = 20
|
||||
request_conn_timeout = 60
|
||||
request_timeout = 120
|
||||
well_known_conn_timeout = 60
|
||||
well_known_timeout = 60
|
||||
federation_idle_timeout = 300
|
||||
sender_timeout = 300
|
||||
sender_idle_timeout = 300
|
||||
sender_retry_backoff_limit = 300
|
||||
startup_netburst = false
|
||||
|
||||
[global.tls]
|
||||
certs = "/certificate.crt"
|
||||
|
||||
@@ -9,22 +9,19 @@
|
||||
, openssl
|
||||
, stdenv
|
||||
, tini
|
||||
, valgrind
|
||||
, writeShellScriptBin
|
||||
}:
|
||||
|
||||
let
|
||||
main' = main.override {
|
||||
#profile = "test";
|
||||
profile = "release-debuginfo";
|
||||
profile = "test";
|
||||
all_features = true;
|
||||
disable_release_max_log_level = true;
|
||||
disable_features = [
|
||||
# no reason to use jemalloc for complement, just has compatibility/build issues
|
||||
"jemalloc"
|
||||
"jemalloc_stats"
|
||||
"jemalloc_prof"
|
||||
"jemalloc_conf"
|
||||
"io_uring"
|
||||
# console/CLI stuff isn't used or relevant for complement
|
||||
"console"
|
||||
"tokio_console"
|
||||
@@ -32,7 +29,7 @@ let
|
||||
"sentry_telemetry"
|
||||
"perf_measurements"
|
||||
# the containers don't use or need systemd signal support
|
||||
#"systemd"
|
||||
"systemd"
|
||||
# this is non-functional on nix for some reason
|
||||
"hardened_malloc"
|
||||
# dont include experimental features
|
||||
@@ -47,50 +44,8 @@ let
|
||||
"url_preview"
|
||||
];
|
||||
};
|
||||
# TODO: figure out why a suspicious amounnt of complement tests fail with valgrind only under complement.
|
||||
# maybe issue with direct TLS mode?
|
||||
#${lib.getExe' valgrind "valgrind"} \
|
||||
#--leak-check=no \
|
||||
#--undef-value-errors=no \
|
||||
#--exit-on-first-error=yes \
|
||||
#--error-exitcode=1 \
|
||||
|
||||
# valgrind only works in non-static ocntexts
|
||||
start = if !stdenv.hostPlatform.isStatic then writeShellScriptBin "start" ''
|
||||
set -euxo pipefail
|
||||
|
||||
${lib.getExe openssl} genrsa -out private_key.key 2048
|
||||
${lib.getExe openssl} req \
|
||||
-new \
|
||||
-sha256 \
|
||||
-key private_key.key \
|
||||
-subj "/C=US/ST=CA/O=MyOrg, Inc./CN=$SERVER_NAME" \
|
||||
-out signing_request.csr
|
||||
cp ${./v3.ext} v3.ext
|
||||
echo "DNS.1 = $SERVER_NAME" >> v3.ext
|
||||
echo "IP.1 = $(${lib.getExe gawk} 'END{print $1}' /etc/hosts)" \
|
||||
>> v3.ext
|
||||
${lib.getExe openssl} x509 \
|
||||
-req \
|
||||
-extfile v3.ext \
|
||||
-in signing_request.csr \
|
||||
-CA /complement/ca/ca.crt \
|
||||
-CAkey /complement/ca/ca.key \
|
||||
-CAcreateserial \
|
||||
-out certificate.crt \
|
||||
-days 1 \
|
||||
-sha256
|
||||
|
||||
${lib.getExe' coreutils "env"} \
|
||||
CONDUWUIT_SERVER_NAME="$SERVER_NAME" \
|
||||
TMPDIR="/" \
|
||||
${lib.getExe' valgrind "valgrind"} \
|
||||
--leak-check=no \
|
||||
--undef-value-errors=no \
|
||||
--exit-on-first-error=yes \
|
||||
--error-exitcode=1 \
|
||||
${lib.getExe main'}
|
||||
'' else writeShellScriptBin "start" ''
|
||||
start = writeShellScriptBin "start" ''
|
||||
set -euxo pipefail
|
||||
|
||||
${lib.getExe openssl} genrsa -out private_key.key 2048
|
||||
@@ -135,7 +90,6 @@ dockerTools.buildImage {
|
||||
coreutils
|
||||
main'
|
||||
start
|
||||
valgrind
|
||||
];
|
||||
};
|
||||
|
||||
|
||||
+19
-30
@@ -82,7 +82,7 @@ rust-jemalloc-sys' = (rust-jemalloc-sys.override {
|
||||
buildDepsOnlyEnv =
|
||||
let
|
||||
rocksdb' = (rocksdb.override {
|
||||
jemalloc = lib.optional (featureEnabled "jemalloc") rust-jemalloc-sys';
|
||||
jemalloc = rust-jemalloc-sys';
|
||||
# rocksdb fails to build with prefixed jemalloc, which is required on
|
||||
# darwin due to [1]. In this case, fall back to building rocksdb with
|
||||
# libc malloc. This should not cause conflicts, because all of the
|
||||
@@ -103,11 +103,6 @@ buildDepsOnlyEnv =
|
||||
++ [ "-DPORTABLE=haswell" ]) else ([ "-DPORTABLE=1" ])
|
||||
)
|
||||
++ old.cmakeFlags;
|
||||
# outputs has "tools" which we dont need or use
|
||||
outputs = [ "out" ];
|
||||
|
||||
# preInstall hooks has stuff for messing with ldb/sst_dump which we dont need or use
|
||||
preInstall = "";
|
||||
});
|
||||
in
|
||||
{
|
||||
@@ -161,19 +156,6 @@ commonAttrs = {
|
||||
];
|
||||
};
|
||||
|
||||
# This is redundant with CI
|
||||
doCheck = false;
|
||||
|
||||
cargoTestCommand = "cargo test --locked ";
|
||||
cargoExtraArgs = "--no-default-features --locked "
|
||||
+ lib.optionalString
|
||||
(features'' != [])
|
||||
"--features " + (builtins.concatStringsSep "," features'');
|
||||
cargoTestExtraArgs = "--no-default-features --locked "
|
||||
+ lib.optionalString
|
||||
(features'' != [])
|
||||
"--features " + (builtins.concatStringsSep "," features'');
|
||||
|
||||
dontStrip = profile == "dev" || profile == "test";
|
||||
dontPatchELF = profile == "dev" || profile == "test";
|
||||
|
||||
@@ -199,6 +181,9 @@ commonAttrs = {
|
||||
# differing values for `NIX_CFLAGS_COMPILE`, which contributes to spurious
|
||||
# rebuilds of bindgen and its depedents.
|
||||
jq
|
||||
|
||||
# needed so we can get rid of gcc and other unused deps that bloat OCI images
|
||||
removeReferencesTo
|
||||
]
|
||||
# needed to build Rust applications on macOS
|
||||
++ lib.optionals stdenv.hostPlatform.isDarwin [
|
||||
@@ -210,6 +195,13 @@ commonAttrs = {
|
||||
# https://discourse.nixos.org/t/compile-a-rust-binary-on-macos-dbcrossbar/8612
|
||||
pkgsBuildHost.darwin.apple_sdk.frameworks.Security
|
||||
];
|
||||
|
||||
# for some reason gcc and other weird deps are added to OCI images and bloats it up
|
||||
#
|
||||
# <https://github.com/input-output-hk/haskell.nix/issues/829>
|
||||
postInstall = with pkgsBuildHost; ''
|
||||
find "$out" -type f -exec remove-references-to -t ${stdenv.cc} -t ${gcc} -t ${llvm} -t ${rustc.unwrapped} -t ${rustc} '{}' +
|
||||
'';
|
||||
};
|
||||
in
|
||||
|
||||
@@ -218,18 +210,15 @@ craneLib.buildPackage ( commonAttrs // {
|
||||
env = buildDepsOnlyEnv;
|
||||
});
|
||||
|
||||
# This is redundant with CI
|
||||
doCheck = false;
|
||||
cargoExtraArgs = "--no-default-features "
|
||||
+ lib.optionalString
|
||||
(features'' != [])
|
||||
"--features " + (builtins.concatStringsSep "," features'');
|
||||
|
||||
cargoTestCommand = "cargo test --locked ";
|
||||
cargoExtraArgs = "--no-default-features --locked "
|
||||
+ lib.optionalString
|
||||
(features'' != [])
|
||||
"--features " + (builtins.concatStringsSep "," features'');
|
||||
cargoTestExtraArgs = "--no-default-features --locked "
|
||||
+ lib.optionalString
|
||||
(features'' != [])
|
||||
"--features " + (builtins.concatStringsSep "," features'');
|
||||
# This is redundant with CI
|
||||
cargoTestCommand = "";
|
||||
cargoCheckCommand = "";
|
||||
doCheck = false;
|
||||
|
||||
env = buildPackageEnv;
|
||||
|
||||
|
||||
@@ -28,18 +28,5 @@ dockerTools.buildLayeredImage {
|
||||
Env = [
|
||||
"RUST_BACKTRACE=full"
|
||||
];
|
||||
Labels = {
|
||||
"org.opencontainers.image.authors" = "June Clementine Strawberry <june@girlboss.ceo> and Jason Volk
|
||||
<jason@zemos.net>";
|
||||
"org.opencontainers.image.created" ="@${toString inputs.self.lastModified}";
|
||||
"org.opencontainers.image.description" = "a very cool Matrix chat homeserver written in Rust";
|
||||
"org.opencontainers.image.documentation" = "https://conduwuit.puppyirl.gay/";
|
||||
"org.opencontainers.image.licenses" = "Apache-2.0";
|
||||
"org.opencontainers.image.revision" = inputs.self.rev or inputs.self.dirtyRev or "";
|
||||
"org.opencontainers.image.title" = main.pname;
|
||||
"org.opencontainers.image.url" = "https://conduwuit.puppyirl.gay/";
|
||||
"org.opencontainers.image.vendor" = "girlbossceo";
|
||||
"org.opencontainers.image.version" = main.version;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
# If you're having trouble making the relevant changes, bug a maintainer.
|
||||
|
||||
[toolchain]
|
||||
channel = "1.84.0"
|
||||
channel = "1.83.0"
|
||||
profile = "minimal"
|
||||
components = [
|
||||
# For rust-analyzer
|
||||
|
||||
+7
-5
@@ -1,5 +1,6 @@
|
||||
use clap::Parser;
|
||||
use conduwuit::Result;
|
||||
use ruma::events::room::message::RoomMessageEventContent;
|
||||
|
||||
use crate::{
|
||||
appservice, appservice::AppserviceCommand, check, check::CheckCommand, command::Command,
|
||||
@@ -49,10 +50,13 @@ pub(super) enum AdminCommand {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all, name = "command")]
|
||||
pub(super) async fn process(command: AdminCommand, context: &Command<'_>) -> Result {
|
||||
pub(super) async fn process(
|
||||
command: AdminCommand,
|
||||
context: &Command<'_>,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
use AdminCommand::*;
|
||||
|
||||
match command {
|
||||
Ok(match command {
|
||||
| Appservices(command) => appservice::process(command, context).await?,
|
||||
| Media(command) => media::process(command, context).await?,
|
||||
| Users(command) => user::process(command, context).await?,
|
||||
@@ -62,7 +66,5 @@ pub(super) async fn process(command: AdminCommand, context: &Command<'_>) -> Res
|
||||
| Debug(command) => debug::process(command, context).await?,
|
||||
| Query(command) => query::process(command, context).await?,
|
||||
| Check(command) => check::process(command, context).await?,
|
||||
};
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
+12
-3
@@ -2,11 +2,20 @@ mod commands;
|
||||
|
||||
use clap::Subcommand;
|
||||
use conduwuit::Result;
|
||||
use ruma::events::room::message::RoomMessageEventContent;
|
||||
|
||||
use crate::admin_command_dispatch;
|
||||
use crate::Command;
|
||||
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub(super) enum CheckCommand {
|
||||
CheckAllUsers,
|
||||
AllUsers,
|
||||
}
|
||||
|
||||
pub(super) async fn process(
|
||||
command: CheckCommand,
|
||||
context: &Command<'_>,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
Ok(match command {
|
||||
| CheckCommand::AllUsers => context.check_all_users().await?,
|
||||
})
|
||||
}
|
||||
|
||||
+1
-29
@@ -1,12 +1,6 @@
|
||||
use std::{fmt, time::SystemTime};
|
||||
use std::time::SystemTime;
|
||||
|
||||
use conduwuit::Result;
|
||||
use conduwuit_service::Services;
|
||||
use futures::{
|
||||
io::{AsyncWriteExt, BufWriter},
|
||||
lock::Mutex,
|
||||
Future, FutureExt,
|
||||
};
|
||||
use ruma::EventId;
|
||||
|
||||
pub(crate) struct Command<'a> {
|
||||
@@ -14,26 +8,4 @@ pub(crate) struct Command<'a> {
|
||||
pub(crate) body: &'a [&'a str],
|
||||
pub(crate) timer: SystemTime,
|
||||
pub(crate) reply_id: Option<&'a EventId>,
|
||||
pub(crate) output: Mutex<BufWriter<Vec<u8>>>,
|
||||
}
|
||||
|
||||
impl Command<'_> {
|
||||
pub(crate) fn write_fmt(
|
||||
&self,
|
||||
arguments: fmt::Arguments<'_>,
|
||||
) -> impl Future<Output = Result> + Send + '_ {
|
||||
let buf = format!("{arguments}");
|
||||
self.output.lock().then(|mut output| async move {
|
||||
output.write_all(buf.as_bytes()).await.map_err(Into::into)
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn write_str<'a>(
|
||||
&'a self,
|
||||
s: &'a str,
|
||||
) -> impl Future<Output = Result> + Send + 'a {
|
||||
self.output.lock().then(move |mut output| async move {
|
||||
output.write_all(s.as_bytes()).await.map_err(Into::into)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+24
-74
@@ -6,8 +6,7 @@ use std::{
|
||||
};
|
||||
|
||||
use conduwuit::{
|
||||
debug_error, err, info, trace, utils, utils::string::EMPTY, warn, Error, PduEvent, PduId,
|
||||
RawPduId, Result,
|
||||
debug_error, err, info, trace, utils, utils::string::EMPTY, warn, Error, PduEvent, Result,
|
||||
};
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use ruma::{
|
||||
@@ -16,10 +15,7 @@ use ruma::{
|
||||
CanonicalJsonObject, EventId, OwnedEventId, OwnedRoomOrAliasId, RoomId, RoomVersionId,
|
||||
ServerName,
|
||||
};
|
||||
use service::rooms::{
|
||||
short::{ShortEventId, ShortRoomId},
|
||||
state_compressor::HashSetCompressStateEvent,
|
||||
};
|
||||
use service::rooms::state_compressor::HashSetCompressStateEvent;
|
||||
use tracing_subscriber::EnvFilter;
|
||||
|
||||
use crate::admin_command;
|
||||
@@ -135,42 +131,13 @@ pub(super) async fn get_pdu(&self, event_id: Box<EventId>) -> Result<RoomMessage
|
||||
}
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn get_short_pdu(
|
||||
&self,
|
||||
shortroomid: ShortRoomId,
|
||||
shorteventid: ShortEventId,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
let pdu_id: RawPduId = PduId {
|
||||
shortroomid,
|
||||
shorteventid: shorteventid.into(),
|
||||
}
|
||||
.into();
|
||||
|
||||
let pdu_json = self
|
||||
.services
|
||||
.rooms
|
||||
.timeline
|
||||
.get_pdu_json_from_id(&pdu_id)
|
||||
.await;
|
||||
|
||||
match pdu_json {
|
||||
| Ok(json) => {
|
||||
let json_text =
|
||||
serde_json::to_string_pretty(&json).expect("canonical json is valid json");
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!("```json\n{json_text}\n```",)))
|
||||
},
|
||||
| Err(_) => Ok(RoomMessageEventContent::text_plain("PDU not found locally.")),
|
||||
}
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn get_remote_pdu_list(
|
||||
&self,
|
||||
server: Box<ServerName>,
|
||||
force: bool,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
if !self.services.server.config.allow_federation {
|
||||
if !self.services.globals.config.allow_federation {
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
"Federation is disabled on this homeserver.",
|
||||
));
|
||||
@@ -235,7 +202,7 @@ pub(super) async fn get_remote_pdu(
|
||||
event_id: Box<EventId>,
|
||||
server: Box<ServerName>,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
if !self.services.server.config.allow_federation {
|
||||
if !self.services.globals.config.allow_federation {
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
"Federation is disabled on this homeserver.",
|
||||
));
|
||||
@@ -419,7 +386,7 @@ pub(super) async fn change_log_level(
|
||||
let handles = &["console"];
|
||||
|
||||
if reset {
|
||||
let old_filter_layer = match EnvFilter::try_new(&self.services.server.config.log) {
|
||||
let old_filter_layer = match EnvFilter::try_new(&self.services.globals.config.log) {
|
||||
| Ok(s) => s,
|
||||
| Err(e) => {
|
||||
return Ok(RoomMessageEventContent::text_plain(format!(
|
||||
@@ -438,7 +405,7 @@ pub(super) async fn change_log_level(
|
||||
| Ok(()) => {
|
||||
return Ok(RoomMessageEventContent::text_plain(format!(
|
||||
"Successfully changed log level back to config value {}",
|
||||
self.services.server.config.log
|
||||
self.services.globals.config.log
|
||||
)));
|
||||
},
|
||||
| Err(e) => {
|
||||
@@ -554,7 +521,7 @@ pub(super) async fn first_pdu_in_room(
|
||||
.services
|
||||
.rooms
|
||||
.state_cache
|
||||
.server_in_room(&self.services.server.config.server_name, &room_id)
|
||||
.server_in_room(&self.services.globals.config.server_name, &room_id)
|
||||
.await
|
||||
{
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
@@ -583,7 +550,7 @@ pub(super) async fn latest_pdu_in_room(
|
||||
.services
|
||||
.rooms
|
||||
.state_cache
|
||||
.server_in_room(&self.services.server.config.server_name, &room_id)
|
||||
.server_in_room(&self.services.globals.config.server_name, &room_id)
|
||||
.await
|
||||
{
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
@@ -613,7 +580,7 @@ pub(super) async fn force_set_room_state_from_server(
|
||||
.services
|
||||
.rooms
|
||||
.state_cache
|
||||
.server_in_room(&self.services.server.config.server_name, &room_id)
|
||||
.server_in_room(&self.services.globals.config.server_name, &room_id)
|
||||
.await
|
||||
{
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
@@ -725,7 +692,7 @@ pub(super) async fn force_set_room_state_from_server(
|
||||
.save_state(room_id.clone().as_ref(), new_room_state)
|
||||
.await?;
|
||||
|
||||
let state_lock = self.services.rooms.state.mutex.lock(&*room_id).await;
|
||||
let state_lock = self.services.rooms.state.mutex.lock(&room_id).await;
|
||||
self.services
|
||||
.rooms
|
||||
.state
|
||||
@@ -818,13 +785,13 @@ pub(super) async fn resolve_true_destination(
|
||||
server_name: Box<ServerName>,
|
||||
no_cache: bool,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
if !self.services.server.config.allow_federation {
|
||||
if !self.services.globals.config.allow_federation {
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
"Federation is disabled on this homeserver.",
|
||||
));
|
||||
}
|
||||
|
||||
if server_name == self.services.server.config.server_name {
|
||||
if server_name == self.services.globals.config.server_name {
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
"Not allowed to send federation requests to ourselves. Please use `get-pdu` for \
|
||||
fetching local PDUs.",
|
||||
@@ -843,27 +810,19 @@ pub(super) async fn resolve_true_destination(
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn memory_stats(&self, opts: Option<String>) -> Result<RoomMessageEventContent> {
|
||||
const OPTS: &str = "abcdefghijklmnopqrstuvwxyz";
|
||||
pub(super) async fn memory_stats(&self) -> Result<RoomMessageEventContent> {
|
||||
let html_body = conduwuit::alloc::memory_stats();
|
||||
|
||||
let opts: String = OPTS
|
||||
.chars()
|
||||
.filter(|&c| {
|
||||
let allow_any = opts.as_ref().is_some_and(|opts| opts == "*");
|
||||
if html_body.is_none() {
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
"malloc stats are not supported on your compiled malloc.",
|
||||
));
|
||||
}
|
||||
|
||||
let allow = allow_any || opts.as_ref().is_some_and(|opts| opts.contains(c));
|
||||
|
||||
!allow
|
||||
})
|
||||
.collect();
|
||||
|
||||
let stats = conduwuit::alloc::memory_stats(&opts).unwrap_or_default();
|
||||
|
||||
self.write_str("```\n").await?;
|
||||
self.write_str(&stats).await?;
|
||||
self.write_str("\n```").await?;
|
||||
|
||||
Ok(RoomMessageEventContent::text_plain(""))
|
||||
Ok(RoomMessageEventContent::text_html(
|
||||
"This command's output can only be viewed by clients that render HTML.".to_owned(),
|
||||
html_body.expect("string result"),
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(tokio_unstable)]
|
||||
@@ -936,7 +895,7 @@ pub(super) async fn list_dependencies(&self, names: bool) -> Result<RoomMessageE
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
writeln!(out, "| {name} | {version} | {feats} |")?;
|
||||
writeln!(out, "{name} | {version} | {feats}")?;
|
||||
}
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(out))
|
||||
@@ -964,12 +923,3 @@ pub(super) async fn database_stats(
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(out))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn trim_memory(&self) -> Result<RoomMessageEventContent> {
|
||||
conduwuit::alloc::trim(None)?;
|
||||
|
||||
writeln!(self, "done").await?;
|
||||
|
||||
Ok(RoomMessageEventContent::notice_plain(""))
|
||||
}
|
||||
|
||||
+2
-21
@@ -4,7 +4,6 @@ pub(crate) mod tester;
|
||||
use clap::Subcommand;
|
||||
use conduwuit::Result;
|
||||
use ruma::{EventId, OwnedRoomOrAliasId, RoomId, ServerName};
|
||||
use service::rooms::short::{ShortEventId, ShortRoomId};
|
||||
|
||||
use self::tester::TesterCommand;
|
||||
use crate::admin_command_dispatch;
|
||||
@@ -32,21 +31,12 @@ pub(super) enum DebugCommand {
|
||||
/// the command.
|
||||
ParsePdu,
|
||||
|
||||
/// - Retrieve and print a PDU by EventID from the conduwuit database
|
||||
/// - Retrieve and print a PDU by ID from the conduwuit database
|
||||
GetPdu {
|
||||
/// An event ID (a $ followed by the base64 reference hash)
|
||||
event_id: Box<EventId>,
|
||||
},
|
||||
|
||||
/// - Retrieve and print a PDU by PduId from the conduwuit database
|
||||
GetShortPdu {
|
||||
/// Shortroomid integer
|
||||
shortroomid: ShortRoomId,
|
||||
|
||||
/// Shorteventid integer
|
||||
shorteventid: ShortEventId,
|
||||
},
|
||||
|
||||
/// - Attempts to retrieve a PDU from a remote server. Inserts it into our
|
||||
/// database/timeline if found and we do not have this PDU already
|
||||
/// (following normal event auth rules, handles it as an incoming PDU).
|
||||
@@ -191,13 +181,7 @@ pub(super) enum DebugCommand {
|
||||
},
|
||||
|
||||
/// - Print extended memory usage
|
||||
///
|
||||
/// Optional argument is a character mask (a sequence of characters in any
|
||||
/// order) which enable additional extended statistics. Known characters are
|
||||
/// "abdeglmx". For convenience, a '*' will enable everything.
|
||||
MemoryStats {
|
||||
opts: Option<String>,
|
||||
},
|
||||
MemoryStats,
|
||||
|
||||
/// - Print general tokio runtime metric totals.
|
||||
RuntimeMetrics,
|
||||
@@ -223,9 +207,6 @@ pub(super) enum DebugCommand {
|
||||
map: Option<String>,
|
||||
},
|
||||
|
||||
/// - Trim memory usage
|
||||
TrimMemory,
|
||||
|
||||
/// - Developer test stubs
|
||||
#[command(subcommand)]
|
||||
#[allow(non_snake_case)]
|
||||
|
||||
@@ -31,7 +31,7 @@ async fn failure(&self) -> Result<RoomMessageEventContent> {
|
||||
#[admin_command]
|
||||
async fn tester(&self) -> Result<RoomMessageEventContent> {
|
||||
|
||||
Ok(RoomMessageEventContent::notice_plain("legacy"))
|
||||
Ok(RoomMessageEventContent::notice_plain("completed"))
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
|
||||
@@ -92,7 +92,7 @@ pub(super) async fn remote_user_in_rooms(
|
||||
&self,
|
||||
user_id: Box<UserId>,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
if user_id.server_name() == self.services.server.config.server_name {
|
||||
if user_id.server_name() == self.services.globals.config.server_name {
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
"User belongs to our server, please use `list-joined-rooms` user admin command \
|
||||
instead.",
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#![recursion_limit = "192"]
|
||||
#![allow(clippy::wildcard_imports)]
|
||||
#![allow(clippy::enum_glob_use)]
|
||||
#![allow(clippy::too_many_arguments)]
|
||||
|
||||
pub(crate) mod admin;
|
||||
pub(crate) mod command;
|
||||
|
||||
+15
-28
@@ -1,6 +1,5 @@
|
||||
use std::{
|
||||
fmt::Write,
|
||||
mem::take,
|
||||
panic::AssertUnwindSafe,
|
||||
sync::{Arc, Mutex},
|
||||
time::SystemTime,
|
||||
@@ -18,7 +17,7 @@ use conduwuit::{
|
||||
utils::string::{collect_stream, common_prefix},
|
||||
warn, Error, Result,
|
||||
};
|
||||
use futures::{future::FutureExt, io::BufWriter, AsyncWriteExt};
|
||||
use futures::future::FutureExt;
|
||||
use ruma::{
|
||||
events::{
|
||||
relation::InReplyTo,
|
||||
@@ -63,32 +62,9 @@ async fn process_command(services: Arc<Services>, input: &CommandInput) -> Proce
|
||||
body: &body,
|
||||
timer: SystemTime::now(),
|
||||
reply_id: input.reply_id.as_deref(),
|
||||
output: BufWriter::new(Vec::new()).into(),
|
||||
};
|
||||
|
||||
let (result, mut logs) = process(&context, command, &args).await;
|
||||
|
||||
let output = &mut context.output.lock().await;
|
||||
output.flush().await.expect("final flush of output stream");
|
||||
|
||||
let output =
|
||||
String::from_utf8(take(output.get_mut())).expect("invalid utf8 in command output stream");
|
||||
|
||||
match result {
|
||||
| Ok(()) if logs.is_empty() =>
|
||||
Ok(Some(reply(RoomMessageEventContent::notice_markdown(output), context.reply_id))),
|
||||
|
||||
| Ok(()) => {
|
||||
logs.write_str(output.as_str()).expect("output buffer");
|
||||
Ok(Some(reply(RoomMessageEventContent::notice_markdown(logs), context.reply_id)))
|
||||
},
|
||||
| Err(error) => {
|
||||
write!(&mut logs, "Command failed with error:\n```\n{error:#?}\n```")
|
||||
.expect("output buffer");
|
||||
|
||||
Err(reply(RoomMessageEventContent::notice_markdown(logs), context.reply_id))
|
||||
},
|
||||
}
|
||||
process(&context, command, &args).await
|
||||
}
|
||||
|
||||
fn handle_panic(error: &Error, command: &CommandInput) -> ProcessorResult {
|
||||
@@ -105,7 +81,7 @@ async fn process(
|
||||
context: &Command<'_>,
|
||||
command: AdminCommand,
|
||||
args: &[String],
|
||||
) -> (Result, String) {
|
||||
) -> ProcessorResult {
|
||||
let (capture, logs) = capture_create(context);
|
||||
|
||||
let capture_scope = capture.start();
|
||||
@@ -128,7 +104,18 @@ async fn process(
|
||||
}
|
||||
drop(logs);
|
||||
|
||||
(result, output)
|
||||
match result {
|
||||
| Ok(content) => {
|
||||
write!(&mut output, "{0}", content.body())
|
||||
.expect("failed to format command result to output buffer");
|
||||
Ok(Some(reply(RoomMessageEventContent::notice_markdown(output), context.reply_id)))
|
||||
},
|
||||
| Err(error) => {
|
||||
write!(&mut output, "Command failed with error:\n```\n{error:#?}\n```")
|
||||
.expect("failed to format command result to output");
|
||||
Err(reply(RoomMessageEventContent::notice_markdown(output), context.reply_id))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn capture_create(context: &Command<'_>) -> (Arc<Capture>, Arc<Mutex<String>>) {
|
||||
|
||||
@@ -3,9 +3,8 @@ use conduwuit::Result;
|
||||
use futures::StreamExt;
|
||||
use ruma::{events::room::message::RoomMessageEventContent, RoomId, UserId};
|
||||
|
||||
use crate::{admin_command, admin_command_dispatch};
|
||||
use crate::Command;
|
||||
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
/// All the getters and iterators from src/database/key_value/account_data.rs
|
||||
pub(crate) enum AccountDataCommand {
|
||||
@@ -20,7 +19,7 @@ pub(crate) enum AccountDataCommand {
|
||||
},
|
||||
|
||||
/// - Searches the account data for a specific kind.
|
||||
AccountDataGet {
|
||||
Get {
|
||||
/// Full user ID
|
||||
user_id: Box<UserId>,
|
||||
/// Account data event type
|
||||
@@ -30,43 +29,38 @@ pub(crate) enum AccountDataCommand {
|
||||
},
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
async fn changes_since(
|
||||
&self,
|
||||
user_id: Box<UserId>,
|
||||
since: u64,
|
||||
room_id: Option<Box<RoomId>>,
|
||||
/// All the getters and iterators from src/database/key_value/account_data.rs
|
||||
pub(super) async fn process(
|
||||
subcommand: AccountDataCommand,
|
||||
context: &Command<'_>,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results: Vec<_> = self
|
||||
.services
|
||||
.account_data
|
||||
.changes_since(room_id.as_deref(), &user_id, since)
|
||||
.collect()
|
||||
.await;
|
||||
let query_time = timer.elapsed();
|
||||
let services = context.services;
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
async fn account_data_get(
|
||||
&self,
|
||||
user_id: Box<UserId>,
|
||||
kind: String,
|
||||
room_id: Option<Box<RoomId>>,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = self
|
||||
.services
|
||||
.account_data
|
||||
.get_raw(room_id.as_deref(), &user_id, &kind)
|
||||
.await;
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
match subcommand {
|
||||
| AccountDataCommand::ChangesSince { user_id, since, room_id } => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results: Vec<_> = services
|
||||
.account_data
|
||||
.changes_since(room_id.as_deref(), &user_id, since)
|
||||
.collect()
|
||||
.await;
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
| AccountDataCommand::Get { user_id, kind, room_id } => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = services
|
||||
.account_data
|
||||
.get_raw(room_id.as_deref(), &user_id, &kind)
|
||||
.await;
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use clap::Subcommand;
|
||||
use conduwuit::Result;
|
||||
use ruma::events::room::message::RoomMessageEventContent;
|
||||
|
||||
use crate::Command;
|
||||
|
||||
@@ -17,7 +18,10 @@ pub(crate) enum AppserviceCommand {
|
||||
}
|
||||
|
||||
/// All the getters and iterators from src/database/key_value/appservice.rs
|
||||
pub(super) async fn process(subcommand: AppserviceCommand, context: &Command<'_>) -> Result {
|
||||
pub(super) async fn process(
|
||||
subcommand: AppserviceCommand,
|
||||
context: &Command<'_>,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
let services = context.services;
|
||||
|
||||
match subcommand {
|
||||
@@ -27,15 +31,18 @@ pub(super) async fn process(subcommand: AppserviceCommand, context: &Command<'_>
|
||||
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
write!(context, "Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```")
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
| AppserviceCommand::All => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = services.appservice.all().await;
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
write!(context, "Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```")
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
}
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use clap::Subcommand;
|
||||
use conduwuit::Result;
|
||||
use ruma::ServerName;
|
||||
use ruma::{events::room::message::RoomMessageEventContent, ServerName};
|
||||
|
||||
use crate::Command;
|
||||
|
||||
@@ -21,7 +21,10 @@ pub(crate) enum GlobalsCommand {
|
||||
}
|
||||
|
||||
/// All the getters and iterators from src/database/key_value/globals.rs
|
||||
pub(super) async fn process(subcommand: GlobalsCommand, context: &Command<'_>) -> Result {
|
||||
pub(super) async fn process(
|
||||
subcommand: GlobalsCommand,
|
||||
context: &Command<'_>,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
let services = context.services;
|
||||
|
||||
match subcommand {
|
||||
@@ -30,29 +33,36 @@ pub(super) async fn process(subcommand: GlobalsCommand, context: &Command<'_>) -
|
||||
let results = services.globals.db.database_version().await;
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
write!(context, "Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```")
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
| GlobalsCommand::CurrentCount => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = services.globals.db.current_count();
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
write!(context, "Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```")
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
| GlobalsCommand::LastCheckForUpdatesId => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = services.updates.last_check_for_updates_id().await;
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
write!(context, "Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```")
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
| GlobalsCommand::SigningKeysFor { origin } => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = services.server_keys.verify_keys_for(&origin).await;
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
write!(context, "Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```")
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
}
|
||||
.await
|
||||
}
|
||||
|
||||
+2
-18
@@ -3,13 +3,10 @@ mod appservice;
|
||||
mod globals;
|
||||
mod presence;
|
||||
mod pusher;
|
||||
mod raw;
|
||||
mod resolver;
|
||||
mod room_alias;
|
||||
mod room_state_cache;
|
||||
mod room_timeline;
|
||||
mod sending;
|
||||
mod short;
|
||||
mod users;
|
||||
|
||||
use clap::Subcommand;
|
||||
@@ -17,10 +14,9 @@ use conduwuit::Result;
|
||||
|
||||
use self::{
|
||||
account_data::AccountDataCommand, appservice::AppserviceCommand, globals::GlobalsCommand,
|
||||
presence::PresenceCommand, pusher::PusherCommand, raw::RawCommand, resolver::ResolverCommand,
|
||||
presence::PresenceCommand, pusher::PusherCommand, resolver::ResolverCommand,
|
||||
room_alias::RoomAliasCommand, room_state_cache::RoomStateCacheCommand,
|
||||
room_timeline::RoomTimelineCommand, sending::SendingCommand, short::ShortCommand,
|
||||
users::UsersCommand,
|
||||
sending::SendingCommand, users::UsersCommand,
|
||||
};
|
||||
use crate::admin_command_dispatch;
|
||||
|
||||
@@ -48,10 +44,6 @@ pub(super) enum QueryCommand {
|
||||
#[command(subcommand)]
|
||||
RoomStateCache(RoomStateCacheCommand),
|
||||
|
||||
/// - rooms/timeline iterators and getters
|
||||
#[command(subcommand)]
|
||||
RoomTimeline(RoomTimelineCommand),
|
||||
|
||||
/// - globals.rs iterators and getters
|
||||
#[command(subcommand)]
|
||||
Globals(GlobalsCommand),
|
||||
@@ -71,12 +63,4 @@ pub(super) enum QueryCommand {
|
||||
/// - pusher service
|
||||
#[command(subcommand)]
|
||||
Pusher(PusherCommand),
|
||||
|
||||
/// - short service
|
||||
#[command(subcommand)]
|
||||
Short(ShortCommand),
|
||||
|
||||
/// - raw service
|
||||
#[command(subcommand)]
|
||||
Raw(RawCommand),
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use clap::Subcommand;
|
||||
use conduwuit::Result;
|
||||
use futures::StreamExt;
|
||||
use ruma::UserId;
|
||||
use ruma::{events::room::message::RoomMessageEventContent, UserId};
|
||||
|
||||
use crate::Command;
|
||||
|
||||
@@ -23,16 +23,21 @@ pub(crate) enum PresenceCommand {
|
||||
}
|
||||
|
||||
/// All the getters and iterators in key_value/presence.rs
|
||||
pub(super) async fn process(subcommand: PresenceCommand, context: &Command<'_>) -> Result {
|
||||
pub(super) async fn process(
|
||||
subcommand: PresenceCommand,
|
||||
context: &Command<'_>,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
let services = context.services;
|
||||
|
||||
match subcommand {
|
||||
| PresenceCommand::GetPresence { user_id } => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = services.presence.get_presence(&user_id).await;
|
||||
let results = services.presence.db.get_presence(&user_id).await;
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
write!(context, "Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```")
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
| PresenceCommand::PresenceSince { since } => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
@@ -44,8 +49,9 @@ pub(super) async fn process(subcommand: PresenceCommand, context: &Command<'_>)
|
||||
.await;
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
write!(context, "Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```")
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
}
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use clap::Subcommand;
|
||||
use conduwuit::Result;
|
||||
use ruma::UserId;
|
||||
use ruma::{events::room::message::RoomMessageEventContent, UserId};
|
||||
|
||||
use crate::Command;
|
||||
|
||||
@@ -13,7 +13,10 @@ pub(crate) enum PusherCommand {
|
||||
},
|
||||
}
|
||||
|
||||
pub(super) async fn process(subcommand: PusherCommand, context: &Command<'_>) -> Result {
|
||||
pub(super) async fn process(
|
||||
subcommand: PusherCommand,
|
||||
context: &Command<'_>,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
let services = context.services;
|
||||
|
||||
match subcommand {
|
||||
@@ -22,8 +25,9 @@ pub(super) async fn process(subcommand: PusherCommand, context: &Command<'_>) ->
|
||||
let results = services.pusher.get_pushers(&user_id).await;
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
write!(context, "Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```")
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
}
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -1,576 +0,0 @@
|
||||
use std::{borrow::Cow, collections::BTreeMap, ops::Deref};
|
||||
|
||||
use clap::Subcommand;
|
||||
use conduwuit::{
|
||||
apply, at, is_zero,
|
||||
utils::{
|
||||
stream::{ReadyExt, TryIgnore, TryParallelExt},
|
||||
string::EMPTY,
|
||||
IterStream,
|
||||
},
|
||||
Err, Result,
|
||||
};
|
||||
use futures::{FutureExt, StreamExt, TryStreamExt};
|
||||
use ruma::events::room::message::RoomMessageEventContent;
|
||||
use tokio::time::Instant;
|
||||
|
||||
use crate::{admin_command, admin_command_dispatch};
|
||||
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
/// Query tables from database
|
||||
pub(crate) enum RawCommand {
|
||||
/// - List database maps
|
||||
RawMaps,
|
||||
|
||||
/// - Raw database query
|
||||
RawGet {
|
||||
/// Map name
|
||||
map: String,
|
||||
|
||||
/// Key
|
||||
key: String,
|
||||
},
|
||||
|
||||
/// - Raw database delete (for string keys)
|
||||
RawDel {
|
||||
/// Map name
|
||||
map: String,
|
||||
|
||||
/// Key
|
||||
key: String,
|
||||
},
|
||||
|
||||
/// - Raw database keys iteration
|
||||
RawKeys {
|
||||
/// Map name
|
||||
map: String,
|
||||
|
||||
/// Key prefix
|
||||
prefix: Option<String>,
|
||||
},
|
||||
|
||||
/// - Raw database key size breakdown
|
||||
RawKeysSizes {
|
||||
/// Map name
|
||||
map: Option<String>,
|
||||
|
||||
/// Key prefix
|
||||
prefix: Option<String>,
|
||||
},
|
||||
|
||||
/// - Raw database keys total bytes
|
||||
RawKeysTotal {
|
||||
/// Map name
|
||||
map: Option<String>,
|
||||
|
||||
/// Key prefix
|
||||
prefix: Option<String>,
|
||||
},
|
||||
|
||||
/// - Raw database values size breakdown
|
||||
RawValsSizes {
|
||||
/// Map name
|
||||
map: Option<String>,
|
||||
|
||||
/// Key prefix
|
||||
prefix: Option<String>,
|
||||
},
|
||||
|
||||
/// - Raw database values total bytes
|
||||
RawValsTotal {
|
||||
/// Map name
|
||||
map: Option<String>,
|
||||
|
||||
/// Key prefix
|
||||
prefix: Option<String>,
|
||||
},
|
||||
|
||||
/// - Raw database items iteration
|
||||
RawIter {
|
||||
/// Map name
|
||||
map: String,
|
||||
|
||||
/// Key prefix
|
||||
prefix: Option<String>,
|
||||
},
|
||||
|
||||
/// - Raw database keys iteration
|
||||
RawKeysFrom {
|
||||
/// Map name
|
||||
map: String,
|
||||
|
||||
/// Lower-bound
|
||||
start: String,
|
||||
|
||||
/// Limit
|
||||
#[arg(short, long)]
|
||||
limit: Option<usize>,
|
||||
},
|
||||
|
||||
/// - Raw database items iteration
|
||||
RawIterFrom {
|
||||
/// Map name
|
||||
map: String,
|
||||
|
||||
/// Lower-bound
|
||||
start: String,
|
||||
|
||||
/// Limit
|
||||
#[arg(short, long)]
|
||||
limit: Option<usize>,
|
||||
},
|
||||
|
||||
/// - Raw database record count
|
||||
RawCount {
|
||||
/// Map name
|
||||
map: Option<String>,
|
||||
|
||||
/// Key prefix
|
||||
prefix: Option<String>,
|
||||
},
|
||||
|
||||
/// - Compact database
|
||||
Compact {
|
||||
#[arg(short, long, alias("column"))]
|
||||
map: Option<Vec<String>>,
|
||||
|
||||
#[arg(long)]
|
||||
start: Option<String>,
|
||||
|
||||
#[arg(long)]
|
||||
stop: Option<String>,
|
||||
|
||||
#[arg(long)]
|
||||
from: Option<usize>,
|
||||
|
||||
#[arg(long)]
|
||||
into: Option<usize>,
|
||||
|
||||
/// There is one compaction job per column; then this controls how many
|
||||
/// columns are compacted in parallel. If zero, one compaction job is
|
||||
/// still run at a time here, but in exclusive-mode blocking any other
|
||||
/// automatic compaction jobs until complete.
|
||||
#[arg(long)]
|
||||
parallelism: Option<usize>,
|
||||
|
||||
#[arg(long, default_value("false"))]
|
||||
exhaustive: bool,
|
||||
},
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn compact(
|
||||
&self,
|
||||
map: Option<Vec<String>>,
|
||||
start: Option<String>,
|
||||
stop: Option<String>,
|
||||
from: Option<usize>,
|
||||
into: Option<usize>,
|
||||
parallelism: Option<usize>,
|
||||
exhaustive: bool,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
use conduwuit_database::compact::Options;
|
||||
|
||||
let default_all_maps = map
|
||||
.is_none()
|
||||
.then(|| {
|
||||
self.services
|
||||
.db
|
||||
.keys()
|
||||
.map(Deref::deref)
|
||||
.map(ToOwned::to_owned)
|
||||
})
|
||||
.into_iter()
|
||||
.flatten();
|
||||
|
||||
let maps: Vec<_> = map
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.chain(default_all_maps)
|
||||
.map(|map| self.services.db.get(&map))
|
||||
.filter_map(Result::ok)
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
if maps.is_empty() {
|
||||
return Err!("--map argument invalid. not found in database");
|
||||
}
|
||||
|
||||
let range = (
|
||||
start.as_ref().map(String::as_bytes).map(Into::into),
|
||||
stop.as_ref().map(String::as_bytes).map(Into::into),
|
||||
);
|
||||
|
||||
let options = Options {
|
||||
range,
|
||||
level: (from, into),
|
||||
exclusive: parallelism.is_some_and(is_zero!()),
|
||||
exhaustive,
|
||||
};
|
||||
|
||||
let runtime = self.services.server.runtime().clone();
|
||||
let parallelism = parallelism.unwrap_or(1);
|
||||
let results = maps
|
||||
.into_iter()
|
||||
.try_stream()
|
||||
.paralleln_and_then(runtime, parallelism, move |map| {
|
||||
map.compact_blocking(options.clone())?;
|
||||
Ok(map.name().to_owned())
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let timer = Instant::now();
|
||||
let results = results.await;
|
||||
let query_time = timer.elapsed();
|
||||
self.write_str(&format!("Jobs completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"))
|
||||
.await?;
|
||||
|
||||
Ok(RoomMessageEventContent::text_plain(""))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn raw_count(
|
||||
&self,
|
||||
map: Option<String>,
|
||||
prefix: Option<String>,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
let prefix = prefix.as_deref().unwrap_or(EMPTY);
|
||||
|
||||
let default_all_maps = map
|
||||
.is_none()
|
||||
.then(|| self.services.db.keys().map(Deref::deref))
|
||||
.into_iter()
|
||||
.flatten();
|
||||
|
||||
let maps: Vec<_> = map
|
||||
.iter()
|
||||
.map(String::as_str)
|
||||
.chain(default_all_maps)
|
||||
.map(|map| self.services.db.get(map))
|
||||
.filter_map(Result::ok)
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
let timer = Instant::now();
|
||||
let count = maps
|
||||
.iter()
|
||||
.stream()
|
||||
.then(|map| map.raw_count_prefix(&prefix))
|
||||
.ready_fold(0_usize, usize::saturating_add)
|
||||
.await;
|
||||
|
||||
let query_time = timer.elapsed();
|
||||
self.write_str(&format!("Query completed in {query_time:?}:\n\n```rs\n{count:#?}\n```"))
|
||||
.await?;
|
||||
|
||||
Ok(RoomMessageEventContent::text_plain(""))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn raw_keys(
|
||||
&self,
|
||||
map: String,
|
||||
prefix: Option<String>,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
writeln!(self, "```").boxed().await?;
|
||||
|
||||
let map = self.services.db.get(map.as_str())?;
|
||||
let timer = Instant::now();
|
||||
prefix
|
||||
.as_deref()
|
||||
.map_or_else(|| map.raw_keys().boxed(), |prefix| map.raw_keys_prefix(prefix).boxed())
|
||||
.map_ok(String::from_utf8_lossy)
|
||||
.try_for_each(|str| writeln!(self, "{str:?}"))
|
||||
.boxed()
|
||||
.await?;
|
||||
|
||||
let query_time = timer.elapsed();
|
||||
let out = format!("\n```\n\nQuery completed in {query_time:?}");
|
||||
self.write_str(out.as_str()).await?;
|
||||
|
||||
Ok(RoomMessageEventContent::text_plain(""))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn raw_keys_sizes(
|
||||
&self,
|
||||
map: Option<String>,
|
||||
prefix: Option<String>,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
let prefix = prefix.as_deref().unwrap_or(EMPTY);
|
||||
|
||||
let default_all_maps = map
|
||||
.is_none()
|
||||
.then(|| self.services.db.keys().map(Deref::deref))
|
||||
.into_iter()
|
||||
.flatten();
|
||||
|
||||
let maps: Vec<_> = map
|
||||
.iter()
|
||||
.map(String::as_str)
|
||||
.chain(default_all_maps)
|
||||
.map(|map| self.services.db.get(map))
|
||||
.filter_map(Result::ok)
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
let timer = Instant::now();
|
||||
let result = maps
|
||||
.iter()
|
||||
.stream()
|
||||
.map(|map| map.raw_keys_prefix(&prefix))
|
||||
.flatten()
|
||||
.ignore_err()
|
||||
.map(<[u8]>::len)
|
||||
.ready_fold_default(|mut map: BTreeMap<_, usize>, len| {
|
||||
let entry = map.entry(len).or_default();
|
||||
*entry = entry.saturating_add(1);
|
||||
map
|
||||
})
|
||||
.await;
|
||||
|
||||
let query_time = timer.elapsed();
|
||||
let result = format!("```\n{result:#?}\n```\n\nQuery completed in {query_time:?}");
|
||||
self.write_str(result.as_str()).await?;
|
||||
|
||||
Ok(RoomMessageEventContent::text_plain(""))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn raw_keys_total(
|
||||
&self,
|
||||
map: Option<String>,
|
||||
prefix: Option<String>,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
let prefix = prefix.as_deref().unwrap_or(EMPTY);
|
||||
|
||||
let default_all_maps = map
|
||||
.is_none()
|
||||
.then(|| self.services.db.keys().map(Deref::deref))
|
||||
.into_iter()
|
||||
.flatten();
|
||||
|
||||
let maps: Vec<_> = map
|
||||
.iter()
|
||||
.map(String::as_str)
|
||||
.chain(default_all_maps)
|
||||
.map(|map| self.services.db.get(map))
|
||||
.filter_map(Result::ok)
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
let timer = Instant::now();
|
||||
let result = maps
|
||||
.iter()
|
||||
.stream()
|
||||
.map(|map| map.raw_keys_prefix(&prefix))
|
||||
.flatten()
|
||||
.ignore_err()
|
||||
.map(<[u8]>::len)
|
||||
.ready_fold_default(|acc: usize, len| acc.saturating_add(len))
|
||||
.await;
|
||||
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
self.write_str(&format!("```\n{result:#?}\n\n```\n\nQuery completed in {query_time:?}"))
|
||||
.await?;
|
||||
|
||||
Ok(RoomMessageEventContent::text_plain(""))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn raw_vals_sizes(
|
||||
&self,
|
||||
map: Option<String>,
|
||||
prefix: Option<String>,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
let prefix = prefix.as_deref().unwrap_or(EMPTY);
|
||||
|
||||
let default_all_maps = map
|
||||
.is_none()
|
||||
.then(|| self.services.db.keys().map(Deref::deref))
|
||||
.into_iter()
|
||||
.flatten();
|
||||
|
||||
let maps: Vec<_> = map
|
||||
.iter()
|
||||
.map(String::as_str)
|
||||
.chain(default_all_maps)
|
||||
.map(|map| self.services.db.get(map))
|
||||
.filter_map(Result::ok)
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
let timer = Instant::now();
|
||||
let result = maps
|
||||
.iter()
|
||||
.stream()
|
||||
.map(|map| map.raw_stream_prefix(&prefix))
|
||||
.flatten()
|
||||
.ignore_err()
|
||||
.map(at!(1))
|
||||
.map(<[u8]>::len)
|
||||
.ready_fold_default(|mut map: BTreeMap<_, usize>, len| {
|
||||
let entry = map.entry(len).or_default();
|
||||
*entry = entry.saturating_add(1);
|
||||
map
|
||||
})
|
||||
.await;
|
||||
|
||||
let query_time = timer.elapsed();
|
||||
let result = format!("```\n{result:#?}\n```\n\nQuery completed in {query_time:?}");
|
||||
self.write_str(result.as_str()).await?;
|
||||
|
||||
Ok(RoomMessageEventContent::text_plain(""))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn raw_vals_total(
|
||||
&self,
|
||||
map: Option<String>,
|
||||
prefix: Option<String>,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
let prefix = prefix.as_deref().unwrap_or(EMPTY);
|
||||
|
||||
let default_all_maps = map
|
||||
.is_none()
|
||||
.then(|| self.services.db.keys().map(Deref::deref))
|
||||
.into_iter()
|
||||
.flatten();
|
||||
|
||||
let maps: Vec<_> = map
|
||||
.iter()
|
||||
.map(String::as_str)
|
||||
.chain(default_all_maps)
|
||||
.map(|map| self.services.db.get(map))
|
||||
.filter_map(Result::ok)
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
let timer = Instant::now();
|
||||
let result = maps
|
||||
.iter()
|
||||
.stream()
|
||||
.map(|map| map.raw_stream_prefix(&prefix))
|
||||
.flatten()
|
||||
.ignore_err()
|
||||
.map(at!(1))
|
||||
.map(<[u8]>::len)
|
||||
.ready_fold_default(|acc: usize, len| acc.saturating_add(len))
|
||||
.await;
|
||||
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
self.write_str(&format!("```\n{result:#?}\n\n```\n\nQuery completed in {query_time:?}"))
|
||||
.await?;
|
||||
|
||||
Ok(RoomMessageEventContent::text_plain(""))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn raw_iter(
|
||||
&self,
|
||||
map: String,
|
||||
prefix: Option<String>,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
writeln!(self, "```").await?;
|
||||
|
||||
let map = self.services.db.get(&map)?;
|
||||
let timer = Instant::now();
|
||||
prefix
|
||||
.as_deref()
|
||||
.map_or_else(|| map.raw_stream().boxed(), |prefix| map.raw_stream_prefix(prefix).boxed())
|
||||
.map_ok(apply!(2, String::from_utf8_lossy))
|
||||
.map_ok(apply!(2, Cow::into_owned))
|
||||
.try_for_each(|keyval| writeln!(self, "{keyval:?}"))
|
||||
.boxed()
|
||||
.await?;
|
||||
|
||||
let query_time = timer.elapsed();
|
||||
self.write_str(&format!("\n```\n\nQuery completed in {query_time:?}"))
|
||||
.await?;
|
||||
|
||||
Ok(RoomMessageEventContent::text_plain(""))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn raw_keys_from(
|
||||
&self,
|
||||
map: String,
|
||||
start: String,
|
||||
limit: Option<usize>,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
writeln!(self, "```").await?;
|
||||
|
||||
let map = self.services.db.get(&map)?;
|
||||
let timer = Instant::now();
|
||||
map.raw_keys_from(&start)
|
||||
.map_ok(String::from_utf8_lossy)
|
||||
.take(limit.unwrap_or(usize::MAX))
|
||||
.try_for_each(|str| writeln!(self, "{str:?}"))
|
||||
.boxed()
|
||||
.await?;
|
||||
|
||||
let query_time = timer.elapsed();
|
||||
self.write_str(&format!("\n```\n\nQuery completed in {query_time:?}"))
|
||||
.await?;
|
||||
|
||||
Ok(RoomMessageEventContent::text_plain(""))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn raw_iter_from(
|
||||
&self,
|
||||
map: String,
|
||||
start: String,
|
||||
limit: Option<usize>,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
let map = self.services.db.get(&map)?;
|
||||
let timer = Instant::now();
|
||||
let result = map
|
||||
.raw_stream_from(&start)
|
||||
.map_ok(apply!(2, String::from_utf8_lossy))
|
||||
.map_ok(apply!(2, Cow::into_owned))
|
||||
.take(limit.unwrap_or(usize::MAX))
|
||||
.try_collect::<Vec<(String, String)>>()
|
||||
.await?;
|
||||
|
||||
let query_time = timer.elapsed();
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{result:#?}\n```"
|
||||
)))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn raw_del(&self, map: String, key: String) -> Result<RoomMessageEventContent> {
|
||||
let map = self.services.db.get(&map)?;
|
||||
let timer = Instant::now();
|
||||
map.remove(&key);
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Operation completed in {query_time:?}"
|
||||
)))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn raw_get(&self, map: String, key: String) -> Result<RoomMessageEventContent> {
|
||||
let map = self.services.db.get(&map)?;
|
||||
let timer = Instant::now();
|
||||
let handle = map.get(&key).await?;
|
||||
let query_time = timer.elapsed();
|
||||
let result = String::from_utf8_lossy(&handle);
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{result:?}\n```"
|
||||
)))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn raw_maps(&self) -> Result<RoomMessageEventContent> {
|
||||
let list: Vec<_> = self.services.db.iter().map(at!(0)).copied().collect();
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!("{list:#?}")))
|
||||
}
|
||||
+42
-33
@@ -1,6 +1,7 @@
|
||||
use std::fmt::Write;
|
||||
|
||||
use clap::Subcommand;
|
||||
use conduwuit::{utils::time, Result};
|
||||
use futures::StreamExt;
|
||||
use ruma::{events::room::message::RoomMessageEventContent, OwnedServerName};
|
||||
|
||||
use crate::{admin_command, admin_command_dispatch};
|
||||
@@ -27,48 +28,56 @@ async fn destinations_cache(
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
use service::resolver::cache::CachedDest;
|
||||
|
||||
writeln!(self, "| Server Name | Destination | Hostname | Expires |").await?;
|
||||
writeln!(self, "| ----------- | ----------- | -------- | ------- |").await?;
|
||||
|
||||
let mut destinations = self.services.resolver.cache.destinations().boxed();
|
||||
|
||||
while let Some((name, CachedDest { dest, host, expire })) = destinations.next().await {
|
||||
if let Some(server_name) = server_name.as_ref() {
|
||||
if name != server_name {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let mut out = String::new();
|
||||
writeln!(out, "| Server Name | Destination | Hostname | Expires |")?;
|
||||
writeln!(out, "| ----------- | ----------- | -------- | ------- |")?;
|
||||
let row = |(name, &CachedDest { ref dest, ref host, expire })| {
|
||||
let expire = time::format(expire, "%+");
|
||||
self.write_str(&format!("| {name} | {dest} | {host} | {expire} |\n"))
|
||||
.await?;
|
||||
writeln!(out, "| {name} | {dest} | {host} | {expire} |").expect("wrote line");
|
||||
};
|
||||
|
||||
let map = self
|
||||
.services
|
||||
.resolver
|
||||
.cache
|
||||
.destinations
|
||||
.read()
|
||||
.expect("locked");
|
||||
|
||||
if let Some(server_name) = server_name.as_ref() {
|
||||
map.get_key_value(server_name).map(row);
|
||||
} else {
|
||||
map.iter().for_each(row);
|
||||
}
|
||||
|
||||
Ok(RoomMessageEventContent::notice_plain(""))
|
||||
Ok(RoomMessageEventContent::notice_markdown(out))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
async fn overrides_cache(&self, server_name: Option<String>) -> Result<RoomMessageEventContent> {
|
||||
use service::resolver::cache::CachedOverride;
|
||||
|
||||
writeln!(self, "| Server Name | IP | Port | Expires | Overriding |").await?;
|
||||
writeln!(self, "| ----------- | --- | ----:| ------- | ---------- |").await?;
|
||||
|
||||
let mut overrides = self.services.resolver.cache.overrides().boxed();
|
||||
|
||||
while let Some((name, CachedOverride { ips, port, expire, overriding })) =
|
||||
overrides.next().await
|
||||
{
|
||||
if let Some(server_name) = server_name.as_ref() {
|
||||
if name != server_name {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let mut out = String::new();
|
||||
writeln!(out, "| Server Name | IP | Port | Expires |")?;
|
||||
writeln!(out, "| ----------- | --- | ----:| ------- |")?;
|
||||
let row = |(name, &CachedOverride { ref ips, port, expire })| {
|
||||
let expire = time::format(expire, "%+");
|
||||
self.write_str(&format!("| {name} | {ips:?} | {port} | {expire} | {overriding:?} |\n"))
|
||||
.await?;
|
||||
writeln!(out, "| {name} | {ips:?} | {port} | {expire} |").expect("wrote line");
|
||||
};
|
||||
|
||||
let map = self
|
||||
.services
|
||||
.resolver
|
||||
.cache
|
||||
.overrides
|
||||
.read()
|
||||
.expect("locked");
|
||||
|
||||
if let Some(server_name) = server_name.as_ref() {
|
||||
map.get_key_value(server_name).map(row);
|
||||
} else {
|
||||
map.iter().for_each(row);
|
||||
}
|
||||
|
||||
Ok(RoomMessageEventContent::notice_plain(""))
|
||||
Ok(RoomMessageEventContent::notice_markdown(out))
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use clap::Subcommand;
|
||||
use conduwuit::Result;
|
||||
use futures::StreamExt;
|
||||
use ruma::{RoomAliasId, RoomId};
|
||||
use ruma::{events::room::message::RoomMessageEventContent, RoomAliasId, RoomId};
|
||||
|
||||
use crate::Command;
|
||||
|
||||
@@ -24,7 +24,10 @@ pub(crate) enum RoomAliasCommand {
|
||||
}
|
||||
|
||||
/// All the getters and iterators in src/database/key_value/rooms/alias.rs
|
||||
pub(super) async fn process(subcommand: RoomAliasCommand, context: &Command<'_>) -> Result {
|
||||
pub(super) async fn process(
|
||||
subcommand: RoomAliasCommand,
|
||||
context: &Command<'_>,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
let services = context.services;
|
||||
|
||||
match subcommand {
|
||||
@@ -33,7 +36,9 @@ pub(super) async fn process(subcommand: RoomAliasCommand, context: &Command<'_>)
|
||||
let results = services.rooms.alias.resolve_local_alias(&alias).await;
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
write!(context, "Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```")
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
| RoomAliasCommand::LocalAliasesForRoom { room_id } => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
@@ -46,7 +51,9 @@ pub(super) async fn process(subcommand: RoomAliasCommand, context: &Command<'_>)
|
||||
.await;
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
write!(context, "Query completed in {query_time:?}:\n\n```rs\n{aliases:#?}\n```")
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{aliases:#?}\n```"
|
||||
)))
|
||||
},
|
||||
| RoomAliasCommand::AllLocalAliases => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
@@ -59,8 +66,9 @@ pub(super) async fn process(subcommand: RoomAliasCommand, context: &Command<'_>)
|
||||
.await;
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
write!(context, "Query completed in {query_time:?}:\n\n```rs\n{aliases:#?}\n```")
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{aliases:#?}\n```"
|
||||
)))
|
||||
},
|
||||
}
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use clap::Subcommand;
|
||||
use conduwuit::{Error, Result};
|
||||
use conduwuit::Result;
|
||||
use futures::StreamExt;
|
||||
use ruma::{events::room::message::RoomMessageEventContent, RoomId, ServerName, UserId};
|
||||
|
||||
@@ -76,10 +76,13 @@ pub(crate) enum RoomStateCacheCommand {
|
||||
},
|
||||
}
|
||||
|
||||
pub(super) async fn process(subcommand: RoomStateCacheCommand, context: &Command<'_>) -> Result {
|
||||
pub(super) async fn process(
|
||||
subcommand: RoomStateCacheCommand,
|
||||
context: &Command<'_>,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
let services = context.services;
|
||||
|
||||
let c = match subcommand {
|
||||
match subcommand {
|
||||
| RoomStateCacheCommand::ServerInRoom { server, room_id } => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let result = services
|
||||
@@ -89,7 +92,7 @@ pub(super) async fn process(subcommand: RoomStateCacheCommand, context: &Command
|
||||
.await;
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Result::<_, Error>::Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{result:#?}\n```"
|
||||
)))
|
||||
},
|
||||
@@ -104,7 +107,7 @@ pub(super) async fn process(subcommand: RoomStateCacheCommand, context: &Command
|
||||
.await;
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Result::<_, Error>::Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
@@ -119,7 +122,7 @@ pub(super) async fn process(subcommand: RoomStateCacheCommand, context: &Command
|
||||
.await;
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Result::<_, Error>::Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
@@ -134,7 +137,7 @@ pub(super) async fn process(subcommand: RoomStateCacheCommand, context: &Command
|
||||
.await;
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Result::<_, Error>::Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
@@ -149,7 +152,7 @@ pub(super) async fn process(subcommand: RoomStateCacheCommand, context: &Command
|
||||
.await;
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Result::<_, Error>::Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
@@ -164,7 +167,7 @@ pub(super) async fn process(subcommand: RoomStateCacheCommand, context: &Command
|
||||
.await;
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Result::<_, Error>::Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
@@ -173,7 +176,7 @@ pub(super) async fn process(subcommand: RoomStateCacheCommand, context: &Command
|
||||
let results = services.rooms.state_cache.room_joined_count(&room_id).await;
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Result::<_, Error>::Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
@@ -186,7 +189,7 @@ pub(super) async fn process(subcommand: RoomStateCacheCommand, context: &Command
|
||||
.await;
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Result::<_, Error>::Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
@@ -201,7 +204,7 @@ pub(super) async fn process(subcommand: RoomStateCacheCommand, context: &Command
|
||||
.await;
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Result::<_, Error>::Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
@@ -216,7 +219,7 @@ pub(super) async fn process(subcommand: RoomStateCacheCommand, context: &Command
|
||||
.await;
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Result::<_, Error>::Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
@@ -229,7 +232,7 @@ pub(super) async fn process(subcommand: RoomStateCacheCommand, context: &Command
|
||||
.await;
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Result::<_, Error>::Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
@@ -242,7 +245,7 @@ pub(super) async fn process(subcommand: RoomStateCacheCommand, context: &Command
|
||||
.await;
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Result::<_, Error>::Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
@@ -257,7 +260,7 @@ pub(super) async fn process(subcommand: RoomStateCacheCommand, context: &Command
|
||||
.await;
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Result::<_, Error>::Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
@@ -271,7 +274,7 @@ pub(super) async fn process(subcommand: RoomStateCacheCommand, context: &Command
|
||||
.await;
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Result::<_, Error>::Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
@@ -285,7 +288,7 @@ pub(super) async fn process(subcommand: RoomStateCacheCommand, context: &Command
|
||||
.await;
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Result::<_, Error>::Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
@@ -298,13 +301,9 @@ pub(super) async fn process(subcommand: RoomStateCacheCommand, context: &Command
|
||||
.await;
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Result::<_, Error>::Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
}?;
|
||||
|
||||
context.write_str(c.body()).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,14 +62,7 @@ pub(crate) enum SendingCommand {
|
||||
}
|
||||
|
||||
/// All the getters and iterators in key_value/sending.rs
|
||||
pub(super) async fn process(subcommand: SendingCommand, context: &Command<'_>) -> Result {
|
||||
let c = reprocess(subcommand, context).await?;
|
||||
context.write_str(c.body()).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// All the getters and iterators in key_value/sending.rs
|
||||
pub(super) async fn reprocess(
|
||||
pub(super) async fn process(
|
||||
subcommand: SendingCommand,
|
||||
context: &Command<'_>,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
use clap::Subcommand;
|
||||
use conduwuit::Result;
|
||||
use ruma::{events::room::message::RoomMessageEventContent, OwnedEventId, OwnedRoomOrAliasId};
|
||||
|
||||
use crate::{admin_command, admin_command_dispatch};
|
||||
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
/// Query tables from database
|
||||
pub(crate) enum ShortCommand {
|
||||
ShortEventId {
|
||||
event_id: OwnedEventId,
|
||||
},
|
||||
|
||||
ShortRoomId {
|
||||
room_id: OwnedRoomOrAliasId,
|
||||
},
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn short_event_id(
|
||||
&self,
|
||||
event_id: OwnedEventId,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
let shortid = self
|
||||
.services
|
||||
.rooms
|
||||
.short
|
||||
.get_shorteventid(&event_id)
|
||||
.await?;
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!("{shortid:#?}")))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn short_room_id(
|
||||
&self,
|
||||
room_id: OwnedRoomOrAliasId,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
let room_id = self.services.rooms.alias.resolve(&room_id).await?;
|
||||
|
||||
let shortid = self.services.rooms.short.get_shortroomid(&room_id).await?;
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!("{shortid:#?}")))
|
||||
}
|
||||
@@ -15,8 +15,6 @@ pub(crate) enum UsersCommand {
|
||||
|
||||
IterUsers,
|
||||
|
||||
IterUsers2,
|
||||
|
||||
PasswordHash {
|
||||
user_id: OwnedUserId,
|
||||
},
|
||||
@@ -91,33 +89,6 @@ pub(crate) enum UsersCommand {
|
||||
room_id: OwnedRoomId,
|
||||
session_id: String,
|
||||
},
|
||||
|
||||
GetSharedRooms {
|
||||
user_a: OwnedUserId,
|
||||
user_b: OwnedUserId,
|
||||
},
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
async fn get_shared_rooms(
|
||||
&self,
|
||||
user_a: OwnedUserId,
|
||||
user_b: OwnedUserId,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let result: Vec<_> = self
|
||||
.services
|
||||
.rooms
|
||||
.state_cache
|
||||
.get_shared_rooms(&user_a, &user_b)
|
||||
.map(ToOwned::to_owned)
|
||||
.collect()
|
||||
.await;
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{result:#?}\n```"
|
||||
)))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
@@ -236,23 +207,6 @@ async fn iter_users(&self) -> Result<RoomMessageEventContent> {
|
||||
)))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
async fn iter_users2(&self) -> Result<RoomMessageEventContent> {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let result: Vec<_> = self.services.users.stream().collect().await;
|
||||
let result: Vec<_> = result
|
||||
.into_iter()
|
||||
.map(ruma::UserId::as_bytes)
|
||||
.map(String::from_utf8_lossy)
|
||||
.collect();
|
||||
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{result:?}\n```"
|
||||
)))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
async fn count_users(&self) -> Result<RoomMessageEventContent> {
|
||||
let timer = tokio::time::Instant::now();
|
||||
|
||||
@@ -44,14 +44,7 @@ pub(crate) enum RoomAliasCommand {
|
||||
},
|
||||
}
|
||||
|
||||
pub(super) async fn process(command: RoomAliasCommand, context: &Command<'_>) -> Result {
|
||||
let c = reprocess(command, context).await?;
|
||||
context.write_str(c.body()).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) async fn reprocess(
|
||||
pub(super) async fn process(
|
||||
command: RoomAliasCommand,
|
||||
context: &Command<'_>,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
|
||||
@@ -25,13 +25,7 @@ pub(crate) enum RoomDirectoryCommand {
|
||||
},
|
||||
}
|
||||
|
||||
pub(super) async fn process(command: RoomDirectoryCommand, context: &Command<'_>) -> Result {
|
||||
let c = reprocess(command, context).await?;
|
||||
context.write_str(c.body()).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) async fn reprocess(
|
||||
pub(super) async fn process(
|
||||
command: RoomDirectoryCommand,
|
||||
context: &Command<'_>,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::{fmt::Write, path::PathBuf, sync::Arc};
|
||||
use std::{fmt::Write, sync::Arc};
|
||||
|
||||
use conduwuit::{info, utils::time, warn, Config, Err, Result};
|
||||
use conduwuit::{info, utils::time, warn, Err, Result};
|
||||
use ruma::events::room::message::RoomMessageEventContent;
|
||||
|
||||
use crate::admin_command;
|
||||
@@ -22,27 +22,11 @@ pub(super) async fn uptime(&self) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn show_config(&self) -> Result<RoomMessageEventContent> {
|
||||
// Construct and send the response
|
||||
Ok(RoomMessageEventContent::text_markdown(format!(
|
||||
"{}",
|
||||
*self.services.server.config
|
||||
"```\n{}\n```",
|
||||
self.services.globals.config
|
||||
)))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn reload_config(
|
||||
&self,
|
||||
path: Option<PathBuf>,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
let path = path.as_deref().into_iter();
|
||||
let config = Config::load(path).and_then(|raw| Config::new(&raw))?;
|
||||
if config.server_name != self.services.server.config.server_name {
|
||||
return Err!("You can't change the server name.");
|
||||
}
|
||||
|
||||
let _old = self.services.server.config.update(config)?;
|
||||
|
||||
Ok(RoomMessageEventContent::text_plain("Successfully reconfigured."))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn list_features(
|
||||
&self,
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
mod commands;
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use clap::Subcommand;
|
||||
use conduwuit::Result;
|
||||
|
||||
@@ -16,11 +14,6 @@ pub(super) enum ServerCommand {
|
||||
/// - Show configuration values
|
||||
ShowConfig,
|
||||
|
||||
/// - Reload configuration values
|
||||
ReloadConfig {
|
||||
path: Option<PathBuf>,
|
||||
},
|
||||
|
||||
/// - List the features built into the server
|
||||
ListFeatures {
|
||||
#[arg(short, long)]
|
||||
|
||||
+28
-31
@@ -31,21 +31,19 @@ const BULK_JOIN_REASON: &str = "Bulk force joining this room as initiated by the
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn list_users(&self) -> Result<RoomMessageEventContent> {
|
||||
let users: Vec<_> = self
|
||||
let users = self
|
||||
.services
|
||||
.users
|
||||
.list_local_users()
|
||||
.map(ToString::to_string)
|
||||
.collect()
|
||||
.collect::<Vec<_>>()
|
||||
.await;
|
||||
|
||||
let mut plain_msg = format!("Found {} local user account(s):\n```\n", users.len());
|
||||
plain_msg += users.join("\n").as_str();
|
||||
plain_msg += "\n```";
|
||||
|
||||
self.write_str(plain_msg.as_str()).await?;
|
||||
|
||||
Ok(RoomMessageEventContent::text_plain(""))
|
||||
Ok(RoomMessageEventContent::notice_markdown(plain_msg))
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
@@ -83,12 +81,12 @@ pub(super) async fn create_user(
|
||||
// content is set to the user's display name with a space before it
|
||||
if !self
|
||||
.services
|
||||
.server
|
||||
.globals
|
||||
.config
|
||||
.new_user_displayname_suffix
|
||||
.is_empty()
|
||||
{
|
||||
write!(displayname, " {}", self.services.server.config.new_user_displayname_suffix)
|
||||
write!(displayname, " {}", self.services.globals.config.new_user_displayname_suffix)
|
||||
.expect("should be able to write to string buffer");
|
||||
}
|
||||
|
||||
@@ -114,8 +112,8 @@ pub(super) async fn create_user(
|
||||
)
|
||||
.await?;
|
||||
|
||||
if !self.services.server.config.auto_join_rooms.is_empty() {
|
||||
for room in &self.services.server.config.auto_join_rooms {
|
||||
if !self.services.globals.config.auto_join_rooms.is_empty() {
|
||||
for room in &self.services.globals.config.auto_join_rooms {
|
||||
let Ok(room_id) = self.services.rooms.alias.resolve(room).await else {
|
||||
error!(%user_id, "Failed to resolve room alias to room ID when attempting to auto join {room}, skipping");
|
||||
continue;
|
||||
@@ -914,30 +912,29 @@ pub(super) async fn redact_event(
|
||||
self.services.globals.server_name()
|
||||
);
|
||||
|
||||
let redaction_event_id = {
|
||||
let state_lock = self.services.rooms.state.mutex.lock(&room_id).await;
|
||||
let state_lock = self.services.rooms.state.mutex.lock(&room_id).await;
|
||||
|
||||
self.services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder {
|
||||
let redaction_event_id = self
|
||||
.services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder {
|
||||
redacts: Some(event.event_id.clone()),
|
||||
..PduBuilder::timeline(&RoomRedactionEventContent {
|
||||
redacts: Some(event.event_id.clone()),
|
||||
..PduBuilder::timeline(&RoomRedactionEventContent {
|
||||
redacts: Some(event.event_id.clone()),
|
||||
reason: Some(reason),
|
||||
})
|
||||
},
|
||||
&sender_user,
|
||||
&room_id,
|
||||
&state_lock,
|
||||
)
|
||||
.await?
|
||||
};
|
||||
reason: Some(reason),
|
||||
})
|
||||
},
|
||||
&sender_user,
|
||||
&room_id,
|
||||
&state_lock,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let out = format!("Successfully redacted event. Redaction event ID: {redaction_event_id}");
|
||||
drop(state_lock);
|
||||
|
||||
self.write_str(out.as_str()).await?;
|
||||
|
||||
Ok(RoomMessageEventContent::text_plain(""))
|
||||
Ok(RoomMessageEventContent::text_plain(format!(
|
||||
"Successfully redacted event. Redaction event ID: {redaction_event_id}"
|
||||
)))
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ http.workspace = true
|
||||
http-body-util.workspace = true
|
||||
hyper.workspace = true
|
||||
ipaddress.workspace = true
|
||||
jsonwebtoken.workspace = true
|
||||
log.workspace = true
|
||||
rand.workspace = true
|
||||
reqwest.workspace = true
|
||||
|
||||
@@ -299,7 +299,7 @@ pub(crate) async fn register_route(
|
||||
if !services.globals.new_user_displayname_suffix().is_empty()
|
||||
&& body.appservice_info.is_none()
|
||||
{
|
||||
write!(displayname, " {}", services.server.config.new_user_displayname_suffix)
|
||||
write!(displayname, " {}", services.globals.config.new_user_displayname_suffix)
|
||||
.expect("should be able to write to string buffer");
|
||||
}
|
||||
|
||||
@@ -365,7 +365,7 @@ pub(crate) async fn register_route(
|
||||
\"{device_display_name}\""
|
||||
);
|
||||
|
||||
if services.server.config.admin_room_notices {
|
||||
if services.globals.config.admin_room_notices {
|
||||
services
|
||||
.admin
|
||||
.send_message(RoomMessageEventContent::notice_plain(format!(
|
||||
@@ -378,7 +378,7 @@ pub(crate) async fn register_route(
|
||||
} else {
|
||||
info!("New user \"{user_id}\" registered on this server.");
|
||||
|
||||
if services.server.config.admin_room_notices {
|
||||
if services.globals.config.admin_room_notices {
|
||||
services
|
||||
.admin
|
||||
.send_message(RoomMessageEventContent::notice_plain(format!(
|
||||
@@ -395,7 +395,7 @@ pub(crate) async fn register_route(
|
||||
info!("New guest user \"{user_id}\" registered on this server.");
|
||||
|
||||
if !device_display_name.is_empty() {
|
||||
if services.server.config.admin_room_notices {
|
||||
if services.globals.config.admin_room_notices {
|
||||
services
|
||||
.admin
|
||||
.send_message(RoomMessageEventContent::notice_plain(format!(
|
||||
@@ -407,7 +407,7 @@ pub(crate) async fn register_route(
|
||||
}
|
||||
} else {
|
||||
#[allow(clippy::collapsible_else_if)]
|
||||
if services.server.config.admin_room_notices {
|
||||
if services.globals.config.admin_room_notices {
|
||||
services
|
||||
.admin
|
||||
.send_message(RoomMessageEventContent::notice_plain(format!(
|
||||
@@ -438,10 +438,10 @@ pub(crate) async fn register_route(
|
||||
}
|
||||
|
||||
if body.appservice_info.is_none()
|
||||
&& !services.server.config.auto_join_rooms.is_empty()
|
||||
&& !services.globals.config.auto_join_rooms.is_empty()
|
||||
&& (services.globals.allow_guests_auto_join_rooms() || !is_guest)
|
||||
{
|
||||
for room in &services.server.config.auto_join_rooms {
|
||||
for room in &services.globals.config.auto_join_rooms {
|
||||
let Ok(room_id) = services.rooms.alias.resolve(room).await else {
|
||||
error!(
|
||||
"Failed to resolve room alias to room ID when attempting to auto join \
|
||||
@@ -570,7 +570,7 @@ pub(crate) async fn change_password_route(
|
||||
|
||||
info!("User {sender_user} changed their password.");
|
||||
|
||||
if services.server.config.admin_room_notices {
|
||||
if services.globals.config.admin_room_notices {
|
||||
services
|
||||
.admin
|
||||
.send_message(RoomMessageEventContent::notice_plain(format!(
|
||||
@@ -673,7 +673,7 @@ pub(crate) async fn deactivate_route(
|
||||
|
||||
info!("User {sender_user} deactivated their account.");
|
||||
|
||||
if services.server.config.admin_room_notices {
|
||||
if services.globals.config.admin_room_notices {
|
||||
services
|
||||
.admin
|
||||
.send_message(RoomMessageEventContent::notice_plain(format!(
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use axum::extract::State;
|
||||
use conduwuit::{Result, Server};
|
||||
use ruma::{
|
||||
api::client::discovery::get_capabilities::{
|
||||
self, Capabilities, GetLoginTokenCapability, RoomVersionStability,
|
||||
@@ -11,7 +10,7 @@ use ruma::{
|
||||
};
|
||||
use serde_json::json;
|
||||
|
||||
use crate::Ruma;
|
||||
use crate::{Result, Ruma};
|
||||
|
||||
/// # `GET /_matrix/client/v3/capabilities`
|
||||
///
|
||||
@@ -22,7 +21,7 @@ pub(crate) async fn get_capabilities_route(
|
||||
_body: Ruma<get_capabilities::v3::Request>,
|
||||
) -> Result<get_capabilities::v3::Response> {
|
||||
let available: BTreeMap<RoomVersionId, RoomVersionStability> =
|
||||
Server::available_room_versions().collect();
|
||||
services.server.available_room_versions().collect();
|
||||
|
||||
let mut capabilities = Capabilities::default();
|
||||
capabilities.room_versions = RoomVersionsCapability {
|
||||
@@ -33,9 +32,8 @@ pub(crate) async fn get_capabilities_route(
|
||||
// we do not implement 3PID stuff
|
||||
capabilities.thirdparty_id_changes = ThirdPartyIdChangesCapability { enabled: false };
|
||||
|
||||
capabilities.get_login_token = GetLoginTokenCapability {
|
||||
enabled: services.server.config.login_via_existing_session,
|
||||
};
|
||||
// we dont support generating tokens yet
|
||||
capabilities.get_login_token = GetLoginTokenCapability { enabled: false };
|
||||
|
||||
// MSC4133 capability
|
||||
capabilities
|
||||
|
||||
+56
-55
@@ -1,6 +1,6 @@
|
||||
use axum::extract::State;
|
||||
use conduwuit::{
|
||||
at, deref_at, err, ref_at,
|
||||
at, err, ref_at,
|
||||
utils::{
|
||||
future::TryExtExt,
|
||||
stream::{BroadbandExt, ReadyExt, TryIgnore, WidebandExt},
|
||||
@@ -8,15 +8,15 @@ use conduwuit::{
|
||||
},
|
||||
Err, PduEvent, Result,
|
||||
};
|
||||
use futures::{
|
||||
future::{join, join3, try_join3, OptionFuture},
|
||||
FutureExt, StreamExt, TryFutureExt,
|
||||
use futures::{join, try_join, FutureExt, StreamExt, TryFutureExt};
|
||||
use ruma::{
|
||||
api::client::{context::get_context, filter::LazyLoadOptions},
|
||||
events::StateEventType,
|
||||
OwnedEventId, UserId,
|
||||
};
|
||||
use ruma::{api::client::context::get_context, events::StateEventType, OwnedEventId, UserId};
|
||||
use service::rooms::{lazy_loading, lazy_loading::Options};
|
||||
|
||||
use crate::{
|
||||
client::message::{event_filter, ignored_filter, lazy_loading_witness, visibility_filter},
|
||||
client::message::{event_filter, ignored_filter, update_lazy, visibility_filter, LazySet},
|
||||
Ruma,
|
||||
};
|
||||
|
||||
@@ -33,10 +33,10 @@ pub(crate) async fn get_context_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<get_context::v3::Request>,
|
||||
) -> Result<get_context::v3::Response> {
|
||||
let sender = body.sender();
|
||||
let (sender_user, sender_device) = sender;
|
||||
let room_id = &body.room_id;
|
||||
let filter = &body.filter;
|
||||
let sender = body.sender();
|
||||
let (sender_user, _) = sender;
|
||||
let room_id = &body.room_id;
|
||||
|
||||
// Use limit or else 10, with maximum 100
|
||||
let limit: usize = body
|
||||
@@ -45,6 +45,18 @@ pub(crate) async fn get_context_route(
|
||||
.unwrap_or(LIMIT_DEFAULT)
|
||||
.min(LIMIT_MAX);
|
||||
|
||||
// some clients, at least element, seem to require knowledge of redundant
|
||||
// members for "inline" profiles on the timeline to work properly
|
||||
let lazy_load_enabled = matches!(filter.lazy_load_options, LazyLoadOptions::Enabled { .. });
|
||||
|
||||
let lazy_load_redundant = if let LazyLoadOptions::Enabled { include_redundant_members } =
|
||||
filter.lazy_load_options
|
||||
{
|
||||
include_redundant_members
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let base_id = services
|
||||
.rooms
|
||||
.timeline
|
||||
@@ -63,7 +75,7 @@ pub(crate) async fn get_context_route(
|
||||
.user_can_see_event(sender_user, &body.room_id, &body.event_id)
|
||||
.map(Ok);
|
||||
|
||||
let (base_id, base_pdu, visible) = try_join3(base_id, base_pdu, visible).await?;
|
||||
let (base_id, base_pdu, visible) = try_join!(base_id, base_pdu, visible)?;
|
||||
|
||||
if base_pdu.room_id != body.room_id || base_pdu.event_id != body.event_id {
|
||||
return Err!(Request(NotFound("Base event not found.")));
|
||||
@@ -100,32 +112,12 @@ pub(crate) async fn get_context_route(
|
||||
.collect();
|
||||
|
||||
let (base_event, events_before, events_after): (_, Vec<_>, Vec<_>) =
|
||||
join3(base_event, events_before, events_after).await;
|
||||
|
||||
let lazy_loading_context = lazy_loading::Context {
|
||||
user_id: sender_user,
|
||||
device_id: sender_device,
|
||||
room_id,
|
||||
token: Some(base_count.into_unsigned()),
|
||||
options: Some(&filter.lazy_load_options),
|
||||
};
|
||||
|
||||
let lazy_loading_witnessed: OptionFuture<_> = filter
|
||||
.lazy_load_options
|
||||
.is_enabled()
|
||||
.then_some(
|
||||
base_event
|
||||
.iter()
|
||||
.chain(events_before.iter())
|
||||
.chain(events_after.iter()),
|
||||
)
|
||||
.map(|witnessed| lazy_loading_witness(&services, &lazy_loading_context, witnessed))
|
||||
.into();
|
||||
join!(base_event, events_before, events_after);
|
||||
|
||||
let state_at = events_after
|
||||
.last()
|
||||
.map(ref_at!(1))
|
||||
.map_or(body.event_id.as_ref(), |pdu| pdu.event_id.as_ref());
|
||||
.map_or(body.event_id.as_ref(), |e| e.event_id.as_ref());
|
||||
|
||||
let state_ids = services
|
||||
.rooms
|
||||
@@ -134,32 +126,41 @@ pub(crate) async fn get_context_route(
|
||||
.or_else(|_| services.rooms.state.get_room_shortstatehash(room_id))
|
||||
.and_then(|shortstatehash| services.rooms.state_accessor.state_full_ids(shortstatehash))
|
||||
.map_err(|e| err!(Database("State not found: {e}")))
|
||||
.boxed();
|
||||
.await?;
|
||||
|
||||
let (lazy_loading_witnessed, state_ids) = join(lazy_loading_witnessed, state_ids).await;
|
||||
let lazy = base_event
|
||||
.iter()
|
||||
.chain(events_before.iter())
|
||||
.chain(events_after.iter())
|
||||
.stream()
|
||||
.fold(LazySet::new(), |lazy, item| {
|
||||
update_lazy(&services, room_id, sender, lazy, item, lazy_load_redundant)
|
||||
})
|
||||
.await;
|
||||
|
||||
let state_ids = state_ids?;
|
||||
let lazy_loading_witnessed = lazy_loading_witnessed.unwrap_or_default();
|
||||
let shortstatekeys = state_ids.iter().stream().map(deref_at!(0));
|
||||
|
||||
let state: Vec<_> = services
|
||||
.rooms
|
||||
.short
|
||||
.multi_get_statekey_from_short(shortstatekeys)
|
||||
.zip(state_ids.iter().stream().map(at!(1)))
|
||||
.ready_filter_map(|item| Some((item.0.ok()?, item.1)))
|
||||
.ready_filter_map(|((event_type, state_key), event_id)| {
|
||||
if filter.lazy_load_options.is_enabled()
|
||||
&& event_type == StateEventType::RoomMember
|
||||
&& state_key
|
||||
.as_str()
|
||||
.try_into()
|
||||
.is_ok_and(|user_id: &UserId| !lazy_loading_witnessed.contains(user_id))
|
||||
{
|
||||
return None;
|
||||
let lazy = &lazy;
|
||||
let state: Vec<_> = state_ids
|
||||
.iter()
|
||||
.stream()
|
||||
.broad_filter_map(|(shortstatekey, event_id)| {
|
||||
services
|
||||
.rooms
|
||||
.short
|
||||
.get_statekey_from_short(*shortstatekey)
|
||||
.map_ok(move |(event_type, state_key)| (event_type, state_key, event_id))
|
||||
.ok()
|
||||
})
|
||||
.ready_filter_map(|(event_type, state_key, event_id)| {
|
||||
if !lazy_load_enabled || event_type != StateEventType::RoomMember {
|
||||
return Some(event_id);
|
||||
}
|
||||
|
||||
Some(event_id)
|
||||
state_key
|
||||
.as_str()
|
||||
.try_into()
|
||||
.ok()
|
||||
.filter(|&user_id: &&UserId| lazy.contains(user_id))
|
||||
.map(|_| event_id)
|
||||
})
|
||||
.broad_filter_map(|event_id: &OwnedEventId| {
|
||||
services.rooms.timeline.get_pdu(event_id).ok()
|
||||
|
||||
@@ -152,7 +152,7 @@ pub(crate) async fn set_room_visibility_route(
|
||||
|
||||
match &body.visibility {
|
||||
| room::Visibility::Public => {
|
||||
if services.server.config.lockdown_public_room_directory
|
||||
if services.globals.config.lockdown_public_room_directory
|
||||
&& !services.users.is_admin(sender_user).await
|
||||
&& body.appservice_info.is_none()
|
||||
{
|
||||
@@ -162,7 +162,7 @@ pub(crate) async fn set_room_visibility_route(
|
||||
body.room_id
|
||||
);
|
||||
|
||||
if services.server.config.admin_room_notices {
|
||||
if services.globals.config.admin_room_notices {
|
||||
services
|
||||
.admin
|
||||
.send_text(&format!(
|
||||
@@ -181,7 +181,7 @@ pub(crate) async fn set_room_visibility_route(
|
||||
|
||||
services.rooms.directory.set_public(&body.room_id);
|
||||
|
||||
if services.server.config.admin_room_notices {
|
||||
if services.globals.config.admin_room_notices {
|
||||
services
|
||||
.admin
|
||||
.send_text(&format!(
|
||||
|
||||
@@ -31,7 +31,7 @@ pub(crate) async fn get_media_config_route(
|
||||
_body: Ruma<get_media_config::v1::Request>,
|
||||
) -> Result<get_media_config::v1::Response> {
|
||||
Ok(get_media_config::v1::Response {
|
||||
upload_size: ruma_from_usize(services.server.config.max_request_size),
|
||||
upload_size: ruma_from_usize(services.globals.config.max_request_size),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ pub(crate) async fn get_media_config_legacy_route(
|
||||
_body: Ruma<get_media_config::v3::Request>,
|
||||
) -> Result<get_media_config::v3::Response> {
|
||||
Ok(get_media_config::v3::Response {
|
||||
upload_size: ruma_from_usize(services.server.config.max_request_size),
|
||||
upload_size: ruma_from_usize(services.globals.config.max_request_size),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ pub(crate) async fn get_media_config_legacy_legacy_route(
|
||||
/// # `GET /_matrix/media/v3/preview_url`
|
||||
///
|
||||
/// Returns URL preview.
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "url_preview_legacy", level = "debug")]
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "url_preview_legacy")]
|
||||
pub(crate) async fn get_media_preview_legacy_route(
|
||||
State(services): State<crate::State>,
|
||||
InsecureClientIp(client): InsecureClientIp,
|
||||
@@ -131,7 +131,7 @@ pub(crate) async fn create_content_legacy_route(
|
||||
/// - Only redirects if `allow_redirect` is true
|
||||
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
||||
/// seconds
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "media_get_legacy", level = "debug")]
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "media_get_legacy")]
|
||||
pub(crate) async fn get_content_legacy_route(
|
||||
State(services): State<crate::State>,
|
||||
InsecureClientIp(client): InsecureClientIp,
|
||||
@@ -197,7 +197,7 @@ pub(crate) async fn get_content_legacy_route(
|
||||
/// - Only redirects if `allow_redirect` is true
|
||||
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
||||
/// seconds
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "media_get_legacy", level = "debug")]
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "media_get_legacy")]
|
||||
pub(crate) async fn get_content_legacy_legacy_route(
|
||||
State(services): State<crate::State>,
|
||||
InsecureClientIp(client): InsecureClientIp,
|
||||
@@ -216,7 +216,7 @@ pub(crate) async fn get_content_legacy_legacy_route(
|
||||
/// - Only redirects if `allow_redirect` is true
|
||||
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
||||
/// seconds
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "media_get_legacy", level = "debug")]
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "media_get_legacy")]
|
||||
pub(crate) async fn get_content_as_filename_legacy_route(
|
||||
State(services): State<crate::State>,
|
||||
InsecureClientIp(client): InsecureClientIp,
|
||||
@@ -303,7 +303,7 @@ pub(crate) async fn get_content_as_filename_legacy_legacy_route(
|
||||
/// - Only redirects if `allow_redirect` is true
|
||||
/// - Uses client-provided `timeout_ms` if available, else defaults to 20
|
||||
/// seconds
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "media_thumbnail_get_legacy", level = "debug")]
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "media_thumbnail_get_legacy")]
|
||||
pub(crate) async fn get_content_thumbnail_legacy_route(
|
||||
State(services): State<crate::State>,
|
||||
InsecureClientIp(client): InsecureClientIp,
|
||||
|
||||
+35
-705
@@ -1,5 +1,4 @@
|
||||
use std::{
|
||||
borrow::Borrow,
|
||||
collections::{BTreeMap, HashMap, HashSet},
|
||||
net::IpAddr,
|
||||
sync::Arc,
|
||||
@@ -9,7 +8,7 @@ use axum::extract::State;
|
||||
use axum_client_ip::InsecureClientIp;
|
||||
use conduwuit::{
|
||||
debug, debug_info, debug_warn, err, info,
|
||||
pdu::{gen_event_id_canonical_json, PduBuilder},
|
||||
pdu::{self, gen_event_id_canonical_json, PduBuilder},
|
||||
result::FlatOk,
|
||||
trace,
|
||||
utils::{self, shuffle, IterStream, ReadyExt},
|
||||
@@ -20,7 +19,6 @@ use ruma::{
|
||||
api::{
|
||||
client::{
|
||||
error::ErrorKind,
|
||||
knock::knock_room,
|
||||
membership::{
|
||||
ban_user, forget_room, get_member_events, invite_user, join_room_by_id,
|
||||
join_room_by_id_or_alias,
|
||||
@@ -39,12 +37,11 @@ use ruma::{
|
||||
},
|
||||
StateEventType,
|
||||
},
|
||||
state_res, CanonicalJsonObject, CanonicalJsonValue, OwnedEventId, OwnedRoomId,
|
||||
OwnedServerName, OwnedUserId, RoomId, RoomVersionId, ServerName, UserId,
|
||||
state_res, CanonicalJsonObject, CanonicalJsonValue, OwnedRoomId, OwnedServerName,
|
||||
OwnedUserId, RoomId, RoomVersionId, ServerName, UserId,
|
||||
};
|
||||
use service::{
|
||||
appservice::RegistrationInfo,
|
||||
pdu::gen_event_id,
|
||||
rooms::{state::RoomMutexGuard, state_compressor::HashSetCompressStateEvent},
|
||||
Services,
|
||||
};
|
||||
@@ -71,7 +68,7 @@ async fn banned_room_check(
|
||||
if let Some(room_id) = room_id {
|
||||
if services.rooms.metadata.is_banned(room_id).await
|
||||
|| services
|
||||
.server
|
||||
.globals
|
||||
.config
|
||||
.forbidden_remote_server_names
|
||||
.contains(&room_id.server_name().unwrap().to_owned())
|
||||
@@ -81,12 +78,12 @@ async fn banned_room_check(
|
||||
attempted to join a banned room or banned room server name: {room_id}"
|
||||
);
|
||||
|
||||
if services.server.config.auto_deactivate_banned_room_attempts {
|
||||
if services.globals.config.auto_deactivate_banned_room_attempts {
|
||||
warn!(
|
||||
"Automatically deactivating user {user_id} due to attempted banned room join"
|
||||
);
|
||||
|
||||
if services.server.config.admin_room_notices {
|
||||
if services.globals.config.admin_room_notices {
|
||||
services
|
||||
.admin
|
||||
.send_message(RoomMessageEventContent::text_plain(format!(
|
||||
@@ -112,7 +109,7 @@ async fn banned_room_check(
|
||||
}
|
||||
} else if let Some(server_name) = server_name {
|
||||
if services
|
||||
.server
|
||||
.globals
|
||||
.config
|
||||
.forbidden_remote_server_names
|
||||
.contains(&server_name.to_owned())
|
||||
@@ -122,12 +119,12 @@ async fn banned_room_check(
|
||||
name {server_name} that is globally forbidden. Rejecting.",
|
||||
);
|
||||
|
||||
if services.server.config.auto_deactivate_banned_room_attempts {
|
||||
if services.globals.config.auto_deactivate_banned_room_attempts {
|
||||
warn!(
|
||||
"Automatically deactivating user {user_id} due to attempted banned room join"
|
||||
);
|
||||
|
||||
if services.server.config.admin_room_notices {
|
||||
if services.globals.config.admin_room_notices {
|
||||
services
|
||||
.admin
|
||||
.send_message(RoomMessageEventContent::text_plain(format!(
|
||||
@@ -351,116 +348,6 @@ pub(crate) async fn join_room_by_id_or_alias_route(
|
||||
Ok(join_room_by_id_or_alias::v3::Response { room_id: join_room_response.room_id })
|
||||
}
|
||||
|
||||
/// # `POST /_matrix/client/*/knock/{roomIdOrAlias}`
|
||||
///
|
||||
/// Tries to knock the room to ask permission to join for the sender user.
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "knock")]
|
||||
pub(crate) async fn knock_room_route(
|
||||
State(services): State<crate::State>,
|
||||
InsecureClientIp(client): InsecureClientIp,
|
||||
body: Ruma<knock_room::v3::Request>,
|
||||
) -> Result<knock_room::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let body = body.body;
|
||||
|
||||
let (servers, room_id) = match OwnedRoomId::try_from(body.room_id_or_alias) {
|
||||
| Ok(room_id) => {
|
||||
banned_room_check(
|
||||
&services,
|
||||
sender_user,
|
||||
Some(&room_id),
|
||||
room_id.server_name(),
|
||||
client,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut servers = body.via.clone();
|
||||
servers.extend(
|
||||
services
|
||||
.rooms
|
||||
.state_cache
|
||||
.servers_invite_via(&room_id)
|
||||
.map(ToOwned::to_owned)
|
||||
.collect::<Vec<_>>()
|
||||
.await,
|
||||
);
|
||||
|
||||
servers.extend(
|
||||
services
|
||||
.rooms
|
||||
.state_cache
|
||||
.invite_state(sender_user, &room_id)
|
||||
.await
|
||||
.unwrap_or_default()
|
||||
.iter()
|
||||
.filter_map(|event| event.get_field("sender").ok().flatten())
|
||||
.filter_map(|sender: &str| UserId::parse(sender).ok())
|
||||
.map(|user| user.server_name().to_owned()),
|
||||
);
|
||||
|
||||
if let Some(server) = room_id.server_name() {
|
||||
servers.push(server.to_owned());
|
||||
}
|
||||
|
||||
servers.sort_unstable();
|
||||
servers.dedup();
|
||||
shuffle(&mut servers);
|
||||
|
||||
(servers, room_id)
|
||||
},
|
||||
| Err(room_alias) => {
|
||||
let (room_id, mut servers) = services
|
||||
.rooms
|
||||
.alias
|
||||
.resolve_alias(&room_alias, Some(body.via.clone()))
|
||||
.await?;
|
||||
|
||||
banned_room_check(
|
||||
&services,
|
||||
sender_user,
|
||||
Some(&room_id),
|
||||
Some(room_alias.server_name()),
|
||||
client,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let addl_via_servers = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.servers_invite_via(&room_id)
|
||||
.map(ToOwned::to_owned);
|
||||
|
||||
let addl_state_servers = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.invite_state(sender_user, &room_id)
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
|
||||
let mut addl_servers: Vec<_> = addl_state_servers
|
||||
.iter()
|
||||
.map(|event| event.get_field("sender"))
|
||||
.filter_map(FlatOk::flat_ok)
|
||||
.map(|user: &UserId| user.server_name().to_owned())
|
||||
.stream()
|
||||
.chain(addl_via_servers)
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
addl_servers.sort_unstable();
|
||||
addl_servers.dedup();
|
||||
shuffle(&mut addl_servers);
|
||||
servers.append(&mut addl_servers);
|
||||
|
||||
(servers, room_id)
|
||||
},
|
||||
};
|
||||
|
||||
knock_room_by_id_helper(&services, sender_user, &room_id, body.reason.clone(), &servers)
|
||||
.boxed()
|
||||
.await
|
||||
}
|
||||
|
||||
/// # `POST /_matrix/client/v3/rooms/{roomId}/leave`
|
||||
///
|
||||
/// Tries to leave the sender user from a room.
|
||||
@@ -516,17 +403,6 @@ pub(crate) async fn invite_user_route(
|
||||
)));
|
||||
}
|
||||
|
||||
if let Ok(target_user_membership) = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.get_member(&body.room_id, user_id)
|
||||
.await
|
||||
{
|
||||
if target_user_membership.membership == MembershipState::Ban {
|
||||
return Err!(Request(Forbidden("User is banned from this room.")));
|
||||
}
|
||||
}
|
||||
|
||||
if recipient_ignored_by_sender {
|
||||
// silently drop the invite to the recipient if they've been ignored by the
|
||||
// sender, pretend it worked
|
||||
@@ -563,16 +439,6 @@ pub(crate) async fn kick_user_route(
|
||||
return Ok(kick_user::v3::Response::new());
|
||||
};
|
||||
|
||||
if !matches!(
|
||||
event.membership,
|
||||
MembershipState::Invite | MembershipState::Knock | MembershipState::Join,
|
||||
) {
|
||||
return Err!(Request(Forbidden(
|
||||
"Cannot kick a user who is not apart of the room (current membership: {})",
|
||||
event.membership
|
||||
)));
|
||||
}
|
||||
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
@@ -661,7 +527,7 @@ pub(crate) async fn unban_user_route(
|
||||
|
||||
if current_member_content.membership != MembershipState::Ban {
|
||||
return Err!(Request(Forbidden(
|
||||
"Cannot unban a user who is not banned (current membership: {})",
|
||||
"Cannot ban a user who is not banned (current membership: {})",
|
||||
current_member_content.membership
|
||||
)));
|
||||
}
|
||||
@@ -986,7 +852,7 @@ async fn join_room_by_id_helper_remote(
|
||||
.hash_and_sign_event(&mut join_event_stub, &room_version_id)?;
|
||||
|
||||
// Generate event id
|
||||
let event_id = gen_event_id(&join_event_stub, &room_version_id)?;
|
||||
let event_id = pdu::gen_event_id(&join_event_stub, &room_version_id)?;
|
||||
|
||||
// Add event_id back
|
||||
join_event_stub
|
||||
@@ -1154,7 +1020,7 @@ async fn join_room_by_id_helper_remote(
|
||||
};
|
||||
|
||||
let auth_check = state_res::event_auth::auth_check(
|
||||
&state_res::RoomVersion::new(&room_version_id)?,
|
||||
&state_res::RoomVersion::new(&room_version_id).expect("room version is supported"),
|
||||
&parsed_join_pdu,
|
||||
None, // TODO: third party invite
|
||||
|k, s| state_fetch(k, s.to_owned()),
|
||||
@@ -1167,10 +1033,10 @@ async fn join_room_by_id_helper_remote(
|
||||
}
|
||||
|
||||
info!("Compressing state from send_join");
|
||||
let compressed: HashSet<_> = services
|
||||
.rooms
|
||||
.state_compressor
|
||||
.compress_state_events(state.iter().map(|(ssk, eid)| (ssk, eid.borrow())))
|
||||
let compressed = state
|
||||
.iter()
|
||||
.stream()
|
||||
.then(|(&k, id)| services.rooms.state_compressor.compress_state_event(k, id))
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
@@ -1406,7 +1272,7 @@ async fn join_room_by_id_helper_local(
|
||||
.hash_and_sign_event(&mut join_event_stub, &room_version_id)?;
|
||||
|
||||
// Generate event id
|
||||
let event_id = gen_event_id(&join_event_stub, &room_version_id)?;
|
||||
let event_id = pdu::gen_event_id(&join_event_stub, &room_version_id)?;
|
||||
|
||||
// Add event_id back
|
||||
join_event_stub
|
||||
@@ -1516,7 +1382,6 @@ async fn make_join_request(
|
||||
);
|
||||
make_join_response_and_server =
|
||||
Err!(BadServerResponse("No server available to assist in joining."));
|
||||
|
||||
return make_join_response_and_server;
|
||||
}
|
||||
}
|
||||
@@ -1694,7 +1559,7 @@ pub async fn leave_all_rooms(services: &Services, user_id: &UserId) {
|
||||
for room_id in all_rooms {
|
||||
// ignore errors
|
||||
if let Err(e) = leave_room(services, user_id, &room_id, None).await {
|
||||
warn!(%user_id, "Failed to leave {room_id} remotely: {e}");
|
||||
warn!(%room_id, %user_id, %e, "Failed to leave room");
|
||||
}
|
||||
|
||||
services.rooms.state_cache.forget(&room_id, user_id);
|
||||
@@ -1710,15 +1575,11 @@ pub async fn leave_room(
|
||||
//use conduwuit::utils::stream::OptionStream;
|
||||
use futures::TryFutureExt;
|
||||
|
||||
// Ask a remote server if we don't have this room and are not knocking on it
|
||||
// Ask a remote server if we don't have this room
|
||||
if !services
|
||||
.rooms
|
||||
.state_cache
|
||||
.server_in_room(services.globals.server_name(), room_id)
|
||||
.await && !services
|
||||
.rooms
|
||||
.state_cache
|
||||
.is_knocked(user_id, room_id)
|
||||
.await
|
||||
{
|
||||
if let Err(e) = remote_leave_room(services, user_id, room_id).await {
|
||||
@@ -1730,8 +1591,7 @@ pub async fn leave_room(
|
||||
.rooms
|
||||
.state_cache
|
||||
.invite_state(user_id, room_id)
|
||||
.or_else(|_| services.rooms.state_cache.knock_state(user_id, room_id))
|
||||
.or_else(|_| services.rooms.state_cache.left_state(user_id, room_id))
|
||||
.map_err(|_| services.rooms.state_cache.left_state(user_id, room_id))
|
||||
.await
|
||||
.ok();
|
||||
|
||||
@@ -1813,6 +1673,13 @@ async fn remote_leave_room(
|
||||
let mut make_leave_response_and_server =
|
||||
Err!(BadServerResponse("No server available to assist in leaving."));
|
||||
|
||||
let invite_state = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.invite_state(user_id, room_id)
|
||||
.await
|
||||
.map_err(|_| err!(Request(BadState("User is not invited."))))?;
|
||||
|
||||
let mut servers: HashSet<OwnedServerName> = services
|
||||
.rooms
|
||||
.state_cache
|
||||
@@ -1821,39 +1688,13 @@ async fn remote_leave_room(
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
if let Ok(invite_state) = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.invite_state(user_id, room_id)
|
||||
.await
|
||||
{
|
||||
servers.extend(
|
||||
invite_state
|
||||
.iter()
|
||||
.filter_map(|event| event.get_field("sender").ok().flatten())
|
||||
.filter_map(|sender: &str| UserId::parse(sender).ok())
|
||||
.map(|user| user.server_name().to_owned()),
|
||||
);
|
||||
} else if let Ok(knock_state) = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.knock_state(user_id, room_id)
|
||||
.await
|
||||
{
|
||||
servers.extend(
|
||||
knock_state
|
||||
.iter()
|
||||
.filter_map(|event| event.get_field("sender").ok().flatten())
|
||||
.filter_map(|sender: &str| UserId::parse(sender).ok())
|
||||
.filter_map(|sender| {
|
||||
if !services.globals.user_is_local(sender) {
|
||||
Some(sender.server_name().to_owned())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
servers.extend(
|
||||
invite_state
|
||||
.iter()
|
||||
.filter_map(|event| event.get_field("sender").ok().flatten())
|
||||
.filter_map(|sender: &str| UserId::parse(sender).ok())
|
||||
.map(|user| user.server_name().to_owned()),
|
||||
);
|
||||
|
||||
if let Some(room_id_server_name) = room_id.server_name() {
|
||||
servers.insert(room_id_server_name.to_owned());
|
||||
@@ -1928,7 +1769,7 @@ async fn remote_leave_room(
|
||||
.hash_and_sign_event(&mut leave_event_stub, &room_version_id)?;
|
||||
|
||||
// Generate event id
|
||||
let event_id = gen_event_id(&leave_event_stub, &room_version_id)?;
|
||||
let event_id = pdu::gen_event_id(&leave_event_stub, &room_version_id)?;
|
||||
|
||||
// Add event_id back
|
||||
leave_event_stub
|
||||
@@ -1954,514 +1795,3 @@ async fn remote_leave_room(
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn knock_room_by_id_helper(
|
||||
services: &Services,
|
||||
sender_user: &UserId,
|
||||
room_id: &RoomId,
|
||||
reason: Option<String>,
|
||||
servers: &[OwnedServerName],
|
||||
) -> Result<knock_room::v3::Response> {
|
||||
let state_lock = services.rooms.state.mutex.lock(room_id).await;
|
||||
|
||||
if services
|
||||
.rooms
|
||||
.state_cache
|
||||
.is_invited(sender_user, room_id)
|
||||
.await
|
||||
{
|
||||
debug_warn!("{sender_user} is already invited in {room_id} but attempted to knock");
|
||||
return Err!(Request(Forbidden(
|
||||
"You cannot knock on a room you are already invited/accepted to."
|
||||
)));
|
||||
}
|
||||
|
||||
if services
|
||||
.rooms
|
||||
.state_cache
|
||||
.is_joined(sender_user, room_id)
|
||||
.await
|
||||
{
|
||||
debug_warn!("{sender_user} is already joined in {room_id} but attempted to knock");
|
||||
return Err!(Request(Forbidden("You cannot knock on a room you are already joined in.")));
|
||||
}
|
||||
|
||||
if services
|
||||
.rooms
|
||||
.state_cache
|
||||
.is_knocked(sender_user, room_id)
|
||||
.await
|
||||
{
|
||||
debug_warn!("{sender_user} is already knocked in {room_id}");
|
||||
return Ok(knock_room::v3::Response { room_id: room_id.into() });
|
||||
}
|
||||
|
||||
if let Ok(membership) = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.get_member(room_id, sender_user)
|
||||
.await
|
||||
{
|
||||
if membership.membership == MembershipState::Ban {
|
||||
debug_warn!("{sender_user} is banned from {room_id} but attempted to knock");
|
||||
return Err!(Request(Forbidden("You cannot knock on a room you are banned from.")));
|
||||
}
|
||||
}
|
||||
|
||||
let server_in_room = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.server_in_room(services.globals.server_name(), room_id)
|
||||
.await;
|
||||
|
||||
let local_knock = server_in_room
|
||||
|| servers.is_empty()
|
||||
|| (servers.len() == 1 && services.globals.server_is_ours(&servers[0]));
|
||||
|
||||
if local_knock {
|
||||
knock_room_helper_local(services, sender_user, room_id, reason, servers, state_lock)
|
||||
.boxed()
|
||||
.await?;
|
||||
} else {
|
||||
knock_room_helper_remote(services, sender_user, room_id, reason, servers, state_lock)
|
||||
.boxed()
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(knock_room::v3::Response::new(room_id.to_owned()))
|
||||
}
|
||||
|
||||
async fn knock_room_helper_local(
|
||||
services: &Services,
|
||||
sender_user: &UserId,
|
||||
room_id: &RoomId,
|
||||
reason: Option<String>,
|
||||
servers: &[OwnedServerName],
|
||||
state_lock: RoomMutexGuard,
|
||||
) -> Result {
|
||||
debug_info!("We can knock locally");
|
||||
|
||||
let room_version_id = services.rooms.state.get_room_version(room_id).await?;
|
||||
|
||||
if matches!(
|
||||
room_version_id,
|
||||
RoomVersionId::V1
|
||||
| RoomVersionId::V2
|
||||
| RoomVersionId::V3
|
||||
| RoomVersionId::V4
|
||||
| RoomVersionId::V5
|
||||
| RoomVersionId::V6
|
||||
) {
|
||||
return Err!(Request(Forbidden("This room does not support knocking.")));
|
||||
}
|
||||
|
||||
let content = RoomMemberEventContent {
|
||||
displayname: services.users.displayname(sender_user).await.ok(),
|
||||
avatar_url: services.users.avatar_url(sender_user).await.ok(),
|
||||
blurhash: services.users.blurhash(sender_user).await.ok(),
|
||||
reason: reason.clone(),
|
||||
..RoomMemberEventContent::new(MembershipState::Knock)
|
||||
};
|
||||
|
||||
// Try normal knock first
|
||||
let Err(error) = services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(sender_user.to_string(), &content),
|
||||
sender_user,
|
||||
room_id,
|
||||
&state_lock,
|
||||
)
|
||||
.await
|
||||
else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
if servers.is_empty() || (servers.len() == 1 && services.globals.server_is_ours(&servers[0]))
|
||||
{
|
||||
return Err(error);
|
||||
}
|
||||
|
||||
warn!("We couldn't do the knock locally, maybe federation can help to satisfy the knock");
|
||||
|
||||
let (make_knock_response, remote_server) =
|
||||
make_knock_request(services, sender_user, room_id, servers).await?;
|
||||
|
||||
info!("make_knock finished");
|
||||
|
||||
let room_version_id = make_knock_response.room_version;
|
||||
|
||||
if !services.server.supported_room_version(&room_version_id) {
|
||||
return Err!(BadServerResponse(
|
||||
"Remote room version {room_version_id} is not supported by conduwuit"
|
||||
));
|
||||
}
|
||||
|
||||
let mut knock_event_stub = serde_json::from_str::<CanonicalJsonObject>(
|
||||
make_knock_response.event.get(),
|
||||
)
|
||||
.map_err(|e| {
|
||||
err!(BadServerResponse("Invalid make_knock event json received from server: {e:?}"))
|
||||
})?;
|
||||
|
||||
knock_event_stub.insert(
|
||||
"origin".to_owned(),
|
||||
CanonicalJsonValue::String(services.globals.server_name().as_str().to_owned()),
|
||||
);
|
||||
knock_event_stub.insert(
|
||||
"origin_server_ts".to_owned(),
|
||||
CanonicalJsonValue::Integer(
|
||||
utils::millis_since_unix_epoch()
|
||||
.try_into()
|
||||
.expect("Timestamp is valid js_int value"),
|
||||
),
|
||||
);
|
||||
knock_event_stub.insert(
|
||||
"content".to_owned(),
|
||||
to_canonical_value(RoomMemberEventContent {
|
||||
displayname: services.users.displayname(sender_user).await.ok(),
|
||||
avatar_url: services.users.avatar_url(sender_user).await.ok(),
|
||||
blurhash: services.users.blurhash(sender_user).await.ok(),
|
||||
reason,
|
||||
..RoomMemberEventContent::new(MembershipState::Knock)
|
||||
})
|
||||
.expect("event is valid, we just created it"),
|
||||
);
|
||||
|
||||
// In order to create a compatible ref hash (EventID) the `hashes` field needs
|
||||
// to be present
|
||||
services
|
||||
.server_keys
|
||||
.hash_and_sign_event(&mut knock_event_stub, &room_version_id)?;
|
||||
|
||||
// Generate event id
|
||||
let event_id = gen_event_id(&knock_event_stub, &room_version_id)?;
|
||||
|
||||
// Add event_id
|
||||
knock_event_stub
|
||||
.insert("event_id".to_owned(), CanonicalJsonValue::String(event_id.clone().into()));
|
||||
|
||||
// It has enough fields to be called a proper event now
|
||||
let knock_event = knock_event_stub;
|
||||
|
||||
info!("Asking {remote_server} for send_knock in room {room_id}");
|
||||
let send_knock_request = federation::knock::send_knock::v1::Request {
|
||||
room_id: room_id.to_owned(),
|
||||
event_id: event_id.clone(),
|
||||
pdu: services
|
||||
.sending
|
||||
.convert_to_outgoing_federation_event(knock_event.clone())
|
||||
.await,
|
||||
};
|
||||
|
||||
let send_knock_response = services
|
||||
.sending
|
||||
.send_federation_request(&remote_server, send_knock_request)
|
||||
.await?;
|
||||
|
||||
info!("send_knock finished");
|
||||
|
||||
services
|
||||
.rooms
|
||||
.short
|
||||
.get_or_create_shortroomid(room_id)
|
||||
.await;
|
||||
|
||||
info!("Parsing knock event");
|
||||
|
||||
let parsed_knock_pdu = PduEvent::from_id_val(&event_id, knock_event.clone())
|
||||
.map_err(|e| err!(BadServerResponse("Invalid knock event PDU: {e:?}")))?;
|
||||
|
||||
info!("Updating membership locally to knock state with provided stripped state events");
|
||||
services
|
||||
.rooms
|
||||
.state_cache
|
||||
.update_membership(
|
||||
room_id,
|
||||
sender_user,
|
||||
parsed_knock_pdu
|
||||
.get_content::<RoomMemberEventContent>()
|
||||
.expect("we just created this"),
|
||||
sender_user,
|
||||
Some(send_knock_response.knock_room_state),
|
||||
None,
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
info!("Appending room knock event locally");
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.append_pdu(
|
||||
&parsed_knock_pdu,
|
||||
knock_event,
|
||||
vec![(*parsed_knock_pdu.event_id).to_owned()],
|
||||
&state_lock,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn knock_room_helper_remote(
|
||||
services: &Services,
|
||||
sender_user: &UserId,
|
||||
room_id: &RoomId,
|
||||
reason: Option<String>,
|
||||
servers: &[OwnedServerName],
|
||||
state_lock: RoomMutexGuard,
|
||||
) -> Result {
|
||||
info!("Knocking {room_id} over federation.");
|
||||
|
||||
let (make_knock_response, remote_server) =
|
||||
make_knock_request(services, sender_user, room_id, servers).await?;
|
||||
|
||||
info!("make_knock finished");
|
||||
|
||||
let room_version_id = make_knock_response.room_version;
|
||||
|
||||
if !services.server.supported_room_version(&room_version_id) {
|
||||
return Err!(BadServerResponse(
|
||||
"Remote room version {room_version_id} is not supported by conduwuit"
|
||||
));
|
||||
}
|
||||
|
||||
let mut knock_event_stub: CanonicalJsonObject =
|
||||
serde_json::from_str(make_knock_response.event.get()).map_err(|e| {
|
||||
err!(BadServerResponse("Invalid make_knock event json received from server: {e:?}"))
|
||||
})?;
|
||||
|
||||
knock_event_stub.insert(
|
||||
"origin".to_owned(),
|
||||
CanonicalJsonValue::String(services.globals.server_name().as_str().to_owned()),
|
||||
);
|
||||
knock_event_stub.insert(
|
||||
"origin_server_ts".to_owned(),
|
||||
CanonicalJsonValue::Integer(
|
||||
utils::millis_since_unix_epoch()
|
||||
.try_into()
|
||||
.expect("Timestamp is valid js_int value"),
|
||||
),
|
||||
);
|
||||
knock_event_stub.insert(
|
||||
"content".to_owned(),
|
||||
to_canonical_value(RoomMemberEventContent {
|
||||
displayname: services.users.displayname(sender_user).await.ok(),
|
||||
avatar_url: services.users.avatar_url(sender_user).await.ok(),
|
||||
blurhash: services.users.blurhash(sender_user).await.ok(),
|
||||
reason,
|
||||
..RoomMemberEventContent::new(MembershipState::Knock)
|
||||
})
|
||||
.expect("event is valid, we just created it"),
|
||||
);
|
||||
|
||||
// In order to create a compatible ref hash (EventID) the `hashes` field needs
|
||||
// to be present
|
||||
services
|
||||
.server_keys
|
||||
.hash_and_sign_event(&mut knock_event_stub, &room_version_id)?;
|
||||
|
||||
// Generate event id
|
||||
let event_id = gen_event_id(&knock_event_stub, &room_version_id)?;
|
||||
|
||||
// Add event_id
|
||||
knock_event_stub
|
||||
.insert("event_id".to_owned(), CanonicalJsonValue::String(event_id.clone().into()));
|
||||
|
||||
// It has enough fields to be called a proper event now
|
||||
let knock_event = knock_event_stub;
|
||||
|
||||
info!("Asking {remote_server} for send_knock in room {room_id}");
|
||||
let send_knock_request = federation::knock::send_knock::v1::Request {
|
||||
room_id: room_id.to_owned(),
|
||||
event_id: event_id.clone(),
|
||||
pdu: services
|
||||
.sending
|
||||
.convert_to_outgoing_federation_event(knock_event.clone())
|
||||
.await,
|
||||
};
|
||||
|
||||
let send_knock_response = services
|
||||
.sending
|
||||
.send_federation_request(&remote_server, send_knock_request)
|
||||
.await?;
|
||||
|
||||
info!("send_knock finished");
|
||||
|
||||
services
|
||||
.rooms
|
||||
.short
|
||||
.get_or_create_shortroomid(room_id)
|
||||
.await;
|
||||
|
||||
info!("Parsing knock event");
|
||||
let parsed_knock_pdu = PduEvent::from_id_val(&event_id, knock_event.clone())
|
||||
.map_err(|e| err!(BadServerResponse("Invalid knock event PDU: {e:?}")))?;
|
||||
|
||||
info!("Going through send_knock response knock state events");
|
||||
let state = send_knock_response
|
||||
.knock_room_state
|
||||
.iter()
|
||||
.map(|event| serde_json::from_str::<CanonicalJsonObject>(event.clone().into_json().get()))
|
||||
.filter_map(Result::ok);
|
||||
|
||||
let mut state_map: HashMap<u64, OwnedEventId> = HashMap::new();
|
||||
|
||||
for event in state {
|
||||
let Some(state_key) = event.get("state_key") else {
|
||||
debug_warn!("send_knock stripped state event missing state_key: {event:?}");
|
||||
continue;
|
||||
};
|
||||
let Some(event_type) = event.get("type") else {
|
||||
debug_warn!("send_knock stripped state event missing event type: {event:?}");
|
||||
continue;
|
||||
};
|
||||
|
||||
let Ok(state_key) = serde_json::from_value::<String>(state_key.clone().into()) else {
|
||||
debug_warn!("send_knock stripped state event has invalid state_key: {event:?}");
|
||||
continue;
|
||||
};
|
||||
let Ok(event_type) = serde_json::from_value::<StateEventType>(event_type.clone().into())
|
||||
else {
|
||||
debug_warn!("send_knock stripped state event has invalid event type: {event:?}");
|
||||
continue;
|
||||
};
|
||||
|
||||
let event_id = gen_event_id(&event, &room_version_id)?;
|
||||
let shortstatekey = services
|
||||
.rooms
|
||||
.short
|
||||
.get_or_create_shortstatekey(&event_type, &state_key)
|
||||
.await;
|
||||
|
||||
services.rooms.outlier.add_pdu_outlier(&event_id, &event);
|
||||
state_map.insert(shortstatekey, event_id.clone());
|
||||
}
|
||||
|
||||
info!("Compressing state from send_knock");
|
||||
let compressed: HashSet<_> = services
|
||||
.rooms
|
||||
.state_compressor
|
||||
.compress_state_events(state_map.iter().map(|(ssk, eid)| (ssk, eid.borrow())))
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
debug!("Saving compressed state");
|
||||
let HashSetCompressStateEvent {
|
||||
shortstatehash: statehash_before_knock,
|
||||
added,
|
||||
removed,
|
||||
} = services
|
||||
.rooms
|
||||
.state_compressor
|
||||
.save_state(room_id, Arc::new(compressed))
|
||||
.await?;
|
||||
|
||||
debug!("Forcing state for new room");
|
||||
services
|
||||
.rooms
|
||||
.state
|
||||
.force_state(room_id, statehash_before_knock, added, removed, &state_lock)
|
||||
.await?;
|
||||
|
||||
let statehash_after_knock = services
|
||||
.rooms
|
||||
.state
|
||||
.append_to_state(&parsed_knock_pdu)
|
||||
.await?;
|
||||
|
||||
info!("Updating membership locally to knock state with provided stripped state events");
|
||||
services
|
||||
.rooms
|
||||
.state_cache
|
||||
.update_membership(
|
||||
room_id,
|
||||
sender_user,
|
||||
parsed_knock_pdu
|
||||
.get_content::<RoomMemberEventContent>()
|
||||
.expect("we just created this"),
|
||||
sender_user,
|
||||
Some(send_knock_response.knock_room_state),
|
||||
None,
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
info!("Appending room knock event locally");
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.append_pdu(
|
||||
&parsed_knock_pdu,
|
||||
knock_event,
|
||||
vec![(*parsed_knock_pdu.event_id).to_owned()],
|
||||
&state_lock,
|
||||
)
|
||||
.await?;
|
||||
|
||||
info!("Setting final room state for new room");
|
||||
// We set the room state after inserting the pdu, so that we never have a moment
|
||||
// in time where events in the current room state do not exist
|
||||
services
|
||||
.rooms
|
||||
.state
|
||||
.set_room_state(room_id, statehash_after_knock, &state_lock);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn make_knock_request(
|
||||
services: &Services,
|
||||
sender_user: &UserId,
|
||||
room_id: &RoomId,
|
||||
servers: &[OwnedServerName],
|
||||
) -> Result<(federation::knock::create_knock_event_template::v1::Response, OwnedServerName)> {
|
||||
let mut make_knock_response_and_server =
|
||||
Err!(BadServerResponse("No server available to assist in knocking."));
|
||||
|
||||
let mut make_knock_counter: usize = 0;
|
||||
|
||||
for remote_server in servers {
|
||||
if services.globals.server_is_ours(remote_server) {
|
||||
continue;
|
||||
}
|
||||
|
||||
info!("Asking {remote_server} for make_knock ({make_knock_counter})");
|
||||
|
||||
let make_knock_response = services
|
||||
.sending
|
||||
.send_federation_request(
|
||||
remote_server,
|
||||
federation::knock::create_knock_event_template::v1::Request {
|
||||
room_id: room_id.to_owned(),
|
||||
user_id: sender_user.to_owned(),
|
||||
ver: services.server.supported_room_versions().collect(),
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
trace!("make_knock response: {make_knock_response:?}");
|
||||
make_knock_counter = make_knock_counter.saturating_add(1);
|
||||
|
||||
make_knock_response_and_server = make_knock_response.map(|r| (r, remote_server.clone()));
|
||||
|
||||
if make_knock_response_and_server.is_ok() {
|
||||
break;
|
||||
}
|
||||
|
||||
if make_knock_counter > 40 {
|
||||
warn!(
|
||||
"50 servers failed to provide valid make_knock response, assuming no server can \
|
||||
assist in knocking."
|
||||
);
|
||||
make_knock_response_and_server =
|
||||
Err!(BadServerResponse("No server available to assist in knocking."));
|
||||
|
||||
return make_knock_response_and_server;
|
||||
}
|
||||
}
|
||||
|
||||
make_knock_response_and_server
|
||||
}
|
||||
|
||||
+73
-78
@@ -1,3 +1,5 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use axum::extract::State;
|
||||
use conduwuit::{
|
||||
at, is_equal_to,
|
||||
@@ -8,7 +10,7 @@ use conduwuit::{
|
||||
},
|
||||
Event, PduCount, Result,
|
||||
};
|
||||
use futures::{future::OptionFuture, pin_mut, FutureExt, StreamExt};
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use ruma::{
|
||||
api::{
|
||||
client::{filter::RoomEventFilter, message::get_message_events},
|
||||
@@ -16,19 +18,14 @@ use ruma::{
|
||||
},
|
||||
events::{AnyStateEvent, StateEventType, TimelineEventType, TimelineEventType::*},
|
||||
serde::Raw,
|
||||
RoomId, UserId,
|
||||
};
|
||||
use service::{
|
||||
rooms::{
|
||||
lazy_loading,
|
||||
lazy_loading::{Options, Witness},
|
||||
timeline::PdusIterItem,
|
||||
},
|
||||
Services,
|
||||
DeviceId, OwnedUserId, RoomId, UserId,
|
||||
};
|
||||
use service::{rooms::timeline::PdusIterItem, Services};
|
||||
|
||||
use crate::Ruma;
|
||||
|
||||
pub(crate) type LazySet = HashSet<OwnedUserId>;
|
||||
|
||||
/// list of safe and common non-state events to ignore if the user is ignored
|
||||
const IGNORED_MESSAGE_TYPES: &[TimelineEventType; 17] = &[
|
||||
Audio,
|
||||
@@ -87,6 +84,13 @@ pub(crate) async fn get_message_events_route(
|
||||
.unwrap_or(LIMIT_DEFAULT)
|
||||
.min(LIMIT_MAX);
|
||||
|
||||
services.rooms.lazy_loading.lazy_load_confirm_delivery(
|
||||
sender_user,
|
||||
sender_device,
|
||||
room_id,
|
||||
from,
|
||||
);
|
||||
|
||||
if matches!(body.dir, Direction::Backward) {
|
||||
services
|
||||
.rooms
|
||||
@@ -123,34 +127,35 @@ pub(crate) async fn get_message_events_route(
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
let lazy_loading_context = lazy_loading::Context {
|
||||
user_id: sender_user,
|
||||
device_id: sender_device,
|
||||
room_id,
|
||||
token: Some(from.into_unsigned()),
|
||||
options: Some(&filter.lazy_load_options),
|
||||
};
|
||||
|
||||
let witness: OptionFuture<_> = filter
|
||||
.lazy_load_options
|
||||
.is_enabled()
|
||||
.then(|| lazy_loading_witness(&services, &lazy_loading_context, events.iter()))
|
||||
.into();
|
||||
|
||||
let state = witness
|
||||
.map(Option::into_iter)
|
||||
.map(|option| option.flat_map(Witness::into_iter))
|
||||
.map(IterStream::stream)
|
||||
.into_stream()
|
||||
.flatten()
|
||||
.broad_filter_map(|user_id| async move {
|
||||
get_member_event(&services, room_id, &user_id).await
|
||||
let lazy = events
|
||||
.iter()
|
||||
.stream()
|
||||
.fold(LazySet::new(), |lazy, item| {
|
||||
update_lazy(&services, room_id, sender, lazy, item, false)
|
||||
})
|
||||
.await;
|
||||
|
||||
let state = lazy
|
||||
.iter()
|
||||
.stream()
|
||||
.broad_filter_map(|user_id| get_member_event(&services, room_id, user_id))
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
let next_token = events.last().map(at!(0));
|
||||
|
||||
if !cfg!(feature = "element_hacks") {
|
||||
if let Some(next_token) = next_token {
|
||||
services.rooms.lazy_loading.lazy_load_mark_sent(
|
||||
sender_user,
|
||||
sender_device,
|
||||
room_id,
|
||||
lazy,
|
||||
next_token,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let chunk = events
|
||||
.into_iter()
|
||||
.map(at!(1))
|
||||
@@ -165,52 +170,6 @@ pub(crate) async fn get_message_events_route(
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) async fn lazy_loading_witness<'a, I>(
|
||||
services: &Services,
|
||||
lazy_loading_context: &lazy_loading::Context<'_>,
|
||||
events: I,
|
||||
) -> Witness
|
||||
where
|
||||
I: Iterator<Item = &'a PdusIterItem> + Clone + Send,
|
||||
{
|
||||
let oldest = events
|
||||
.clone()
|
||||
.map(|(count, _)| count)
|
||||
.copied()
|
||||
.min()
|
||||
.unwrap_or_else(PduCount::max);
|
||||
|
||||
let newest = events
|
||||
.clone()
|
||||
.map(|(count, _)| count)
|
||||
.copied()
|
||||
.max()
|
||||
.unwrap_or_else(PduCount::max);
|
||||
|
||||
let receipts = services
|
||||
.rooms
|
||||
.read_receipt
|
||||
.readreceipts_since(lazy_loading_context.room_id, oldest.into_unsigned());
|
||||
|
||||
pin_mut!(receipts);
|
||||
let witness: Witness = events
|
||||
.stream()
|
||||
.map(|(_, pdu)| pdu.sender.clone())
|
||||
.chain(
|
||||
receipts
|
||||
.ready_take_while(|(_, c, _)| *c <= newest.into_unsigned())
|
||||
.map(|(user_id, ..)| user_id.to_owned()),
|
||||
)
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
services
|
||||
.rooms
|
||||
.lazy_loading
|
||||
.witness_retain(witness, lazy_loading_context)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_member_event(
|
||||
services: &Services,
|
||||
room_id: &RoomId,
|
||||
@@ -225,6 +184,42 @@ async fn get_member_event(
|
||||
.ok()
|
||||
}
|
||||
|
||||
pub(crate) async fn update_lazy(
|
||||
services: &Services,
|
||||
room_id: &RoomId,
|
||||
sender: (&UserId, &DeviceId),
|
||||
mut lazy: LazySet,
|
||||
item: &PdusIterItem,
|
||||
force: bool,
|
||||
) -> LazySet {
|
||||
let (_, event) = &item;
|
||||
let (sender_user, sender_device) = sender;
|
||||
|
||||
/* TODO: Remove the "element_hacks" check when these are resolved:
|
||||
* https://github.com/vector-im/element-android/issues/3417
|
||||
* https://github.com/vector-im/element-web/issues/21034
|
||||
*/
|
||||
if force || cfg!(features = "element_hacks") {
|
||||
lazy.insert(event.sender().into());
|
||||
return lazy;
|
||||
}
|
||||
|
||||
if lazy.contains(event.sender()) {
|
||||
return lazy;
|
||||
}
|
||||
|
||||
if !services
|
||||
.rooms
|
||||
.lazy_loading
|
||||
.lazy_load_was_sent_before(sender_user, sender_device, room_id, event.sender())
|
||||
.await
|
||||
{
|
||||
lazy.insert(event.sender().into());
|
||||
}
|
||||
|
||||
lazy
|
||||
}
|
||||
|
||||
pub(crate) async fn ignored_filter(
|
||||
services: &Services,
|
||||
item: PdusIterItem,
|
||||
|
||||
@@ -37,7 +37,7 @@ pub(crate) async fn create_openid_token_route(
|
||||
Ok(account::request_openid_token::v3::Response {
|
||||
access_token,
|
||||
token_type: TokenType::Bearer,
|
||||
matrix_server_name: services.server.config.server_name.clone(),
|
||||
matrix_server_name: services.globals.config.server_name.clone(),
|
||||
expires_in: Duration::from_secs(expires_in),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -82,19 +82,14 @@ pub(crate) async fn get_presence_route(
|
||||
presence.content.status_msg
|
||||
};
|
||||
|
||||
let last_active_ago = match presence.content.currently_active {
|
||||
| Some(true) => None,
|
||||
| _ => presence
|
||||
.content
|
||||
.last_active_ago
|
||||
.map(|millis| Duration::from_millis(millis.into())),
|
||||
};
|
||||
|
||||
Ok(get_presence::v3::Response {
|
||||
// TODO: Should ruma just use the presenceeventcontent type here?
|
||||
status_msg,
|
||||
currently_active: presence.content.currently_active,
|
||||
last_active_ago,
|
||||
last_active_ago: presence
|
||||
.content
|
||||
.last_active_ago
|
||||
.map(|millis| Duration::from_millis(millis.into())),
|
||||
presence: presence.content.presence,
|
||||
})
|
||||
} else {
|
||||
|
||||
@@ -50,7 +50,7 @@ pub(crate) async fn report_room_route(
|
||||
if !services
|
||||
.rooms
|
||||
.state_cache
|
||||
.server_in_room(&services.server.config.server_name, &body.room_id)
|
||||
.server_in_room(&services.globals.config.server_name, &body.room_id)
|
||||
.await
|
||||
{
|
||||
return Err!(Request(NotFound(
|
||||
|
||||
@@ -71,7 +71,7 @@ pub(crate) async fn create_room_route(
|
||||
let room_id: OwnedRoomId = if let Some(custom_room_id) = &body.room_id {
|
||||
custom_room_id_check(&services, custom_room_id)?
|
||||
} else {
|
||||
RoomId::new(&services.server.config.server_name)
|
||||
RoomId::new(&services.globals.config.server_name)
|
||||
};
|
||||
|
||||
// check if room ID doesn't already exist instead of erroring on auth check
|
||||
@@ -83,7 +83,7 @@ pub(crate) async fn create_room_route(
|
||||
}
|
||||
|
||||
if body.visibility == room::Visibility::Public
|
||||
&& services.server.config.lockdown_public_room_directory
|
||||
&& services.globals.config.lockdown_public_room_directory
|
||||
&& !services.users.is_admin(sender_user).await
|
||||
&& body.appservice_info.is_none()
|
||||
{
|
||||
@@ -93,7 +93,7 @@ pub(crate) async fn create_room_route(
|
||||
&room_id
|
||||
);
|
||||
|
||||
if services.server.config.admin_room_notices {
|
||||
if services.globals.config.admin_room_notices {
|
||||
services
|
||||
.admin
|
||||
.send_text(&format!(
|
||||
@@ -450,7 +450,7 @@ pub(crate) async fn create_room_route(
|
||||
if body.visibility == room::Visibility::Public {
|
||||
services.rooms.directory.set_public(&room_id);
|
||||
|
||||
if services.server.config.admin_room_notices {
|
||||
if services.globals.config.admin_room_notices {
|
||||
services
|
||||
.admin
|
||||
.send_text(&format!(
|
||||
|
||||
+50
-93
@@ -1,5 +1,3 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::InsecureClientIp;
|
||||
use conduwuit::{debug, err, info, utils::ReadyExt, warn, Err};
|
||||
@@ -8,10 +6,9 @@ use ruma::{
|
||||
api::client::{
|
||||
error::ErrorKind,
|
||||
session::{
|
||||
get_login_token,
|
||||
get_login_types::{
|
||||
self,
|
||||
v3::{ApplicationServiceLoginType, PasswordLoginType, TokenLoginType},
|
||||
v3::{ApplicationServiceLoginType, PasswordLoginType},
|
||||
},
|
||||
login::{
|
||||
self,
|
||||
@@ -19,31 +16,33 @@ use ruma::{
|
||||
},
|
||||
logout, logout_all,
|
||||
},
|
||||
uiaa,
|
||||
uiaa::UserIdentifier,
|
||||
},
|
||||
OwnedUserId, UserId,
|
||||
};
|
||||
use service::uiaa::SESSION_ID_LENGTH;
|
||||
use serde::Deserialize;
|
||||
|
||||
use super::{DEVICE_ID_LENGTH, TOKEN_LENGTH};
|
||||
use crate::{utils, utils::hash, Error, Result, Ruma};
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct Claims {
|
||||
sub: String,
|
||||
//exp: usize,
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/client/v3/login`
|
||||
///
|
||||
/// Get the supported login types of this server. One of these should be used as
|
||||
/// the `type` field when logging in.
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "login")]
|
||||
pub(crate) async fn get_login_types_route(
|
||||
State(services): State<crate::State>,
|
||||
InsecureClientIp(client): InsecureClientIp,
|
||||
_body: Ruma<get_login_types::v3::Request>,
|
||||
) -> Result<get_login_types::v3::Response> {
|
||||
Ok(get_login_types::v3::Response::new(vec![
|
||||
get_login_types::v3::LoginType::Password(PasswordLoginType::default()),
|
||||
get_login_types::v3::LoginType::ApplicationService(ApplicationServiceLoginType::default()),
|
||||
get_login_types::v3::LoginType::Token(TokenLoginType {
|
||||
get_login_token: services.server.config.login_via_existing_session,
|
||||
}),
|
||||
]))
|
||||
}
|
||||
|
||||
@@ -78,9 +77,7 @@ pub(crate) async fn login_route(
|
||||
..
|
||||
}) => {
|
||||
debug!("Got password login type");
|
||||
let user_id = if let Some(uiaa::UserIdentifier::UserIdOrLocalpart(user_id)) =
|
||||
identifier
|
||||
{
|
||||
let user_id = if let Some(UserIdentifier::UserIdOrLocalpart(user_id)) = identifier {
|
||||
UserId::parse_with_server_name(
|
||||
user_id.to_lowercase(),
|
||||
services.globals.server_name(),
|
||||
@@ -111,10 +108,32 @@ pub(crate) async fn login_route(
|
||||
},
|
||||
| login::v3::LoginInfo::Token(login::v3::Token { token }) => {
|
||||
debug!("Got token login type");
|
||||
if !services.server.config.login_via_existing_session {
|
||||
return Err!(Request(Unknown("Token login is not enabled.")));
|
||||
if let Some(jwt_decoding_key) = services.globals.jwt_decoding_key() {
|
||||
let token = jsonwebtoken::decode::<Claims>(
|
||||
token,
|
||||
jwt_decoding_key,
|
||||
&jsonwebtoken::Validation::default(),
|
||||
)
|
||||
.map_err(|e| {
|
||||
warn!("Failed to parse JWT token from user logging in: {e}");
|
||||
Error::BadRequest(ErrorKind::InvalidUsername, "Token is invalid.")
|
||||
})?;
|
||||
|
||||
let username = token.claims.sub.to_lowercase();
|
||||
|
||||
UserId::parse_with_server_name(username, services.globals.server_name()).map_err(
|
||||
|e| {
|
||||
err!(Request(InvalidUsername(debug_error!(
|
||||
?e,
|
||||
"Failed to parse login username"
|
||||
))))
|
||||
},
|
||||
)?
|
||||
} else {
|
||||
return Err!(Request(Unknown(
|
||||
"Token login is not supported (server has no jwt decoding key)."
|
||||
)));
|
||||
}
|
||||
services.users.find_from_login_token(token).await?
|
||||
},
|
||||
#[allow(deprecated)]
|
||||
| login::v3::LoginInfo::ApplicationService(login::v3::ApplicationService {
|
||||
@@ -122,22 +141,21 @@ pub(crate) async fn login_route(
|
||||
user,
|
||||
}) => {
|
||||
debug!("Got appservice login type");
|
||||
let user_id =
|
||||
if let Some(uiaa::UserIdentifier::UserIdOrLocalpart(user_id)) = identifier {
|
||||
UserId::parse_with_server_name(
|
||||
user_id.to_lowercase(),
|
||||
services.globals.server_name(),
|
||||
)
|
||||
} else if let Some(user) = user {
|
||||
OwnedUserId::parse(user)
|
||||
} else {
|
||||
warn!("Bad login type: {:?}", &body.login_info);
|
||||
return Err(Error::BadRequest(ErrorKind::forbidden(), "Bad login type."));
|
||||
}
|
||||
.map_err(|e| {
|
||||
warn!("Failed to parse username from appservice logging in: {e}");
|
||||
Error::BadRequest(ErrorKind::InvalidUsername, "Username is invalid.")
|
||||
})?;
|
||||
let user_id = 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 {
|
||||
OwnedUserId::parse(user)
|
||||
} else {
|
||||
warn!("Bad login type: {:?}", &body.login_info);
|
||||
return Err(Error::BadRequest(ErrorKind::forbidden(), "Bad login type."));
|
||||
}
|
||||
.map_err(|e| {
|
||||
warn!("Failed to parse username from appservice logging in: {e}");
|
||||
Error::BadRequest(ErrorKind::InvalidUsername, "Username is invalid.")
|
||||
})?;
|
||||
|
||||
if let Some(ref info) = body.appservice_info {
|
||||
if !info.is_user_match(&user_id) {
|
||||
@@ -229,67 +247,6 @@ pub(crate) async fn login_route(
|
||||
})
|
||||
}
|
||||
|
||||
/// # `POST /_matrix/client/v1/login/get_token`
|
||||
///
|
||||
/// Allows a logged-in user to get a short-lived token which can be used
|
||||
/// to log in with the m.login.token flow.
|
||||
///
|
||||
/// <https://spec.matrix.org/v1.13/client-server-api/#post_matrixclientv1loginget_token>
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "login_token")]
|
||||
pub(crate) async fn login_token_route(
|
||||
State(services): State<crate::State>,
|
||||
InsecureClientIp(client): InsecureClientIp,
|
||||
body: Ruma<get_login_token::v1::Request>,
|
||||
) -> Result<get_login_token::v1::Response> {
|
||||
if !services.server.config.login_via_existing_session {
|
||||
return Err!(Request(Forbidden("Login via an existing session is not enabled")));
|
||||
}
|
||||
|
||||
let sender_user = body.sender_user();
|
||||
let sender_device = body.sender_device();
|
||||
|
||||
// This route SHOULD have UIA
|
||||
// TODO: How do we make only UIA sessions that have not been used before valid?
|
||||
|
||||
let mut uiaainfo = uiaa::UiaaInfo {
|
||||
flows: vec![uiaa::AuthFlow { stages: vec![uiaa::AuthType::Password] }],
|
||||
completed: Vec::new(),
|
||||
params: Box::default(),
|
||||
session: None,
|
||||
auth_error: None,
|
||||
};
|
||||
|
||||
if let Some(auth) = &body.auth {
|
||||
let (worked, uiaainfo) = services
|
||||
.uiaa
|
||||
.try_auth(sender_user, sender_device, auth, &uiaainfo)
|
||||
.await?;
|
||||
|
||||
if !worked {
|
||||
return Err(Error::Uiaa(uiaainfo));
|
||||
}
|
||||
|
||||
// Success!
|
||||
} else if let Some(json) = body.json_body.as_ref() {
|
||||
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
||||
services
|
||||
.uiaa
|
||||
.create(sender_user, sender_device, &uiaainfo, json);
|
||||
|
||||
return Err(Error::Uiaa(uiaainfo));
|
||||
} else {
|
||||
return Err!(Request(NotJson("No JSON body was sent when required.")));
|
||||
}
|
||||
|
||||
let login_token = utils::random_string(TOKEN_LENGTH);
|
||||
let expires_in = services.users.create_login_token(sender_user, &login_token);
|
||||
|
||||
Ok(get_login_token::v1::Response {
|
||||
expires_in: Duration::from_millis(expires_in),
|
||||
login_token,
|
||||
})
|
||||
}
|
||||
|
||||
/// # `POST /_matrix/client/v3/logout`
|
||||
///
|
||||
/// Log out the current device.
|
||||
|
||||
+277
-207
@@ -6,9 +6,9 @@ use std::{
|
||||
|
||||
use axum::extract::State;
|
||||
use conduwuit::{
|
||||
at, err, error, extract_variant, is_equal_to,
|
||||
at, err, error, extract_variant, is_equal_to, is_false,
|
||||
pdu::EventHash,
|
||||
result::FlatOk,
|
||||
result::{FlatOk, LogDebugErr},
|
||||
utils::{
|
||||
self,
|
||||
future::OptionExt,
|
||||
@@ -19,26 +19,22 @@ use conduwuit::{
|
||||
Error, PduCount, PduEvent, Result,
|
||||
};
|
||||
use conduwuit_service::{
|
||||
rooms::{
|
||||
lazy_loading,
|
||||
lazy_loading::{Options, Witness},
|
||||
short::ShortStateHash,
|
||||
},
|
||||
rooms::short::{ShortStateHash, ShortStateKey},
|
||||
Services,
|
||||
};
|
||||
use futures::{
|
||||
future::{join, join3, join4, join5, try_join, try_join4, OptionFuture},
|
||||
future::{join, join3, join4, join5, try_join, try_join3, OptionFuture},
|
||||
FutureExt, StreamExt, TryFutureExt,
|
||||
};
|
||||
use ruma::{
|
||||
api::client::{
|
||||
filter::FilterDefinition,
|
||||
filter::{FilterDefinition, LazyLoadOptions},
|
||||
sync::sync_events::{
|
||||
self,
|
||||
v3::{
|
||||
Ephemeral, Filter, GlobalAccountData, InviteState, InvitedRoom, JoinedRoom,
|
||||
KnockState, KnockedRoom, LeftRoom, Presence, RoomAccountData, RoomSummary, Rooms,
|
||||
State as RoomState, Timeline, ToDevice,
|
||||
LeftRoom, Presence, RoomAccountData, RoomSummary, Rooms, State as RoomState,
|
||||
Timeline, ToDevice,
|
||||
},
|
||||
DeviceLists, UnreadNotificationsCount,
|
||||
},
|
||||
@@ -156,14 +152,9 @@ pub(crate) async fn build_sync_events(
|
||||
let (sender_user, sender_device) = body.sender();
|
||||
|
||||
let next_batch = services.globals.current_count()?;
|
||||
let since = body
|
||||
.body
|
||||
.since
|
||||
.as_ref()
|
||||
.and_then(|string| string.parse().ok())
|
||||
.unwrap_or(0);
|
||||
let next_batch_string = next_batch.to_string();
|
||||
|
||||
let full_state = body.body.full_state;
|
||||
// Load filter
|
||||
let filter = match body.body.filter.as_ref() {
|
||||
| None => FilterDefinition::default(),
|
||||
| Some(Filter::FilterDefinition(ref filter)) => filter.clone(),
|
||||
@@ -174,6 +165,24 @@ pub(crate) async fn build_sync_events(
|
||||
.unwrap_or_default(),
|
||||
};
|
||||
|
||||
// some clients, at least element, seem to require knowledge of redundant
|
||||
// members for "inline" profiles on the timeline to work properly
|
||||
let (lazy_load_enabled, lazy_load_send_redundant) = match filter.room.state.lazy_load_options
|
||||
{
|
||||
| LazyLoadOptions::Enabled { include_redundant_members } =>
|
||||
(true, include_redundant_members),
|
||||
| LazyLoadOptions::Disabled => (false, cfg!(feature = "element_hacks")),
|
||||
};
|
||||
|
||||
let full_state = body.body.full_state;
|
||||
|
||||
let since = body
|
||||
.body
|
||||
.since
|
||||
.as_ref()
|
||||
.and_then(|string| string.parse().ok())
|
||||
.unwrap_or(0);
|
||||
|
||||
let joined_rooms = services
|
||||
.rooms
|
||||
.state_cache
|
||||
@@ -187,8 +196,9 @@ pub(crate) async fn build_sync_events(
|
||||
room_id.clone(),
|
||||
since,
|
||||
next_batch,
|
||||
lazy_load_enabled,
|
||||
lazy_load_send_redundant,
|
||||
full_state,
|
||||
&filter,
|
||||
)
|
||||
.map_ok(move |(joined_room, dlu, jeu)| (room_id, joined_room, dlu, jeu))
|
||||
.ok()
|
||||
@@ -217,9 +227,9 @@ pub(crate) async fn build_sync_events(
|
||||
since,
|
||||
room_id.clone(),
|
||||
sender_user,
|
||||
next_batch,
|
||||
&next_batch_string,
|
||||
full_state,
|
||||
&filter,
|
||||
lazy_load_enabled,
|
||||
)
|
||||
.map_ok(move |left_room| (room_id, left_room))
|
||||
.ok()
|
||||
@@ -232,6 +242,10 @@ pub(crate) async fn build_sync_events(
|
||||
.state_cache
|
||||
.rooms_invited(sender_user)
|
||||
.fold_default(|mut invited_rooms: BTreeMap<_, _>, (room_id, invite_state)| async move {
|
||||
// Get and drop the lock to wait for remaining operations to finish
|
||||
let insert_lock = services.rooms.timeline.mutex_insert.lock(&room_id).await;
|
||||
drop(insert_lock);
|
||||
|
||||
let invite_count = services
|
||||
.rooms
|
||||
.state_cache
|
||||
@@ -252,31 +266,6 @@ pub(crate) async fn build_sync_events(
|
||||
invited_rooms
|
||||
});
|
||||
|
||||
let knocked_rooms = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.rooms_knocked(sender_user)
|
||||
.fold_default(|mut knocked_rooms: BTreeMap<_, _>, (room_id, knock_state)| async move {
|
||||
let knock_count = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.get_knock_count(&room_id, sender_user)
|
||||
.await
|
||||
.ok();
|
||||
|
||||
// Knocked before last sync
|
||||
if Some(since) >= knock_count {
|
||||
return knocked_rooms;
|
||||
}
|
||||
|
||||
let knocked_room = KnockedRoom {
|
||||
knock_state: KnockState { events: knock_state },
|
||||
};
|
||||
|
||||
knocked_rooms.insert(room_id, knocked_room);
|
||||
knocked_rooms
|
||||
});
|
||||
|
||||
let presence_updates: OptionFuture<_> = services
|
||||
.globals
|
||||
.allow_local_presence()
|
||||
@@ -311,7 +300,7 @@ pub(crate) async fn build_sync_events(
|
||||
.users
|
||||
.remove_to_device_events(sender_user, sender_device, since);
|
||||
|
||||
let rooms = join4(joined_rooms, left_rooms, invited_rooms, knocked_rooms);
|
||||
let rooms = join3(joined_rooms, left_rooms, invited_rooms);
|
||||
let ephemeral = join3(remove_to_device_events, to_device_events, presence_updates);
|
||||
let top = join5(account_data, ephemeral, device_one_time_keys_count, keys_changed, rooms)
|
||||
.boxed()
|
||||
@@ -319,7 +308,7 @@ pub(crate) async fn build_sync_events(
|
||||
|
||||
let (account_data, ephemeral, device_one_time_keys_count, keys_changed, rooms) = top;
|
||||
let ((), to_device_events, presence_updates) = ephemeral;
|
||||
let (joined_rooms, left_rooms, invited_rooms, knocked_rooms) = rooms;
|
||||
let (joined_rooms, left_rooms, invited_rooms) = rooms;
|
||||
let (joined_rooms, mut device_list_updates, left_encrypted_users) = joined_rooms;
|
||||
device_list_updates.extend(keys_changed);
|
||||
|
||||
@@ -348,7 +337,7 @@ pub(crate) async fn build_sync_events(
|
||||
device_one_time_keys_count,
|
||||
// Fallback keys are not yet supported
|
||||
device_unused_fallback_key_types: None,
|
||||
next_batch: next_batch.to_string(),
|
||||
next_batch: next_batch_string,
|
||||
presence: Presence {
|
||||
events: presence_updates
|
||||
.unwrap_or_default()
|
||||
@@ -360,7 +349,7 @@ pub(crate) async fn build_sync_events(
|
||||
leave: left_rooms,
|
||||
join: joined_rooms,
|
||||
invite: invited_rooms,
|
||||
knock: knocked_rooms,
|
||||
knock: BTreeMap::new(), // TODO
|
||||
},
|
||||
to_device: ToDevice { events: to_device_events },
|
||||
};
|
||||
@@ -393,13 +382,7 @@ async fn process_presence_updates(
|
||||
.ready_fold(PresenceUpdates::new(), |mut updates, (user_id, event)| {
|
||||
match updates.entry(user_id.into()) {
|
||||
| Entry::Vacant(slot) => {
|
||||
let mut new_event = event;
|
||||
new_event.content.last_active_ago = match new_event.content.currently_active {
|
||||
| Some(true) => None,
|
||||
| _ => new_event.content.last_active_ago,
|
||||
};
|
||||
|
||||
slot.insert(new_event);
|
||||
slot.insert(event);
|
||||
},
|
||||
| Entry::Occupied(mut slot) => {
|
||||
let curr_event = slot.get_mut();
|
||||
@@ -411,6 +394,8 @@ async fn process_presence_updates(
|
||||
curr_content.status_msg = new_content
|
||||
.status_msg
|
||||
.or_else(|| curr_content.status_msg.take());
|
||||
curr_content.last_active_ago =
|
||||
new_content.last_active_ago.or(curr_content.last_active_ago);
|
||||
curr_content.displayname = new_content
|
||||
.displayname
|
||||
.or_else(|| curr_content.displayname.take());
|
||||
@@ -420,10 +405,6 @@ async fn process_presence_updates(
|
||||
curr_content.currently_active = new_content
|
||||
.currently_active
|
||||
.or(curr_content.currently_active);
|
||||
curr_content.last_active_ago = match curr_content.currently_active {
|
||||
| Some(true) => None,
|
||||
| _ => new_content.last_active_ago.or(curr_content.last_active_ago),
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
@@ -439,6 +420,7 @@ async fn process_presence_updates(
|
||||
fields(
|
||||
room_id = %room_id,
|
||||
full = %full_state,
|
||||
ll = %lazy_load_enabled,
|
||||
),
|
||||
)]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
@@ -447,10 +429,14 @@ async fn handle_left_room(
|
||||
since: u64,
|
||||
ref room_id: OwnedRoomId,
|
||||
sender_user: &UserId,
|
||||
next_batch: u64,
|
||||
next_batch_string: &str,
|
||||
full_state: bool,
|
||||
filter: &FilterDefinition,
|
||||
lazy_load_enabled: bool,
|
||||
) -> Result<Option<LeftRoom>> {
|
||||
// Get and drop the lock to wait for remaining operations to finish
|
||||
let insert_lock = services.rooms.timeline.mutex_insert.lock(room_id).await;
|
||||
drop(insert_lock);
|
||||
|
||||
let left_count = services
|
||||
.rooms
|
||||
.state_cache
|
||||
@@ -492,7 +478,7 @@ async fn handle_left_room(
|
||||
account_data: RoomAccountData { events: Vec::new() },
|
||||
timeline: Timeline {
|
||||
limited: false,
|
||||
prev_batch: Some(next_batch.to_string()),
|
||||
prev_batch: Some(next_batch_string.to_owned()),
|
||||
events: Vec::new(),
|
||||
},
|
||||
state: RoomState {
|
||||
@@ -556,32 +542,28 @@ async fn handle_left_room(
|
||||
.get_statekey_from_short(shortstatekey)
|
||||
.await?;
|
||||
|
||||
if filter.room.state.lazy_load_options.is_enabled()
|
||||
&& event_type == StateEventType::RoomMember
|
||||
&& !full_state
|
||||
&& state_key
|
||||
.as_str()
|
||||
.try_into()
|
||||
.is_ok_and(|user_id: &UserId| sender_user != user_id)
|
||||
// TODO: Delete "element_hacks" when this is resolved: https://github.com/vector-im/element-web/issues/22565
|
||||
if !lazy_load_enabled
|
||||
|| event_type != StateEventType::RoomMember
|
||||
|| full_state
|
||||
|| (cfg!(feature = "element_hacks") && *sender_user == state_key)
|
||||
{
|
||||
continue;
|
||||
let Ok(pdu) = services.rooms.timeline.get_pdu(&event_id).await else {
|
||||
error!("Pdu in state not found: {event_id}");
|
||||
continue;
|
||||
};
|
||||
|
||||
left_state_events.push(pdu.to_sync_state_event());
|
||||
}
|
||||
|
||||
let Ok(pdu) = services.rooms.timeline.get_pdu(&event_id).await else {
|
||||
error!("Pdu in state not found: {event_id}");
|
||||
continue;
|
||||
};
|
||||
|
||||
left_state_events.push(pdu.to_sync_state_event());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Some(LeftRoom {
|
||||
account_data: RoomAccountData { events: Vec::new() },
|
||||
timeline: Timeline {
|
||||
// TODO: support left timeline events so we dont need to set limited to true
|
||||
limited: true,
|
||||
prev_batch: Some(next_batch.to_string()),
|
||||
limited: true, /* TODO: support left timeline events so we dont need to set this to
|
||||
* true */
|
||||
prev_batch: Some(next_batch_string.to_owned()),
|
||||
events: Vec::new(), // and so we dont need to set this to empty vec
|
||||
},
|
||||
state: RoomState { events: left_state_events },
|
||||
@@ -604,9 +586,15 @@ async fn load_joined_room(
|
||||
ref room_id: OwnedRoomId,
|
||||
since: u64,
|
||||
next_batch: u64,
|
||||
lazy_load_enabled: bool,
|
||||
lazy_load_send_redundant: bool,
|
||||
full_state: bool,
|
||||
filter: &FilterDefinition,
|
||||
) -> Result<(JoinedRoom, HashSet<OwnedUserId>, HashSet<OwnedUserId>)> {
|
||||
// Get and drop the lock to wait for remaining operations to finish
|
||||
// This will make sure the we have all events until next_batch
|
||||
let insert_lock = services.rooms.timeline.mutex_insert.lock(room_id).await;
|
||||
drop(insert_lock);
|
||||
|
||||
let sincecount = PduCount::Normal(since);
|
||||
let next_batchcount = PduCount::Normal(next_batch);
|
||||
|
||||
@@ -632,26 +620,17 @@ async fn load_joined_room(
|
||||
10_usize,
|
||||
);
|
||||
|
||||
let receipt_events = services
|
||||
.rooms
|
||||
.read_receipt
|
||||
.readreceipts_since(room_id, since)
|
||||
.filter_map(|(read_user, _, edu)| async move {
|
||||
services
|
||||
.users
|
||||
.user_is_ignored(read_user, sender_user)
|
||||
.await
|
||||
.or_some((read_user.to_owned(), edu))
|
||||
})
|
||||
.collect::<HashMap<OwnedUserId, Raw<AnySyncEphemeralRoomEvent>>>()
|
||||
.map(Ok);
|
||||
|
||||
let (current_shortstatehash, since_shortstatehash, timeline, receipt_events) =
|
||||
try_join4(current_shortstatehash, since_shortstatehash, timeline, receipt_events)
|
||||
.boxed()
|
||||
.await?;
|
||||
let (current_shortstatehash, since_shortstatehash, timeline) =
|
||||
try_join3(current_shortstatehash, since_shortstatehash, timeline).await?;
|
||||
|
||||
let (timeline_pdus, limited) = timeline;
|
||||
let timeline_users =
|
||||
timeline_pdus
|
||||
.iter()
|
||||
.fold(HashSet::new(), |mut timeline_users, (_, event)| {
|
||||
timeline_users.insert(event.sender.as_str().to_owned());
|
||||
timeline_users
|
||||
});
|
||||
|
||||
let last_notification_read: OptionFuture<_> = timeline_pdus
|
||||
.is_empty()
|
||||
@@ -663,68 +642,21 @@ async fn load_joined_room(
|
||||
})
|
||||
.into();
|
||||
|
||||
let send_notification_counts = last_notification_read
|
||||
.is_none_or(|&count| count > since)
|
||||
.await;
|
||||
|
||||
services.rooms.lazy_loading.lazy_load_confirm_delivery(
|
||||
sender_user,
|
||||
sender_device,
|
||||
room_id,
|
||||
sincecount,
|
||||
);
|
||||
|
||||
let no_state_changes = timeline_pdus.is_empty()
|
||||
&& (since_shortstatehash.is_none()
|
||||
|| since_shortstatehash.is_some_and(is_equal_to!(current_shortstatehash)));
|
||||
|
||||
let since_sender_member: OptionFuture<_> = since_shortstatehash
|
||||
.map(|short| {
|
||||
services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.state_get_content(short, &StateEventType::RoomMember, sender_user.as_str())
|
||||
.ok()
|
||||
})
|
||||
.into();
|
||||
|
||||
let joined_since_last_sync =
|
||||
since_sender_member
|
||||
.await
|
||||
.flatten()
|
||||
.is_none_or(|content: RoomMemberEventContent| {
|
||||
content.membership != MembershipState::Join
|
||||
});
|
||||
|
||||
let lazy_loading_enabled = filter.room.state.lazy_load_options.is_enabled()
|
||||
|| filter.room.timeline.lazy_load_options.is_enabled();
|
||||
|
||||
let generate_witness =
|
||||
lazy_loading_enabled && (since_shortstatehash.is_none() || joined_since_last_sync);
|
||||
|
||||
let lazy_reset = lazy_loading_enabled && since_shortstatehash.is_none();
|
||||
|
||||
let lazy_loading_context = &lazy_loading::Context {
|
||||
user_id: sender_user,
|
||||
device_id: sender_device,
|
||||
room_id,
|
||||
token: None,
|
||||
options: Some(&filter.room.state.lazy_load_options),
|
||||
};
|
||||
|
||||
// Reset lazy loading because this is an initial sync
|
||||
let lazy_load_reset: OptionFuture<_> = lazy_reset
|
||||
.then(|| services.rooms.lazy_loading.reset(lazy_loading_context))
|
||||
.into();
|
||||
|
||||
lazy_load_reset.await;
|
||||
let witness: Option<Witness> = generate_witness.then(|| {
|
||||
timeline_pdus
|
||||
.iter()
|
||||
.map(|(_, pdu)| pdu.sender.clone())
|
||||
.chain(receipt_events.keys().cloned())
|
||||
.collect()
|
||||
});
|
||||
|
||||
let witness: OptionFuture<_> = witness
|
||||
.map(|witness| {
|
||||
services
|
||||
.rooms
|
||||
.lazy_loading
|
||||
.witness_retain(witness, lazy_loading_context)
|
||||
})
|
||||
.into();
|
||||
|
||||
let witness = witness.await;
|
||||
let mut device_list_updates = HashSet::<OwnedUserId>::new();
|
||||
let mut left_encrypted_users = HashSet::<OwnedUserId>::new();
|
||||
let StateChanges {
|
||||
@@ -739,17 +671,19 @@ async fn load_joined_room(
|
||||
calculate_state_changes(
|
||||
services,
|
||||
sender_user,
|
||||
sender_device,
|
||||
room_id,
|
||||
next_batchcount,
|
||||
lazy_load_enabled,
|
||||
lazy_load_send_redundant,
|
||||
full_state,
|
||||
filter,
|
||||
&mut device_list_updates,
|
||||
&mut left_encrypted_users,
|
||||
since_shortstatehash,
|
||||
current_shortstatehash,
|
||||
joined_since_last_sync,
|
||||
witness.as_ref(),
|
||||
&timeline_pdus,
|
||||
&timeline_users,
|
||||
)
|
||||
.boxed()
|
||||
.await?
|
||||
};
|
||||
|
||||
@@ -774,6 +708,19 @@ async fn load_joined_room(
|
||||
.map(|(_, pdu)| pdu.to_sync_room_event())
|
||||
.collect();
|
||||
|
||||
let receipt_events = services
|
||||
.rooms
|
||||
.read_receipt
|
||||
.readreceipts_since(room_id, since)
|
||||
.filter_map(|(read_user, _, edu)| async move {
|
||||
services
|
||||
.users
|
||||
.user_is_ignored(read_user, sender_user)
|
||||
.await
|
||||
.or_some((read_user.to_owned(), edu))
|
||||
})
|
||||
.collect::<HashMap<OwnedUserId, Raw<AnySyncEphemeralRoomEvent>>>();
|
||||
|
||||
let typing_events = services
|
||||
.rooms
|
||||
.typing
|
||||
@@ -793,10 +740,6 @@ async fn load_joined_room(
|
||||
})
|
||||
.unwrap_or(Vec::new());
|
||||
|
||||
let send_notification_counts = last_notification_read
|
||||
.is_none_or(|&count| count > since)
|
||||
.await;
|
||||
|
||||
let notification_count: OptionFuture<_> = send_notification_counts
|
||||
.then(|| {
|
||||
services
|
||||
@@ -819,14 +762,14 @@ async fn load_joined_room(
|
||||
})
|
||||
.into();
|
||||
|
||||
let events = join3(room_events, account_data_events, typing_events);
|
||||
let events = join4(room_events, account_data_events, receipt_events, typing_events);
|
||||
let unread_notifications = join(notification_count, highlight_count);
|
||||
let (unread_notifications, events, device_updates) =
|
||||
join3(unread_notifications, events, device_updates)
|
||||
.boxed()
|
||||
.await;
|
||||
|
||||
let (room_events, account_data_events, typing_events) = events;
|
||||
let (room_events, account_data_events, receipt_events, typing_events) = events;
|
||||
let (notification_count, highlight_count) = unread_notifications;
|
||||
|
||||
device_list_updates.extend(device_updates);
|
||||
@@ -903,6 +846,7 @@ async fn load_joined_room(
|
||||
skip_all,
|
||||
fields(
|
||||
full = %full_state,
|
||||
ll = ?(lazy_load_enabled, lazy_load_send_redundant),
|
||||
cs = %current_shortstatehash,
|
||||
ss = ?since_shortstatehash,
|
||||
)
|
||||
@@ -911,38 +855,64 @@ async fn load_joined_room(
|
||||
async fn calculate_state_changes(
|
||||
services: &Services,
|
||||
sender_user: &UserId,
|
||||
sender_device: &DeviceId,
|
||||
room_id: &RoomId,
|
||||
next_batchcount: PduCount,
|
||||
lazy_load_enabled: bool,
|
||||
lazy_load_send_redundant: bool,
|
||||
full_state: bool,
|
||||
filter: &FilterDefinition,
|
||||
device_list_updates: &mut HashSet<OwnedUserId>,
|
||||
left_encrypted_users: &mut HashSet<OwnedUserId>,
|
||||
since_shortstatehash: Option<ShortStateHash>,
|
||||
current_shortstatehash: ShortStateHash,
|
||||
joined_since_last_sync: bool,
|
||||
witness: Option<&Witness>,
|
||||
timeline_pdus: &Vec<(PduCount, PduEvent)>,
|
||||
timeline_users: &HashSet<String>,
|
||||
) -> Result<StateChanges> {
|
||||
let since_sender_member: OptionFuture<_> = since_shortstatehash
|
||||
.map(|short| {
|
||||
services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.state_get_content(short, &StateEventType::RoomMember, sender_user.as_str())
|
||||
.ok()
|
||||
})
|
||||
.into();
|
||||
|
||||
let joined_since_last_sync =
|
||||
since_sender_member
|
||||
.await
|
||||
.flatten()
|
||||
.is_none_or(|content: RoomMemberEventContent| {
|
||||
content.membership != MembershipState::Join
|
||||
});
|
||||
|
||||
if since_shortstatehash.is_none() || joined_since_last_sync {
|
||||
calculate_state_initial(
|
||||
services,
|
||||
sender_user,
|
||||
sender_device,
|
||||
room_id,
|
||||
next_batchcount,
|
||||
lazy_load_enabled,
|
||||
full_state,
|
||||
filter,
|
||||
current_shortstatehash,
|
||||
witness,
|
||||
timeline_users,
|
||||
)
|
||||
.await
|
||||
} else {
|
||||
calculate_state_incremental(
|
||||
services,
|
||||
sender_user,
|
||||
sender_device,
|
||||
room_id,
|
||||
next_batchcount,
|
||||
lazy_load_send_redundant,
|
||||
full_state,
|
||||
filter,
|
||||
device_list_updates,
|
||||
left_encrypted_users,
|
||||
since_shortstatehash,
|
||||
current_shortstatehash,
|
||||
timeline_pdus,
|
||||
joined_since_last_sync,
|
||||
)
|
||||
.await
|
||||
@@ -954,54 +924,87 @@ async fn calculate_state_changes(
|
||||
async fn calculate_state_initial(
|
||||
services: &Services,
|
||||
sender_user: &UserId,
|
||||
sender_device: &DeviceId,
|
||||
room_id: &RoomId,
|
||||
next_batchcount: PduCount,
|
||||
lazy_load_enabled: bool,
|
||||
full_state: bool,
|
||||
filter: &FilterDefinition,
|
||||
current_shortstatehash: ShortStateHash,
|
||||
witness: Option<&Witness>,
|
||||
timeline_users: &HashSet<String>,
|
||||
) -> Result<StateChanges> {
|
||||
let state_events = services
|
||||
// Probably since = 0, we will do an initial sync
|
||||
let state = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.state_full_ids(current_shortstatehash)
|
||||
.await?;
|
||||
.await?
|
||||
.into_iter()
|
||||
.stream()
|
||||
.broad_filter_map(|(shortstatekey, event_id): (ShortStateKey, OwnedEventId)| {
|
||||
services
|
||||
.rooms
|
||||
.short
|
||||
.get_statekey_from_short(shortstatekey)
|
||||
.map_ok(move |(event_type, state_key)| ((event_type, state_key), event_id))
|
||||
.ok()
|
||||
})
|
||||
.fold((Vec::new(), HashSet::new()), |a, item| async move {
|
||||
let (mut state_events, mut lazy_loaded) = a;
|
||||
let ((event_type, state_key), event_id) = item;
|
||||
|
||||
let shortstatekeys = state_events.keys().copied().stream();
|
||||
if event_type != StateEventType::RoomMember {
|
||||
let Ok(pdu) = services.rooms.timeline.get_pdu(&event_id).await else {
|
||||
error!("Pdu in state not found: {event_id}");
|
||||
return (state_events, lazy_loaded);
|
||||
};
|
||||
|
||||
let state_events = services
|
||||
.rooms
|
||||
.short
|
||||
.multi_get_statekey_from_short(shortstatekeys)
|
||||
.zip(state_events.values().cloned().stream())
|
||||
.ready_filter_map(|item| Some((item.0.ok()?, item.1)))
|
||||
.ready_filter_map(|((event_type, state_key), event_id)| {
|
||||
let lazy_load_enabled = filter.room.state.lazy_load_options.is_enabled()
|
||||
|| filter.room.timeline.lazy_load_options.is_enabled();
|
||||
|
||||
if lazy_load_enabled
|
||||
&& event_type == StateEventType::RoomMember
|
||||
&& !full_state
|
||||
&& state_key.as_str().try_into().is_ok_and(|user_id: &UserId| {
|
||||
sender_user != user_id
|
||||
&& witness.is_some_and(|witness| !witness.contains(user_id))
|
||||
}) {
|
||||
return None;
|
||||
state_events.push(pdu);
|
||||
return (state_events, lazy_loaded);
|
||||
}
|
||||
|
||||
Some(event_id)
|
||||
// TODO: Delete "element_hacks" when this is resolved: https://github.com/vector-im/element-web/issues/22565
|
||||
if !lazy_load_enabled
|
||||
|| full_state
|
||||
|| timeline_users.contains(&state_key)
|
||||
|| (cfg!(feature = "element_hacks") && *sender_user == state_key)
|
||||
{
|
||||
let Ok(pdu) = services.rooms.timeline.get_pdu(&event_id).await else {
|
||||
error!("Pdu in state not found: {event_id}");
|
||||
return (state_events, lazy_loaded);
|
||||
};
|
||||
|
||||
// This check is in case a bad user ID made it into the database
|
||||
if let Ok(uid) = OwnedUserId::parse(&state_key) {
|
||||
lazy_loaded.insert(uid);
|
||||
}
|
||||
|
||||
state_events.push(pdu);
|
||||
}
|
||||
|
||||
(state_events, lazy_loaded)
|
||||
})
|
||||
.broad_filter_map(|event_id: OwnedEventId| async move {
|
||||
services.rooms.timeline.get_pdu(&event_id).await.ok()
|
||||
})
|
||||
.collect()
|
||||
.map(Ok);
|
||||
|
||||
let counts = calculate_counts(services, room_id, sender_user);
|
||||
let ((joined_member_count, invited_member_count, heroes), state_events) =
|
||||
try_join(counts, state_events).boxed().await?;
|
||||
let ((joined_member_count, invited_member_count, heroes), (state_events, lazy_loaded)) =
|
||||
try_join(counts, state).boxed().await?;
|
||||
|
||||
// Reset lazy loading because this is an initial sync
|
||||
services
|
||||
.rooms
|
||||
.lazy_loading
|
||||
.lazy_load_reset(sender_user, sender_device, room_id)
|
||||
.await;
|
||||
|
||||
// The state_events above should contain all timeline_users, let's mark them as
|
||||
// lazy loaded.
|
||||
services.rooms.lazy_loading.lazy_load_mark_sent(
|
||||
sender_user,
|
||||
sender_device,
|
||||
room_id,
|
||||
lazy_loaded,
|
||||
next_batchcount,
|
||||
);
|
||||
|
||||
Ok(StateChanges {
|
||||
heroes,
|
||||
@@ -1017,13 +1020,16 @@ async fn calculate_state_initial(
|
||||
async fn calculate_state_incremental(
|
||||
services: &Services,
|
||||
sender_user: &UserId,
|
||||
sender_device: &DeviceId,
|
||||
room_id: &RoomId,
|
||||
next_batchcount: PduCount,
|
||||
lazy_load_send_redundant: bool,
|
||||
full_state: bool,
|
||||
_filter: &FilterDefinition,
|
||||
device_list_updates: &mut HashSet<OwnedUserId>,
|
||||
left_encrypted_users: &mut HashSet<OwnedUserId>,
|
||||
since_shortstatehash: Option<ShortStateHash>,
|
||||
current_shortstatehash: ShortStateHash,
|
||||
timeline_pdus: &Vec<(PduCount, PduEvent)>,
|
||||
joined_since_last_sync: bool,
|
||||
) -> Result<StateChanges> {
|
||||
// Incremental /sync
|
||||
@@ -1136,12 +1142,76 @@ async fn calculate_state_incremental(
|
||||
(None, None, None)
|
||||
};
|
||||
|
||||
let mut state_events = delta_state_events;
|
||||
|
||||
// Mark all member events we're returning as lazy-loaded
|
||||
let mut lazy_loaded = state_events
|
||||
.iter()
|
||||
.filter(|pdu| pdu.kind == RoomMember)
|
||||
.filter_map(|pdu| {
|
||||
pdu.state_key
|
||||
.clone()
|
||||
.map(TryInto::try_into)
|
||||
.map(LogDebugErr::log_debug_err)
|
||||
.flat_ok()
|
||||
})
|
||||
.fold(HashSet::new(), |mut lazy_loaded, user_id| {
|
||||
lazy_loaded.insert(user_id);
|
||||
lazy_loaded
|
||||
});
|
||||
|
||||
// Fetch contextual member state events for events from the timeline, and
|
||||
// mark them as lazy-loaded as well.
|
||||
for (_, event) in timeline_pdus {
|
||||
if lazy_loaded.contains(&event.sender) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let sent_before: OptionFuture<_> = (!lazy_load_send_redundant)
|
||||
.then(|| {
|
||||
services.rooms.lazy_loading.lazy_load_was_sent_before(
|
||||
sender_user,
|
||||
sender_device,
|
||||
room_id,
|
||||
&event.sender,
|
||||
)
|
||||
})
|
||||
.into();
|
||||
|
||||
let member_event: OptionFuture<_> = sent_before
|
||||
.await
|
||||
.is_none_or(is_false!())
|
||||
.then(|| {
|
||||
services.rooms.state_accessor.room_state_get(
|
||||
room_id,
|
||||
&StateEventType::RoomMember,
|
||||
event.sender.as_str(),
|
||||
)
|
||||
})
|
||||
.into();
|
||||
|
||||
let Some(Ok(member_event)) = member_event.await else {
|
||||
continue;
|
||||
};
|
||||
|
||||
lazy_loaded.insert(event.sender.clone());
|
||||
state_events.push(member_event);
|
||||
}
|
||||
|
||||
services.rooms.lazy_loading.lazy_load_mark_sent(
|
||||
sender_user,
|
||||
sender_device,
|
||||
room_id,
|
||||
lazy_loaded,
|
||||
next_batchcount,
|
||||
);
|
||||
|
||||
Ok(StateChanges {
|
||||
heroes,
|
||||
joined_member_count,
|
||||
invited_member_count,
|
||||
joined_since_last_sync,
|
||||
state_events: delta_state_events,
|
||||
state_events,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -112,22 +112,12 @@ pub(crate) async fn sync_events_v4_route(
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
let all_knocked_rooms: Vec<_> = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.rooms_knocked(sender_user)
|
||||
.map(|r| r.0)
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
let all_invited_rooms: Vec<&RoomId> = all_invited_rooms.iter().map(AsRef::as_ref).collect();
|
||||
let all_knocked_rooms: Vec<&RoomId> = all_knocked_rooms.iter().map(AsRef::as_ref).collect();
|
||||
|
||||
let all_rooms: Vec<&RoomId> = all_joined_rooms
|
||||
.iter()
|
||||
.map(AsRef::as_ref)
|
||||
.chain(all_invited_rooms.iter().map(AsRef::as_ref))
|
||||
.chain(all_knocked_rooms.iter().map(AsRef::as_ref))
|
||||
.collect();
|
||||
|
||||
let all_joined_rooms = all_joined_rooms.iter().map(AsRef::as_ref).collect();
|
||||
|
||||
+17
-32
@@ -37,16 +37,6 @@ use crate::{
|
||||
|
||||
type SyncInfo<'a> = (&'a UserId, &'a DeviceId, u64, &'a sync_events::v5::Request);
|
||||
|
||||
/// `POST /_matrix/client/unstable/org.matrix.simplified_msc3575/sync`
|
||||
/// ([MSC4186])
|
||||
///
|
||||
/// A simplified version of sliding sync ([MSC3575]).
|
||||
///
|
||||
/// Get all new events in a sliding window of rooms since the last sync or a
|
||||
/// given point in time.
|
||||
///
|
||||
/// [MSC3575]: https://github.com/matrix-org/matrix-spec-proposals/pull/3575
|
||||
/// [MSC4186]: https://github.com/matrix-org/matrix-spec-proposals/pull/4186
|
||||
pub(crate) async fn sync_events_v5_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<sync_events::v5::Request>,
|
||||
@@ -115,19 +105,10 @@ pub(crate) async fn sync_events_v5_route(
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
let all_knocked_rooms: Vec<_> = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.rooms_knocked(sender_user)
|
||||
.map(|r| r.0)
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
let all_rooms: Vec<&RoomId> = all_joined_rooms
|
||||
.iter()
|
||||
.map(AsRef::as_ref)
|
||||
.chain(all_invited_rooms.iter().map(AsRef::as_ref))
|
||||
.chain(all_knocked_rooms.iter().map(AsRef::as_ref))
|
||||
.collect();
|
||||
|
||||
let all_joined_rooms = all_joined_rooms.iter().map(AsRef::as_ref).collect();
|
||||
@@ -152,19 +133,23 @@ pub(crate) async fn sync_events_v5_route(
|
||||
},
|
||||
};
|
||||
|
||||
handle_lists(
|
||||
services,
|
||||
sync_info,
|
||||
&all_invited_rooms,
|
||||
&all_joined_rooms,
|
||||
&all_rooms,
|
||||
&mut todo_rooms,
|
||||
&known_rooms,
|
||||
&mut response,
|
||||
)
|
||||
.await;
|
||||
{
|
||||
let _test2 = handle_lists(
|
||||
services,
|
||||
sync_info,
|
||||
&all_invited_rooms,
|
||||
&all_joined_rooms,
|
||||
&all_rooms,
|
||||
&mut todo_rooms,
|
||||
&known_rooms,
|
||||
&mut response,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
fetch_subscriptions(services, sync_info, &known_rooms, &mut todo_rooms).await;
|
||||
{
|
||||
fetch_subscriptions(services, sync_info, &known_rooms, &mut todo_rooms).await;
|
||||
};
|
||||
|
||||
response.rooms = process_rooms(
|
||||
services,
|
||||
@@ -385,7 +370,7 @@ async fn process_rooms(
|
||||
};
|
||||
}
|
||||
|
||||
if body.extensions.account_data.enabled == Some(true) {
|
||||
if body.extensions.to_device.enabled == Some(true) {
|
||||
response.extensions.account_data.rooms.insert(
|
||||
room_id.to_owned(),
|
||||
services
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use axum::extract::State;
|
||||
use conduwuit::{utils::math::Tried, Err};
|
||||
use conduwuit::Err;
|
||||
use ruma::api::client::typing::create_typing_event;
|
||||
|
||||
use crate::{utils, Result, Ruma};
|
||||
@@ -31,15 +31,17 @@ pub(crate) async fn create_typing_event_route(
|
||||
let duration = utils::clamp(
|
||||
duration.as_millis().try_into().unwrap_or(u64::MAX),
|
||||
services
|
||||
.server
|
||||
.globals
|
||||
.config
|
||||
.typing_client_timeout_min_s
|
||||
.try_mul(1000)?,
|
||||
.checked_mul(1000)
|
||||
.unwrap(),
|
||||
services
|
||||
.server
|
||||
.globals
|
||||
.config
|
||||
.typing_client_timeout_max_s
|
||||
.try_mul(1000)?,
|
||||
.checked_mul(1000)
|
||||
.unwrap(),
|
||||
);
|
||||
services
|
||||
.rooms
|
||||
|
||||
@@ -38,7 +38,7 @@ pub(crate) async fn turn_server_route(
|
||||
let user = body.sender_user.unwrap_or_else(|| {
|
||||
UserId::parse_with_server_name(
|
||||
utils::random_string(RANDOM_USER_ID_LENGTH).to_lowercase(),
|
||||
&services.server.config.server_name,
|
||||
&services.globals.config.server_name,
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
@@ -34,7 +34,6 @@ pub fn build(router: Router<State>, server: &Server) -> Router<State> {
|
||||
.ruma_route(&client::register_route)
|
||||
.ruma_route(&client::get_login_types_route)
|
||||
.ruma_route(&client::login_route)
|
||||
.ruma_route(&client::login_token_route)
|
||||
.ruma_route(&client::whoami_route)
|
||||
.ruma_route(&client::logout_route)
|
||||
.ruma_route(&client::logout_all_route)
|
||||
@@ -100,7 +99,6 @@ pub fn build(router: Router<State>, server: &Server) -> Router<State> {
|
||||
.ruma_route(&client::join_room_by_id_route)
|
||||
.ruma_route(&client::join_room_by_id_or_alias_route)
|
||||
.ruma_route(&client::joined_members_route)
|
||||
.ruma_route(&client::knock_room_route)
|
||||
.ruma_route(&client::leave_room_route)
|
||||
.ruma_route(&client::forget_room_route)
|
||||
.ruma_route(&client::joined_rooms_route)
|
||||
@@ -207,10 +205,8 @@ pub fn build(router: Router<State>, server: &Server) -> Router<State> {
|
||||
.ruma_route(&server::get_room_state_route)
|
||||
.ruma_route(&server::get_room_state_ids_route)
|
||||
.ruma_route(&server::create_leave_event_template_route)
|
||||
.ruma_route(&server::create_knock_event_template_route)
|
||||
.ruma_route(&server::create_leave_event_v1_route)
|
||||
.ruma_route(&server::create_leave_event_v2_route)
|
||||
.ruma_route(&server::create_knock_event_v1_route)
|
||||
.ruma_route(&server::create_join_event_template_route)
|
||||
.ruma_route(&server::create_join_event_v1_route)
|
||||
.ruma_route(&server::create_join_event_v2_route)
|
||||
|
||||
@@ -71,7 +71,7 @@ pub(super) async fn auth(
|
||||
match metadata {
|
||||
| &get_public_rooms::v3::Request::METADATA => {
|
||||
if !services
|
||||
.server
|
||||
.globals
|
||||
.config
|
||||
.allow_public_room_directory_without_auth
|
||||
{
|
||||
@@ -94,7 +94,7 @@ pub(super) async fn auth(
|
||||
| &get_display_name::v3::Request::METADATA
|
||||
| &get_avatar_url::v3::Request::METADATA
|
||||
| &get_timezone_key::unstable::Request::METADATA => {
|
||||
if services.server.config.require_auth_for_profile_requests {
|
||||
if services.globals.config.require_auth_for_profile_requests {
|
||||
match token {
|
||||
| Token::Appservice(_) | Token::User(_) => {
|
||||
// we should have validated the token above
|
||||
@@ -127,7 +127,7 @@ pub(super) async fn auth(
|
||||
}),
|
||||
| (AuthScheme::AccessToken, Token::None) => match metadata {
|
||||
| &get_turn_server_info::v3::Request::METADATA => {
|
||||
if services.server.config.turn_allow_guests {
|
||||
if services.globals.config.turn_allow_guests {
|
||||
Ok(Auth {
|
||||
origin: None,
|
||||
sender_user: None,
|
||||
|
||||
@@ -32,7 +32,7 @@ pub(super) async fn from(
|
||||
let query = serde_html_form::from_str(query)
|
||||
.map_err(|e| err!(Request(Unknown("Failed to read query parameters: {e}"))))?;
|
||||
|
||||
let max_body_size = services.server.config.max_request_size;
|
||||
let max_body_size = services.globals.config.max_request_size;
|
||||
|
||||
let body = axum::body::to_bytes(body, max_body_size)
|
||||
.await
|
||||
|
||||
@@ -6,9 +6,8 @@ use ruma::{
|
||||
api::{client::error::ErrorKind, federation::membership::create_invite},
|
||||
events::room::member::{MembershipState, RoomMemberEventContent},
|
||||
serde::JsonObject,
|
||||
CanonicalJsonValue, OwnedUserId, UserId,
|
||||
CanonicalJsonValue, OwnedEventId, OwnedUserId, UserId,
|
||||
};
|
||||
use service::pdu::gen_event_id;
|
||||
|
||||
use crate::Ruma;
|
||||
|
||||
@@ -37,7 +36,7 @@ pub(crate) async fn create_invite_route(
|
||||
|
||||
if let Some(server) = body.room_id.server_name() {
|
||||
if services
|
||||
.server
|
||||
.globals
|
||||
.config
|
||||
.forbidden_remote_server_names
|
||||
.contains(&server.to_owned())
|
||||
@@ -47,7 +46,7 @@ pub(crate) async fn create_invite_route(
|
||||
}
|
||||
|
||||
if services
|
||||
.server
|
||||
.globals
|
||||
.config
|
||||
.forbidden_remote_server_names
|
||||
.contains(body.origin())
|
||||
@@ -87,7 +86,12 @@ pub(crate) async fn create_invite_route(
|
||||
.map_err(|e| err!(Request(InvalidParam("Failed to sign event: {e}"))))?;
|
||||
|
||||
// Generate event id
|
||||
let event_id = gen_event_id(&signed_event, &body.room_version)?;
|
||||
let event_id = OwnedEventId::parse(format!(
|
||||
"${}",
|
||||
ruma::signatures::reference_hash(&signed_event, &body.room_version)
|
||||
.expect("ruma can calculate reference hashes")
|
||||
))
|
||||
.expect("ruma's reference hashes are valid event ids");
|
||||
|
||||
// Add event_id back
|
||||
signed_event.insert("event_id".to_owned(), CanonicalJsonValue::String(event_id.to_string()));
|
||||
@@ -111,12 +115,12 @@ pub(crate) async fn create_invite_route(
|
||||
let mut invite_state = body.invite_room_state.clone();
|
||||
|
||||
let mut event: JsonObject = serde_json::from_str(body.event.get())
|
||||
.map_err(|e| err!(Request(BadJson("Invalid invite event PDU: {e}"))))?;
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Invalid invite event bytes."))?;
|
||||
|
||||
event.insert("event_id".to_owned(), "$placeholder".into());
|
||||
|
||||
let pdu: PduEvent = serde_json::from_value(event.into())
|
||||
.map_err(|e| err!(Request(BadJson("Invalid invite event PDU: {e}"))))?;
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Invalid invite event."))?;
|
||||
|
||||
invite_state.push(pdu.to_stripped_state_event());
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ pub(crate) async fn create_join_event_template_route(
|
||||
.await?;
|
||||
|
||||
if services
|
||||
.server
|
||||
.globals
|
||||
.config
|
||||
.forbidden_remote_server_names
|
||||
.contains(body.origin())
|
||||
@@ -59,7 +59,7 @@ pub(crate) async fn create_join_event_template_route(
|
||||
|
||||
if let Some(server) = body.room_id.server_name() {
|
||||
if services
|
||||
.server
|
||||
.globals
|
||||
.config
|
||||
.forbidden_remote_server_names
|
||||
.contains(&server.to_owned())
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use axum::extract::State;
|
||||
use conduwuit::{debug_warn, Err};
|
||||
use conduwuit::Err;
|
||||
use ruma::{
|
||||
api::{client::error::ErrorKind, federation::knock::create_knock_event_template},
|
||||
events::room::member::{MembershipState, RoomMemberEventContent},
|
||||
@@ -15,8 +15,7 @@ use crate::{service::pdu::PduBuilder, Error, Result, Ruma};
|
||||
///
|
||||
/// Creates a knock template.
|
||||
pub(crate) async fn create_knock_event_template_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<create_knock_event_template::v1::Request>,
|
||||
State(services): State<crate::State>, body: Ruma<create_knock_event_template::v1::Request>,
|
||||
) -> Result<create_knock_event_template::v1::Response> {
|
||||
if !services.rooms.metadata.exists(&body.room_id).await {
|
||||
return Err!(Request(NotFound("Room is unknown to this server.")));
|
||||
@@ -34,14 +33,14 @@ pub(crate) async fn create_knock_event_template_route(
|
||||
.await?;
|
||||
|
||||
if services
|
||||
.server
|
||||
.globals
|
||||
.config
|
||||
.forbidden_remote_server_names
|
||||
.contains(body.origin())
|
||||
{
|
||||
warn!(
|
||||
"Server {} for remote user {} tried knocking room ID {} which has a server name \
|
||||
that is globally forbidden. Rejecting.",
|
||||
"Server {} for remote user {} tried knocking room ID {} which has a server name that is globally \
|
||||
forbidden. Rejecting.",
|
||||
body.origin(),
|
||||
&body.user_id,
|
||||
&body.room_id,
|
||||
@@ -51,7 +50,7 @@ pub(crate) async fn create_knock_event_template_route(
|
||||
|
||||
if let Some(server) = body.room_id.server_name() {
|
||||
if services
|
||||
.server
|
||||
.globals
|
||||
.config
|
||||
.forbidden_remote_server_names
|
||||
.contains(&server.to_owned())
|
||||
@@ -64,44 +63,29 @@ pub(crate) async fn create_knock_event_template_route(
|
||||
|
||||
if matches!(room_version_id, V1 | V2 | V3 | V4 | V5 | V6) {
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::IncompatibleRoomVersion { room_version: room_version_id },
|
||||
ErrorKind::IncompatibleRoomVersion {
|
||||
room_version: room_version_id,
|
||||
},
|
||||
"Room version does not support knocking.",
|
||||
));
|
||||
}
|
||||
|
||||
if !body.ver.contains(&room_version_id) {
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::IncompatibleRoomVersion { room_version: room_version_id },
|
||||
ErrorKind::IncompatibleRoomVersion {
|
||||
room_version: room_version_id,
|
||||
},
|
||||
"Your homeserver does not support the features required to knock on this room.",
|
||||
));
|
||||
}
|
||||
|
||||
let state_lock = services.rooms.state.mutex.lock(&body.room_id).await;
|
||||
|
||||
if let Ok(membership) = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.get_member(&body.room_id, &body.user_id)
|
||||
.await
|
||||
{
|
||||
if membership.membership == MembershipState::Ban {
|
||||
debug_warn!(
|
||||
"Remote user {} is banned from {} but attempted to knock",
|
||||
&body.user_id,
|
||||
&body.room_id
|
||||
);
|
||||
return Err!(Request(Forbidden("You cannot knock on a room you are banned from.")));
|
||||
}
|
||||
}
|
||||
|
||||
let (_pdu, mut pdu_json) = services
|
||||
.rooms
|
||||
.timeline
|
||||
.create_hash_and_sign_event(
|
||||
PduBuilder::state(
|
||||
body.user_id.to_string(),
|
||||
&RoomMemberEventContent::new(MembershipState::Knock),
|
||||
),
|
||||
PduBuilder::state(body.user_id.to_string(), &RoomMemberEventContent::new(MembershipState::Knock)),
|
||||
&body.user_id,
|
||||
&body.room_id,
|
||||
&state_lock,
|
||||
|
||||
@@ -9,7 +9,7 @@ use serde_json::value::to_raw_value;
|
||||
use super::make_join::maybe_strip_event_id;
|
||||
use crate::{service::pdu::PduBuilder, Ruma};
|
||||
|
||||
/// # `GET /_matrix/federation/v1/make_leave/{roomId}/{eventId}`
|
||||
/// # `PUT /_matrix/federation/v1/make_leave/{roomId}/{eventId}`
|
||||
///
|
||||
/// Creates a leave template.
|
||||
pub(crate) async fn create_leave_event_template_route(
|
||||
@@ -21,9 +21,7 @@ pub(crate) async fn create_leave_event_template_route(
|
||||
}
|
||||
|
||||
if body.user_id.server_name() != body.origin() {
|
||||
return Err!(Request(Forbidden(
|
||||
"Not allowed to leave on behalf of another server/user."
|
||||
)));
|
||||
return Err!(Request(BadJson("Not allowed to leave on behalf of another server/user.")));
|
||||
}
|
||||
|
||||
// ACL check origin
|
||||
|
||||
+2
-12
@@ -14,12 +14,7 @@ use crate::Ruma;
|
||||
/// # `GET /_matrix/federation/v1/media/download/{mediaId}`
|
||||
///
|
||||
/// Load media from our server.
|
||||
#[tracing::instrument(
|
||||
name = "media_get",
|
||||
level = "debug",
|
||||
skip_all,
|
||||
fields(%client)
|
||||
)]
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "media_get")]
|
||||
pub(crate) async fn get_content_route(
|
||||
State(services): State<crate::State>,
|
||||
InsecureClientIp(client): InsecureClientIp,
|
||||
@@ -56,12 +51,7 @@ pub(crate) async fn get_content_route(
|
||||
/// # `GET /_matrix/federation/v1/media/thumbnail/{mediaId}`
|
||||
///
|
||||
/// Load media thumbnail from our server.
|
||||
#[tracing::instrument(
|
||||
name = "media_thumbnail_get",
|
||||
level = "debug",
|
||||
skip_all,
|
||||
fields(%client)
|
||||
)]
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "media_thumbnail_get")]
|
||||
pub(crate) async fn get_content_thumbnail_route(
|
||||
State(services): State<crate::State>,
|
||||
InsecureClientIp(client): InsecureClientIp,
|
||||
|
||||
@@ -6,7 +6,6 @@ pub(super) mod hierarchy;
|
||||
pub(super) mod invite;
|
||||
pub(super) mod key;
|
||||
pub(super) mod make_join;
|
||||
pub(super) mod make_knock;
|
||||
pub(super) mod make_leave;
|
||||
pub(super) mod media;
|
||||
pub(super) mod openid;
|
||||
@@ -14,7 +13,6 @@ pub(super) mod publicrooms;
|
||||
pub(super) mod query;
|
||||
pub(super) mod send;
|
||||
pub(super) mod send_join;
|
||||
pub(super) mod send_knock;
|
||||
pub(super) mod send_leave;
|
||||
pub(super) mod state;
|
||||
pub(super) mod state_ids;
|
||||
@@ -30,7 +28,6 @@ pub(super) use hierarchy::*;
|
||||
pub(super) use invite::*;
|
||||
pub(super) use key::*;
|
||||
pub(super) use make_join::*;
|
||||
pub(super) use make_knock::*;
|
||||
pub(super) use make_leave::*;
|
||||
pub(super) use media::*;
|
||||
pub(super) use openid::*;
|
||||
@@ -38,7 +35,6 @@ pub(super) use publicrooms::*;
|
||||
pub(super) use query::*;
|
||||
pub(super) use send::*;
|
||||
pub(super) use send_join::*;
|
||||
pub(super) use send_knock::*;
|
||||
pub(super) use send_leave::*;
|
||||
pub(super) use state::*;
|
||||
pub(super) use state_ids::*;
|
||||
|
||||
@@ -20,7 +20,7 @@ pub(crate) async fn get_public_rooms_filtered_route(
|
||||
body: Ruma<get_public_rooms_filtered::v1::Request>,
|
||||
) -> Result<get_public_rooms_filtered::v1::Response> {
|
||||
if !services
|
||||
.server
|
||||
.globals
|
||||
.config
|
||||
.allow_public_room_directory_over_federation
|
||||
{
|
||||
|
||||
@@ -63,7 +63,7 @@ pub(crate) async fn get_profile_information_route(
|
||||
body: Ruma<get_profile_information::v1::Request>,
|
||||
) -> Result<get_profile_information::v1::Response> {
|
||||
if !services
|
||||
.server
|
||||
.globals
|
||||
.config
|
||||
.allow_inbound_profile_lookup_federation_requests
|
||||
{
|
||||
|
||||
+3
-11
@@ -39,15 +39,7 @@ type ResolvedMap = BTreeMap<OwnedEventId, Result<()>>;
|
||||
/// # `PUT /_matrix/federation/v1/send/{txnId}`
|
||||
///
|
||||
/// Push EDUs and PDUs to this server.
|
||||
#[tracing::instrument(
|
||||
name = "send",
|
||||
level = "debug",
|
||||
skip_all,
|
||||
fields(
|
||||
%client,
|
||||
origin = body.origin().as_str()
|
||||
),
|
||||
)]
|
||||
#[tracing::instrument(skip_all, fields(%client, origin = body.origin().as_str()), name = "send")]
|
||||
pub(crate) async fn send_transaction_message_route(
|
||||
State(services): State<crate::State>,
|
||||
InsecureClientIp(client): InsecureClientIp,
|
||||
@@ -309,7 +301,7 @@ async fn handle_edu_typing(
|
||||
origin: &ServerName,
|
||||
typing: TypingContent,
|
||||
) {
|
||||
if !services.server.config.allow_incoming_typing {
|
||||
if !services.globals.config.allow_incoming_typing {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -344,7 +336,7 @@ async fn handle_edu_typing(
|
||||
if typing.typing {
|
||||
let timeout = utils::millis_since_unix_epoch().saturating_add(
|
||||
services
|
||||
.server
|
||||
.globals
|
||||
.config
|
||||
.typing_federation_timeout_s
|
||||
.saturating_mul(1000),
|
||||
|
||||
+11
-10
@@ -186,13 +186,14 @@ async fn create_join_event(
|
||||
.map_err(|e| err!(Request(InvalidParam(warn!("Failed to sign send_join event: {e}")))))?;
|
||||
|
||||
let origin: OwnedServerName = serde_json::from_value(
|
||||
value
|
||||
.get("origin")
|
||||
.ok_or_else(|| err!(Request(BadJson("Event does not have an origin server name."))))?
|
||||
.clone()
|
||||
.into(),
|
||||
serde_json::to_value(
|
||||
value
|
||||
.get("origin")
|
||||
.ok_or_else(|| err!(Request(BadJson("Event missing origin property."))))?,
|
||||
)
|
||||
.expect("CanonicalJson is valid json value"),
|
||||
)
|
||||
.map_err(|e| err!(Request(BadJson("Event has an invalid origin server name: {e}"))))?;
|
||||
.map_err(|e| err!(Request(BadJson(warn!("origin field is not a valid server name: {e}")))))?;
|
||||
|
||||
let mutex_lock = services
|
||||
.rooms
|
||||
@@ -268,7 +269,7 @@ pub(crate) async fn create_join_event_v1_route(
|
||||
body: Ruma<create_join_event::v1::Request>,
|
||||
) -> Result<create_join_event::v1::Response> {
|
||||
if services
|
||||
.server
|
||||
.globals
|
||||
.config
|
||||
.forbidden_remote_server_names
|
||||
.contains(body.origin())
|
||||
@@ -284,7 +285,7 @@ pub(crate) async fn create_join_event_v1_route(
|
||||
|
||||
if let Some(server) = body.room_id.server_name() {
|
||||
if services
|
||||
.server
|
||||
.globals
|
||||
.config
|
||||
.forbidden_remote_server_names
|
||||
.contains(&server.to_owned())
|
||||
@@ -316,7 +317,7 @@ pub(crate) async fn create_join_event_v2_route(
|
||||
body: Ruma<create_join_event::v2::Request>,
|
||||
) -> Result<create_join_event::v2::Response> {
|
||||
if services
|
||||
.server
|
||||
.globals
|
||||
.config
|
||||
.forbidden_remote_server_names
|
||||
.contains(body.origin())
|
||||
@@ -326,7 +327,7 @@ pub(crate) async fn create_join_event_v2_route(
|
||||
|
||||
if let Some(server) = body.room_id.server_name() {
|
||||
if services
|
||||
.server
|
||||
.globals
|
||||
.config
|
||||
.forbidden_remote_server_names
|
||||
.contains(&server.to_owned())
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
use axum::extract::State;
|
||||
use conduwuit::{err, pdu::gen_event_id_canonical_json, warn, Err, PduEvent, Result};
|
||||
use futures::FutureExt;
|
||||
use conduwuit::{err, pdu::gen_event_id_canonical_json, warn, Err, Error, PduEvent, Result};
|
||||
use ruma::{
|
||||
api::federation::knock::send_knock,
|
||||
api::{client::error::ErrorKind, federation::knock::send_knock},
|
||||
events::{
|
||||
room::member::{MembershipState, RoomMemberEventContent},
|
||||
StateEventType,
|
||||
@@ -18,18 +17,16 @@ use crate::Ruma;
|
||||
///
|
||||
/// Submits a signed knock event.
|
||||
pub(crate) async fn create_knock_event_v1_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<send_knock::v1::Request>,
|
||||
State(services): State<crate::State>, body: Ruma<send_knock::v1::Request>,
|
||||
) -> Result<send_knock::v1::Response> {
|
||||
if services
|
||||
.server
|
||||
.globals
|
||||
.config
|
||||
.forbidden_remote_server_names
|
||||
.contains(body.origin())
|
||||
{
|
||||
warn!(
|
||||
"Server {} tried knocking room ID {} who has a server name that is globally \
|
||||
forbidden. Rejecting.",
|
||||
"Server {} tried knocking room ID {} who has a server name that is globally forbidden. Rejecting.",
|
||||
body.origin(),
|
||||
&body.room_id,
|
||||
);
|
||||
@@ -38,14 +35,13 @@ pub(crate) async fn create_knock_event_v1_route(
|
||||
|
||||
if let Some(server) = body.room_id.server_name() {
|
||||
if services
|
||||
.server
|
||||
.globals
|
||||
.config
|
||||
.forbidden_remote_server_names
|
||||
.contains(&server.to_owned())
|
||||
{
|
||||
warn!(
|
||||
"Server {} tried knocking room ID {} which has a server name that is globally \
|
||||
forbidden. Rejecting.",
|
||||
"Server {} tried knocking room ID {} which has a server name that is globally forbidden. Rejecting.",
|
||||
body.origin(),
|
||||
&body.room_id,
|
||||
);
|
||||
@@ -54,7 +50,7 @@ pub(crate) async fn create_knock_event_v1_route(
|
||||
}
|
||||
|
||||
if !services.rooms.metadata.exists(&body.room_id).await {
|
||||
return Err!(Request(NotFound("Room is unknown to this server.")));
|
||||
return Err(Error::BadRequest(ErrorKind::NotFound, "Room is unknown to this server."));
|
||||
}
|
||||
|
||||
// ACL check origin server
|
||||
@@ -78,42 +74,44 @@ pub(crate) async fn create_knock_event_v1_route(
|
||||
let event_type: StateEventType = serde_json::from_value(
|
||||
value
|
||||
.get("type")
|
||||
.ok_or_else(|| err!(Request(InvalidParam("Event has no event type."))))?
|
||||
.ok_or_else(|| Error::BadRequest(ErrorKind::InvalidParam, "Event missing type property."))?
|
||||
.clone()
|
||||
.into(),
|
||||
)
|
||||
.map_err(|e| err!(Request(InvalidParam("Event has invalid event type: {e}"))))?;
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Event has invalid event type."))?;
|
||||
|
||||
if event_type != StateEventType::RoomMember {
|
||||
return Err!(Request(InvalidParam(
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::InvalidParam,
|
||||
"Not allowed to send non-membership state event to knock endpoint.",
|
||||
)));
|
||||
));
|
||||
}
|
||||
|
||||
let content: RoomMemberEventContent = serde_json::from_value(
|
||||
value
|
||||
.get("content")
|
||||
.ok_or_else(|| err!(Request(InvalidParam("Membership event has no content"))))?
|
||||
.ok_or_else(|| Error::BadRequest(ErrorKind::InvalidParam, "Event missing content property"))?
|
||||
.clone()
|
||||
.into(),
|
||||
)
|
||||
.map_err(|e| err!(Request(InvalidParam("Event has invalid membership content: {e}"))))?;
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Event content is empty or invalid"))?;
|
||||
|
||||
if content.membership != MembershipState::Knock {
|
||||
return Err!(Request(InvalidParam(
|
||||
"Not allowed to send a non-knock membership event to knock endpoint."
|
||||
)));
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::InvalidParam,
|
||||
"Not allowed to send a non-knock membership event to knock endpoint.",
|
||||
));
|
||||
}
|
||||
|
||||
// ACL check sender server name
|
||||
let sender: OwnedUserId = serde_json::from_value(
|
||||
value
|
||||
.get("sender")
|
||||
.ok_or_else(|| err!(Request(InvalidParam("Event has no sender user ID."))))?
|
||||
.ok_or_else(|| Error::BadRequest(ErrorKind::InvalidParam, "Event missing sender property."))?
|
||||
.clone()
|
||||
.into(),
|
||||
)
|
||||
.map_err(|e| err!(Request(BadJson("Event sender is not a valid user ID: {e}"))))?;
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::BadJson, "sender is not a valid user ID."))?;
|
||||
|
||||
services
|
||||
.rooms
|
||||
@@ -129,32 +127,36 @@ pub(crate) async fn create_knock_event_v1_route(
|
||||
let state_key: OwnedUserId = serde_json::from_value(
|
||||
value
|
||||
.get("state_key")
|
||||
.ok_or_else(|| err!(Request(InvalidParam("Event does not have a state_key"))))?
|
||||
.ok_or_else(|| Error::BadRequest(ErrorKind::InvalidParam, "Event missing state_key property."))?
|
||||
.clone()
|
||||
.into(),
|
||||
)
|
||||
.map_err(|e| err!(Request(BadJson("Event does not have a valid state_key: {e}"))))?;
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::BadJson, "state_key is invalid or not a user ID."))?;
|
||||
|
||||
if state_key != sender {
|
||||
return Err!(Request(InvalidParam("state_key does not match sender user of event.")));
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::InvalidParam,
|
||||
"State key does not match sender user",
|
||||
));
|
||||
};
|
||||
|
||||
let origin: OwnedServerName = serde_json::from_value(
|
||||
value
|
||||
.get("origin")
|
||||
.ok_or_else(|| err!(Request(BadJson("Event does not have an origin server name."))))?
|
||||
.clone()
|
||||
.into(),
|
||||
serde_json::to_value(
|
||||
value
|
||||
.get("origin")
|
||||
.ok_or_else(|| Error::BadRequest(ErrorKind::InvalidParam, "Event missing origin property."))?,
|
||||
)
|
||||
.expect("CanonicalJson is valid json value"),
|
||||
)
|
||||
.map_err(|e| err!(Request(BadJson("Event has an invalid origin server name: {e}"))))?;
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "origin is not a server name."))?;
|
||||
|
||||
let mut event: JsonObject = serde_json::from_str(body.pdu.get())
|
||||
.map_err(|e| err!(Request(InvalidParam("Invalid knock event PDU: {e}"))))?;
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Invalid knock event PDU."))?;
|
||||
|
||||
event.insert("event_id".to_owned(), "$placeholder".into());
|
||||
|
||||
let pdu: PduEvent = serde_json::from_value(event.into())
|
||||
.map_err(|e| err!(Request(InvalidParam("Invalid knock event PDU: {e}"))))?;
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Invalid knock event PDU."))?;
|
||||
|
||||
let mutex_lock = services
|
||||
.rooms
|
||||
@@ -167,18 +169,19 @@ pub(crate) async fn create_knock_event_v1_route(
|
||||
.rooms
|
||||
.event_handler
|
||||
.handle_incoming_pdu(&origin, &body.room_id, &event_id, value.clone(), true)
|
||||
.boxed()
|
||||
.await?
|
||||
.ok_or_else(|| err!(Request(InvalidParam("Could not accept as timeline event."))))?;
|
||||
|
||||
drop(mutex_lock);
|
||||
|
||||
let knock_room_state = services.rooms.state.summary_stripped(&pdu).await;
|
||||
|
||||
services
|
||||
.sending
|
||||
.send_pdu_room(&body.room_id, &pdu_id)
|
||||
.await?;
|
||||
|
||||
let knock_room_state = services.rooms.state.summary_stripped(&pdu).await;
|
||||
|
||||
Ok(send_knock::v1::Response { knock_room_state })
|
||||
Ok(send_knock::v1::Response {
|
||||
knock_room_state,
|
||||
})
|
||||
}
|
||||
|
||||
+4
-13
@@ -1,6 +1,6 @@
|
||||
use conduwuit::{implement, is_false, Err, Result};
|
||||
use conduwuit_service::Services;
|
||||
use futures::{future::OptionFuture, join, FutureExt, StreamExt};
|
||||
use futures::{future::OptionFuture, join, FutureExt};
|
||||
use ruma::{EventId, RoomId, ServerName};
|
||||
|
||||
pub(super) struct AccessCheck<'a> {
|
||||
@@ -31,15 +31,6 @@ pub(super) async fn check(&self) -> Result {
|
||||
.state_cache
|
||||
.server_in_room(self.origin, self.room_id);
|
||||
|
||||
// if any user on our homeserver is trying to knock this room, we'll need to
|
||||
// acknowledge bans or leaves
|
||||
let user_is_knocking = self
|
||||
.services
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_members_knocked(self.room_id)
|
||||
.count();
|
||||
|
||||
let server_can_see: OptionFuture<_> = self
|
||||
.event_id
|
||||
.map(|event_id| {
|
||||
@@ -51,14 +42,14 @@ pub(super) async fn check(&self) -> Result {
|
||||
})
|
||||
.into();
|
||||
|
||||
let (world_readable, server_in_room, server_can_see, acl_check, user_is_knocking) =
|
||||
join!(world_readable, server_in_room, server_can_see, acl_check, user_is_knocking);
|
||||
let (world_readable, server_in_room, server_can_see, acl_check) =
|
||||
join!(world_readable, server_in_room, server_can_see, acl_check);
|
||||
|
||||
if !acl_check {
|
||||
return Err!(Request(Forbidden("Server access denied.")));
|
||||
}
|
||||
|
||||
if !world_readable && !server_in_room && user_is_knocking == 0 {
|
||||
if !world_readable && !server_in_room {
|
||||
return Err!(Request(Forbidden("Server is not in room.")));
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,6 @@ jemalloc_stats = [
|
||||
"tikv-jemalloc-ctl/stats",
|
||||
"tikv-jemallocator/stats",
|
||||
]
|
||||
jemalloc_conf = []
|
||||
hardened_malloc = [
|
||||
"dep:hardened_malloc-rs"
|
||||
]
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
//! Default allocator with no special features
|
||||
|
||||
/// Always returns Ok
|
||||
pub fn trim<I: Into<Option<usize>>>(_: I) -> crate::Result { Ok(()) }
|
||||
|
||||
/// Always returns None
|
||||
#[must_use]
|
||||
pub fn memory_stats(_opts: &str) -> Option<String> { None }
|
||||
pub fn memory_stats() -> Option<String> { None }
|
||||
|
||||
/// Always returns None
|
||||
#[must_use]
|
||||
|
||||
@@ -3,13 +3,11 @@
|
||||
#[global_allocator]
|
||||
static HMALLOC: hardened_malloc_rs::HardenedMalloc = hardened_malloc_rs::HardenedMalloc;
|
||||
|
||||
pub fn trim<I: Into<Option<usize>>>(_: I) -> crate::Result { Ok(()) }
|
||||
|
||||
#[must_use]
|
||||
//TODO: get usage
|
||||
pub fn memory_usage() -> Option<String> { None }
|
||||
pub fn memory_usage() -> Option<string> { None }
|
||||
|
||||
#[must_use]
|
||||
pub fn memory_stats(_opts: &str) -> Option<String> {
|
||||
pub fn memory_stats() -> Option<String> {
|
||||
Some("Extended statistics are not available from hardened_malloc.".to_owned())
|
||||
}
|
||||
|
||||
+11
-313
@@ -1,58 +1,18 @@
|
||||
//! jemalloc allocator
|
||||
|
||||
use std::{
|
||||
cell::OnceCell,
|
||||
ffi::{c_char, c_void, CStr},
|
||||
fmt::Debug,
|
||||
sync::RwLock,
|
||||
};
|
||||
use std::ffi::{c_char, c_void};
|
||||
|
||||
use arrayvec::ArrayVec;
|
||||
use tikv_jemalloc_ctl as mallctl;
|
||||
use tikv_jemalloc_sys as ffi;
|
||||
use tikv_jemallocator as jemalloc;
|
||||
|
||||
use crate::{
|
||||
err, is_equal_to, is_nonzero,
|
||||
utils::{math, math::Tried},
|
||||
Result,
|
||||
};
|
||||
|
||||
#[cfg(feature = "jemalloc_conf")]
|
||||
#[unsafe(no_mangle)]
|
||||
pub static malloc_conf: &[u8] = b"\
|
||||
metadata_thp:always\
|
||||
,percpu_arena:percpu\
|
||||
,background_thread:true\
|
||||
,max_background_threads:-1\
|
||||
,lg_extent_max_active_fit:4\
|
||||
,oversize_threshold:16777216\
|
||||
,tcache_max:2097152\
|
||||
,dirty_decay_ms:16000\
|
||||
,muzzy_decay_ms:144000\
|
||||
,prof_active:false\
|
||||
\0";
|
||||
|
||||
#[global_allocator]
|
||||
static JEMALLOC: jemalloc::Jemalloc = jemalloc::Jemalloc;
|
||||
static CONTROL: RwLock<()> = RwLock::new(());
|
||||
|
||||
type Name = ArrayVec<u8, NAME_MAX>;
|
||||
type Key = ArrayVec<usize, KEY_SEGS>;
|
||||
|
||||
const NAME_MAX: usize = 128;
|
||||
const KEY_SEGS: usize = 8;
|
||||
|
||||
#[crate::ctor]
|
||||
fn _static_initialization() {
|
||||
acq_epoch().expect("pre-initialization of jemalloc failed");
|
||||
acq_epoch().expect("pre-initialization of jemalloc failed");
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
#[cfg(feature = "jemalloc_stats")]
|
||||
pub fn memory_usage() -> Option<String> {
|
||||
use mallctl::stats;
|
||||
use tikv_jemalloc_ctl as mallctl;
|
||||
|
||||
let mibs = |input: Result<usize, mallctl::Error>| {
|
||||
let input = input.unwrap_or_default();
|
||||
@@ -62,9 +22,6 @@ pub fn memory_usage() -> Option<String> {
|
||||
kibs / 1024.0
|
||||
};
|
||||
|
||||
// Acquire the epoch; ensure latest stats are pulled in
|
||||
acq_epoch().ok()?;
|
||||
|
||||
let allocated = mibs(stats::allocated::read());
|
||||
let active = mibs(stats::active::read());
|
||||
let mapped = mibs(stats::mapped::read());
|
||||
@@ -82,293 +39,34 @@ pub fn memory_usage() -> Option<String> {
|
||||
#[cfg(not(feature = "jemalloc_stats"))]
|
||||
pub fn memory_usage() -> Option<String> { None }
|
||||
|
||||
pub fn memory_stats(opts: &str) -> Option<String> {
|
||||
const MAX_LENGTH: usize = 1_048_576;
|
||||
#[must_use]
|
||||
pub fn memory_stats() -> Option<String> {
|
||||
const MAX_LENGTH: usize = 65536 - 4096;
|
||||
|
||||
let opts_s = "d";
|
||||
let mut str = String::new();
|
||||
|
||||
let opaque = std::ptr::from_mut(&mut str).cast::<c_void>();
|
||||
let opts_p: *const c_char = std::ffi::CString::new(opts)
|
||||
let opts_p: *const c_char = std::ffi::CString::new(opts_s)
|
||||
.expect("cstring")
|
||||
.into_raw()
|
||||
.cast_const();
|
||||
|
||||
// Acquire the epoch; ensure latest stats are pulled in
|
||||
acq_epoch().ok()?;
|
||||
|
||||
// SAFETY: calls malloc_stats_print() with our string instance which must remain
|
||||
// in this frame. https://docs.rs/tikv-jemalloc-sys/latest/tikv_jemalloc_sys/fn.malloc_stats_print.html
|
||||
unsafe { ffi::malloc_stats_print(Some(malloc_stats_cb), opaque, opts_p) };
|
||||
|
||||
str.truncate(MAX_LENGTH);
|
||||
|
||||
Some(str)
|
||||
Some(format!("<pre><code>{str}</code></pre>"))
|
||||
}
|
||||
|
||||
unsafe extern "C" fn malloc_stats_cb(opaque: *mut c_void, msg: *const c_char) {
|
||||
// SAFETY: we have to trust the opaque points to our String
|
||||
let res: &mut String = unsafe {
|
||||
opaque
|
||||
.cast::<String>()
|
||||
.as_mut()
|
||||
.expect("failed to cast void* to &mut String")
|
||||
};
|
||||
let res: &mut String = unsafe { opaque.cast::<String>().as_mut().unwrap() };
|
||||
|
||||
// SAFETY: we have to trust the string is null terminated.
|
||||
let msg = unsafe { CStr::from_ptr(msg) };
|
||||
let msg = unsafe { std::ffi::CStr::from_ptr(msg) };
|
||||
|
||||
let msg = String::from_utf8_lossy(msg.to_bytes());
|
||||
res.push_str(msg.as_ref());
|
||||
}
|
||||
|
||||
macro_rules! mallctl {
|
||||
($name:expr) => {{
|
||||
thread_local! {
|
||||
static KEY: OnceCell<Key> = OnceCell::default();
|
||||
};
|
||||
|
||||
KEY.with(|once| {
|
||||
once.get_or_init(move || key($name).expect("failed to translate name into mib key"))
|
||||
.clone()
|
||||
})
|
||||
}};
|
||||
}
|
||||
|
||||
pub mod this_thread {
|
||||
use super::{is_nonzero, key, math, Debug, Key, OnceCell, Result};
|
||||
|
||||
thread_local! {
|
||||
static ALLOCATED_BYTES: OnceCell<&'static u64> = const { OnceCell::new() };
|
||||
static DEALLOCATED_BYTES: OnceCell<&'static u64> = const { OnceCell::new() };
|
||||
}
|
||||
|
||||
pub fn trim() -> Result { decay().and_then(|()| purge()) }
|
||||
|
||||
pub fn purge() -> Result { notify(mallctl!("arena.0.purge")) }
|
||||
|
||||
pub fn decay() -> Result { notify(mallctl!("arena.0.decay")) }
|
||||
|
||||
pub fn idle() -> Result { super::notify(&mallctl!("thread.idle")) }
|
||||
|
||||
pub fn flush() -> Result { super::notify(&mallctl!("thread.tcache.flush")) }
|
||||
|
||||
pub fn set_muzzy_decay(decay_ms: isize) -> Result<isize> {
|
||||
set(mallctl!("arena.0.muzzy_decay_ms"), decay_ms)
|
||||
}
|
||||
|
||||
pub fn get_muzzy_decay() -> Result<isize> { get(mallctl!("arena.0.muzzy_decay_ms")) }
|
||||
|
||||
pub fn set_dirty_decay(decay_ms: isize) -> Result<isize> {
|
||||
set(mallctl!("arena.0.dirty_decay_ms"), decay_ms)
|
||||
}
|
||||
|
||||
pub fn get_dirty_decay() -> Result<isize> { get(mallctl!("arena.0.dirty_decay_ms")) }
|
||||
|
||||
pub fn cache_enable(enable: bool) -> Result<bool> {
|
||||
super::set::<u8>(&mallctl!("thread.tcache.enabled"), enable.into()).map(is_nonzero!())
|
||||
}
|
||||
|
||||
pub fn is_cache_enabled() -> Result<bool> {
|
||||
super::get::<u8>(&mallctl!("thread.tcache.enabled")).map(is_nonzero!())
|
||||
}
|
||||
|
||||
pub fn set_arena(id: usize) -> Result<usize> {
|
||||
super::set::<u32>(&mallctl!("thread.arena"), id.try_into()?).and_then(math::try_into)
|
||||
}
|
||||
|
||||
pub fn arena_id() -> Result<usize> {
|
||||
super::get::<u32>(&mallctl!("thread.arena")).and_then(math::try_into)
|
||||
}
|
||||
|
||||
pub fn prof_enable(enable: bool) -> Result<bool> {
|
||||
super::set::<u8>(&mallctl!("thread.prof.active"), enable.into()).map(is_nonzero!())
|
||||
}
|
||||
|
||||
pub fn is_prof_enabled() -> Result<bool> {
|
||||
super::get::<u8>(&mallctl!("thread.prof.active")).map(is_nonzero!())
|
||||
}
|
||||
|
||||
pub fn reset_peak() -> Result { super::notify(&mallctl!("thread.peak.reset")) }
|
||||
|
||||
pub fn peak() -> Result<u64> { super::get(&mallctl!("thread.peak.read")) }
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn allocated() -> u64 {
|
||||
*ALLOCATED_BYTES.with(|once| init_tls_cell(once, "thread.allocatedp"))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn deallocated() -> u64 {
|
||||
*DEALLOCATED_BYTES.with(|once| init_tls_cell(once, "thread.deallocatedp"))
|
||||
}
|
||||
|
||||
fn notify(key: Key) -> Result { super::notify_by_arena(Some(arena_id()?), key) }
|
||||
|
||||
fn set<T>(key: Key, val: T) -> Result<T>
|
||||
where
|
||||
T: Copy + Debug,
|
||||
{
|
||||
super::set_by_arena(Some(arena_id()?), key, val)
|
||||
}
|
||||
|
||||
fn get<T>(key: Key) -> Result<T>
|
||||
where
|
||||
T: Copy + Debug,
|
||||
{
|
||||
super::get_by_arena(Some(arena_id()?), key)
|
||||
}
|
||||
|
||||
fn init_tls_cell(cell: &OnceCell<&'static u64>, name: &str) -> &'static u64 {
|
||||
cell.get_or_init(|| {
|
||||
let ptr: *const u64 = super::get(&mallctl!(name)).expect("failed to obtain pointer");
|
||||
|
||||
// SAFETY: ptr points directly to the internal state of jemalloc for this thread
|
||||
unsafe { ptr.as_ref() }.expect("pointer must not be null")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stats_reset() -> Result { notify(&mallctl!("stats.mutexes.reset")) }
|
||||
|
||||
pub fn prof_reset() -> Result { notify(&mallctl!("prof.reset")) }
|
||||
|
||||
pub fn prof_enable(enable: bool) -> Result<bool> {
|
||||
set::<u8>(&mallctl!("prof.active"), enable.into()).map(is_nonzero!())
|
||||
}
|
||||
|
||||
pub fn is_prof_enabled() -> Result<bool> {
|
||||
get::<u8>(&mallctl!("prof.active")).map(is_nonzero!())
|
||||
}
|
||||
|
||||
pub fn trim<I: Into<Option<usize>> + Copy>(arena: I) -> Result {
|
||||
decay(arena).and_then(|()| purge(arena))
|
||||
}
|
||||
|
||||
pub fn purge<I: Into<Option<usize>>>(arena: I) -> Result {
|
||||
notify_by_arena(arena.into(), mallctl!("arena.4096.purge"))
|
||||
}
|
||||
|
||||
pub fn decay<I: Into<Option<usize>>>(arena: I) -> Result {
|
||||
notify_by_arena(arena.into(), mallctl!("arena.4096.decay"))
|
||||
}
|
||||
|
||||
pub fn set_muzzy_decay<I: Into<Option<usize>>>(arena: I, decay_ms: isize) -> Result<isize> {
|
||||
if let Some(arena) = arena.into() {
|
||||
set_by_arena(Some(arena), mallctl!("arena.4096.muzzy_decay_ms"), decay_ms)
|
||||
} else {
|
||||
set(&mallctl!("arenas.muzzy_decay_ms"), decay_ms)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_dirty_decay<I: Into<Option<usize>>>(arena: I, decay_ms: isize) -> Result<isize> {
|
||||
if let Some(arena) = arena.into() {
|
||||
set_by_arena(Some(arena), mallctl!("arena.4096.dirty_decay_ms"), decay_ms)
|
||||
} else {
|
||||
set(&mallctl!("arenas.dirty_decay_ms"), decay_ms)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn is_affine_arena() -> bool { is_percpu_arena() || is_phycpu_arena() }
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn is_percpu_arena() -> bool { percpu_arenas().is_ok_and(is_equal_to!("percpu")) }
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn is_phycpu_arena() -> bool { percpu_arenas().is_ok_and(is_equal_to!("phycpu")) }
|
||||
|
||||
pub fn percpu_arenas() -> Result<&'static str> {
|
||||
let ptr = get::<*const c_char>(&mallctl!("opt.percpu_arena"))?;
|
||||
//SAFETY: ptr points to a null-terminated string returned for opt.percpu_arena.
|
||||
let cstr = unsafe { CStr::from_ptr(ptr) };
|
||||
cstr.to_str().map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn arenas() -> Result<usize> {
|
||||
get::<u32>(&mallctl!("arenas.narenas")).and_then(math::try_into)
|
||||
}
|
||||
|
||||
pub fn inc_epoch() -> Result<u64> { xchg(&mallctl!("epoch"), 1_u64) }
|
||||
|
||||
pub fn acq_epoch() -> Result<u64> { xchg(&mallctl!("epoch"), 0_u64) }
|
||||
|
||||
fn notify_by_arena(id: Option<usize>, mut key: Key) -> Result {
|
||||
key[1] = id.unwrap_or(4096);
|
||||
notify(&key)
|
||||
}
|
||||
|
||||
fn set_by_arena<T>(id: Option<usize>, mut key: Key, val: T) -> Result<T>
|
||||
where
|
||||
T: Copy + Debug,
|
||||
{
|
||||
key[1] = id.unwrap_or(4096);
|
||||
set(&key, val)
|
||||
}
|
||||
|
||||
fn get_by_arena<T>(id: Option<usize>, mut key: Key) -> Result<T>
|
||||
where
|
||||
T: Copy + Debug,
|
||||
{
|
||||
key[1] = id.unwrap_or(4096);
|
||||
get(&key)
|
||||
}
|
||||
|
||||
fn notify(key: &Key) -> Result { xchg(key, ()) }
|
||||
|
||||
fn set<T>(key: &Key, val: T) -> Result<T>
|
||||
where
|
||||
T: Copy + Debug,
|
||||
{
|
||||
let _lock = CONTROL.write()?;
|
||||
let res = xchg(key, val)?;
|
||||
inc_epoch()?;
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
fn get<T>(key: &Key) -> Result<T>
|
||||
where
|
||||
T: Copy + Debug,
|
||||
{
|
||||
acq_epoch()?;
|
||||
acq_epoch()?;
|
||||
|
||||
// SAFETY: T must be perfectly valid to receive value.
|
||||
unsafe { mallctl::raw::read_mib(key.as_slice()) }.map_err(map_err)
|
||||
}
|
||||
|
||||
fn xchg<T>(key: &Key, val: T) -> Result<T>
|
||||
where
|
||||
T: Copy + Debug,
|
||||
{
|
||||
// SAFETY: T must be the exact expected type.
|
||||
unsafe { mallctl::raw::update_mib(key.as_slice(), val) }.map_err(map_err)
|
||||
}
|
||||
|
||||
fn key(name: &str) -> Result<Key> {
|
||||
// tikv asserts the output buffer length is tight to the number of required mibs
|
||||
// so we slice that down here.
|
||||
let segs = name.chars().filter(is_equal_to!(&'.')).count().try_add(1)?;
|
||||
|
||||
let name = self::name(name)?;
|
||||
let mut buf = [0_usize; KEY_SEGS];
|
||||
mallctl::raw::name_to_mib(name.as_slice(), &mut buf[0..segs])
|
||||
.map_err(map_err)
|
||||
.map(move |()| buf.into_iter().take(segs).collect())
|
||||
}
|
||||
|
||||
fn name(name: &str) -> Result<Name> {
|
||||
let mut buf = Name::new();
|
||||
buf.try_extend_from_slice(name.as_bytes())?;
|
||||
buf.try_extend_from_slice(b"\0")?;
|
||||
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
fn map_err(error: tikv_jemalloc_ctl::Error) -> crate::Error {
|
||||
err!("mallctl: {}", error.to_string())
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#[cfg(all(not(target_env = "msvc"), feature = "jemalloc"))]
|
||||
pub mod je;
|
||||
#[cfg(all(not(target_env = "msvc"), feature = "jemalloc"))]
|
||||
pub use je::{memory_stats, memory_usage, trim};
|
||||
pub use je::{memory_stats, memory_usage};
|
||||
|
||||
#[cfg(all(not(target_env = "msvc"), feature = "hardened_malloc", not(feature = "jemalloc")))]
|
||||
pub mod hardened;
|
||||
@@ -13,7 +13,7 @@ pub mod hardened;
|
||||
feature = "hardened_malloc",
|
||||
not(feature = "jemalloc")
|
||||
))]
|
||||
pub use hardened::{memory_stats, memory_usage, trim};
|
||||
pub use hardened::{memory_stats, memory_usage};
|
||||
|
||||
#[cfg(any(
|
||||
target_env = "msvc",
|
||||
@@ -24,4 +24,4 @@ pub mod default;
|
||||
target_env = "msvc",
|
||||
all(not(feature = "hardened_malloc"), not(feature = "jemalloc"))
|
||||
))]
|
||||
pub use default::{memory_stats, memory_usage, trim};
|
||||
pub use default::{memory_stats, memory_usage};
|
||||
|
||||
@@ -4,7 +4,7 @@ use either::Either;
|
||||
use figment::Figment;
|
||||
|
||||
use super::DEPRECATED_KEYS;
|
||||
use crate::{debug, debug_info, debug_warn, error, warn, Config, Err, Result, Server};
|
||||
use crate::{debug, debug_info, debug_warn, error, warn, Config, Err, Result};
|
||||
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
pub fn check(config: &Config) -> Result<()> {
|
||||
@@ -233,16 +233,6 @@ pub fn check(config: &Config) -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
if !Server::available_room_versions()
|
||||
.any(|(version, _)| version == config.default_room_version)
|
||||
{
|
||||
return Err!(Config(
|
||||
"default_room_version",
|
||||
"Room version {:?} is not available",
|
||||
config.default_room_version
|
||||
));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,128 +0,0 @@
|
||||
use std::{
|
||||
cell::{Cell, RefCell},
|
||||
ops::Deref,
|
||||
ptr,
|
||||
ptr::null_mut,
|
||||
sync::{
|
||||
atomic::{AtomicPtr, Ordering},
|
||||
Arc,
|
||||
},
|
||||
};
|
||||
|
||||
use super::Config;
|
||||
use crate::{implement, Result};
|
||||
|
||||
/// The configuration manager is an indirection to reload the configuration for
|
||||
/// the server while it is running. In order to not burden or clutter the many
|
||||
/// callsites which query for configuration items, this object implements Deref
|
||||
/// for the actively loaded configuration.
|
||||
pub struct Manager {
|
||||
active: AtomicPtr<Config>,
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
static INDEX: Cell<usize> = 0.into();
|
||||
static HANDLE: RefCell<Handles> = const {
|
||||
RefCell::new([const { None }; HISTORY])
|
||||
};
|
||||
}
|
||||
|
||||
type Handle = Option<Arc<Config>>;
|
||||
type Handles = [Handle; HISTORY];
|
||||
|
||||
const HISTORY: usize = 8;
|
||||
|
||||
impl Manager {
|
||||
pub(crate) fn new(config: Config) -> Self {
|
||||
let config = Arc::new(config);
|
||||
Self {
|
||||
active: AtomicPtr::new(Arc::into_raw(config).cast_mut()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Manager {
|
||||
fn drop(&mut self) {
|
||||
let config = self.active.swap(null_mut(), Ordering::AcqRel);
|
||||
|
||||
// SAFETY: The active pointer was set using an Arc::into_raw(). We're obliged to
|
||||
// reconstitute that into Arc otherwise it will leak.
|
||||
unsafe { Arc::from_raw(config) };
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Manager {
|
||||
type Target = Arc<Config>;
|
||||
|
||||
fn deref(&self) -> &Self::Target { HANDLE.with_borrow_mut(|handle| self.load(handle)) }
|
||||
}
|
||||
|
||||
/// Update the active configuration, returning prior configuration.
|
||||
#[implement(Manager)]
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub fn update(&self, config: Config) -> Result<Arc<Config>> {
|
||||
let config = Arc::new(config);
|
||||
let new = Arc::into_raw(config);
|
||||
let old = self.active.swap(new.cast_mut(), Ordering::AcqRel);
|
||||
|
||||
// SAFETY: The old active pointer was set using an Arc::into_raw(). We're
|
||||
// obliged to reconstitute that into Arc otherwise it will leak.
|
||||
Ok(unsafe { Arc::from_raw(old) })
|
||||
}
|
||||
|
||||
#[implement(Manager)]
|
||||
fn load(&self, handle: &mut [Option<Arc<Config>>]) -> &'static Arc<Config> {
|
||||
let config = self.active.load(Ordering::Acquire);
|
||||
|
||||
// Branch taken after config reload or first access by this thread.
|
||||
if handle[INDEX.get()]
|
||||
.as_ref()
|
||||
.is_none_or(|handle| !ptr::eq(config, Arc::as_ptr(handle)))
|
||||
{
|
||||
INDEX.set(INDEX.get().wrapping_add(1).wrapping_rem(HISTORY));
|
||||
return load_miss(handle, INDEX.get(), config);
|
||||
}
|
||||
|
||||
let config: &Arc<Config> = handle[INDEX.get()]
|
||||
.as_ref()
|
||||
.expect("handle was already cached for this thread");
|
||||
|
||||
// SAFETY: The caller should not hold multiple references at a time directly
|
||||
// into Config, as a subsequent reference might invalidate the thread's cache
|
||||
// causing another reference to dangle.
|
||||
//
|
||||
// This is a highly unusual pattern as most config values are copied by value or
|
||||
// used immediately without running overlap with another value. Even if it does
|
||||
// actually occur somewhere, the window of danger is limited to the config being
|
||||
// reloaded while the reference is held and another access is made by the same
|
||||
// thread into a different config value. This is mitigated by creating a buffer
|
||||
// of old configs rather than discarding at the earliest opportunity; the odds
|
||||
// of this scenario are thus astronomical.
|
||||
unsafe { std::mem::transmute(config) }
|
||||
}
|
||||
|
||||
#[tracing::instrument(
|
||||
name = "miss",
|
||||
level = "trace",
|
||||
skip_all,
|
||||
fields(%index, ?config)
|
||||
)]
|
||||
#[allow(clippy::transmute_ptr_to_ptr)]
|
||||
fn load_miss(
|
||||
handle: &mut [Option<Arc<Config>>],
|
||||
index: usize,
|
||||
config: *const Config,
|
||||
) -> &'static Arc<Config> {
|
||||
// SAFETY: The active pointer was set prior and always remains valid. We're
|
||||
// reconstituting the Arc here but as a new reference, so the count is
|
||||
// incremented. This instance will be cached in the thread-local.
|
||||
let config = unsafe {
|
||||
Arc::increment_strong_count(config);
|
||||
Arc::from_raw(config)
|
||||
};
|
||||
|
||||
// SAFETY: See the note on the transmute above. The caller should not hold more
|
||||
// than one reference at a time directly into Config, as the second access
|
||||
// might invalidate the thread's cache, dangling the reference to the first.
|
||||
unsafe { std::mem::transmute(handle[index].insert(config)) }
|
||||
}
|
||||
+418
-59
@@ -1,11 +1,11 @@
|
||||
pub mod check;
|
||||
pub mod manager;
|
||||
pub mod proxy;
|
||||
|
||||
use std::{
|
||||
collections::{BTreeMap, BTreeSet, HashSet},
|
||||
fmt,
|
||||
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
|
||||
path::{Path, PathBuf},
|
||||
path::PathBuf,
|
||||
};
|
||||
|
||||
use conduwuit_macros::config_example_generator;
|
||||
@@ -15,6 +15,7 @@ use either::{
|
||||
};
|
||||
use figment::providers::{Env, Format, Toml};
|
||||
pub use figment::{value::Value as FigmentValue, Figment};
|
||||
use itertools::Itertools;
|
||||
use regex::RegexSet;
|
||||
use ruma::{
|
||||
api::client::discovery::discover_support::ContactRole, OwnedRoomOrAliasId, OwnedServerName,
|
||||
@@ -23,8 +24,8 @@ use ruma::{
|
||||
use serde::{de::IgnoredAny, Deserialize};
|
||||
use url::Url;
|
||||
|
||||
pub use self::check::check;
|
||||
use self::proxy::ProxyConfig;
|
||||
pub use self::{check::check, manager::Manager};
|
||||
use crate::{err, error::Error, utils::sys, Result};
|
||||
|
||||
/// All the config options for conduwuit.
|
||||
@@ -492,22 +493,16 @@ pub struct Config {
|
||||
#[serde(default)]
|
||||
pub allow_registration: bool,
|
||||
|
||||
/// Enabling this setting opens registration to anyone without restrictions.
|
||||
/// This makes your server vulnerable to abuse
|
||||
#[serde(default)]
|
||||
pub yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse: bool,
|
||||
|
||||
/// A static registration token that new users will have to provide when
|
||||
/// creating an account. If unset and `allow_registration` is true,
|
||||
/// you must set
|
||||
/// `yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse`
|
||||
/// to true to allow open registration without any conditions.
|
||||
/// registration is open without any condition.
|
||||
///
|
||||
/// YOU NEED TO EDIT THIS OR USE registration_token_file.
|
||||
///
|
||||
/// example: "o&^uCtes4HPf0Vu@F20jQeeWE7"
|
||||
///
|
||||
/// display: sensitive
|
||||
pub registration_token: Option<String>,
|
||||
|
||||
/// Path to a file on the system that gets read for the registration token.
|
||||
@@ -676,6 +671,8 @@ pub struct Config {
|
||||
#[serde(default)]
|
||||
pub proxy: ProxyConfig,
|
||||
|
||||
pub jwt_secret: Option<String>,
|
||||
|
||||
/// Servers listed here will be used to gather public keys of other servers
|
||||
/// (notary trusted key servers).
|
||||
///
|
||||
@@ -772,24 +769,6 @@ pub struct Config {
|
||||
#[serde(default = "default_openid_token_ttl")]
|
||||
pub openid_token_ttl: u64,
|
||||
|
||||
/// Allow an existing session to mint a login token for another client.
|
||||
/// This requires interactive authentication, but has security ramifications
|
||||
/// as a malicious client could use the mechanism to spawn more than one
|
||||
/// session.
|
||||
/// Enabled by default.
|
||||
#[serde(default = "true_fn")]
|
||||
pub login_via_existing_session: bool,
|
||||
|
||||
/// Login token expiration/TTL in milliseconds.
|
||||
///
|
||||
/// These are short-lived tokens for the m.login.token endpoint.
|
||||
/// This is used to allow existing sessions to create new sessions.
|
||||
/// see login_via_existing_session.
|
||||
///
|
||||
/// default: 120000
|
||||
#[serde(default = "default_login_token_ttl")]
|
||||
pub login_token_ttl: u64,
|
||||
|
||||
/// Static TURN username to provide the client if not using a shared secret
|
||||
/// ("turn_secret"), It is recommended to use a shared secret over static
|
||||
/// credentials.
|
||||
@@ -799,8 +778,6 @@ pub struct Config {
|
||||
/// Static TURN password to provide the client if not using a shared secret
|
||||
/// ("turn_secret"). It is recommended to use a shared secret over static
|
||||
/// credentials.
|
||||
///
|
||||
/// display: sensitive
|
||||
#[serde(default)]
|
||||
pub turn_password: String,
|
||||
|
||||
@@ -822,8 +799,6 @@ pub struct Config {
|
||||
///
|
||||
/// This is more secure, but if needed you can use traditional static
|
||||
/// username/password credentials.
|
||||
///
|
||||
/// display: sensitive
|
||||
#[serde(default)]
|
||||
pub turn_secret: String,
|
||||
|
||||
@@ -967,9 +942,6 @@ pub struct Config {
|
||||
/// magic number and translated to the library's default compression level
|
||||
/// as they all differ. See their `kDefaultCompressionLevel`.
|
||||
///
|
||||
/// Note when using the default value we may override it with a setting
|
||||
/// tailored specifically conduwuit.
|
||||
///
|
||||
/// default: 32767
|
||||
#[serde(default = "default_rocksdb_compression_level")]
|
||||
pub rocksdb_compression_level: i32,
|
||||
@@ -985,9 +957,6 @@ pub struct Config {
|
||||
/// less likely for this data to be used. Research your chosen compression
|
||||
/// algorithm.
|
||||
///
|
||||
/// Note when using the default value we may override it with a setting
|
||||
/// tailored specifically conduwuit.
|
||||
///
|
||||
/// default: 32767
|
||||
#[serde(default = "default_rocksdb_bottommost_compression_level")]
|
||||
pub rocksdb_bottommost_compression_level: i32,
|
||||
@@ -1000,7 +969,7 @@ pub struct Config {
|
||||
/// if you're trying to reduce storage usage from the database.
|
||||
///
|
||||
/// See https://github.com/facebook/rocksdb/wiki/Compression for more details.
|
||||
#[serde(default = "true_fn")]
|
||||
#[serde(default)]
|
||||
pub rocksdb_bottommost_compression: bool,
|
||||
|
||||
/// Database recovery mode (for RocksDB WAL corruption).
|
||||
@@ -1121,8 +1090,6 @@ pub struct Config {
|
||||
/// security purposes.
|
||||
///
|
||||
/// example: "F670$2CP@Hw8mG7RY1$%!#Ic7YA"
|
||||
///
|
||||
/// display: sensitive
|
||||
pub emergency_password: Option<String>,
|
||||
|
||||
/// default: "/_matrix/push/v1/notify"
|
||||
@@ -1572,7 +1539,6 @@ pub struct Config {
|
||||
|
||||
/// Sentry reporting URL, if a custom one is desired.
|
||||
///
|
||||
/// display: sensitive
|
||||
/// default: "https://fe2eb4536aa04949e28eff3128d64757@o4506996327251968.ingest.us.sentry.io/4506996334657536"
|
||||
#[serde(default = "default_sentry_endpoint")]
|
||||
pub sentry_endpoint: Option<Url>,
|
||||
@@ -1728,11 +1694,6 @@ pub struct Config {
|
||||
#[serde(default)]
|
||||
pub sender_workers: usize,
|
||||
|
||||
/// Enables listener sockets; can be set to false to disable listening. This
|
||||
/// option is intended for developer/diagnostic purposes only.
|
||||
#[serde(default = "true_fn")]
|
||||
pub listening: bool,
|
||||
|
||||
#[serde(flatten)]
|
||||
#[allow(clippy::zero_sized_map_values)]
|
||||
// this is a catchall, the map shouldn't be zero at runtime
|
||||
@@ -1811,17 +1772,14 @@ const DEPRECATED_KEYS: &[&str; 9] = &[
|
||||
|
||||
impl Config {
|
||||
/// Pre-initialize config
|
||||
pub fn load<'a, I>(paths: I) -> Result<Figment>
|
||||
where
|
||||
I: Iterator<Item = &'a Path>,
|
||||
{
|
||||
let envs = [Env::var("CONDUIT_CONFIG"), Env::var("CONDUWUIT_CONFIG")];
|
||||
pub fn load(paths: Option<&[PathBuf]>) -> Result<Figment> {
|
||||
let paths_files = paths.into_iter().flatten().map(Toml::file);
|
||||
|
||||
let config = envs
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.map(Toml::file)
|
||||
.chain(paths.map(Toml::file))
|
||||
let envs = [Env::var("CONDUIT_CONFIG"), Env::var("CONDUWUIT_CONFIG")];
|
||||
let envs_files = envs.into_iter().flatten().map(Toml::file);
|
||||
|
||||
let config = envs_files
|
||||
.chain(paths_files)
|
||||
.fold(Figment::new(), |config, file| config.merge(file.nested()))
|
||||
.merge(Env::prefixed("CONDUIT_").global().split("__"))
|
||||
.merge(Env::prefixed("CONDUWUIT_").global().split("__"));
|
||||
@@ -1874,6 +1832,409 @@ impl Config {
|
||||
pub fn check(&self) -> Result<(), Error> { check(self) }
|
||||
}
|
||||
|
||||
impl fmt::Display for Config {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
writeln!(f, "Active config values:\n").expect("wrote line to formatter stream");
|
||||
let mut line = |key: &str, val: &str| {
|
||||
writeln!(f, "{key}: {val}").expect("wrote line to formatter stream");
|
||||
};
|
||||
|
||||
line("Server name", self.server_name.host());
|
||||
line("Database path", &self.database_path.to_string_lossy());
|
||||
line(
|
||||
"Database backup path",
|
||||
self.database_backup_path
|
||||
.as_ref()
|
||||
.map_or("", |path| path.to_str().unwrap_or("")),
|
||||
);
|
||||
line("Database backups to keep", &self.database_backups_to_keep.to_string());
|
||||
line("Database cache capacity (MB)", &self.db_cache_capacity_mb.to_string());
|
||||
line("Cache capacity modifier", &self.cache_capacity_modifier.to_string());
|
||||
line("PDU cache capacity", &self.pdu_cache_capacity.to_string());
|
||||
line("Auth chain cache capacity", &self.auth_chain_cache_capacity.to_string());
|
||||
line("Short eventid cache capacity", &self.shorteventid_cache_capacity.to_string());
|
||||
line("Eventid short cache capacity", &self.eventidshort_cache_capacity.to_string());
|
||||
line("Short statekey cache capacity", &self.shortstatekey_cache_capacity.to_string());
|
||||
line("Statekey short cache capacity", &self.statekeyshort_cache_capacity.to_string());
|
||||
line(
|
||||
"Server visibility cache capacity",
|
||||
&self.server_visibility_cache_capacity.to_string(),
|
||||
);
|
||||
line(
|
||||
"User visibility cache capacity",
|
||||
&self.user_visibility_cache_capacity.to_string(),
|
||||
);
|
||||
line("Stateinfo cache capacity", &self.stateinfo_cache_capacity.to_string());
|
||||
line(
|
||||
"Roomid space hierarchy cache capacity",
|
||||
&self.roomid_spacehierarchy_cache_capacity.to_string(),
|
||||
);
|
||||
line("DNS cache entry limit", &self.dns_cache_entries.to_string());
|
||||
line("DNS minimum TTL", &self.dns_min_ttl.to_string());
|
||||
line("DNS minimum NXDOMAIN TTL", &self.dns_min_ttl_nxdomain.to_string());
|
||||
line("DNS attempts", &self.dns_attempts.to_string());
|
||||
line("DNS timeout", &self.dns_timeout.to_string());
|
||||
line("DNS fallback to TCP", &self.dns_tcp_fallback.to_string());
|
||||
line("DNS query over TCP only", &self.query_over_tcp_only.to_string());
|
||||
line("Query all nameservers", &self.query_all_nameservers.to_string());
|
||||
line("Maximum request size (bytes)", &self.max_request_size.to_string());
|
||||
line("Sender retry backoff limit", &self.sender_retry_backoff_limit.to_string());
|
||||
line("Request connect timeout", &self.request_conn_timeout.to_string());
|
||||
line("Request timeout", &self.request_timeout.to_string());
|
||||
line("Request total timeout", &self.request_total_timeout.to_string());
|
||||
line("Idle connections per host", &self.request_idle_per_host.to_string());
|
||||
line("Request pool idle timeout", &self.request_idle_timeout.to_string());
|
||||
line("Well_known connect timeout", &self.well_known_conn_timeout.to_string());
|
||||
line("Well_known timeout", &self.well_known_timeout.to_string());
|
||||
line("Federation timeout", &self.federation_timeout.to_string());
|
||||
line("Federation pool idle per host", &self.federation_idle_per_host.to_string());
|
||||
line("Federation pool idle timeout", &self.federation_idle_timeout.to_string());
|
||||
line("Sender timeout", &self.sender_timeout.to_string());
|
||||
line("Sender pool idle timeout", &self.sender_idle_timeout.to_string());
|
||||
line("Appservice timeout", &self.appservice_timeout.to_string());
|
||||
line("Appservice pool idle timeout", &self.appservice_idle_timeout.to_string());
|
||||
line("Pusher pool idle timeout", &self.pusher_idle_timeout.to_string());
|
||||
line("Allow registration", &self.allow_registration.to_string());
|
||||
line(
|
||||
"Registration token",
|
||||
if self.registration_token.is_none()
|
||||
&& self.registration_token_file.is_none()
|
||||
&& self.allow_registration
|
||||
{
|
||||
"not set (⚠️ open registration!)"
|
||||
} else if self.registration_token.is_none() && self.registration_token_file.is_none()
|
||||
{
|
||||
"not set"
|
||||
} else {
|
||||
"set"
|
||||
},
|
||||
);
|
||||
line(
|
||||
"Registration token file path",
|
||||
self.registration_token_file
|
||||
.as_ref()
|
||||
.map_or("", |path| path.to_str().unwrap_or_default()),
|
||||
);
|
||||
line(
|
||||
"Allow guest registration (inherently false if allow registration is false)",
|
||||
&self.allow_guest_registration.to_string(),
|
||||
);
|
||||
line(
|
||||
"Log guest registrations in admin room",
|
||||
&self.log_guest_registrations.to_string(),
|
||||
);
|
||||
line(
|
||||
"Allow guests to auto join rooms",
|
||||
&self.allow_guests_auto_join_rooms.to_string(),
|
||||
);
|
||||
line("New user display name suffix", &self.new_user_displayname_suffix);
|
||||
line("Allow encryption", &self.allow_encryption.to_string());
|
||||
line("Allow federation", &self.allow_federation.to_string());
|
||||
line("Federation loopback", &self.federation_loopback.to_string());
|
||||
line(
|
||||
"Require authentication for profile requests",
|
||||
&self.require_auth_for_profile_requests.to_string(),
|
||||
);
|
||||
line(
|
||||
"Allow incoming federated presence requests (updates)",
|
||||
&self.allow_incoming_presence.to_string(),
|
||||
);
|
||||
line(
|
||||
"Allow outgoing federated presence requests (updates)",
|
||||
&self.allow_outgoing_presence.to_string(),
|
||||
);
|
||||
line(
|
||||
"Allow local presence requests (updates)",
|
||||
&self.allow_local_presence.to_string(),
|
||||
);
|
||||
line(
|
||||
"Allow incoming remote read receipts",
|
||||
&self.allow_incoming_read_receipts.to_string(),
|
||||
);
|
||||
line(
|
||||
"Allow outgoing remote read receipts",
|
||||
&self.allow_outgoing_read_receipts.to_string(),
|
||||
);
|
||||
line(
|
||||
"Block non-admin room invites (local and remote, admins can still send and receive \
|
||||
invites)",
|
||||
&self.block_non_admin_invites.to_string(),
|
||||
);
|
||||
line("Enable admin escape commands", &self.admin_escape_commands.to_string());
|
||||
line(
|
||||
"Activate admin console after startup",
|
||||
&self.admin_console_automatic.to_string(),
|
||||
);
|
||||
line("Execute admin commands after startup", &self.admin_execute.join(", "));
|
||||
line(
|
||||
"Continue startup even if some commands fail",
|
||||
&self.admin_execute_errors_ignore.to_string(),
|
||||
);
|
||||
line("Filter for admin command log capture", &self.admin_log_capture);
|
||||
line("Admin room tag", &self.admin_room_tag);
|
||||
line("Allow outgoing federated typing", &self.allow_outgoing_typing.to_string());
|
||||
line("Allow incoming federated typing", &self.allow_incoming_typing.to_string());
|
||||
line(
|
||||
"Incoming federated typing timeout",
|
||||
&self.typing_federation_timeout_s.to_string(),
|
||||
);
|
||||
line("Client typing timeout minimum", &self.typing_client_timeout_min_s.to_string());
|
||||
line("Client typing timeout maxmimum", &self.typing_client_timeout_max_s.to_string());
|
||||
line("Allow device name federation", &self.allow_device_name_federation.to_string());
|
||||
line(
|
||||
"Allow incoming profile lookup federation requests",
|
||||
&self
|
||||
.allow_inbound_profile_lookup_federation_requests
|
||||
.to_string(),
|
||||
);
|
||||
line(
|
||||
"Auto deactivate banned room join attempts",
|
||||
&self.auto_deactivate_banned_room_attempts.to_string(),
|
||||
);
|
||||
line("Notification push path", &self.notification_push_path);
|
||||
line("Allow room creation", &self.allow_room_creation.to_string());
|
||||
line(
|
||||
"Allow public room directory over federation",
|
||||
&self.allow_public_room_directory_over_federation.to_string(),
|
||||
);
|
||||
line(
|
||||
"Allow public room directory without authentication",
|
||||
&self.allow_public_room_directory_without_auth.to_string(),
|
||||
);
|
||||
line(
|
||||
"Lockdown public room directory (only allow admins to publish)",
|
||||
&self.lockdown_public_room_directory.to_string(),
|
||||
);
|
||||
line("JWT secret", match self.jwt_secret {
|
||||
| Some(_) => "set",
|
||||
| None => "not set",
|
||||
});
|
||||
line(
|
||||
"Trusted key servers",
|
||||
&self
|
||||
.trusted_servers
|
||||
.iter()
|
||||
.map(|server| server.host())
|
||||
.join(", "),
|
||||
);
|
||||
line("OpenID Token TTL", &self.openid_token_ttl.to_string());
|
||||
line(
|
||||
"TURN username",
|
||||
if self.turn_username.is_empty() {
|
||||
"not set"
|
||||
} else {
|
||||
&self.turn_username
|
||||
},
|
||||
);
|
||||
line("TURN password", {
|
||||
if self.turn_password.is_empty() {
|
||||
"not set"
|
||||
} else {
|
||||
"set"
|
||||
}
|
||||
});
|
||||
line("TURN secret", {
|
||||
if self.turn_secret.is_empty() && self.turn_secret_file.is_none() {
|
||||
"not set"
|
||||
} else {
|
||||
"set"
|
||||
}
|
||||
});
|
||||
line("TURN secret file path", {
|
||||
self.turn_secret_file
|
||||
.as_ref()
|
||||
.map_or("", |path| path.to_str().unwrap_or_default())
|
||||
});
|
||||
line("Turn TTL", &self.turn_ttl.to_string());
|
||||
line("Turn URIs", {
|
||||
let mut lst = Vec::with_capacity(self.turn_uris.len());
|
||||
for item in self.turn_uris.iter().cloned().enumerate() {
|
||||
let (_, uri): (usize, String) = item;
|
||||
lst.push(uri);
|
||||
}
|
||||
&lst.join(", ")
|
||||
});
|
||||
line("Auto Join Rooms", {
|
||||
let mut lst = Vec::with_capacity(self.auto_join_rooms.len());
|
||||
for room in &self.auto_join_rooms {
|
||||
lst.push(room);
|
||||
}
|
||||
&lst.into_iter().join(", ")
|
||||
});
|
||||
line("Zstd HTTP Compression", &self.zstd_compression.to_string());
|
||||
line("Gzip HTTP Compression", &self.gzip_compression.to_string());
|
||||
line("Brotli HTTP Compression", &self.brotli_compression.to_string());
|
||||
line("RocksDB database LOG level", &self.rocksdb_log_level);
|
||||
line("RocksDB database LOG to stderr", &self.rocksdb_log_stderr.to_string());
|
||||
line("RocksDB database LOG time-to-roll", &self.rocksdb_log_time_to_roll.to_string());
|
||||
line("RocksDB Max LOG Files", &self.rocksdb_max_log_files.to_string());
|
||||
line(
|
||||
"RocksDB database max LOG file size",
|
||||
&self.rocksdb_max_log_file_size.to_string(),
|
||||
);
|
||||
line(
|
||||
"RocksDB database optimize for spinning disks",
|
||||
&self.rocksdb_optimize_for_spinning_disks.to_string(),
|
||||
);
|
||||
line("RocksDB Direct-IO", &self.rocksdb_direct_io.to_string());
|
||||
line("RocksDB Parallelism Threads", &self.rocksdb_parallelism_threads.to_string());
|
||||
line("RocksDB Compression Algorithm", &self.rocksdb_compression_algo);
|
||||
line("RocksDB Compression Level", &self.rocksdb_compression_level.to_string());
|
||||
line(
|
||||
"RocksDB Bottommost Compression Level",
|
||||
&self.rocksdb_bottommost_compression_level.to_string(),
|
||||
);
|
||||
line(
|
||||
"RocksDB Bottommost Level Compression",
|
||||
&self.rocksdb_bottommost_compression.to_string(),
|
||||
);
|
||||
line("RocksDB Recovery Mode", &self.rocksdb_recovery_mode.to_string());
|
||||
line("RocksDB Repair Mode", &self.rocksdb_repair.to_string());
|
||||
line("RocksDB Read-only Mode", &self.rocksdb_read_only.to_string());
|
||||
line("RocksDB Secondary Mode", &self.rocksdb_secondary.to_string());
|
||||
line(
|
||||
"RocksDB Compaction Idle Priority",
|
||||
&self.rocksdb_compaction_prio_idle.to_string(),
|
||||
);
|
||||
line(
|
||||
"RocksDB Compaction Idle IOPriority",
|
||||
&self.rocksdb_compaction_ioprio_idle.to_string(),
|
||||
);
|
||||
line("RocksDB Compaction enabled", &self.rocksdb_compaction.to_string());
|
||||
line("RocksDB Statistics level", &self.rocksdb_stats_level.to_string());
|
||||
line("Media integrity checks on startup", &self.media_startup_check.to_string());
|
||||
line("Media compatibility filesystem links", &self.media_compat_file_link.to_string());
|
||||
line("Prune missing media from database", &self.prune_missing_media.to_string());
|
||||
line("Allow legacy (unauthenticated) media", &self.allow_legacy_media.to_string());
|
||||
line("Freeze legacy (unauthenticated) media", &self.freeze_legacy_media.to_string());
|
||||
line("Prevent Media Downloads From", {
|
||||
let mut lst = Vec::with_capacity(self.prevent_media_downloads_from.len());
|
||||
for domain in &self.prevent_media_downloads_from {
|
||||
lst.push(domain.host());
|
||||
}
|
||||
&lst.join(", ")
|
||||
});
|
||||
line("Forbidden Remote Server Names (\"Global\" ACLs)", {
|
||||
let mut lst = Vec::with_capacity(self.forbidden_remote_server_names.len());
|
||||
for domain in &self.forbidden_remote_server_names {
|
||||
lst.push(domain.host());
|
||||
}
|
||||
&lst.join(", ")
|
||||
});
|
||||
line("Forbidden Remote Room Directory Server Names", {
|
||||
let mut lst =
|
||||
Vec::with_capacity(self.forbidden_remote_room_directory_server_names.len());
|
||||
for domain in &self.forbidden_remote_room_directory_server_names {
|
||||
lst.push(domain.host());
|
||||
}
|
||||
&lst.join(", ")
|
||||
});
|
||||
line("Outbound Request IP Range (CIDR) Denylist", {
|
||||
let mut lst = Vec::with_capacity(self.ip_range_denylist.len());
|
||||
for item in self.ip_range_denylist.iter().cloned().enumerate() {
|
||||
let (_, ip): (usize, String) = item;
|
||||
lst.push(ip);
|
||||
}
|
||||
&lst.join(", ")
|
||||
});
|
||||
line("Forbidden usernames", {
|
||||
&self.forbidden_usernames.patterns().iter().join(", ")
|
||||
});
|
||||
line("Forbidden room aliases", {
|
||||
&self.forbidden_alias_names.patterns().iter().join(", ")
|
||||
});
|
||||
line(
|
||||
"URL preview bound interface",
|
||||
self.url_preview_bound_interface
|
||||
.as_ref()
|
||||
.map(Either::as_ref)
|
||||
.map(|either| either.map_left(ToString::to_string))
|
||||
.map(Either::either_into::<String>)
|
||||
.unwrap_or_default()
|
||||
.as_str(),
|
||||
);
|
||||
line(
|
||||
"URL preview domain contains allowlist",
|
||||
&self.url_preview_domain_contains_allowlist.join(", "),
|
||||
);
|
||||
line(
|
||||
"URL preview domain explicit allowlist",
|
||||
&self.url_preview_domain_explicit_allowlist.join(", "),
|
||||
);
|
||||
line(
|
||||
"URL preview domain explicit denylist",
|
||||
&self.url_preview_domain_explicit_denylist.join(", "),
|
||||
);
|
||||
line(
|
||||
"URL preview URL contains allowlist",
|
||||
&self.url_preview_url_contains_allowlist.join(", "),
|
||||
);
|
||||
line("URL preview maximum spider size", &self.url_preview_max_spider_size.to_string());
|
||||
line("URL preview check root domain", &self.url_preview_check_root_domain.to_string());
|
||||
line(
|
||||
"Allow check for updates / announcements check",
|
||||
&self.allow_check_for_updates.to_string(),
|
||||
);
|
||||
line("Enable netburst on startup", &self.startup_netburst.to_string());
|
||||
#[cfg(feature = "sentry_telemetry")]
|
||||
line("Sentry.io reporting and tracing", &self.sentry.to_string());
|
||||
#[cfg(feature = "sentry_telemetry")]
|
||||
line("Sentry.io send server_name in logs", &self.sentry_send_server_name.to_string());
|
||||
#[cfg(feature = "sentry_telemetry")]
|
||||
line("Sentry.io tracing sample rate", &self.sentry_traces_sample_rate.to_string());
|
||||
line("Sentry.io attach stacktrace", &self.sentry_attach_stacktrace.to_string());
|
||||
line("Sentry.io send panics", &self.sentry_send_panic.to_string());
|
||||
line("Sentry.io send errors", &self.sentry_send_error.to_string());
|
||||
line("Sentry.io tracing filter", &self.sentry_filter);
|
||||
line(
|
||||
"Well-known server name",
|
||||
self.well_known
|
||||
.server
|
||||
.as_ref()
|
||||
.map_or("", |server| server.as_str()),
|
||||
);
|
||||
line(
|
||||
"Well-known client URL",
|
||||
self.well_known
|
||||
.client
|
||||
.as_ref()
|
||||
.map_or("", |url| url.as_str()),
|
||||
);
|
||||
line(
|
||||
"Well-known support email",
|
||||
self.well_known
|
||||
.support_email
|
||||
.as_ref()
|
||||
.map_or("", |str| str.as_ref()),
|
||||
);
|
||||
line(
|
||||
"Well-known support Matrix ID",
|
||||
self.well_known
|
||||
.support_mxid
|
||||
.as_ref()
|
||||
.map_or("", |mxid| mxid.as_str()),
|
||||
);
|
||||
line(
|
||||
"Well-known support role",
|
||||
self.well_known
|
||||
.support_role
|
||||
.as_ref()
|
||||
.map_or("", |role| role.as_str()),
|
||||
);
|
||||
line(
|
||||
"Well-known support page/URL",
|
||||
self.well_known
|
||||
.support_page
|
||||
.as_ref()
|
||||
.map_or("", |url| url.as_str()),
|
||||
);
|
||||
line("Enable the tokio-console", &self.tokio_console.to_string());
|
||||
line("Admin room notices", &self.admin_room_notices.to_string());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn true_fn() -> bool { true }
|
||||
|
||||
fn default_address() -> ListeningAddr {
|
||||
@@ -2018,8 +2379,6 @@ fn default_notification_push_path() -> String { "/_matrix/push/v1/notify".to_own
|
||||
|
||||
fn default_openid_token_ttl() -> u64 { 60 * 60 }
|
||||
|
||||
fn default_login_token_ttl() -> u64 { 2 * 60 * 1000 }
|
||||
|
||||
fn default_turn_ttl() -> u64 { 60 * 60 * 24 }
|
||||
|
||||
fn default_presence_idle_timeout_s() -> u64 { 5 * 60 }
|
||||
|
||||
+4
-14
@@ -4,7 +4,7 @@ mod panic;
|
||||
mod response;
|
||||
mod serde;
|
||||
|
||||
use std::{any::Any, borrow::Cow, convert::Infallible, sync::PoisonError};
|
||||
use std::{any::Any, borrow::Cow, convert::Infallible, fmt, sync::PoisonError};
|
||||
|
||||
pub use self::{err::visit, log::*};
|
||||
|
||||
@@ -17,7 +17,7 @@ pub enum Error {
|
||||
|
||||
// std
|
||||
#[error(transparent)]
|
||||
Fmt(#[from] std::fmt::Error),
|
||||
Fmt(#[from] fmt::Error),
|
||||
#[error(transparent)]
|
||||
FromUtf8(#[from] std::string::FromUtf8Error),
|
||||
#[error("I/O error: {0}")]
|
||||
@@ -27,10 +27,6 @@ pub enum Error {
|
||||
#[error(transparent)]
|
||||
ParseInt(#[from] std::num::ParseIntError),
|
||||
#[error(transparent)]
|
||||
Std(#[from] Box<dyn std::error::Error + Send>),
|
||||
#[error(transparent)]
|
||||
ThreadAccessError(#[from] std::thread::AccessError),
|
||||
#[error(transparent)]
|
||||
TryFromInt(#[from] std::num::TryFromIntError),
|
||||
#[error(transparent)]
|
||||
TryFromSlice(#[from] std::array::TryFromSliceError),
|
||||
@@ -131,10 +127,6 @@ pub enum Error {
|
||||
}
|
||||
|
||||
impl Error {
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn from_errno() -> Self { Self::Io(std::io::Error::last_os_error()) }
|
||||
|
||||
//#[deprecated]
|
||||
pub fn bad_database(message: &'static str) -> Self {
|
||||
crate::err!(Database(error!("{message}")))
|
||||
@@ -197,10 +189,8 @@ impl Error {
|
||||
pub fn is_not_found(&self) -> bool { self.status_code() == http::StatusCode::NOT_FOUND }
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.message())
|
||||
}
|
||||
impl fmt::Debug for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.message()) }
|
||||
}
|
||||
|
||||
impl<T> From<PoisonError<T>> for Error {
|
||||
|
||||
@@ -20,8 +20,6 @@ pub const STABLE_ROOM_VERSIONS: &[RoomVersionId] = &[
|
||||
pub const UNSTABLE_ROOM_VERSIONS: &[RoomVersionId] =
|
||||
&[RoomVersionId::V2, RoomVersionId::V3, RoomVersionId::V4, RoomVersionId::V5];
|
||||
|
||||
type RoomVersion = (RoomVersionId, RoomVersionStability);
|
||||
|
||||
impl crate::Server {
|
||||
#[inline]
|
||||
pub fn supported_room_version(&self, version: &RoomVersionId) -> bool {
|
||||
@@ -30,13 +28,15 @@ impl crate::Server {
|
||||
|
||||
#[inline]
|
||||
pub fn supported_room_versions(&self) -> impl Iterator<Item = RoomVersionId> + '_ {
|
||||
Self::available_room_versions()
|
||||
self.available_room_versions()
|
||||
.filter(|(_, stability)| self.supported_stability(stability))
|
||||
.map(at!(0))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn available_room_versions() -> impl Iterator<Item = RoomVersion> {
|
||||
pub fn available_room_versions(
|
||||
&self,
|
||||
) -> impl Iterator<Item = (RoomVersionId, RoomVersionStability)> {
|
||||
available_room_versions()
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ impl crate::Server {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn available_room_versions() -> impl Iterator<Item = RoomVersion> {
|
||||
pub fn available_room_versions() -> impl Iterator<Item = (RoomVersionId, RoomVersionStability)> {
|
||||
let unstable_room_versions = UNSTABLE_ROOM_VERSIONS
|
||||
.iter()
|
||||
.cloned()
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
use std::sync::OnceLock;
|
||||
|
||||
static BRANDING: &str = "conduwuit";
|
||||
static BRANDING: &str = "Conduwuit";
|
||||
static SEMANTIC: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
static VERSION: OnceLock<String> = OnceLock::new();
|
||||
|
||||
+3
-3
@@ -8,12 +8,12 @@ use std::{
|
||||
|
||||
use tokio::{runtime, sync::broadcast};
|
||||
|
||||
use crate::{config, config::Config, err, log::Log, metrics::Metrics, Err, Result};
|
||||
use crate::{config::Config, err, log::Log, metrics::Metrics, Err, Result};
|
||||
|
||||
/// Server runtime state; public portion
|
||||
pub struct Server {
|
||||
/// Server-wide configuration instance
|
||||
pub config: config::Manager,
|
||||
pub config: Config,
|
||||
|
||||
/// Timestamp server was started; used for uptime.
|
||||
pub started: SystemTime,
|
||||
@@ -46,7 +46,7 @@ impl Server {
|
||||
#[must_use]
|
||||
pub fn new(config: Config, runtime: Option<runtime::Handle>, log: Log) -> Self {
|
||||
Self {
|
||||
config: config::Manager::new(config),
|
||||
config,
|
||||
started: SystemTime::now(),
|
||||
stopping: AtomicBool::new(false),
|
||||
reloading: AtomicBool::new(false),
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
mod expected;
|
||||
mod tried;
|
||||
|
||||
use std::{cmp, convert::TryFrom};
|
||||
|
||||
pub use checked_ops::checked_ops;
|
||||
|
||||
pub use self::{expected::Expected, tried::Tried};
|
||||
pub use self::expected::Expected;
|
||||
use crate::{debug::type_name, err, Err, Error, Result};
|
||||
|
||||
/// Checked arithmetic expression. Returns a Result<R, Error::Arithmetic>
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
use num_traits::ops::checked::{CheckedAdd, CheckedDiv, CheckedMul, CheckedRem, CheckedSub};
|
||||
|
||||
use crate::{checked, Result};
|
||||
|
||||
pub trait Tried {
|
||||
#[inline]
|
||||
fn try_add(self, rhs: Self) -> Result<Self>
|
||||
where
|
||||
Self: CheckedAdd + Sized,
|
||||
{
|
||||
checked!(self + rhs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_sub(self, rhs: Self) -> Result<Self>
|
||||
where
|
||||
Self: CheckedSub + Sized,
|
||||
{
|
||||
checked!(self - rhs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_mul(self, rhs: Self) -> Result<Self>
|
||||
where
|
||||
Self: CheckedMul + Sized,
|
||||
{
|
||||
checked!(self * rhs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_div(self, rhs: Self) -> Result<Self>
|
||||
where
|
||||
Self: CheckedDiv + Sized,
|
||||
{
|
||||
checked!(self / rhs)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_rem(self, rhs: Self) -> Result<Self>
|
||||
where
|
||||
Self: CheckedRem + Sized,
|
||||
{
|
||||
checked!(self % rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Tried for T {}
|
||||
+31
-57
@@ -37,7 +37,7 @@ pub use self::{
|
||||
rand::{shuffle, string as random_string},
|
||||
stream::{IterStream, ReadyExt, Tools as StreamTools, TryReadyExt},
|
||||
string::{str_from_bytes, string_from_bytes},
|
||||
sys::compute::available_parallelism,
|
||||
sys::compute::parallelism as available_parallelism,
|
||||
time::{
|
||||
exponential_backoff::{continue_exponential_backoff, continue_exponential_backoff_secs},
|
||||
now_millis as millis_since_unix_epoch, timepoint_ago, timepoint_from_now,
|
||||
@@ -57,14 +57,6 @@ macro_rules! extract_variant {
|
||||
};
|
||||
}
|
||||
|
||||
/// Functor for !is_empty()
|
||||
#[macro_export]
|
||||
macro_rules! is_not_empty {
|
||||
() => {
|
||||
|x| !x.is_empty()
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! apply {
|
||||
(1, $($idx:tt)+) => {
|
||||
@@ -84,35 +76,17 @@ macro_rules! apply {
|
||||
};
|
||||
}
|
||||
|
||||
/// Functor for truthy
|
||||
#[macro_export]
|
||||
macro_rules! is_true {
|
||||
() => {
|
||||
|x| !!x
|
||||
macro_rules! at {
|
||||
($idx:tt) => {
|
||||
|t| t.$idx
|
||||
};
|
||||
}
|
||||
|
||||
/// Functor for falsy
|
||||
#[macro_export]
|
||||
macro_rules! is_false {
|
||||
() => {
|
||||
|x| !x
|
||||
};
|
||||
}
|
||||
|
||||
/// Functor for equality to non-zero
|
||||
#[macro_export]
|
||||
macro_rules! is_nonzero {
|
||||
() => {
|
||||
|x| x != 0
|
||||
};
|
||||
}
|
||||
|
||||
/// Functor for equality to zero
|
||||
#[macro_export]
|
||||
macro_rules! is_zero {
|
||||
() => {
|
||||
$crate::is_matching!(0)
|
||||
macro_rules! ref_at {
|
||||
($idx:tt) => {
|
||||
|ref t| &t.$idx
|
||||
};
|
||||
}
|
||||
|
||||
@@ -140,6 +114,14 @@ macro_rules! is_less_than {
|
||||
};
|
||||
}
|
||||
|
||||
/// Functor for equality to zero
|
||||
#[macro_export]
|
||||
macro_rules! is_zero {
|
||||
() => {
|
||||
$crate::is_matching!(0)
|
||||
};
|
||||
}
|
||||
|
||||
/// Functor for matches! i.e. .is_some_and(is_matching!('A'..='Z'))
|
||||
#[macro_export]
|
||||
macro_rules! is_matching {
|
||||
@@ -152,6 +134,14 @@ macro_rules! is_matching {
|
||||
};
|
||||
}
|
||||
|
||||
/// Functor for !is_empty()
|
||||
#[macro_export]
|
||||
macro_rules! is_not_empty {
|
||||
() => {
|
||||
|x| !x.is_empty()
|
||||
};
|
||||
}
|
||||
|
||||
/// Functor for equality i.e. (a, b).map(is_equal!())
|
||||
#[macro_export]
|
||||
macro_rules! is_equal {
|
||||
@@ -160,34 +150,18 @@ macro_rules! is_equal {
|
||||
};
|
||||
}
|
||||
|
||||
/// Functor for |x| *x.$i
|
||||
/// Functor for truthy
|
||||
#[macro_export]
|
||||
macro_rules! deref_at {
|
||||
($idx:tt) => {
|
||||
|t| *t.$idx
|
||||
macro_rules! is_true {
|
||||
() => {
|
||||
|x| !!x
|
||||
};
|
||||
}
|
||||
|
||||
/// Functor for |ref x| x.$i
|
||||
/// Functor for falsy
|
||||
#[macro_export]
|
||||
macro_rules! ref_at {
|
||||
($idx:tt) => {
|
||||
|ref t| &t.$idx
|
||||
};
|
||||
}
|
||||
|
||||
/// Functor for |&x| x.$i
|
||||
#[macro_export]
|
||||
macro_rules! val_at {
|
||||
($idx:tt) => {
|
||||
|&t| t.$idx
|
||||
};
|
||||
}
|
||||
|
||||
/// Functor for |x| x.$i
|
||||
#[macro_export]
|
||||
macro_rules! at {
|
||||
($idx:tt) => {
|
||||
|t| t.$idx
|
||||
macro_rules! is_false {
|
||||
() => {
|
||||
|x| !x
|
||||
};
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user