mirror of
https://forgejo.ellis.link/continuwuation/continuwuity.git
synced 2026-05-26 20:49:55 +00:00
Compare commits
78 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d875e0c1c0 | |||
| 7a71012589 | |||
| 189688994b | |||
| bc093e9544 | |||
| 2dae3052e2 | |||
| 0f07b86159 | |||
| 2f84bc895d | |||
| 3b34e72456 | |||
| 7eee88160a | |||
| 5f46623371 | |||
| cbb97b4fdf | |||
| 14dcc8db15 | |||
| 213a6d441f | |||
| 98d96b89a5 | |||
| 1c0ed91f6f | |||
| 64705fa27d | |||
| 8affdc43a6 | |||
| 1ccdba8921 | |||
| 50ce87161b | |||
| c6f4b20e17 | |||
| 2cb31275f0 | |||
| 5aee03d14a | |||
| 959fd2e6c4 | |||
| f40a3ea4a6 | |||
| aa963c61da | |||
| c98cf13010 | |||
| 73dd4b1f8f | |||
| c921deaee2 | |||
| a5de4d30bb | |||
| 9e9b256b83 | |||
| ddf327e8b6 | |||
| 3341a8e56e | |||
| 4942d684ea | |||
| f8f6dba674 | |||
| e5c9bf1992 | |||
| 502856a4a2 | |||
| 2be2a0dc91 | |||
| cd3e7394bf | |||
| a543bec1a5 | |||
| a589a34d15 | |||
| 4312ac91c1 | |||
| e83b426caf | |||
| cad1d34611 | |||
| ce4517269b | |||
| 169617ab87 | |||
| 3596fe0e18 | |||
| a22524496d | |||
| 1d1b1644e9 | |||
| 483f0a9c86 | |||
| 3d3d63fdf4 | |||
| 08f2b8579c | |||
| 30e7298dd7 | |||
| 83565007bb | |||
| 3872ae80f7 | |||
| 0923b6f428 | |||
| 48d9677959 | |||
| 02bd67dc4b | |||
| 3813628acd | |||
| 7b7593a6f5 | |||
| 1323506c88 | |||
| 332fc74fb8 | |||
| 03c04ce0a1 | |||
| 08bf074cbb | |||
| 539aa27815 | |||
| 22272bdc16 | |||
| 8b68d6306c | |||
| d4775f0763 | |||
| 571ab6ac2b | |||
| 5df7443437 | |||
| e76e604771 | |||
| 1f9225e4d1 | |||
| c914a4fd91 | |||
| aa34021b27 | |||
| 1bb4021b90 | |||
| 877c04de52 | |||
| 029e1c630a | |||
| 39110ebc3c | |||
| 2ab3231ea6 |
@@ -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/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
|
||||
+25
-13
@@ -30,14 +30,13 @@ env:
|
||||
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 }}
|
||||
# Use the all-features devshell instead of default, to ensure that features
|
||||
# match between nix and cargo
|
||||
DIRENV_DEVSHELL: all-features
|
||||
# Get error output from nix that we can actually use
|
||||
NIX_CONFIG: show-trace = true
|
||||
|
||||
@@ -67,6 +66,11 @@ jobs:
|
||||
|
||||
- name: Install Nix
|
||||
uses: 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: |
|
||||
@@ -76,12 +80,15 @@ jobs:
|
||||
|
||||
- name: Configure Magic Nix Cache
|
||||
uses: 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
|
||||
extra-trusted-public-keys = conduit:eEKoUwlQGDdYmAI/Q/0slVlegqh/QmAvQd7HBSm21Wk= conduwuit:BbycGUgTISsltcmH0qNjFR9dbrQNYgdIAcmViSGoVTE= cache.lix.systems:aBnZUw8zA7H35Cz2RyKFVs3H4PlGTLawyY5KRbvJR8o=
|
||||
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
|
||||
@@ -142,9 +149,6 @@ jobs:
|
||||
- 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)
|
||||
|
||||
- name: Add Complement diff result to Job Summary
|
||||
run: |
|
||||
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
|
||||
@@ -157,7 +161,7 @@ jobs:
|
||||
echo '# ✅ completed suwuccessfully' >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
tail -n 20 test_output.log | sed 's/\x1b\[[0-9;]*m//g' >> $GITHUB_STEP_SUMMARY
|
||||
tail -n 40 test_output.log | sed 's/\x1b\[[0-9;]*m//g' >> $GITHUB_STEP_SUMMARY
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
@@ -165,7 +169,6 @@ jobs:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
needs: tests
|
||||
if: github.event.pull_request.draft != true
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
@@ -177,6 +180,11 @@ jobs:
|
||||
|
||||
- name: Install Nix
|
||||
uses: 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: |
|
||||
@@ -186,12 +194,15 @@ jobs:
|
||||
|
||||
- name: Configure Magic Nix Cache
|
||||
uses: 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
|
||||
extra-trusted-public-keys = conduit:eEKoUwlQGDdYmAI/Q/0slVlegqh/QmAvQd7HBSm21Wk= conduwuit:BbycGUgTISsltcmH0qNjFR9dbrQNYgdIAcmViSGoVTE= cache.lix.systems:aBnZUw8zA7H35Cz2RyKFVs3H4PlGTLawyY5KRbvJR8o=
|
||||
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
|
||||
@@ -212,6 +223,7 @@ jobs:
|
||||
- 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/
|
||||
@@ -255,7 +267,7 @@ jobs:
|
||||
name: Docker publish
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
if: (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main' || (github.event.pull_request.draft != true)) && (vars.DOCKER_USERNAME != '') && (vars.GITLAB_USERNAME != '')
|
||||
if: (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main' || (github.event.pull_request.draft != true)) && (vars.DOCKER_USERNAME != '') && (vars.GITLAB_USERNAME != '') && github.event.pull_request.user.login != 'renovate'
|
||||
env:
|
||||
DOCKER_ARM64: docker.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-arm64v8
|
||||
DOCKER_AMD64: docker.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-amd64
|
||||
|
||||
@@ -16,9 +16,13 @@ env:
|
||||
TERM: ansi
|
||||
# Publishing to my nix binary cache
|
||||
ATTIC_TOKEN: ${{ secrets.ATTIC_TOKEN }}
|
||||
# conduwuit.cachix.org
|
||||
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
# 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
|
||||
|
||||
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
|
||||
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
|
||||
@@ -29,7 +33,6 @@ concurrency:
|
||||
jobs:
|
||||
docs:
|
||||
name: Documentation and GitHub Pages
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
@@ -48,49 +51,51 @@ jobs:
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: actions/configure-pages@v5
|
||||
|
||||
- name: Install Nix (with flakes and nix-command enabled)
|
||||
uses: cachix/install-nix-action@v27
|
||||
- name: Install Nix
|
||||
uses: DeterminateSystems/nix-installer-action@main
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
|
||||
# Add `nix-community`, Crane, upstream Conduit, and conduwuit binary caches
|
||||
extra_nix_config: |
|
||||
diagnostic-endpoint: ""
|
||||
extra-conf: |
|
||||
experimental-features = nix-command flakes
|
||||
extra-substituters = https://nix-community.cachix.org
|
||||
extra-trusted-public-keys = nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=
|
||||
extra-substituters = https://crane.cachix.org
|
||||
extra-trusted-public-keys = crane.cachix.org-1:8Scfpmn9w+hGdXH/Q9tTLiYAE/2dnJYRJP7kl80GuRk=
|
||||
extra-substituters = https://attic.kennel.juneis.dog/conduit
|
||||
extra-trusted-public-keys = conduit:eEKoUwlQGDdYmAI/Q/0slVlegqh/QmAvQd7HBSm21Wk=
|
||||
extra-substituters = https://attic.kennel.juneis.dog/conduwuit
|
||||
extra-trusted-public-keys = conduwuit:BbycGUgTISsltcmH0qNjFR9dbrQNYgdIAcmViSGoVTE=
|
||||
accept-flake-config = true
|
||||
|
||||
- name: Add alternative Nix binary caches if specified
|
||||
- name: Enable Cachix binary cache
|
||||
run: |
|
||||
nix profile install nixpkgs#cachix
|
||||
cachix use crane
|
||||
cachix use nix-community
|
||||
|
||||
- name: Configure Magic Nix Cache
|
||||
uses: 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/QmAvQd7HBSm21Wkconduwuit:BbycGUgTISsltcmH0qNjFR9dbrQNYgdIAcmViSGoVTEcache.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: |
|
||||
echo "extra-substituters = ${{ env.ATTIC_ENDPOINT }}" >> /etc/nix/nix.conf
|
||||
echo "extra-trusted-public-keys = ${{ env.ATTIC_PUBLIC_KEY }}" >> /etc/nix/nix.conf
|
||||
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: Pop/push Magic Nix Cache
|
||||
uses: DeterminateSystems/magic-nix-cache-action@main
|
||||
|
||||
- name: Configure `nix-direnv`
|
||||
- name: Prepare build environment
|
||||
run: |
|
||||
echo 'source $HOME/.nix-profile/share/nix-direnv/direnvrc' > "$HOME/.direnvrc"
|
||||
|
||||
- name: Install `direnv` and `nix-direnv`
|
||||
run: nix-env -f "<nixpkgs>" -iA direnv -iA nix-direnv
|
||||
|
||||
# Do this to shorten the logs for the real CI step
|
||||
- name: Populate `/nix/store`
|
||||
run: nix develop --command true
|
||||
|
||||
- name: Allow direnv
|
||||
run: direnv allow
|
||||
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 --command true
|
||||
|
||||
- name: Cache CI dependencies
|
||||
run: |
|
||||
./bin/nix-build-and-cache ci
|
||||
bin/nix-build-and-cache ci
|
||||
|
||||
- name: Build documentation (book)
|
||||
run: |
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Run Trivy code and vulnerability scanner on repo
|
||||
uses: aquasecurity/trivy-action@0.22.0
|
||||
uses: aquasecurity/trivy-action@0.23.0
|
||||
with:
|
||||
scan-type: repo
|
||||
format: sarif
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
severity: CRITICAL,HIGH,MEDIUM,LOW
|
||||
|
||||
- name: Run Trivy code and vulnerability scanner on filesystem
|
||||
uses: aquasecurity/trivy-action@0.22.0
|
||||
uses: aquasecurity/trivy-action@0.23.0
|
||||
with:
|
||||
scan-type: fs
|
||||
format: sarif
|
||||
|
||||
+2
-2
@@ -58,7 +58,7 @@ before_script:
|
||||
|
||||
ci:
|
||||
stage: ci
|
||||
image: nixos/nix:2.22.1
|
||||
image: nixos/nix:2.23.0
|
||||
script:
|
||||
# Cache CI dependencies
|
||||
- ./bin/nix-build-and-cache ci
|
||||
@@ -83,7 +83,7 @@ ci:
|
||||
|
||||
artifacts:
|
||||
stage: artifacts
|
||||
image: nixos/nix:2.22.1
|
||||
image: nixos/nix:2.23.0
|
||||
script:
|
||||
- ./bin/nix-build-and-cache just .#static-x86_64-unknown-linux-musl
|
||||
- cp result/bin/conduit x86_64-unknown-linux-musl
|
||||
|
||||
Generated
+414
-365
File diff suppressed because it is too large
Load Diff
+43
-33
@@ -20,7 +20,7 @@ license = "Apache-2.0"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/girlbossceo/conduwuit"
|
||||
rust-version = "1.77.0"
|
||||
version = "0.4.2"
|
||||
version = "0.4.3"
|
||||
|
||||
[workspace.metadata.crane]
|
||||
name = "conduit"
|
||||
@@ -159,6 +159,8 @@ default-features = false
|
||||
[workspace.dependencies.tracing-subscriber]
|
||||
version = "0.3.18"
|
||||
features = ["env-filter"]
|
||||
[workspace.dependencies.tracing-core]
|
||||
version = "0.1.32"
|
||||
|
||||
# for URL previews
|
||||
[workspace.dependencies.webpage]
|
||||
@@ -271,13 +273,15 @@ version = "0.1.2"
|
||||
# Used for matrix spec type definitions and helpers
|
||||
[workspace.dependencies.ruma]
|
||||
git = "https://github.com/girlbossceo/ruwuma"
|
||||
branch = "conduwuit-changes"
|
||||
#branch = "conduwuit-changes"
|
||||
rev = "fd686e77950680462377c9105dfb4136dd49c7a0"
|
||||
features = [
|
||||
"compat",
|
||||
"rand",
|
||||
"appservice-api-c",
|
||||
"client-api",
|
||||
"federation-api",
|
||||
"markdown",
|
||||
"push-gateway-api-c",
|
||||
"state-res",
|
||||
"unstable-exhaustive-types",
|
||||
@@ -297,7 +301,7 @@ features = [
|
||||
|
||||
[workspace.dependencies.ruma-identifiers-validation]
|
||||
git = "https://github.com/girlbossceo/ruwuma"
|
||||
branch = "conduwuit-changes"
|
||||
rev = "fd686e77950680462377c9105dfb4136dd49c7a0"
|
||||
|
||||
[workspace.dependencies.rust-rocksdb]
|
||||
path = "deps/rust-rocksdb"
|
||||
@@ -411,26 +415,39 @@ features = [
|
||||
"light",
|
||||
]
|
||||
|
||||
[workspace.dependencies.rustyline-async]
|
||||
version = "0.4.2"
|
||||
|
||||
[workspace.dependencies.termimad]
|
||||
version = "0.29.4"
|
||||
default-features = false
|
||||
|
||||
|
||||
#
|
||||
# Patches
|
||||
#
|
||||
|
||||
# backport of [https://github.com/tokio-rs/tracing/pull/2956] to the 0.1.x branch of tracing.
|
||||
# we can switch back to upstream if #2956 is merged and backported in the upstream repo.
|
||||
# https://github.com/girlbossceo/tracing/commit/b348dca742af641c47bc390261f60711c2af573c
|
||||
[patch.crates-io.tracing-subscriber]
|
||||
git = "https://github.com/girlbossceo/tracing"
|
||||
branch = "tracing-subscriber/env-filter-clone-0.1.x-backport"
|
||||
rev = "b348dca742af641c47bc390261f60711c2af573c"
|
||||
[patch.crates-io.tracing]
|
||||
git = "https://github.com/girlbossceo/tracing"
|
||||
branch = "tracing-subscriber/env-filter-clone-0.1.x-backport"
|
||||
rev = "b348dca742af641c47bc390261f60711c2af573c"
|
||||
[patch.crates-io.tracing-core]
|
||||
git = "https://github.com/girlbossceo/tracing"
|
||||
branch = "tracing-subscriber/env-filter-clone-0.1.x-backport"
|
||||
rev = "b348dca742af641c47bc390261f60711c2af573c"
|
||||
[patch.crates-io.tracing-log]
|
||||
git = "https://github.com/girlbossceo/tracing"
|
||||
rev = "b348dca742af641c47bc390261f60711c2af573c"
|
||||
|
||||
# fixes hyper graceful shutdowns [https://github.com/programatik29/axum-server/issues/114]
|
||||
# https://github.com/girlbossceo/axum-server/commit/8e3368d899079818934e61cc9c839abcbbcada8a
|
||||
[patch.crates-io.axum-server]
|
||||
git = "https://github.com/girlbossceo/axum-server"
|
||||
branch = "phlip9/fix-graceful-shutdown"
|
||||
rev = "8e3368d899079818934e61cc9c839abcbbcada8a"
|
||||
|
||||
#
|
||||
# Our crates
|
||||
@@ -731,34 +748,27 @@ significant_drop_tightening = { level = "allow", priority = 1 } # TODO
|
||||
pedantic = "warn"
|
||||
|
||||
## some sadness
|
||||
cast_possible_truncation = "allow"
|
||||
cast_precision_loss = "allow"
|
||||
cast_sign_loss = "allow"
|
||||
doc_markdown = "allow"
|
||||
error_impl_error = "allow"
|
||||
expect_used = "allow"
|
||||
if_not_else = "allow"
|
||||
if_then_some_else_none = "allow"
|
||||
implicit_return = "allow"
|
||||
inline_always = "allow"
|
||||
map_err_ignore = "allow"
|
||||
missing_docs_in_private_items = "allow"
|
||||
missing_errors_doc = "allow"
|
||||
missing_panics_doc = "allow"
|
||||
mod_module_files = "allow"
|
||||
module_name_repetitions = "allow"
|
||||
multiple_inherent_impl = "allow"
|
||||
no_effect_underscore_binding = "allow"
|
||||
ref_patterns = "allow"
|
||||
same_name_method = "allow"
|
||||
cast_possible_truncation = { level = "allow", priority = 1 }
|
||||
cast_precision_loss = { level = "allow", priority = 1 }
|
||||
cast_sign_loss = { level = "allow", priority = 1 }
|
||||
doc_markdown = { level = "allow", priority = 1 }
|
||||
error_impl_error = { level = "allow", priority = 1 }
|
||||
expect_used = { level = "allow", priority = 1 }
|
||||
if_not_else = { level = "allow", priority = 1 }
|
||||
if_then_some_else_none = { level = "allow", priority = 1 }
|
||||
implicit_return = { level = "allow", priority = 1 }
|
||||
inline_always = { level = "allow", priority = 1 }
|
||||
map_err_ignore = { 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 }
|
||||
mod_module_files = { level = "allow", priority = 1 }
|
||||
module_name_repetitions = { level = "allow", priority = 1 }
|
||||
no_effect_underscore_binding = { level = "allow", priority = 1 }
|
||||
similar_names = { level = "allow", priority = 1 }
|
||||
single_call_fn = "allow"
|
||||
string_add = "allow"
|
||||
string_slice = "allow"
|
||||
struct_field_names = { level = "allow", priority = 1 }
|
||||
unnecessary_wraps = { level = "allow", priority = 1 }
|
||||
unused_async = { level = "allow", priority = 1 }
|
||||
unwrap_used = "allow"
|
||||
|
||||
###################
|
||||
perf = "warn"
|
||||
@@ -812,10 +822,10 @@ style = "warn"
|
||||
|
||||
## some sadness
|
||||
# trivial assertions are quite alright
|
||||
assertions_on_constants = "allow"
|
||||
assertions_on_constants = { level = "allow", priority = 1 }
|
||||
|
||||
###################
|
||||
suspicious = "warn"
|
||||
|
||||
## some sadness
|
||||
let_underscore_future = "allow"
|
||||
let_underscore_future = { level = "allow", priority = 1 }
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
[Unit]
|
||||
Description=conduwuit Matrix homeserver
|
||||
After=network.target
|
||||
Documentation=https://conduwuit.puppyirl.gay/
|
||||
RequiresMountsFor=/var/lib/private/conduwuit
|
||||
|
||||
[Service]
|
||||
DynamicUser=yes
|
||||
Type=notify
|
||||
|
||||
AmbientCapabilities=
|
||||
CapabilityBoundingSet=
|
||||
|
||||
DevicePolicy=closed
|
||||
LockPersonality=yes
|
||||
MemoryDenyWriteExecute=yes
|
||||
NoNewPrivileges=yes
|
||||
ProcSubset=pid
|
||||
ProtectClock=yes
|
||||
ProtectControlGroups=yes
|
||||
ProtectHome=yes
|
||||
ProtectHostname=yes
|
||||
ProtectKernelLogs=yes
|
||||
ProtectKernelModules=yes
|
||||
ProtectKernelTunables=yes
|
||||
ProtectProc=invisible
|
||||
ProtectSystem=strict
|
||||
PrivateDevices=yes
|
||||
PrivateMounts=yes
|
||||
PrivateTmp=yes
|
||||
PrivateUsers=yes
|
||||
PrivateIPC=yes
|
||||
RemoveIPC=yes
|
||||
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
|
||||
RestrictNamespaces=yes
|
||||
RestrictRealtime=yes
|
||||
RestrictSUIDSGID=yes
|
||||
SystemCallArchitectures=native
|
||||
SystemCallFilter=@system-service @resources
|
||||
SystemCallFilter=~@clock @debug @module @mount @reboot @swap @cpu-emulation @obsolete @timer @chown @setuid @privileged @keyring @ipc
|
||||
SystemCallErrorNumber=EPERM
|
||||
StateDirectory=conduwuit
|
||||
|
||||
RuntimeDirectory=conduwuit
|
||||
RuntimeDirectoryMode=0750
|
||||
|
||||
Environment="CONDUWUIT_CONFIG=/etc/conduwuit/conduwuit.toml"
|
||||
|
||||
ExecStart=/usr/bin/conduwuit
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
|
||||
TimeoutStopSec=4m
|
||||
TimeoutStartSec=4m
|
||||
|
||||
StartLimitInterval=1m
|
||||
StartLimitBurst=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -52,7 +52,7 @@ just() {
|
||||
"${ATTIC_ENDPOINT:-https://attic.kennel.juneis.dog/conduwuit}" \
|
||||
"$ATTIC_TOKEN"
|
||||
|
||||
# Upload them to Attic (conduwuit store)
|
||||
# Upload them to Attic (conduwuit store) and Cachix
|
||||
#
|
||||
# Use `xargs` and a here-string because something would probably explode if
|
||||
# several thousand arguments got passed to a command at once. Hopefully no
|
||||
@@ -61,6 +61,12 @@ just() {
|
||||
IFS=$'\n'
|
||||
nix shell --inputs-from "$toplevel" attic -c xargs \
|
||||
attic push conduwuit <<< "${cache[*]}"
|
||||
|
||||
# push to cachix if available
|
||||
if [ "$CACHIX_AUTH_TOKEN" ]; then
|
||||
nix shell --inputs-from "$toplevel" cachix -c xargs \
|
||||
cachix push conduwuit <<< "${cache[*]}"
|
||||
fi
|
||||
)
|
||||
}
|
||||
|
||||
@@ -73,6 +79,7 @@ ci() {
|
||||
"$toplevel#devShells.x86_64-linux.default"
|
||||
"$toplevel#devShells.x86_64-linux.all-features"
|
||||
attic#default
|
||||
cachix#default
|
||||
nixpkgs#direnv
|
||||
nixpkgs#jq
|
||||
nixpkgs#nix-direnv
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
[book]
|
||||
title = "conduwuit"
|
||||
title = "conduwuit 🏳️⚧️ 💜 🦴"
|
||||
description = "conduwuit, which is a well-maintained fork of Conduit, is a simple, fast and reliable chat server for the Matrix protocol"
|
||||
language = "en"
|
||||
authors = ["strawberry (June)"]
|
||||
text-direction = "ltr"
|
||||
multilingual = false
|
||||
src = "docs"
|
||||
|
||||
[build]
|
||||
build-dir = "public"
|
||||
create-missing = true
|
||||
extra-watch-dirs = ["debian", "docs"]
|
||||
|
||||
[rust]
|
||||
edition = "2021"
|
||||
|
||||
[output.html]
|
||||
git-repository-url = "https://github.com/girlbossceo/conduwuit"
|
||||
|
||||
@@ -198,6 +198,11 @@ registration_token = "change this token for something specific to your server"
|
||||
# defaults to false
|
||||
# block_non_admin_invites = false
|
||||
|
||||
# Allows admins to enter commands in rooms other than #admins by prefixing with \!admin. The reply
|
||||
# will be publicly visible to the room, originating from the sender.
|
||||
# defaults to true
|
||||
#admin_escape_commands = true
|
||||
|
||||
# List of forbidden username patterns/strings. Values in this list are matched as *contains*.
|
||||
# This is checked upon username availability check, registration, and startup as warnings if any local users in your database
|
||||
# have a forbidden username.
|
||||
|
||||
+2
-2
@@ -18,7 +18,7 @@ First, go into the `#admins` room of your homeserver. The first person that
|
||||
registered on the homeserver automatically joins it. Then send a message into
|
||||
the room like this:
|
||||
|
||||
@conduit:your.server.name: register-appservice
|
||||
!admin appservices register
|
||||
```
|
||||
paste
|
||||
the
|
||||
@@ -33,7 +33,7 @@ the room like this:
|
||||
You can confirm it worked by sending a message like this:
|
||||
`!admin appservices list`
|
||||
|
||||
The `@conduit` bot should answer with `Appservices (1): your-bridge`
|
||||
The server bot should answer with `Appservices (1): your-bridge`
|
||||
|
||||
Then you are done. conduwuit will send messages to the appservices and the
|
||||
appservice can send requests to the homeserver. You don't need to restart
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Conduwuit Community Code of Conduct
|
||||
# conduwuit Community Code of Conduct
|
||||
|
||||
Welcome to the conduwuit community! We’re excited to have you here. Conduwuit is a hard-fork of the Conduit homeserver,
|
||||
Welcome to the conduwuit community! We’re excited to have you here. conduwuit is a hard-fork of the Conduit homeserver,
|
||||
aimed at making Matrix more accessible and inclusive for everyone.
|
||||
|
||||
This space is dedicated to fostering a positive, supportive, and inclusive environment for everyone. This Code of
|
||||
@@ -39,7 +39,7 @@ additional guidelines specific to the conduwuit community.
|
||||
|
||||
## Matrix Community
|
||||
|
||||
This Code of Conduct applies to the entire [Conduwuit Matrix Space](https://matrix.to/#/#conduwuit-space:puppygock.gay)
|
||||
This Code of Conduct applies to the entire [conduwuit Matrix Space](https://matrix.to/#/#conduwuit-space:puppygock.gay)
|
||||
and its rooms, including:
|
||||
|
||||
### [#conduwuit:puppygock.gay](https://matrix.to/#/#conduwuit:puppygock.gay)
|
||||
@@ -74,4 +74,4 @@ a DM to one of the moderators directly.
|
||||
|
||||
Together, let’s build a community where everyone feels valued and respected.
|
||||
|
||||
- The Conduwuit Moderation Team
|
||||
- The conduwuit Moderation Team
|
||||
|
||||
+24
-3
@@ -1,11 +1,32 @@
|
||||
# Example configuration
|
||||
## Example configuration
|
||||
|
||||
``` toml
|
||||
<details>
|
||||
<summary>Example configuration</summary>
|
||||
|
||||
```toml
|
||||
{{#include ../conduwuit-example.toml}}
|
||||
```
|
||||
|
||||
# Example systemd unit file
|
||||
</details>
|
||||
|
||||
## Debian systemd unit file
|
||||
|
||||
<details>
|
||||
<summary>Debian systemd unit file</summary>
|
||||
|
||||
```
|
||||
{{#include ../debian/conduwuit.service}}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## Arch Linux systemd unit file
|
||||
|
||||
<details>
|
||||
<summary>Arch Linux systemd unit file</summary>
|
||||
|
||||
```
|
||||
{{#include ../arch/conduwuit.service}}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
# Deploying
|
||||
|
||||
This chapter describes various ways to deploy Conduwuit.
|
||||
This chapter describes various ways to deploy conduwuit.
|
||||
|
||||
@@ -6,3 +6,4 @@ The conduwuit AUR packages are community maintained and are not maintained by co
|
||||
|
||||
- [conduwuit](https://aur.archlinux.org/packages/conduwuit) - latest tagged conduwuit
|
||||
- [conduwuit-git](https://aur.archlinux.org/packages/conduwuit-git) - latest git conduwuit from `main` branch
|
||||
- [conduwuit-bin](https://aur.archlinux.org/packages/conduwuit-bin) - latest tagged conduwuit static binary
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
To run conduwuit with Docker you can either build the image yourself or pull it from a registry.
|
||||
|
||||
|
||||
### Use a registry
|
||||
|
||||
OCI images for conduwuit are available in the registries listed below.
|
||||
@@ -24,14 +23,12 @@ OCI images for conduwuit are available in the registries listed below.
|
||||
[shield-latest]: https://img.shields.io/docker/image-size/girlbossceo/conduwuit/latest
|
||||
[shield-main]: https://img.shields.io/docker/image-size/girlbossceo/conduwuit/main
|
||||
|
||||
|
||||
Use
|
||||
```bash
|
||||
docker image pull <link>
|
||||
```
|
||||
to pull it to your machine.
|
||||
|
||||
|
||||
### Run
|
||||
|
||||
When you have the image you can simply run it with
|
||||
@@ -39,13 +36,9 @@ When you have the image you can simply run it with
|
||||
```bash
|
||||
docker run -d -p 8448:6167 \
|
||||
-v db:/var/lib/conduwuit/ \
|
||||
-e CONDUIT_SERVER_NAME="your.server.name" \
|
||||
-e CONDUIT_DATABASE_BACKEND="rocksdb" \
|
||||
-e CONDUIT_ALLOW_REGISTRATION=false \
|
||||
-e CONDUIT_ALLOW_FEDERATION=true \
|
||||
-e CONDUIT_MAX_REQUEST_SIZE="40000000" \
|
||||
-e CONDUIT_TRUSTED_SERVERS="[\"matrix.org\"]" \
|
||||
-e CONDUIT_LOG="warn,ruma_state_res=warn" \
|
||||
-e CONDUWUIT_SERVER_NAME="your.server.name" \
|
||||
-e CONDUWUIT_DATABASE_BACKEND="rocksdb" \
|
||||
-e CONDUWUIT_ALLOW_REGISTRATION=false \
|
||||
--name conduit <link>
|
||||
```
|
||||
|
||||
|
||||
@@ -114,7 +114,7 @@ $ sudo systemctl enable conduwuit
|
||||
|
||||
You can open [a Matrix client](https://matrix.org/ecosystem/clients), enter your homeserver and try to register.
|
||||
|
||||
You can also use these commands as a quick health check.
|
||||
You can also use these commands as a quick health check (replace `your.server.name`).
|
||||
|
||||
```bash
|
||||
$ curl https://your.server.name/_conduwuit/server_version
|
||||
|
||||
@@ -30,7 +30,7 @@ The `flake.nix` and `default.nix` do not (currently) provide a NixOS module, so
|
||||
(for now) [`services.matrix-conduit`][module] from Nixpkgs should be used to
|
||||
configure conduwuit.
|
||||
|
||||
If you want to run the latest code, you should get Conduwuit from the `flake.nix`
|
||||
If you want to run the latest code, you should get conduwuit from the `flake.nix`
|
||||
or `default.nix` and set [`services.matrix-conduit.package`][package]
|
||||
appropriately.
|
||||
|
||||
|
||||
+2
-2
@@ -7,7 +7,7 @@ it, you can safely ignore this section. If you plan on contributing, see the
|
||||
## Debugging with `tokio-console`
|
||||
|
||||
[`tokio-console`][1] can be a useful tool for debugging and profiling. To make
|
||||
a `tokio-console`-enabled build of Conduwuit, enable the `tokio_console` feature,
|
||||
a `tokio-console`-enabled build of conduwuit, enable the `tokio_console` feature,
|
||||
disable the default `release_max_log_level` feature, and set the
|
||||
`--cfg tokio_unstable` flag to enable experimental tokio APIs. A build might
|
||||
look like this:
|
||||
@@ -16,7 +16,7 @@ look like this:
|
||||
RUSTFLAGS="--cfg tokio_unstable" cargo build \
|
||||
--release \
|
||||
--no-default-features \
|
||||
--features=rocksdb,systemd,element_hacks,sentry_telemetry,gzip_compression,brotli_compression,zstd_compression,tokio_console
|
||||
--features=rocksdb,systemd,element_hacks,gzip_compression,brotli_compression,zstd_compression,tokio_console
|
||||
```
|
||||
|
||||
[1]: https://docs.rs/tokio-console/latest/tokio_console/
|
||||
|
||||
+65
-32
@@ -5,43 +5,56 @@
|
||||
Outgoing typing indicators, outgoing read receipts, **and** outgoing presence!
|
||||
|
||||
## Performance:
|
||||
- Concurrency support for key fetching for faster remote room joins and room joins that will error less frequently
|
||||
- Concurrency support for individual homeserver key fetching for faster remote room joins and room joins that will error less frequently
|
||||
- Send `Cache-Control` response header with `immutable` and 1 year cache length for all media requests (download and thumbnail) to instruct clients to cache media, and reduce server load from media requests that could be otherwise cached
|
||||
- Add feature flags and config options to enable/build with zstd, brotli, and/or gzip HTTP body compression (response and request)
|
||||
- Eliminate all usage of the thread-blocking `getaddrinfo(3)` call upon DNS queries, significantly improving federation latency/ping and cache DNS results (NXDOMAINs, successful queries, etc) using hickory-dns / hickory-resolver
|
||||
- Enable HTTP/2 support on all requests
|
||||
- Vastly improve RocksDB default settings to use new features that help with performance significantly, uses settings tailored to SSDs, various ways to tweak RocksDB, and a conduwuit setting to tell RocksDB to use settings that are tailored to HDDs or slow spinning rust storage or buggy filesystems.
|
||||
- Add a Cargo build profile for aggressive build-time performance optimisations for release builds (1 codegen unit, no debug, fat LTO, etc, and optimise all crates with same)
|
||||
- Implement database flush and cleanup conduwuit operations when using RocksDB
|
||||
- Implement RocksDB write buffer corking and coalescing in database write-heavy areas
|
||||
- Perform connection pooling and keepalives where necessary to significantly improve federation performance and latency
|
||||
- Various config options to tweak connection pooling, request timeouts, connection timeouts, DNS timeouts and settings, etc with good defaults which also help huge with performance via reusing connections and retrying where needed
|
||||
- Implement building conduwuit with jemalloc (which extends to the RocksDB jemalloc feature for maximum gains) or hardened_malloc light variant, and produce CI builds with jemalloc for performance (Nix doesn't seem to build [hardened_malloc-rs](https://github.com/girlbossceo/hardened_malloc-rs) properly)
|
||||
- Add support for caching DNS results with hickory-dns / hickory-resolver in conduwuit (not a replacement for a proper resolver cache, but still far better than nothing)
|
||||
- Properly get and use the amount of parallelism / tokio workers
|
||||
- Implement building conduwuit with jemalloc (which extends to the RocksDB jemalloc feature for maximum gains) or hardened_malloc light variant, and io_uring support, and produce CI builds with jemalloc and io_uring by default for performance (Nix doesn't seem to build [hardened_malloc-rs](https://github.com/girlbossceo/hardened_malloc-rs) properly)
|
||||
- Add support for caching DNS results with hickory-dns / hickory-resolver in conduwuit (not a replacement for a proper resolver cache, but still far better than nothing), also properly falls back on TCP for UDP errors or if a SRV response is too large
|
||||
- Add config option for using DNS over TCP, and config option for controlling A/AAAA record lookup strategy (e.g. don't query AAAA records if you don't have IPv6 connectivity)
|
||||
- Overall significant database, Client-Server, and federation performance and latency improvements (check out the ping room leaderboards if you don't believe me :>)
|
||||
- Add config options for RocksDB compression and bottommost compression, including choosing the algorithm and compression level
|
||||
- Use [loole](https://github.com/mahdi-shojaee/loole) MPSC channels instead of tokio MPSC channels for huge performance boosts in sending channels (mainly relevant for federation) and presence channels
|
||||
- Use `tracing`/`log`'s `release_max_level_info` feature to improve performance, build speeds, binary size, and CPU usage in release builds by avoid compiling debug/trace log level macros that users will generally never use (can be disabled with a build-time feature flag)
|
||||
- Enable RocksDB async read I/O via `io_uring` by default
|
||||
- Remove some unnecessary checks on EDU handling for incoming transactions, effectively speeding them up
|
||||
- Simplify, dedupe, etc huge chunks of the codebase, including some that were unnecessary overhead, binary bloats, or preventing compiler/linker optimisations
|
||||
|
||||
|
||||
## General Fixes:
|
||||
## General Fixes/Features:
|
||||
- Add legacy Element client hack fixing password changes and deactivations on legacy Element Android/iOS due to usage of an unspecced `user` field for UIAA
|
||||
- Raise and improve all the various request timeouts making some things like room joins and client bugs error less or none at all than they should, and make them all user configurable
|
||||
- Add missing `reason` field to user ban events (`/ban`)
|
||||
- Fixed spec compliance issue with room version 8 - 11 joins (https://github.com/matrix-org/synapse/issues/16717 / https://github.com/matrix-org/matrix-spec/issues/1708)
|
||||
- Safer and cleaner shutdowns on both database side as we run cleanup on shutdown and exits database loop better (no potential hanging issues in database loop), overall cleaner shutdown logic
|
||||
- Safer and cleaner shutdowns across incoming/outgoing requests (graceful shutdown) and the database
|
||||
- Stop sending `make_join` requests on room joins if 15 servers respond with `M_UNSUPPORTED_ROOM_VERSION` or `M_INVALID_ROOM_VERSION`
|
||||
- Stop sending `make_join` requests if 50 servers cannot provide `make_join` for us
|
||||
- Respect *most* client parameters for `/media/` requests (`allow_redirect` still needs work)
|
||||
- Increased graceful shutdown timeout from a low 60 seconds to 180 seconds to avoid killing connections and let the remaining ones finish processing
|
||||
- Return joined member count of rooms for push rules/conditions instead of a hardcoded value of 10
|
||||
- Make `CONDUIT_CONFIG` optional, relevant for container users that configure only by environment variables and no longer need to set `CONDUIT_CONFIG` to an empty string.
|
||||
- Allow HEAD and PATCH (MSC4138) HTTP requests in CORS for clients (despite not being explicity mentioned in Matrix spec, HTTP spec says all HEAD requests need to behave the same as GET requests, Synapse supports HEAD requests)
|
||||
- Fix using conduwuit with flake-compat on NixOS
|
||||
- Resolve and remove some "features" from upstream that result in concurrency hazards, exponential backoff issues, or arbitrary performance limiters
|
||||
- Find more servers for outbound federation `/hierarchy` requests instead of just the room ID server name
|
||||
- Support for suggesting servers to join through at `/_matrix/client/v3/directory/room/{roomAlias}`
|
||||
- Support for suggesting servers to join through us at `/_matrix/federation/v1/query/directory`
|
||||
- Add workaround for [Out Of Your Element](https://gitdab.com/cadence/out-of-your-element) appservice bridge to make it functional on conduwuit (bug has already been reported)
|
||||
- Misc edge-case search fixes (e.g. potentially missing some events)
|
||||
- Misc `/sync` fixes (e.g. returning unnecessary data or incorrect/invalid responses)
|
||||
- Add `replaces_state` and `prev_sender` in `unsigned` for state event changes which primarily makes Element's "See history" button on a state event functional
|
||||
- Fix Conduit not allowing incoming federation requests for various world readable rooms
|
||||
- Fix Conduit not respecting the client-requested file name on media requests
|
||||
- Prevent sending junk / non-membership events to `/send_join` and `/send_leave` endpoints
|
||||
- Only allow the requested membership type on `/send_join` and `/send_leave` endpoints (e.g. don't allow leave memberships on join endpoints)
|
||||
- Prevent state key impersonation on `/send_join` and `/send_leave` endpoints
|
||||
- Validate `X-Matrix` origin and request body `"origin"` field on incoming transactions
|
||||
- Add `GET /_matrix/client/v1/register/m.login.registration_token/validity` endpoint
|
||||
- Explicitly define support for sliding sync at `/_matrix/client/versions` (`org.matrix.msc3575`)
|
||||
- Fix seeing empty status messages on user presences
|
||||
|
||||
|
||||
## Moderation:
|
||||
@@ -50,13 +63,15 @@ Outgoing typing indicators, outgoing read receipts, **and** outgoing presence!
|
||||
- Add support for serving `support` well-known from `[well_known.support]` (MSC1929)
|
||||
- Config option to forbid publishing rooms to the room directory (`lockdown_public_room_directory`) except for admins
|
||||
- Admin commands to delete room aliases and unpublish rooms from our room directory
|
||||
- For all [`/report`](https://spec.matrix.org/v1.9/client-server-api/#post_matrixclientv3roomsroomidreporteventid) requests: check if the reported event ID belongs to the reported room ID, raise report reasoning character limit to 750, fix broken formatting, make a small delayed random response per spec suggestion on privacy, and check if the sender user is in the reported room.
|
||||
- For all [`/report`](https://spec.matrix.org/latest/client-server-api/#post_matrixclientv3roomsroomidreporteventid) requests: check if the reported event ID belongs to the reported room ID, raise report reasoning character limit to 750, fix broken formatting, make a small delayed random response per spec suggestion on privacy, and check if the sender user is in the reported room.
|
||||
- Support blocking servers from downloading remote media from, returning a 404
|
||||
- Don't allow `m.call.invite` events to be sent in public rooms (prevents calling the entire room)
|
||||
- On new public room creations, only allow moderators to send `m.call.invite`, `org.matrix.msc3401.call`, and `org.matrix.msc3401.call.member` events
|
||||
- On new public room creations, only allow moderators to send `m.call.invite`, `org.matrix.msc3401.call`, and `org.matrix.msc3401.call.member` events to prevent unprivileged users from calling the entire room
|
||||
- Add support for a "global ACLs" feature (`forbidden_remote_server_names`) that blocks inbound remote room invites, room joins by room ID on server name, room joins by room alias on server name, incoming federated joins, and incoming federated room directory requests. This is very helpful for blocking servers that are purely toxic/bad and serve no value in allowing our users to suffer from things like room invite spam or such. Please note that this is not a substitute for room ACLs.
|
||||
- Add support for a config option to forbid our local users from sending federated room directory requests for (`forbidden_remote_room_directory_server_names`). Similar to above, useful for blocking servers that help prevent our users from wandering into bad areas of Matrix via room directories of those malicious servers.
|
||||
- Add config option for auto remediating/deactivating local non-admin users who attempt to join bad/forbidden rooms (`auto_deactivate_banned_room_attempts`)
|
||||
- Deactivating users will remove their profile picture, blurhash, display name, and leave all rooms by default just like Synapse and for additional privacy
|
||||
- Reject some EDUs from ACL'd users such as read receipts and typing indicators
|
||||
|
||||
|
||||
## Privacy/Security:
|
||||
@@ -69,10 +84,10 @@ Outgoing typing indicators, outgoing read receipts, **and** outgoing presence!
|
||||
- Config option to block non-admin users from sending room invites or receiving remote room invites. Admin users are still allowed.
|
||||
- Config option to disable incoming and/or outgoing remote read receipts
|
||||
- Config option to disable incoming and/or outgoing remote typing indicators
|
||||
- Config option to disable incoming, outgoing, and/or local presence
|
||||
- Config option to disable incoming, outgoing, and/or local presence and for timing out remote users
|
||||
- Sanitise file names for the `Content-Disposition` header for all media requests (thumbnails, downloads, uploads)
|
||||
- Return `inline` or `attachment` based on the detected file MIME type for the `Content-Disposition` and only allow images/videos/text/audio to be `inline`
|
||||
- Send secure default HTTP headers such as a strong restrictive CSP, deny iframes, disable `X-XSS-Protection`, disable interest cohort in `Permission-Policy`, etc to mitigate any potential attack surface such as from untrusted media
|
||||
- Media repository on handling `Content-Disposition` and `Content-Type` is fully spec compliant and secured
|
||||
- Send secure default HTTP headers such as a strong restrictive CSP (see MSC4149), deny iframes, disable `X-XSS-Protection`, disable interest cohort in `Permission-Policy`, etc to mitigate any potential attack surface such as from untrusted media
|
||||
|
||||
|
||||
## Administration/Logging:
|
||||
@@ -81,36 +96,45 @@ Outgoing typing indicators, outgoing read receipts, **and** outgoing presence!
|
||||
- Substantially clean up, improve, and fix logging (less noisy dead server logging, registration attempts, more useful troubleshooting logging, proper error propagation, etc)
|
||||
- Configurable RocksDB logging (`LOG` files) with proper defaults (rotate, max size, verbosity, etc) to stop LOG files from accumulating so much
|
||||
- Explicit startup error if your configuration allows open registration without a token or such like Synapse with a way to bypass it if needed
|
||||
- Replace the lightning bolt emoji option with support for setting any arbitrary text (e.g. another emoji) to suffix to all new user registrations, with a conduwuit default of 🏳️⚧️
|
||||
- Replace the lightning bolt emoji option with support for setting any arbitrary text (e.g. another emoji) to suffix to all new user registrations, with a conduwuit default of "🏳️⚧️"
|
||||
- Implement config option to auto join rooms upon registration
|
||||
- Warn on unknown config options specified
|
||||
- Add `/_conduwuit/server_version` route to return the version of conduwuit without relying on the federation API `/_matrix/federation/v1/version`
|
||||
- Add `/_conduwuit/local_user_count` route to return the amount of registered active local users on your homeserver *if federation is enabled*
|
||||
- Add configurable RocksDB recovery modes to aid in recovering corrupted RocksDB databases
|
||||
- Support config options via `CONDUWUIT_` prefix and accessing non-global struct config options with the `__` split (e.g. `CONDUWUIT_WELL_KNOWN__SERVER`)
|
||||
- Add support for listening on multiple TCP ports
|
||||
- Disable update check by default as it's not useful for conduwuit
|
||||
- Add support for listening on multiple TCP ports and multiple addresses
|
||||
- **Opt-in** Sentry.io telemetry and metrics, mainly used for crash reporting
|
||||
- Log the client IP on various requests such as registrations, banned room join attempts, logins, deactivations, federation transactions, etc
|
||||
- Fix Conduit dropping some remote server federation response errors
|
||||
|
||||
|
||||
## Maintenance/Stability:
|
||||
- GitLab CI ported to GitHub Actions
|
||||
- Repo is mirrored to GitHub, GitLab, git.gay, git.girlcock.ceo, sourcehut, and Codeberg (see README.md for their links)
|
||||
- Add support for the Matrix spec compliance test suite [Complement](https://github.com/matrix-org/complement/) via the Nix flake and various other fixes for it
|
||||
- Implement running and diff'ing Complement results in CI and error if any mismatch occurs to prevent large cases of conduwuit regressions
|
||||
- Repo is (officially) mirrored to GitHub, GitLab, git.gay, git.girlcock.ceo, sourcehut, and Codeberg (see README.md for their links)
|
||||
- Docker container images published to GitLab Container Registry, GitHub Container Registry, and Dockerhub
|
||||
- Extensively revamp the example config to be extremely helpful and useful to both new users and power users
|
||||
- Fixed every single clippy (default lints) and rustc warnings, including some that were performance related or potential safety issues / unsoundness
|
||||
- Add a **lot** of other clippy and rustc lints and a rustfmt.toml file
|
||||
- Has [Renovate](https://docs.renovatebot.com/), [Trivy](https://github.com/aquasecurity/trivy-action), and keeps ALL dependencies as up to date as possible
|
||||
- Repo uses [Renovate](https://docs.renovatebot.com/), [Trivy](https://github.com/aquasecurity/trivy-action), and keeps ALL dependencies as up to date as possible
|
||||
- Attempts and interest in removing extreme and unnecessary panics/unwraps/expects that can lead to denial of service or such (upstream and upstream contributors want this unusual behaviour for some reason)
|
||||
- Purge unmaintained/irrelevant/broken database backends (heed, sled, persy) and other unnecessary code or overhead
|
||||
- webp support for images
|
||||
- Add cargo audit support to CI
|
||||
- CI tests with all features
|
||||
- Add timestamp by commit date support to building OCI images for keeping image build reproducibility and still have a meaningful "last modified date" for OCI image metadata
|
||||
- Update rusqlite/sqlite (not that you should be using it)
|
||||
- CI tests for all sorts of feature matrixes (jemalloc, non-defaullt, all features, etc)
|
||||
- Add static and dynamic linking smoke tests in CI to prevent any potential linking regressions for Complement, static binaries, Nix devshells, etc
|
||||
- Add timestamp by commit date when building OCI images for keeping image build reproducibility and still have a meaningful "last modified date" for OCI image
|
||||
- Add timestamp by commit date via `SOURCE_DATE_EPOCH` for Debian packages
|
||||
- Startup check if conduwuit running in a container and is listening on 127.0.0.1 (generally containers are using NAT networking and 0.0.0.0 is the intended listening address)
|
||||
- Add a panic catcher layer to return panic messages in HTTP responses if a panic occurs
|
||||
|
||||
|
||||
## Admin Room:
|
||||
- Add support for a console CLI interface that can issue admin commands and output them in your terminal
|
||||
- Add support for an admin-user-only commandline admin room interface that can be issued in any room with the `\\!admin` or `\!admin` prefix and returns the response as yourself in the same room
|
||||
- Add admin commands for uptime, server startup, server shutdown, and server restart
|
||||
- Fix admin room handler to not panic/crash if the admin room command response fails (e.g. too large message)
|
||||
- Add command to dynamically change conduwuit's tracing log level filter on the fly
|
||||
- Add admin command to fetch a server's `/.well-known/matrix/support` file
|
||||
@@ -124,17 +148,26 @@ Outgoing typing indicators, outgoing read receipts, **and** outgoing presence!
|
||||
- Add admin command to bulk delete media via a codeblock list of MXC URLs.
|
||||
- Add admin command to delete both the thumbnail and media MXC URLs from an event ID (e.g. from an abuse report)
|
||||
- Add admin command to list all the rooms a local user is joined in
|
||||
- Add admin command to delete all remote media in the past X minutes as a form of deleting media that you don't want on your server that a remote user posted in a room
|
||||
- Add admin command to return a room's state
|
||||
- Add admin command to list joined members in a room
|
||||
- Add admin command to view the room topic of a room
|
||||
- Add admin command to delete all remote media in the past X minutes as a form of deleting media that you don't want on your server that a remote user posted in a room, a `--force` flag to ignore errors, and support for reading `last modified time` instead of `creation time` for filesystems that don't support file created metadata
|
||||
- Add admin command to return a room's full/complete state
|
||||
- Admin debug command to fetch a PDU from a remote server and inserts it into our database/timeline as backfill
|
||||
- Add admin command to delete media via a specific MXC. This deletes the MXC from our database, and the file locally.
|
||||
- Add admin commands for banning (blocking) room IDs from our local users joining (admins are always allowed) and evicts all our local users from that room, in addition to bulk room banning support, and blocks room invites (remote and local) to the banned room, as a moderation feature
|
||||
- Add admin commands to output jemalloc memory stats and memory usage
|
||||
- Add admin command to get conduwuit's uptime
|
||||
- Add admin command to get rooms a *remote* user shares with us
|
||||
- Add debug admin commands to get the earliest and latest PDU in a room
|
||||
- Add debug admin command to echo a message
|
||||
- Add admin command to insert rooms tags for a user, most useful for inserting the `m.server_notice` tag on your admin room to make it "persistent" in the "System Alerts" section of Element
|
||||
- Add experimental admin debug command for Dendrite's `AdminDownloadState` (`/admin/downloadState/{serverName}/{roomID}`) admin API endpoint to download and use a remote server's room state in the room
|
||||
- Disable URL previews by default in the admin room due to various command outputs having "URLs" in them that clients may needlessly render/request
|
||||
- Extend memory usage admin server command to support showing memory allocator stats such as jemalloc's
|
||||
- Add admin debug command to see memory allocator's full extended debug statistics such as jemalloc's
|
||||
|
||||
|
||||
## Misc:
|
||||
- Add guest support for accessing TURN servers via `turn_allow_guests` like Synapse
|
||||
- Support for creating rooms with custom room IDs like Maunium Synapse (`room_id` request body field to `/createRoom`)
|
||||
- Query parameter `?format=event|content` for returning either the room state event's content (default) for the full room state event on ` /_matrix/client/v3/rooms/{roomId}/state/{eventType}[/{stateKey}]` requests (see https://github.com/matrix-org/matrix-spec/issues/1047)
|
||||
- Add **optional** feature flag to use SHA256 key names for media instead of base64 to overcome filesystem file name length limitations (OS error file name too long)
|
||||
@@ -146,15 +179,15 @@ Outgoing typing indicators, outgoing read receipts, **and** outgoing presence!
|
||||
- Implement legacy Matrix `/v1/` media endpoints that some clients and servers may still call
|
||||
- Config option to change Conduit's behaviour of homeserver key fetching (`query_trusted_key_servers_first`). This option sets whether conduwuit will query trusted notary key servers first before the individual homeserver(s), or vice versa which may help in joining certain rooms.
|
||||
- Implement unstable MSC2666 support for querying mutual rooms with a user
|
||||
- Assume well-knowns are broken if they exceed past 10000 characters.
|
||||
- Add support for the Matrix spec compliance test suite [Complement](https://github.com/matrix-org/complement/) via the Nix flake and various other fixes for it
|
||||
- Implement unstable MSC4125 support for specifying servers to join via on federated invites
|
||||
- Make conduwuit build and be functional under Nix + macOS
|
||||
- Log out all sessions after unsetting the emergency password
|
||||
- Assume well-knowns are broken if they exceed past 12288 characters.
|
||||
- Add support for listening on both HTTP and HTTPS if using direct TLS with conduwuit for usecases such as Complement
|
||||
- Implement running and diff'ing Complement results in CI
|
||||
- Interest in supporting other operating systems such as macOS, BSDs, and Windows, and getting them added into CI and doing builds for them
|
||||
- Add config option for disabling RocksDB Direct IO if needed
|
||||
- Add various documentation on maintaining conduwuit, using RocksDB online backups, some troubleshooting, using admin commands, etc
|
||||
- Add various documentation on maintaining conduwuit, using RocksDB online backups, some troubleshooting, using admin commands, moderation documentation, etc
|
||||
- (Developers): Add support for [hot reloadable/"live" modular development](development/hot_reload.md)
|
||||
- (Developers): Add support for tokio-console
|
||||
- (Developers): Add support for tracing flame graphs
|
||||
- Add `release-debuginfo` Cargo build profile
|
||||
- No cryptocurrency donations allowed, conduwuit is fully maintained by independent queer maintainers, and with a strong priority on inclusitivity and comfort for protected groups 🏳️⚧️
|
||||
- [Add a community Code of Conduct for all conduwuit community spaces, primarily the Matrix space](https://conduwuit.puppyirl.gay/conduwuit_coc.html)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Conduwuit
|
||||
# conduwuit
|
||||
|
||||
{{#include ../README.md:catchphrase}}
|
||||
|
||||
@@ -12,7 +12,7 @@ See the [differences](differences.md) page
|
||||
|
||||
- [Deployment options](deploying.md)
|
||||
|
||||
If you want to connect an Appservice to Conduwuit, take a look at the [appservices documentation](appservices.md).
|
||||
If you want to connect an appservice to conduwuit, take a look at the [appservices documentation](appservices.md).
|
||||
|
||||
#### How can I contribute?
|
||||
|
||||
|
||||
+4
-4
@@ -41,7 +41,7 @@ To restore a backup from an online RocksDB backup:
|
||||
- create a new directory for merging together the data
|
||||
- in the online backup created, copy all `.sst` files in `$DATABASE_BACKUP_PATH/shared_checksum` to your new directory
|
||||
- trim all the strings so instead of `######_sxxxxxxxxx.sst`, it reads `######.sst`. A way of doing this with sed and bash is `for file in *.sst; do mv "$file" "$(echo "$file" | sed 's/_s.*/.sst/')"; done`
|
||||
- copy all the files in `$DATABASE_BACKUP_PATH/1` to your new directory
|
||||
- copy all the files in `$DATABASE_BACKUP_PATH/1` (or the latest backup number if you have multiple) to your new directory
|
||||
- set your `database_path` config option to your new directory, or replace your old one with the new one you crafted
|
||||
- start up conduwuit again and it should open as normal
|
||||
|
||||
@@ -52,12 +52,12 @@ Backing up media is also just copying the `media/` directory from your database
|
||||
## Media
|
||||
|
||||
Media still needs various work, however conduwuit implements media deletion via:
|
||||
- MXC URI
|
||||
- MXC URI or Event ID (unencrypted and attempts to find the MXC URI in the event)
|
||||
- Delete list of MXC URIs
|
||||
- Delete remote media in the past `N` seconds/minutes
|
||||
- Delete remote media in the past `N` seconds/minutes via filesystem metadata on the file created time (`btime`) or file modified time (`mtime`)
|
||||
|
||||
See the `!admin media` command for further information. All media in conduwuit is stored at `$DATABASE_DIR/media`. This will be configurable soon.
|
||||
|
||||
If you are finding yourself needing extensive granular control over media, we recommend looking into [Matrix Media Repo](https://github.com/t2bot/matrix-media-repo). conduwuit intends to implement various utilities for media, but MMR is dedicated to extensive media management.
|
||||
|
||||
Built-in S3 support is also planned, but for now using a "S3 filesystem" on `media/` works. conduwuit also sends a `Cache-Control` header of 1 year and immutable for all media requests (download and thumbnail) to reduce unnecessary media requests from browsers.
|
||||
Built-in S3 support is also planned, but for now using a "S3 filesystem" on `media/` works. conduwuit also sends a `Cache-Control` header of 1 year and immutable for all media requests (download and thumbnail) to reduce unnecessary media requests from browsers, reduce bandwidth usage, and reduce load.
|
||||
|
||||
@@ -16,7 +16,9 @@ Some filesystems may not like RocksDB using [Direct IO](https://github.com/faceb
|
||||
|
||||
#### Database corruption
|
||||
|
||||
If your database is corrupted and is failing to start (e.g. checksum mismatch), it may be recoverable but careful steps must be taken, and there is no guarantee it may be recoverable.
|
||||
If your database is corrupted *and* is failing to start (e.g. checksum mismatch), it may be recoverable but careful steps must be taken, and there is no guarantee it may be recoverable.
|
||||
|
||||
The first thing that can be done is launching conduwuit with the `rocksdb_repair` config option set to true. This will tell RocksDB to attempt to repair itself at launch. If this does not work, disable the option and continue reading.
|
||||
|
||||
RocksDB has the following recovery modes:
|
||||
|
||||
|
||||
+1
-15
@@ -13,21 +13,7 @@ realm=<your server domain>
|
||||
```
|
||||
A common way to generate a suitable alphanumeric secret key is by using `pwgen -s 64 1`.
|
||||
|
||||
These same values need to be set in conduwuit. You can either modify conduwuit.toml to include these lines:
|
||||
|
||||
```
|
||||
turn_uris = ["turn:<your server domain>?transport=udp", "turn:<your server domain>?transport=tcp"]
|
||||
turn_secret = "<secret key from coturn configuration>"
|
||||
```
|
||||
|
||||
or append the following to the docker environment variables dependig on which configuration method you used earlier:
|
||||
|
||||
```yml
|
||||
CONDUIT_TURN_URIS: '["turn:<your server domain>?transport=udp", "turn:<your server domain>?transport=tcp"]'
|
||||
CONDUIT_TURN_SECRET: "<secret key from coturn configuration>"
|
||||
```
|
||||
|
||||
Restart conduwuit to apply these changes.
|
||||
These same values need to be set in conduwuit. See the [example config](configuration.md) in the TURN section for configuring these and restart conduwuit after.
|
||||
|
||||
### Run
|
||||
Run the [Coturn](https://hub.docker.com/r/coturn/coturn) image using
|
||||
|
||||
Generated
+352
-3
@@ -23,6 +23,28 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"cachix": {
|
||||
"inputs": {
|
||||
"devenv": "devenv",
|
||||
"flake-compat": "flake-compat_3",
|
||||
"nixpkgs": "nixpkgs_3",
|
||||
"pre-commit-hooks": "pre-commit-hooks"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1717420532,
|
||||
"narHash": "sha256-OCCmI69EMaA4BcxRKrXJsx5Ozua2f/PKEy4aJbE7ziM=",
|
||||
"owner": "cachix",
|
||||
"repo": "cachix",
|
||||
"rev": "5727f0676f08a4b41ed13d403ec64dcce989f6e5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"ref": "master",
|
||||
"repo": "cachix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"complement": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
@@ -82,6 +104,35 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"devenv": {
|
||||
"inputs": {
|
||||
"flake-compat": [
|
||||
"cachix",
|
||||
"flake-compat"
|
||||
],
|
||||
"nix": "nix",
|
||||
"nixpkgs": "nixpkgs_2",
|
||||
"poetry2nix": "poetry2nix",
|
||||
"pre-commit-hooks": [
|
||||
"cachix",
|
||||
"pre-commit-hooks"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1708704632,
|
||||
"narHash": "sha256-w+dOIW60FKMaHI1q5714CSibk99JfYxm0CzTinYWr+Q=",
|
||||
"owner": "cachix",
|
||||
"repo": "devenv",
|
||||
"rev": "2ee4450b0f4b95a1b90f2eb5ffea98b90e48c196",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"ref": "python-rewrite",
|
||||
"repo": "devenv",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"fenix": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
@@ -121,6 +172,54 @@
|
||||
}
|
||||
},
|
||||
"flake-compat_2": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1673956053,
|
||||
"narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-compat_3": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1696426674,
|
||||
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-compat_4": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1696426674,
|
||||
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-compat_5": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1696426674,
|
||||
@@ -156,6 +255,42 @@
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1689068808,
|
||||
"narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils_3": {
|
||||
"inputs": {
|
||||
"systems": "systems_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1710146030,
|
||||
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils_4": {
|
||||
"inputs": {
|
||||
"systems": "systems_3"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1710146030,
|
||||
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
||||
@@ -171,6 +306,28 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"gitignore": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"cachix",
|
||||
"pre-commit-hooks",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1709087332,
|
||||
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"liburing": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
@@ -188,6 +345,31 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat_2",
|
||||
"nixpkgs": [
|
||||
"cachix",
|
||||
"devenv",
|
||||
"nixpkgs"
|
||||
],
|
||||
"nixpkgs-regression": "nixpkgs-regression"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1708577783,
|
||||
"narHash": "sha256-92xq7eXlxIT5zFNccLpjiP7sdQqQI30Gyui2p/PfKZM=",
|
||||
"owner": "domenkozar",
|
||||
"repo": "nix",
|
||||
"rev": "ecd0af0c1f56de32cbad14daa1d82a132bf298f8",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "domenkozar",
|
||||
"ref": "devenv-2.21",
|
||||
"repo": "nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix-filter": {
|
||||
"locked": {
|
||||
"lastModified": 1710156097,
|
||||
@@ -204,6 +386,29 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix-github-actions": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"cachix",
|
||||
"devenv",
|
||||
"poetry2nix",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1688870561,
|
||||
"narHash": "sha256-4UYkifnPEw1nAzqqPOTL2MvWtm3sNGw1UTYTalkTcGY=",
|
||||
"owner": "nix-community",
|
||||
"repo": "nix-github-actions",
|
||||
"rev": "165b1650b753316aa7f1787f3005a8d2da0f5301",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "nix-github-actions",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1711401922,
|
||||
@@ -220,6 +425,22 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-regression": {
|
||||
"locked": {
|
||||
"lastModified": 1643052045,
|
||||
"narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-stable": {
|
||||
"locked": {
|
||||
"lastModified": 1711460390,
|
||||
@@ -236,7 +457,55 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-stable_2": {
|
||||
"locked": {
|
||||
"lastModified": 1710695816,
|
||||
"narHash": "sha256-3Eh7fhEID17pv9ZxrPwCLfqXnYP006RKzSs0JptsN84=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "614b4613980a522ba49f0d194531beddbb7220d3",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-23.11",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1692808169,
|
||||
"narHash": "sha256-x9Opq06rIiwdwGeK2Ykj69dNc2IvUH1fY55Wm7atwrE=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "9201b5ff357e781bf014d0330d18555695df7ba8",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_3": {
|
||||
"locked": {
|
||||
"lastModified": 1715534503,
|
||||
"narHash": "sha256-5ZSVkFadZbFP1THataCaSf0JH2cAH3S29hU9rrxTEqk=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "2057814051972fa1453ddfb0d98badbea9b83c06",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_4": {
|
||||
"locked": {
|
||||
"lastModified": 1716330097,
|
||||
"narHash": "sha256-8BO3B7e3BiyIDsaKA0tY8O88rClYRTjvAp66y+VBUeU=",
|
||||
@@ -252,6 +521,55 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"poetry2nix": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils_2",
|
||||
"nix-github-actions": "nix-github-actions",
|
||||
"nixpkgs": [
|
||||
"cachix",
|
||||
"devenv",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1692876271,
|
||||
"narHash": "sha256-IXfZEkI0Mal5y1jr6IRWMqK8GW2/f28xJenZIPQqkY0=",
|
||||
"owner": "nix-community",
|
||||
"repo": "poetry2nix",
|
||||
"rev": "d5006be9c2c2417dafb2e2e5034d83fabd207ee3",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "poetry2nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"pre-commit-hooks": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat_4",
|
||||
"flake-utils": "flake-utils_3",
|
||||
"gitignore": "gitignore",
|
||||
"nixpkgs": [
|
||||
"cachix",
|
||||
"nixpkgs"
|
||||
],
|
||||
"nixpkgs-stable": "nixpkgs-stable_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1715609711,
|
||||
"narHash": "sha256-/5u29K0c+4jyQ8x7dUIEUWlz2BoTSZWUP2quPwFCE7M=",
|
||||
"owner": "cachix",
|
||||
"repo": "pre-commit-hooks.nix",
|
||||
"rev": "c182c876690380f8d3b9557c4609472ebfa1b141",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"repo": "pre-commit-hooks.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"rocksdb": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
@@ -272,14 +590,15 @@
|
||||
"root": {
|
||||
"inputs": {
|
||||
"attic": "attic",
|
||||
"cachix": "cachix",
|
||||
"complement": "complement",
|
||||
"crane": "crane_2",
|
||||
"fenix": "fenix",
|
||||
"flake-compat": "flake-compat_2",
|
||||
"flake-utils": "flake-utils_2",
|
||||
"flake-compat": "flake-compat_5",
|
||||
"flake-utils": "flake-utils_4",
|
||||
"liburing": "liburing",
|
||||
"nix-filter": "nix-filter",
|
||||
"nixpkgs": "nixpkgs_2",
|
||||
"nixpkgs": "nixpkgs_4",
|
||||
"rocksdb": "rocksdb"
|
||||
}
|
||||
},
|
||||
@@ -314,6 +633,36 @@
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems_2": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems_3": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
inputs = {
|
||||
attic.url = "github:zhaofengli/attic?ref=main";
|
||||
cachix.url = "github:cachix/cachix?ref=master";
|
||||
complement = { url = "github:matrix-org/complement?ref=main"; flake = false; };
|
||||
crane = { url = "github:ipetkov/crane?ref=master"; inputs.nixpkgs.follows = "nixpkgs"; };
|
||||
fenix = { url = "github:nix-community/fenix?ref=main"; inputs.nixpkgs.follows = "nixpkgs"; };
|
||||
|
||||
@@ -18,7 +18,9 @@ stdenv.mkDerivation {
|
||||
"README.md"
|
||||
"debian/conduwuit.service"
|
||||
"debian/README.md"
|
||||
"arch/conduwuit.service"
|
||||
"docs"
|
||||
"theme"
|
||||
];
|
||||
};
|
||||
|
||||
@@ -27,7 +29,6 @@ stdenv.mkDerivation {
|
||||
];
|
||||
|
||||
buildPhase = ''
|
||||
mdbook build
|
||||
mv public $out
|
||||
mdbook build -d $out
|
||||
'';
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use ruma::{api::appservice::Registration, events::room::message::RoomMessageEventContent};
|
||||
|
||||
use crate::{escape_html, services, Result};
|
||||
use crate::{services, Result};
|
||||
|
||||
pub(crate) async fn register(body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn register(body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
if body.len() < 2 || !body[0].trim().starts_with("```") || body.last().unwrap_or(&"").trim() != "```" {
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
"Expected code block in command body. Add --help for details.",
|
||||
@@ -26,7 +26,7 @@ pub(crate) async fn register(body: Vec<&str>) -> Result<RoomMessageEventContent>
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn unregister(_body: Vec<&str>, appservice_identifier: String) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn unregister(_body: Vec<&str>, appservice_identifier: String) -> Result<RoomMessageEventContent> {
|
||||
match services()
|
||||
.appservice
|
||||
.unregister_appservice(&appservice_identifier)
|
||||
@@ -39,7 +39,7 @@ pub(crate) async fn unregister(_body: Vec<&str>, appservice_identifier: String)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn show(_body: Vec<&str>, appservice_identifier: String) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn show(_body: Vec<&str>, appservice_identifier: String) -> Result<RoomMessageEventContent> {
|
||||
match services()
|
||||
.appservice
|
||||
.get_registration(&appservice_identifier)
|
||||
@@ -48,18 +48,13 @@ pub(crate) async fn show(_body: Vec<&str>, appservice_identifier: String) -> Res
|
||||
Some(config) => {
|
||||
let config_str = serde_yaml::to_string(&config).expect("config should've been validated on register");
|
||||
let output = format!("Config for {appservice_identifier}:\n\n```yaml\n{config_str}\n```",);
|
||||
let output_html = format!(
|
||||
"Config for {}:\n\n<pre><code class=\"language-yaml\">{}</code></pre>",
|
||||
escape_html(&appservice_identifier),
|
||||
escape_html(&config_str),
|
||||
);
|
||||
Ok(RoomMessageEventContent::text_html(output, output_html))
|
||||
Ok(RoomMessageEventContent::notice_markdown(output))
|
||||
},
|
||||
None => Ok(RoomMessageEventContent::text_plain("Appservice does not exist.")),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn list(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn list(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
let appservices = services().appservice.iter_ids().await;
|
||||
let output = format!("Appservices ({}): {}", appservices.len(), appservices.join(", "));
|
||||
Ok(RoomMessageEventContent::text_plain(output))
|
||||
@@ -1,14 +1,14 @@
|
||||
mod commands;
|
||||
|
||||
use clap::Subcommand;
|
||||
use conduit::Result;
|
||||
use ruma::events::room::message::RoomMessageEventContent;
|
||||
|
||||
use self::appservice_command::{list, register, show, unregister};
|
||||
|
||||
pub(crate) mod appservice_command;
|
||||
use self::commands::*;
|
||||
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
#[derive(Subcommand)]
|
||||
pub(crate) enum AppserviceCommand {
|
||||
pub(super) enum AppserviceCommand {
|
||||
/// - Register an appservice using its registration YAML
|
||||
///
|
||||
/// This command needs a YAML generated by an appservice (such as a bridge),
|
||||
@@ -38,7 +38,7 @@ pub(crate) enum AppserviceCommand {
|
||||
List,
|
||||
}
|
||||
|
||||
pub(crate) async fn process(command: AppserviceCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn process(command: AppserviceCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
Ok(match command {
|
||||
AppserviceCommand::Register => register(body).await?,
|
||||
AppserviceCommand::Unregister {
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
use std::{
|
||||
collections::{BTreeMap, HashMap},
|
||||
sync::Arc,
|
||||
sync::{Arc, Mutex},
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
use api::client::validate_and_add_event_id;
|
||||
use conduit::{utils::HtmlEscape, Error, Result};
|
||||
use conduit::{
|
||||
debug, info, log,
|
||||
log::{capture, Capture},
|
||||
warn, Error, Result,
|
||||
};
|
||||
use ruma::{
|
||||
api::{client::error::ErrorKind, federation::event::get_room_state},
|
||||
events::room::message::RoomMessageEventContent,
|
||||
@@ -13,10 +17,15 @@ use ruma::{
|
||||
};
|
||||
use service::{rooms::event_handler::parse_incoming_pdu, sending::resolve::resolve_actual_dest, services, PduEvent};
|
||||
use tokio::sync::RwLock;
|
||||
use tracing::{debug, info, warn};
|
||||
use tracing_subscriber::EnvFilter;
|
||||
|
||||
pub(crate) async fn get_auth_chain(_body: Vec<&str>, event_id: Box<EventId>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn echo(_body: Vec<&str>, message: Vec<String>) -> Result<RoomMessageEventContent> {
|
||||
let message = message.join(" ");
|
||||
|
||||
Ok(RoomMessageEventContent::notice_plain(message))
|
||||
}
|
||||
|
||||
pub(super) async fn get_auth_chain(_body: Vec<&str>, event_id: Box<EventId>) -> Result<RoomMessageEventContent> {
|
||||
let event_id = Arc::<EventId>::from(event_id);
|
||||
if let Some(event) = services().rooms.timeline.get_pdu_json(&event_id)? {
|
||||
let room_id_str = event
|
||||
@@ -42,7 +51,7 @@ pub(crate) async fn get_auth_chain(_body: Vec<&str>, event_id: Box<EventId>) ->
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn parse_pdu(body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn parse_pdu(body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
if body.len() < 2 || !body[0].trim().starts_with("```") || body.last().unwrap_or(&"").trim() != "```" {
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
"Expected code block in command body. Add --help for details.",
|
||||
@@ -70,7 +79,7 @@ pub(crate) async fn parse_pdu(body: Vec<&str>) -> Result<RoomMessageEventContent
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn get_pdu(_body: Vec<&str>, event_id: Box<EventId>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn get_pdu(_body: Vec<&str>, event_id: Box<EventId>) -> Result<RoomMessageEventContent> {
|
||||
let mut outlier = false;
|
||||
let mut pdu_json = services()
|
||||
.rooms
|
||||
@@ -83,32 +92,21 @@ pub(crate) async fn get_pdu(_body: Vec<&str>, event_id: Box<EventId>) -> Result<
|
||||
match pdu_json {
|
||||
Some(json) => {
|
||||
let json_text = serde_json::to_string_pretty(&json).expect("canonical json is valid json");
|
||||
Ok(RoomMessageEventContent::text_html(
|
||||
format!(
|
||||
"{}\n```json\n{}\n```",
|
||||
if outlier {
|
||||
"Outlier PDU found in our database"
|
||||
} else {
|
||||
"PDU found in our database"
|
||||
},
|
||||
json_text
|
||||
),
|
||||
format!(
|
||||
"<p>{}</p>\n<pre><code class=\"language-json\">{}\n</code></pre>\n",
|
||||
if outlier {
|
||||
"Outlier PDU found in our database"
|
||||
} else {
|
||||
"PDU found in our database"
|
||||
},
|
||||
HtmlEscape(&json_text)
|
||||
),
|
||||
))
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"{}\n```json\n{}\n```",
|
||||
if outlier {
|
||||
"Outlier PDU found in our database"
|
||||
} else {
|
||||
"PDU found in our database"
|
||||
},
|
||||
json_text
|
||||
)))
|
||||
},
|
||||
None => Ok(RoomMessageEventContent::text_plain("PDU not found locally.")),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn get_remote_pdu_list(
|
||||
pub(super) async fn get_remote_pdu_list(
|
||||
body: Vec<&str>, server: Box<ServerName>, force: bool,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
if !services().globals.config.allow_federation {
|
||||
@@ -155,7 +153,7 @@ pub(crate) async fn get_remote_pdu_list(
|
||||
Ok(RoomMessageEventContent::text_plain("Fetched list of remote PDUs."))
|
||||
}
|
||||
|
||||
pub(crate) async fn get_remote_pdu(
|
||||
pub(super) async fn get_remote_pdu(
|
||||
_body: Vec<&str>, event_id: Box<EventId>, server: Box<ServerName>,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
if !services().globals.config.allow_federation {
|
||||
@@ -227,17 +225,10 @@ pub(crate) async fn get_remote_pdu(
|
||||
|
||||
let json_text = serde_json::to_string_pretty(&json).expect("canonical json is valid json");
|
||||
|
||||
Ok(RoomMessageEventContent::text_html(
|
||||
format!(
|
||||
"{}\n```json\n{}\n```",
|
||||
"Got PDU from specified server and handled as backfilled PDU successfully. Event body:", json_text
|
||||
),
|
||||
format!(
|
||||
"<p>{}</p>\n<pre><code class=\"language-json\">{}\n</code></pre>\n",
|
||||
"Got PDU from specified server and handled as backfilled PDU successfully. Event body:",
|
||||
HtmlEscape(&json_text)
|
||||
),
|
||||
))
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"{}\n```json\n{}\n```",
|
||||
"Got PDU from specified server and handled as backfilled PDU successfully. Event body:", json_text
|
||||
)))
|
||||
},
|
||||
Err(e) => Ok(RoomMessageEventContent::text_plain(format!(
|
||||
"Remote server did not have PDU or failed sending request to remote server: {e}"
|
||||
@@ -245,7 +236,7 @@ pub(crate) async fn get_remote_pdu(
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn get_room_state(_body: Vec<&str>, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn get_room_state(_body: Vec<&str>, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
|
||||
let room_state = services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
@@ -268,17 +259,13 @@ pub(crate) async fn get_room_state(_body: Vec<&str>, room_id: Box<RoomId>) -> Re
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(RoomMessageEventContent::text_html(
|
||||
format!("{}\n```json\n{}\n```", "Found full room state", json_text),
|
||||
format!(
|
||||
"<p>{}</p>\n<pre><code class=\"language-json\">{}\n</code></pre>\n",
|
||||
"Found full room state",
|
||||
HtmlEscape(&json_text)
|
||||
),
|
||||
))
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"{}\n```json\n{}\n```",
|
||||
"Found full room state", json_text
|
||||
)))
|
||||
}
|
||||
|
||||
pub(crate) async fn ping(_body: Vec<&str>, server: Box<ServerName>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn ping(_body: Vec<&str>, server: Box<ServerName>) -> Result<RoomMessageEventContent> {
|
||||
if server == services().globals.server_name() {
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
"Not allowed to send federation requests to ourselves.",
|
||||
@@ -298,14 +285,9 @@ pub(crate) async fn ping(_body: Vec<&str>, server: Box<ServerName>) -> Result<Ro
|
||||
let json_text_res = serde_json::to_string_pretty(&response.server);
|
||||
|
||||
if let Ok(json) = json_text_res {
|
||||
return Ok(RoomMessageEventContent::text_html(
|
||||
format!("Got response which took {ping_time:?} time:\n```json\n{json}\n```"),
|
||||
format!(
|
||||
"<p>Got response which took {ping_time:?} time:</p>\n<pre><code \
|
||||
class=\"language-json\">{}\n</code></pre>\n",
|
||||
HtmlEscape(&json)
|
||||
),
|
||||
));
|
||||
return Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Got response which took {ping_time:?} time:\n```json\n{json}\n```"
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(RoomMessageEventContent::text_plain(format!(
|
||||
@@ -321,7 +303,7 @@ pub(crate) async fn ping(_body: Vec<&str>, server: Box<ServerName>) -> Result<Ro
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn force_device_list_updates(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn force_device_list_updates(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
// Force E2EE device list updates for all users
|
||||
for user_id in services().users.iter().filter_map(Result::ok) {
|
||||
services().users.mark_device_key_update(&user_id)?;
|
||||
@@ -331,7 +313,7 @@ pub(crate) async fn force_device_list_updates(_body: Vec<&str>) -> Result<RoomMe
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) async fn change_log_level(
|
||||
pub(super) async fn change_log_level(
|
||||
_body: Vec<&str>, filter: Option<String>, reset: bool,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
if reset {
|
||||
@@ -344,11 +326,7 @@ pub(crate) async fn change_log_level(
|
||||
},
|
||||
};
|
||||
|
||||
match services()
|
||||
.server
|
||||
.tracing_reload_handle
|
||||
.reload(&old_filter_layer)
|
||||
{
|
||||
match services().server.log.reload.reload(&old_filter_layer) {
|
||||
Ok(()) => {
|
||||
return Ok(RoomMessageEventContent::text_plain(format!(
|
||||
"Successfully changed log level back to config value {}",
|
||||
@@ -373,11 +351,7 @@ pub(crate) async fn change_log_level(
|
||||
},
|
||||
};
|
||||
|
||||
match services()
|
||||
.server
|
||||
.tracing_reload_handle
|
||||
.reload(&new_filter_layer)
|
||||
{
|
||||
match services().server.log.reload.reload(&new_filter_layer) {
|
||||
Ok(()) => {
|
||||
return Ok(RoomMessageEventContent::text_plain("Successfully changed log level"));
|
||||
},
|
||||
@@ -392,7 +366,7 @@ pub(crate) async fn change_log_level(
|
||||
Ok(RoomMessageEventContent::text_plain("No log level was specified."))
|
||||
}
|
||||
|
||||
pub(crate) async fn sign_json(body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn sign_json(body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
if body.len() < 2 || !body[0].trim().starts_with("```") || body.last().unwrap_or(&"").trim() != "```" {
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
"Expected code block in command body. Add --help for details.",
|
||||
@@ -415,7 +389,7 @@ pub(crate) async fn sign_json(body: Vec<&str>) -> Result<RoomMessageEventContent
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn verify_json(body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn verify_json(body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
if body.len() < 2 || !body[0].trim().starts_with("```") || body.last().unwrap_or(&"").trim() != "```" {
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
"Expected code block in command body. Add --help for details.",
|
||||
@@ -446,7 +420,7 @@ pub(crate) async fn verify_json(body: Vec<&str>) -> Result<RoomMessageEventConte
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(_body))]
|
||||
pub(crate) async fn first_pdu_in_room(_body: Vec<&str>, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn first_pdu_in_room(_body: Vec<&str>, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
|
||||
if !services()
|
||||
.rooms
|
||||
.state_cache
|
||||
@@ -467,7 +441,7 @@ pub(crate) async fn first_pdu_in_room(_body: Vec<&str>, room_id: Box<RoomId>) ->
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(_body))]
|
||||
pub(crate) async fn latest_pdu_in_room(_body: Vec<&str>, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn latest_pdu_in_room(_body: Vec<&str>, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
|
||||
if !services()
|
||||
.rooms
|
||||
.state_cache
|
||||
@@ -488,7 +462,7 @@ pub(crate) async fn latest_pdu_in_room(_body: Vec<&str>, room_id: Box<RoomId>) -
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(_body))]
|
||||
pub(crate) async fn force_set_room_state_from_server(
|
||||
pub(super) async fn force_set_room_state_from_server(
|
||||
_body: Vec<&str>, server_name: Box<ServerName>, room_id: Box<RoomId>,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
if !services()
|
||||
@@ -598,17 +572,7 @@ pub(crate) async fn force_set_room_state_from_server(
|
||||
.state_compressor
|
||||
.save_state(room_id.clone().as_ref(), new_room_state)?;
|
||||
|
||||
let mutex_state = Arc::clone(
|
||||
services()
|
||||
.globals
|
||||
.roomid_mutex_state
|
||||
.write()
|
||||
.await
|
||||
.entry(room_id.clone().into())
|
||||
.or_default(),
|
||||
);
|
||||
let state_lock = mutex_state.lock().await;
|
||||
|
||||
let state_lock = services().globals.roomid_mutex_state.lock(&room_id).await;
|
||||
services()
|
||||
.rooms
|
||||
.state
|
||||
@@ -628,7 +592,7 @@ pub(crate) async fn force_set_room_state_from_server(
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) async fn resolve_true_destination(
|
||||
pub(super) async fn resolve_true_destination(
|
||||
_body: Vec<&str>, server_name: Box<ServerName>, no_cache: bool,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
if !services().globals.config.allow_federation {
|
||||
@@ -643,15 +607,30 @@ pub(crate) async fn resolve_true_destination(
|
||||
));
|
||||
}
|
||||
|
||||
let (actual_dest, hostname_uri) = resolve_actual_dest(&server_name, !no_cache).await?;
|
||||
let filter: &capture::Filter = &|data| {
|
||||
data.level() <= log::Level::DEBUG
|
||||
&& data.mod_name().starts_with("conduit")
|
||||
&& matches!(data.span_name(), "actual" | "well-known" | "srv")
|
||||
};
|
||||
|
||||
Ok(RoomMessageEventContent::text_plain(format!(
|
||||
"Actual destination: {actual_dest} | Hostname URI: {hostname_uri}"
|
||||
)))
|
||||
let state = &services().server.log.capture;
|
||||
let logs = Arc::new(Mutex::new(String::new()));
|
||||
let capture = Capture::new(state, Some(filter), capture::fmt_markdown(logs.clone()));
|
||||
let (actual_dest, hostname_uri);
|
||||
{
|
||||
let _capture_scope = capture.start();
|
||||
(actual_dest, hostname_uri) = resolve_actual_dest(&server_name, !no_cache).await?;
|
||||
};
|
||||
|
||||
let msg = format!(
|
||||
"{}\nDestination: {actual_dest}\nHostname URI: {hostname_uri}",
|
||||
logs.lock().expect("locked")
|
||||
);
|
||||
Ok(RoomMessageEventContent::text_markdown(msg))
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub(crate) fn memory_stats() -> RoomMessageEventContent {
|
||||
pub(super) fn memory_stats() -> RoomMessageEventContent {
|
||||
let html_body = conduit::alloc::memory_stats();
|
||||
|
||||
if html_body.is_empty() {
|
||||
+17
-14
@@ -1,18 +1,19 @@
|
||||
mod commands;
|
||||
|
||||
use clap::Subcommand;
|
||||
use debug_commands::{first_pdu_in_room, force_set_room_state_from_server, latest_pdu_in_room};
|
||||
use conduit::Result;
|
||||
use ruma::{events::room::message::RoomMessageEventContent, EventId, RoomId, ServerName};
|
||||
|
||||
use self::debug_commands::{
|
||||
change_log_level, force_device_list_updates, get_auth_chain, get_pdu, get_remote_pdu, get_remote_pdu_list,
|
||||
get_room_state, memory_stats, parse_pdu, ping, resolve_true_destination, sign_json, verify_json,
|
||||
};
|
||||
use crate::Result;
|
||||
|
||||
pub(crate) mod debug_commands;
|
||||
use self::commands::*;
|
||||
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
#[derive(Subcommand)]
|
||||
pub(crate) enum DebugCommand {
|
||||
pub(super) enum DebugCommand {
|
||||
/// - Echo input of admin command
|
||||
Echo {
|
||||
message: Vec<String>,
|
||||
},
|
||||
|
||||
/// - Get the auth_chain of a PDU
|
||||
GetAuthChain {
|
||||
/// An event ID (the $ character followed by the base64 reference hash)
|
||||
@@ -35,8 +36,8 @@ pub(crate) enum DebugCommand {
|
||||
},
|
||||
|
||||
/// - 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).
|
||||
/// database/timeline if found and we do not have this PDU already
|
||||
/// (following normal event auth rules, handles it as an incoming PDU).
|
||||
GetRemotePdu {
|
||||
/// An event ID (a $ followed by the base64 reference hash)
|
||||
event_id: Box<EventId>,
|
||||
@@ -47,8 +48,7 @@ pub(crate) enum DebugCommand {
|
||||
},
|
||||
|
||||
/// - Same as `get-remote-pdu` but accepts a codeblock newline delimited
|
||||
/// list
|
||||
/// of PDUs and a single server to fetch from
|
||||
/// list of PDUs and a single server to fetch from
|
||||
GetRemotePduList {
|
||||
/// Argument for us to attempt to fetch all the events from the
|
||||
/// specified remote server.
|
||||
@@ -159,8 +159,11 @@ pub(crate) enum DebugCommand {
|
||||
MemoryStats,
|
||||
}
|
||||
|
||||
pub(crate) async fn process(command: DebugCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn process(command: DebugCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
Ok(match command {
|
||||
DebugCommand::Echo {
|
||||
message,
|
||||
} => echo(body, message).await?,
|
||||
DebugCommand::GetAuthChain {
|
||||
event_id,
|
||||
} => get_auth_chain(body, event_id).await?,
|
||||
|
||||
@@ -2,19 +2,19 @@ use std::fmt::Write;
|
||||
|
||||
use ruma::{events::room::message::RoomMessageEventContent, OwnedRoomId, RoomId, ServerName, UserId};
|
||||
|
||||
use crate::{escape_html, get_room_info, services, utils::HtmlEscape, Result};
|
||||
use crate::{escape_html, get_room_info, services, Result};
|
||||
|
||||
pub(crate) async fn disable_room(_body: Vec<&str>, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn disable_room(_body: Vec<&str>, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
|
||||
services().rooms.metadata.disable_room(&room_id, true)?;
|
||||
Ok(RoomMessageEventContent::text_plain("Room disabled."))
|
||||
}
|
||||
|
||||
pub(crate) async fn enable_room(_body: Vec<&str>, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn enable_room(_body: Vec<&str>, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
|
||||
services().rooms.metadata.disable_room(&room_id, false)?;
|
||||
Ok(RoomMessageEventContent::text_plain("Room enabled."))
|
||||
}
|
||||
|
||||
pub(crate) async fn incoming_federation(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn incoming_federation(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
let map = services().globals.roomid_federationhandletime.read().await;
|
||||
let mut msg = format!("Handling {} incoming pdus:\n", map.len());
|
||||
|
||||
@@ -26,7 +26,7 @@ pub(crate) async fn incoming_federation(_body: Vec<&str>) -> Result<RoomMessageE
|
||||
Ok(RoomMessageEventContent::text_plain(&msg))
|
||||
}
|
||||
|
||||
pub(crate) async fn fetch_support_well_known(
|
||||
pub(super) async fn fetch_support_well_known(
|
||||
_body: Vec<&str>, server_name: Box<ServerName>,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
let response = services()
|
||||
@@ -63,16 +63,12 @@ pub(crate) async fn fetch_support_well_known(
|
||||
},
|
||||
};
|
||||
|
||||
Ok(RoomMessageEventContent::text_html(
|
||||
format!("Got JSON response:\n\n```json\n{pretty_json}\n```"),
|
||||
format!(
|
||||
"<p>Got JSON response:</p>\n<pre><code class=\"language-json\">{}\n</code></pre>\n",
|
||||
HtmlEscape(&pretty_json)
|
||||
),
|
||||
))
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Got JSON response:\n\n```json\n{pretty_json}\n```"
|
||||
)))
|
||||
}
|
||||
|
||||
pub(crate) async fn remote_user_in_rooms(_body: Vec<&str>, user_id: Box<UserId>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn remote_user_in_rooms(_body: Vec<&str>, user_id: Box<UserId>) -> Result<RoomMessageEventContent> {
|
||||
if user_id.server_name() == services().globals.config.server_name {
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
"User belongs to our server, please use `list-joined-rooms` user admin command instead.",
|
||||
@@ -1,16 +1,14 @@
|
||||
mod commands;
|
||||
|
||||
use clap::Subcommand;
|
||||
use conduit::Result;
|
||||
use ruma::{events::room::message::RoomMessageEventContent, RoomId, ServerName, UserId};
|
||||
|
||||
use self::federation_commands::{
|
||||
disable_room, enable_room, fetch_support_well_known, incoming_federation, remote_user_in_rooms,
|
||||
};
|
||||
use crate::Result;
|
||||
|
||||
pub(crate) mod federation_commands;
|
||||
use self::commands::*;
|
||||
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
#[derive(Subcommand)]
|
||||
pub(crate) enum FederationCommand {
|
||||
pub(super) enum FederationCommand {
|
||||
/// - List all rooms we are currently handling an incoming pdu from
|
||||
IncomingFederation,
|
||||
|
||||
@@ -43,7 +41,7 @@ pub(crate) enum FederationCommand {
|
||||
},
|
||||
}
|
||||
|
||||
pub(crate) async fn process(command: FederationCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn process(command: FederationCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
Ok(match command {
|
||||
FederationCommand::DisableRoom {
|
||||
room_id,
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
use conduit::Result;
|
||||
use ruma::events::room::message::RoomMessageEventContent;
|
||||
|
||||
use crate::{services, Result};
|
||||
use crate::services;
|
||||
|
||||
/// Uses the iterator in `src/database/key_value/users.rs` to iterator over
|
||||
/// every user in our database (remote and local). Reports total count, any
|
||||
/// errors if there were any, etc
|
||||
pub(crate) async fn check_all_users(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn check_all_users(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = services().users.db.iter();
|
||||
let query_time = timer.elapsed();
|
||||
@@ -21,5 +22,5 @@ pub(crate) async fn check_all_users(_body: Vec<&str>) -> Result<RoomMessageEvent
|
||||
{err_count:?}\nSuccess/Valid user count: {ok_count:?}```"
|
||||
);
|
||||
|
||||
Ok(RoomMessageEventContent::notice_html(message, String::new()))
|
||||
Ok(RoomMessageEventContent::notice_markdown(message))
|
||||
}
|
||||
@@ -1,18 +1,18 @@
|
||||
mod commands;
|
||||
|
||||
use clap::Subcommand;
|
||||
use conduit::Result;
|
||||
use ruma::events::room::message::RoomMessageEventContent;
|
||||
|
||||
use self::fsck_commands::check_all_users;
|
||||
use crate::Result;
|
||||
|
||||
pub(crate) mod fsck_commands;
|
||||
use self::commands::*;
|
||||
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
#[derive(Subcommand)]
|
||||
pub(crate) enum FsckCommand {
|
||||
pub(super) enum FsckCommand {
|
||||
CheckAllUsers,
|
||||
}
|
||||
|
||||
pub(crate) async fn process(command: FsckCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn process(command: FsckCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
Ok(match command {
|
||||
FsckCommand::CheckAllUsers => check_all_users(body).await?,
|
||||
})
|
||||
|
||||
+41
-175
@@ -1,36 +1,27 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use clap::Parser;
|
||||
use regex::Regex;
|
||||
use ruma::{
|
||||
events::{
|
||||
relation::InReplyTo,
|
||||
room::message::{Relation::Reply, RoomMessageEventContent},
|
||||
TimelineEventType,
|
||||
},
|
||||
OwnedRoomId, OwnedUserId, RoomId, ServerName, UserId,
|
||||
use conduit::trace;
|
||||
use ruma::events::{
|
||||
relation::InReplyTo,
|
||||
room::message::{Relation::Reply, RoomMessageEventContent},
|
||||
};
|
||||
use serde_json::value::to_raw_value;
|
||||
use tokio::sync::MutexGuard;
|
||||
use tracing::error;
|
||||
|
||||
extern crate conduit_service as service;
|
||||
|
||||
use conduit::{Error, Result};
|
||||
pub(crate) use service::admin::{AdminRoomEvent, Service};
|
||||
use service::{admin::HandlerResult, pdu::PduBuilder};
|
||||
use conduit::Result;
|
||||
pub(crate) use service::admin::{Command, Service};
|
||||
use service::admin::{CommandOutput, CommandResult, HandlerResult};
|
||||
|
||||
use self::{fsck::FsckCommand, tester::TesterCommands};
|
||||
use crate::{
|
||||
appservice, appservice::AppserviceCommand, debug, debug::DebugCommand, escape_html, federation,
|
||||
federation::FederationCommand, fsck, media, media::MediaCommand, query, query::QueryCommand, room,
|
||||
room::RoomCommand, server, server::ServerCommand, services, tester, user, user::UserCommand,
|
||||
appservice, appservice::AppserviceCommand, debug, debug::DebugCommand, federation, federation::FederationCommand,
|
||||
fsck, media, media::MediaCommand, query, query::QueryCommand, room, room::RoomCommand, server,
|
||||
server::ServerCommand, services, tester, user, user::UserCommand,
|
||||
};
|
||||
pub(crate) const PAGE_SIZE: usize = 100;
|
||||
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
#[derive(Parser)]
|
||||
#[command(name = "@conduit:server.name:", version = env!("CARGO_PKG_VERSION"))]
|
||||
#[command(name = "admin", version = env!("CARGO_PKG_VERSION"))]
|
||||
pub(crate) enum AdminCommand {
|
||||
#[command(subcommand)]
|
||||
/// - Commands for managing appservices
|
||||
@@ -73,87 +64,26 @@ pub(crate) enum AdminCommand {
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn handle(event: AdminRoomEvent, room: OwnedRoomId, user: OwnedUserId) -> HandlerResult {
|
||||
Box::pin(handle_event(event, room, user))
|
||||
}
|
||||
pub fn handle(command: Command) -> HandlerResult { Box::pin(handle_command(command)) }
|
||||
|
||||
async fn handle_event(event: AdminRoomEvent, admin_room: OwnedRoomId, server_user: OwnedUserId) -> Result<()> {
|
||||
let (mut message_content, reply) = match event {
|
||||
AdminRoomEvent::SendMessage(content) => (content, None),
|
||||
AdminRoomEvent::ProcessMessage(room_message, reply_id) => {
|
||||
// This future is ~8 KiB so it's better to start it off the stack.
|
||||
(Box::pin(process_admin_message(room_message)).await, Some(reply_id))
|
||||
#[tracing::instrument(skip_all, name = "admin")]
|
||||
async fn handle_command(command: Command) -> CommandResult {
|
||||
let Some(mut content) = process_admin_message(command.command).await else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
content.relates_to = command.reply_id.map(|event_id| Reply {
|
||||
in_reply_to: InReplyTo {
|
||||
event_id,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
let mutex_state = Arc::clone(
|
||||
services()
|
||||
.globals
|
||||
.roomid_mutex_state
|
||||
.write()
|
||||
.await
|
||||
.entry(admin_room.clone())
|
||||
.or_default(),
|
||||
);
|
||||
let state_lock = mutex_state.lock().await;
|
||||
|
||||
if let Some(reply) = reply {
|
||||
message_content.relates_to = Some(Reply {
|
||||
in_reply_to: InReplyTo {
|
||||
event_id: reply.into(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
let response_pdu = PduBuilder {
|
||||
event_type: TimelineEventType::RoomMessage,
|
||||
content: to_raw_value(&message_content).expect("event is valid, we just created it"),
|
||||
unsigned: None,
|
||||
state_key: None,
|
||||
redacts: None,
|
||||
};
|
||||
|
||||
if let Err(e) = services()
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(response_pdu, &server_user, &admin_room, &state_lock)
|
||||
.await
|
||||
{
|
||||
handle_response_error(&e, &admin_room, &server_user, &state_lock).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_response_error(
|
||||
e: &Error, admin_room: &RoomId, server_user: &UserId, state_lock: &MutexGuard<'_, ()>,
|
||||
) -> Result<()> {
|
||||
error!("Failed to build and append admin room response PDU: \"{e}\"");
|
||||
let error_room_message = RoomMessageEventContent::text_plain(format!(
|
||||
"Failed to build and append admin room PDU: \"{e}\"\n\nThe original admin command may have finished \
|
||||
successfully, but we could not return the output."
|
||||
));
|
||||
|
||||
let response_pdu = PduBuilder {
|
||||
event_type: TimelineEventType::RoomMessage,
|
||||
content: to_raw_value(&error_room_message).expect("event is valid, we just created it"),
|
||||
unsigned: None,
|
||||
state_key: None,
|
||||
redacts: None,
|
||||
};
|
||||
|
||||
services()
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(response_pdu, server_user, admin_room, state_lock)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
Ok(Some(content))
|
||||
}
|
||||
|
||||
// Parse and process a message from the admin room
|
||||
async fn process_admin_message(room_message: String) -> RoomMessageEventContent {
|
||||
let mut lines = room_message.lines().filter(|l| !l.trim().is_empty());
|
||||
async fn process_admin_message(msg: String) -> CommandOutput {
|
||||
let mut lines = msg.lines().filter(|l| !l.trim().is_empty());
|
||||
let command_line = lines.next().expect("each string has at least one line");
|
||||
let body = lines.collect::<Vec<_>>();
|
||||
|
||||
@@ -162,28 +92,34 @@ async fn process_admin_message(room_message: String) -> RoomMessageEventContent
|
||||
Err(error) => {
|
||||
let server_name = services().globals.server_name();
|
||||
let message = error.replace("server.name", server_name.as_str());
|
||||
let html_message = usage_to_html(&message, server_name);
|
||||
|
||||
return RoomMessageEventContent::text_html(message, html_message);
|
||||
return Some(RoomMessageEventContent::notice_markdown(message));
|
||||
},
|
||||
};
|
||||
|
||||
match process_admin_command(admin_command, body).await {
|
||||
Ok(reply_message) => reply_message,
|
||||
Ok(reply_message) => Some(reply_message),
|
||||
Err(error) => {
|
||||
let markdown_message = format!("Encountered an error while handling the command:\n```\n{error}\n```",);
|
||||
let html_message = format!("Encountered an error while handling the command:\n<pre>\n{error}\n</pre>",);
|
||||
|
||||
RoomMessageEventContent::text_html(markdown_message, html_message)
|
||||
Some(RoomMessageEventContent::notice_markdown(markdown_message))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Parse chat messages from the admin room into an AdminCommand object
|
||||
fn parse_admin_command(command_line: &str) -> Result<AdminCommand, String> {
|
||||
// Note: argv[0] is `@conduit:servername:`, which is treated as the main command
|
||||
let mut argv = command_line.split_whitespace().collect::<Vec<_>>();
|
||||
|
||||
// Remove any escapes that came with a server-side escape command
|
||||
if !argv.is_empty() && argv[0].ends_with("admin") {
|
||||
argv[0] = argv[0].trim_start_matches('\\');
|
||||
}
|
||||
|
||||
// First indice has to be "admin" but for console convenience we add it here
|
||||
let server_user = services().globals.server_user.as_str();
|
||||
if !argv.is_empty() && !argv[0].ends_with("admin") && !argv[0].starts_with(server_user) {
|
||||
argv.insert(0, "admin");
|
||||
}
|
||||
|
||||
// Replace `help command` with `command --help`
|
||||
// Clap has a help subcommand, but it omits the long help description.
|
||||
if argv.len() > 1 && argv[1] == "help" {
|
||||
@@ -213,9 +149,11 @@ fn parse_admin_command(command_line: &str) -> Result<AdminCommand, String> {
|
||||
argv[3] = &command_with_dashes_argv3;
|
||||
}
|
||||
|
||||
trace!(?command_line, ?argv, "parse");
|
||||
AdminCommand::try_parse_from(argv).map_err(|error| error.to_string())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all, name = "command")]
|
||||
async fn process_admin_command(command: AdminCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
let reply_message_content = match command {
|
||||
AdminCommand::Appservices(command) => appservice::process(command, body).await?,
|
||||
@@ -232,75 +170,3 @@ async fn process_admin_command(command: AdminCommand, body: Vec<&str>) -> Result
|
||||
|
||||
Ok(reply_message_content)
|
||||
}
|
||||
|
||||
// Utility to turn clap's `--help` text to HTML.
|
||||
fn usage_to_html(text: &str, server_name: &ServerName) -> String {
|
||||
// Replace `@conduit:servername:-subcmdname` with `@conduit:servername:
|
||||
// subcmdname`
|
||||
let text = text.replace(&format!("@conduit:{server_name}:-"), &format!("@conduit:{server_name}: "));
|
||||
|
||||
// For the conduit admin room, subcommands become main commands
|
||||
let text = text.replace("SUBCOMMAND", "COMMAND");
|
||||
let text = text.replace("subcommand", "command");
|
||||
|
||||
// Escape option names (e.g. `<element-id>`) since they look like HTML tags
|
||||
let text = escape_html(&text);
|
||||
|
||||
// Italicize the first line (command name and version text)
|
||||
let re = Regex::new("^(.*?)\n").expect("Regex compilation should not fail");
|
||||
let text = re.replace_all(&text, "<em>$1</em>\n");
|
||||
|
||||
// Unmerge wrapped lines
|
||||
let text = text.replace("\n ", " ");
|
||||
|
||||
// Wrap option names in backticks. The lines look like:
|
||||
// -V, --version Prints version information
|
||||
// And are converted to:
|
||||
// <code>-V, --version</code>: Prints version information
|
||||
// (?m) enables multi-line mode for ^ and $
|
||||
let re = Regex::new("(?m)^ {4}(([a-zA-Z_&;-]+(, )?)+) +(.*)$").expect("Regex compilation should not fail");
|
||||
let text = re.replace_all(&text, "<code>$1</code>: $4");
|
||||
|
||||
// Look for a `[commandbody]` tag. If it exists, use all lines below it that
|
||||
// start with a `#` in the USAGE section.
|
||||
let mut text_lines = text.lines().collect::<Vec<&str>>();
|
||||
let mut command_body = String::new();
|
||||
|
||||
if let Some(line_index) = text_lines.iter().position(|line| *line == "[commandbody]") {
|
||||
text_lines.remove(line_index);
|
||||
|
||||
while text_lines
|
||||
.get(line_index)
|
||||
.is_some_and(|line| line.starts_with('#'))
|
||||
{
|
||||
command_body += if text_lines[line_index].starts_with("# ") {
|
||||
&text_lines[line_index][2..]
|
||||
} else {
|
||||
&text_lines[line_index][1..]
|
||||
};
|
||||
command_body += "[nobr]\n";
|
||||
text_lines.remove(line_index);
|
||||
}
|
||||
}
|
||||
|
||||
let text = text_lines.join("\n");
|
||||
|
||||
// Improve the usage section
|
||||
let text = if command_body.is_empty() {
|
||||
// Wrap the usage line in code tags
|
||||
let re = Regex::new("(?m)^USAGE:\n {4}(@conduit:.*)$").expect("Regex compilation should not fail");
|
||||
re.replace_all(&text, "USAGE:\n<code>$1</code>").to_string()
|
||||
} else {
|
||||
// Wrap the usage line in a code block, and add a yaml block example
|
||||
// This makes the usage of e.g. `register-appservice` more accurate
|
||||
let re = Regex::new("(?m)^USAGE:\n {4}(.*?)\n\n").expect("Regex compilation should not fail");
|
||||
re.replace_all(&text, "USAGE:\n<pre>$1[nobr]\n[commandbodyblock]</pre>")
|
||||
.replace("[commandbodyblock]", &command_body)
|
||||
};
|
||||
|
||||
// Add HTML line-breaks
|
||||
|
||||
text.replace("\n\n\n", "\n\n")
|
||||
.replace('\n', "<br>\n")
|
||||
.replace("[nobr]<br>", "")
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
use conduit::Result;
|
||||
use ruma::{events::room::message::RoomMessageEventContent, EventId, MxcUri};
|
||||
use tracing::{debug, info};
|
||||
|
||||
use crate::{services, Result};
|
||||
use crate::services;
|
||||
|
||||
pub(crate) async fn delete(
|
||||
pub(super) async fn delete(
|
||||
_body: Vec<&str>, mxc: Option<Box<MxcUri>>, event_id: Option<Box<EventId>>,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
if event_id.is_some() && mxc.is_some() {
|
||||
@@ -137,7 +138,7 @@ pub(crate) async fn delete(
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) async fn delete_list(body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn delete_list(body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
if body.len() < 2 || !body[0].trim().starts_with("```") || body.last().unwrap_or(&"").trim() != "```" {
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
"Expected code block in command body. Add --help for details.",
|
||||
@@ -164,7 +165,7 @@ pub(crate) async fn delete_list(body: Vec<&str>) -> Result<RoomMessageEventConte
|
||||
)))
|
||||
}
|
||||
|
||||
pub(crate) async fn delete_past_remote_media(
|
||||
pub(super) async fn delete_past_remote_media(
|
||||
_body: Vec<&str>, duration: String, force: bool,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
let deleted_count = services()
|
||||
@@ -1,14 +1,14 @@
|
||||
mod commands;
|
||||
|
||||
use clap::Subcommand;
|
||||
use conduit::Result;
|
||||
use ruma::{events::room::message::RoomMessageEventContent, EventId, MxcUri};
|
||||
|
||||
use self::media_commands::{delete, delete_list, delete_past_remote_media};
|
||||
use crate::Result;
|
||||
|
||||
pub(crate) mod media_commands;
|
||||
use self::commands::*;
|
||||
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
#[derive(Subcommand)]
|
||||
pub(crate) enum MediaCommand {
|
||||
pub(super) enum MediaCommand {
|
||||
/// - Deletes a single media file from our database and on the filesystem
|
||||
/// via a single MXC URL
|
||||
Delete {
|
||||
@@ -38,7 +38,7 @@ pub(crate) enum MediaCommand {
|
||||
},
|
||||
}
|
||||
|
||||
pub(crate) async fn process(command: MediaCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn process(command: MediaCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
Ok(match command {
|
||||
MediaCommand::Delete {
|
||||
mxc,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![allow(clippy::wildcard_imports)]
|
||||
|
||||
pub(crate) mod appservice;
|
||||
pub(crate) mod debug;
|
||||
pub(crate) mod federation;
|
||||
|
||||
@@ -4,7 +4,7 @@ use super::AccountData;
|
||||
use crate::{services, Result};
|
||||
|
||||
/// All the getters and iterators from src/database/key_value/account_data.rs
|
||||
pub(crate) async fn account_data(subcommand: AccountData) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn account_data(subcommand: AccountData) -> Result<RoomMessageEventContent> {
|
||||
match subcommand {
|
||||
AccountData::ChangesSince {
|
||||
user_id,
|
||||
@@ -18,10 +18,9 @@ pub(crate) async fn account_data(subcommand: AccountData) -> Result<RoomMessageE
|
||||
.changes_since(room_id.as_deref(), &user_id, since)?;
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::text_html(
|
||||
format!("Query completed in {query_time:?}:\n\n```\n{results:?}```"),
|
||||
format!("<p>Query completed in {query_time:?}:</p>\n<pre><code>{results:?}\n</code></pre>"),
|
||||
))
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
AccountData::Get {
|
||||
user_id,
|
||||
@@ -35,10 +34,9 @@ pub(crate) async fn account_data(subcommand: AccountData) -> Result<RoomMessageE
|
||||
.get(room_id.as_deref(), &user_id, kind)?;
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::text_html(
|
||||
format!("Query completed in {query_time:?}:\n\n```\n{results:?}```"),
|
||||
format!("<p>Query completed in {query_time:?}:</p>\n<pre><code>{results:?}\n</code></pre>"),
|
||||
))
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use super::Appservice;
|
||||
use crate::{services, Result};
|
||||
|
||||
/// All the getters and iterators from src/database/key_value/appservice.rs
|
||||
pub(crate) async fn appservice(subcommand: Appservice) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn appservice(subcommand: Appservice) -> Result<RoomMessageEventContent> {
|
||||
match subcommand {
|
||||
Appservice::GetRegistration {
|
||||
appservice_id,
|
||||
@@ -16,20 +16,18 @@ pub(crate) async fn appservice(subcommand: Appservice) -> Result<RoomMessageEven
|
||||
.get_registration(appservice_id.as_ref());
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::text_html(
|
||||
format!("Query completed in {query_time:?}:\n\n```\n{results:?}```"),
|
||||
format!("<p>Query completed in {query_time:?}:</p>\n<pre><code>{results:?}\n</code></pre>"),
|
||||
))
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
Appservice::All => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = services().appservice.db.all();
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::text_html(
|
||||
format!("Query completed in {query_time:?}:\n\n```\n{results:?}```"),
|
||||
format!("<p>Query completed in {query_time:?}:</p>\n<pre><code>{results:?}\n</code></pre>"),
|
||||
))
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
+16
-21
@@ -4,47 +4,43 @@ use super::Globals;
|
||||
use crate::{services, Result};
|
||||
|
||||
/// All the getters and iterators from src/database/key_value/globals.rs
|
||||
pub(crate) async fn globals(subcommand: Globals) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn globals(subcommand: Globals) -> Result<RoomMessageEventContent> {
|
||||
match subcommand {
|
||||
Globals::DatabaseVersion => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = services().globals.db.database_version();
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::text_html(
|
||||
format!("Query completed in {query_time:?}:\n\n```\n{results:?}```"),
|
||||
format!("<p>Query completed in {query_time:?}:</p>\n<pre><code>{results:?}\n</code></pre>"),
|
||||
))
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
Globals::CurrentCount => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = services().globals.db.current_count();
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::text_html(
|
||||
format!("Query completed in {query_time:?}:\n\n```\n{results:?}```"),
|
||||
format!("<p>Query completed in {query_time:?}:</p>\n<pre><code>{results:?}\n</code></pre>"),
|
||||
))
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
Globals::LastCheckForUpdatesId => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = services().globals.db.last_check_for_updates_id();
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::text_html(
|
||||
format!("Query completed in {query_time:?}:\n\n```\n{results:?}```"),
|
||||
format!("<p>Query completed in {query_time:?}:</p>\n<pre><code>{results:?}\n</code></pre>"),
|
||||
))
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
Globals::LoadKeypair => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = services().globals.db.load_keypair();
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::text_html(
|
||||
format!("Query completed in {query_time:?}:\n\n```\n{results:?}```"),
|
||||
format!("<p>Query completed in {query_time:?}:</p>\n<pre><code>{results:?}\n</code></pre>"),
|
||||
))
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
Globals::SigningKeysFor {
|
||||
origin,
|
||||
@@ -53,10 +49,9 @@ pub(crate) async fn globals(subcommand: Globals) -> Result<RoomMessageEventConte
|
||||
let results = services().globals.db.signing_keys_for(&origin);
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::text_html(
|
||||
format!("Query completed in {query_time:?}:\n\n```\n{results:?}```"),
|
||||
format!("<p>Query completed in {query_time:?}:</p>\n<pre><code>{results:?}\n</code></pre>"),
|
||||
))
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
+19
-19
@@ -1,13 +1,14 @@
|
||||
pub(crate) mod account_data;
|
||||
pub(crate) mod appservice;
|
||||
pub(crate) mod globals;
|
||||
pub(crate) mod presence;
|
||||
pub(crate) mod room_alias;
|
||||
pub(crate) mod room_state_cache;
|
||||
pub(crate) mod sending;
|
||||
pub(crate) mod users;
|
||||
mod account_data;
|
||||
mod appservice;
|
||||
mod globals;
|
||||
mod presence;
|
||||
mod room_alias;
|
||||
mod room_state_cache;
|
||||
mod sending;
|
||||
mod users;
|
||||
|
||||
use clap::Subcommand;
|
||||
use conduit::Result;
|
||||
use room_state_cache::room_state_cache;
|
||||
use ruma::{
|
||||
events::{room::message::RoomMessageEventContent, RoomAccountDataEventType},
|
||||
@@ -18,12 +19,11 @@ use self::{
|
||||
account_data::account_data, appservice::appservice, globals::globals, presence::presence, room_alias::room_alias,
|
||||
sending::sending, users::users,
|
||||
};
|
||||
use crate::Result;
|
||||
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
#[derive(Subcommand)]
|
||||
/// Query tables from database
|
||||
pub(crate) enum QueryCommand {
|
||||
pub(super) enum QueryCommand {
|
||||
/// - account_data.rs iterators and getters
|
||||
#[command(subcommand)]
|
||||
AccountData(AccountData),
|
||||
@@ -60,7 +60,7 @@ pub(crate) enum QueryCommand {
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
#[derive(Subcommand)]
|
||||
/// All the getters and iterators from src/database/key_value/account_data.rs
|
||||
pub(crate) enum AccountData {
|
||||
pub(super) enum AccountData {
|
||||
/// - Returns all changes to the account data that happened after `since`.
|
||||
ChangesSince {
|
||||
/// Full user ID
|
||||
@@ -85,7 +85,7 @@ pub(crate) enum AccountData {
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
#[derive(Subcommand)]
|
||||
/// All the getters and iterators from src/database/key_value/appservice.rs
|
||||
pub(crate) enum Appservice {
|
||||
pub(super) enum Appservice {
|
||||
/// - Gets the appservice registration info/details from the ID as a string
|
||||
GetRegistration {
|
||||
/// Appservice registration ID
|
||||
@@ -99,7 +99,7 @@ pub(crate) enum Appservice {
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
#[derive(Subcommand)]
|
||||
/// All the getters and iterators from src/database/key_value/presence.rs
|
||||
pub(crate) enum Presence {
|
||||
pub(super) enum Presence {
|
||||
/// - Returns the latest presence event for the given user.
|
||||
GetPresence {
|
||||
/// Full user ID
|
||||
@@ -117,7 +117,7 @@ pub(crate) enum Presence {
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
#[derive(Subcommand)]
|
||||
/// All the getters and iterators from src/database/key_value/rooms/alias.rs
|
||||
pub(crate) enum RoomAlias {
|
||||
pub(super) enum RoomAlias {
|
||||
ResolveLocalAlias {
|
||||
/// Full room alias
|
||||
alias: Box<RoomAliasId>,
|
||||
@@ -135,7 +135,7 @@ pub(crate) enum RoomAlias {
|
||||
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
#[derive(Subcommand)]
|
||||
pub(crate) enum RoomStateCache {
|
||||
pub(super) enum RoomStateCache {
|
||||
ServerInRoom {
|
||||
server: Box<ServerName>,
|
||||
room_id: Box<RoomId>,
|
||||
@@ -208,7 +208,7 @@ pub(crate) enum RoomStateCache {
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
#[derive(Subcommand)]
|
||||
/// All the getters and iterators from src/database/key_value/globals.rs
|
||||
pub(crate) enum Globals {
|
||||
pub(super) enum Globals {
|
||||
DatabaseVersion,
|
||||
|
||||
CurrentCount,
|
||||
@@ -227,7 +227,7 @@ pub(crate) enum Globals {
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
#[derive(Subcommand)]
|
||||
/// All the getters and iterators from src/database/key_value/sending.rs
|
||||
pub(crate) enum Sending {
|
||||
pub(super) enum Sending {
|
||||
/// - Queries database for all `servercurrentevent_data`
|
||||
ActiveRequests,
|
||||
|
||||
@@ -283,12 +283,12 @@ pub(crate) enum Sending {
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
#[derive(Subcommand)]
|
||||
/// All the getters and iterators from src/database/key_value/users.rs
|
||||
pub(crate) enum Users {
|
||||
pub(super) enum Users {
|
||||
Iter,
|
||||
}
|
||||
|
||||
/// Processes admin query commands
|
||||
pub(crate) async fn process(command: QueryCommand, _body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn process(command: QueryCommand, _body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
Ok(match command {
|
||||
QueryCommand::AccountData(command) => account_data(command).await?,
|
||||
QueryCommand::Appservice(command) => appservice(command).await?,
|
||||
|
||||
@@ -4,7 +4,7 @@ use super::Presence;
|
||||
use crate::{services, Result};
|
||||
|
||||
/// All the getters and iterators in key_value/presence.rs
|
||||
pub(crate) async fn presence(subcommand: Presence) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn presence(subcommand: Presence) -> Result<RoomMessageEventContent> {
|
||||
match subcommand {
|
||||
Presence::GetPresence {
|
||||
user_id,
|
||||
@@ -13,10 +13,9 @@ pub(crate) async fn presence(subcommand: Presence) -> Result<RoomMessageEventCon
|
||||
let results = services().presence.db.get_presence(&user_id)?;
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::text_html(
|
||||
format!("Query completed in {query_time:?}:\n\n```\n{results:?}```"),
|
||||
format!("<p>Query completed in {query_time:?}:</p>\n<pre><code>{results:?}\n</code></pre>"),
|
||||
))
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
Presence::PresenceSince {
|
||||
since,
|
||||
@@ -27,10 +26,9 @@ pub(crate) async fn presence(subcommand: Presence) -> Result<RoomMessageEventCon
|
||||
|
||||
let presence_since: Vec<(_, _, _)> = results.collect();
|
||||
|
||||
Ok(RoomMessageEventContent::text_html(
|
||||
format!("Query completed in {query_time:?}:\n\n```\n{presence_since:?}```"),
|
||||
format!("<p>Query completed in {query_time:?}:</p>\n<pre><code>{presence_since:?}\n</code></pre>"),
|
||||
))
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{presence_since:#?}\n```"
|
||||
)))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use super::RoomAlias;
|
||||
use crate::{services, Result};
|
||||
|
||||
/// All the getters and iterators in src/database/key_value/rooms/alias.rs
|
||||
pub(crate) async fn room_alias(subcommand: RoomAlias) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn room_alias(subcommand: RoomAlias) -> Result<RoomMessageEventContent> {
|
||||
match subcommand {
|
||||
RoomAlias::ResolveLocalAlias {
|
||||
alias,
|
||||
@@ -13,10 +13,9 @@ pub(crate) async fn room_alias(subcommand: RoomAlias) -> Result<RoomMessageEvent
|
||||
let results = services().rooms.alias.db.resolve_local_alias(&alias);
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::text_html(
|
||||
format!("Query completed in {query_time:?}:\n\n```\n{results:?}```"),
|
||||
format!("<p>Query completed in {query_time:?}:</p>\n<pre><code>{results:?}\n</code></pre>"),
|
||||
))
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
RoomAlias::LocalAliasesForRoom {
|
||||
room_id,
|
||||
@@ -27,10 +26,9 @@ pub(crate) async fn room_alias(subcommand: RoomAlias) -> Result<RoomMessageEvent
|
||||
|
||||
let aliases: Vec<_> = results.collect();
|
||||
|
||||
Ok(RoomMessageEventContent::text_html(
|
||||
format!("Query completed in {query_time:?}:\n\n```\n{aliases:?}```"),
|
||||
format!("<p>Query completed in {query_time:?}:</p>\n<pre><code>{aliases:?}\n</code></pre>"),
|
||||
))
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{aliases:#?}\n```"
|
||||
)))
|
||||
},
|
||||
RoomAlias::AllLocalAliases => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
@@ -39,10 +37,9 @@ pub(crate) async fn room_alias(subcommand: RoomAlias) -> Result<RoomMessageEvent
|
||||
|
||||
let aliases: Vec<_> = results.collect();
|
||||
|
||||
Ok(RoomMessageEventContent::text_html(
|
||||
format!("Query completed in {query_time:?}:\n\n```\n{aliases:?}```"),
|
||||
format!("<p>Query completed in {query_time:?}:</p>\n<pre><code>{aliases:?}\n</code></pre>"),
|
||||
))
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{aliases:#?}\n```"
|
||||
)))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ use ruma::events::room::message::RoomMessageEventContent;
|
||||
use super::RoomStateCache;
|
||||
use crate::{services, Result};
|
||||
|
||||
pub(crate) async fn room_state_cache(subcommand: RoomStateCache) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn room_state_cache(subcommand: RoomStateCache) -> Result<RoomMessageEventContent> {
|
||||
match subcommand {
|
||||
RoomStateCache::ServerInRoom {
|
||||
server,
|
||||
@@ -16,10 +16,9 @@ pub(crate) async fn room_state_cache(subcommand: RoomStateCache) -> Result<RoomM
|
||||
.server_in_room(&server, &room_id);
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::text_html(
|
||||
format!("Query completed in {query_time:?}:\n\n```\n{result:?}```"),
|
||||
format!("<p>Query completed in {query_time:?}:</p>\n<pre><code>{result:?}\n</code></pre>"),
|
||||
))
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{result:#?}\n```"
|
||||
)))
|
||||
},
|
||||
RoomStateCache::RoomServers {
|
||||
room_id,
|
||||
@@ -32,10 +31,9 @@ pub(crate) async fn room_state_cache(subcommand: RoomStateCache) -> Result<RoomM
|
||||
.collect();
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::text_html(
|
||||
format!("Query completed in {query_time:?}:\n\n```\n{results:?}```"),
|
||||
format!("<p>Query completed in {query_time:?}:</p>\n<pre><code>{results:?}\n</code></pre>"),
|
||||
))
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
RoomStateCache::ServerRooms {
|
||||
server,
|
||||
@@ -44,10 +42,9 @@ pub(crate) async fn room_state_cache(subcommand: RoomStateCache) -> Result<RoomM
|
||||
let results: Result<Vec<_>> = services().rooms.state_cache.server_rooms(&server).collect();
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::text_html(
|
||||
format!("Query completed in {query_time:?}:\n\n```\n{results:?}```"),
|
||||
format!("<p>Query completed in {query_time:?}:</p>\n<pre><code>{results:?}\n</code></pre>"),
|
||||
))
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
RoomStateCache::RoomMembers {
|
||||
room_id,
|
||||
@@ -60,10 +57,9 @@ pub(crate) async fn room_state_cache(subcommand: RoomStateCache) -> Result<RoomM
|
||||
.collect();
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::text_html(
|
||||
format!("Query completed in {query_time:?}:\n\n```\n{results:?}```"),
|
||||
format!("<p>Query completed in {query_time:?}:</p>\n<pre><code>{results:?}\n</code></pre>"),
|
||||
))
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
RoomStateCache::LocalUsersInRoom {
|
||||
room_id,
|
||||
@@ -76,10 +72,9 @@ pub(crate) async fn room_state_cache(subcommand: RoomStateCache) -> Result<RoomM
|
||||
.collect();
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::text_html(
|
||||
format!("Query completed in {query_time:?}:\n\n```\n{results:?}```"),
|
||||
format!("<p>Query completed in {query_time:?}:</p>\n<pre><code>{results:?}\n</code></pre>"),
|
||||
))
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
RoomStateCache::ActiveLocalUsersInRoom {
|
||||
room_id,
|
||||
@@ -92,10 +87,9 @@ pub(crate) async fn room_state_cache(subcommand: RoomStateCache) -> Result<RoomM
|
||||
.collect();
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::text_html(
|
||||
format!("Query completed in {query_time:?}:\n\n```\n{results:?}```"),
|
||||
format!("<p>Query completed in {query_time:?}:</p>\n<pre><code>{results:?}\n</code></pre>"),
|
||||
))
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
RoomStateCache::RoomJoinedCount {
|
||||
room_id,
|
||||
@@ -104,10 +98,9 @@ pub(crate) async fn room_state_cache(subcommand: RoomStateCache) -> Result<RoomM
|
||||
let results = services().rooms.state_cache.room_joined_count(&room_id);
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::text_html(
|
||||
format!("Query completed in {query_time:?}:\n\n```\n{results:?}```"),
|
||||
format!("<p>Query completed in {query_time:?}:</p>\n<pre><code>{results:?}\n</code></pre>"),
|
||||
))
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
RoomStateCache::RoomInvitedCount {
|
||||
room_id,
|
||||
@@ -116,10 +109,9 @@ pub(crate) async fn room_state_cache(subcommand: RoomStateCache) -> Result<RoomM
|
||||
let results = services().rooms.state_cache.room_invited_count(&room_id);
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::text_html(
|
||||
format!("Query completed in {query_time:?}:\n\n```\n{results:?}```"),
|
||||
format!("<p>Query completed in {query_time:?}:</p>\n<pre><code>{results:?}\n</code></pre>"),
|
||||
))
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
RoomStateCache::RoomUserOnceJoined {
|
||||
room_id,
|
||||
@@ -132,10 +124,9 @@ pub(crate) async fn room_state_cache(subcommand: RoomStateCache) -> Result<RoomM
|
||||
.collect();
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::text_html(
|
||||
format!("Query completed in {query_time:?}:\n\n```\n{results:?}```"),
|
||||
format!("<p>Query completed in {query_time:?}:</p>\n<pre><code>{results:?}\n</code></pre>"),
|
||||
))
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
RoomStateCache::RoomMembersInvited {
|
||||
room_id,
|
||||
@@ -148,10 +139,9 @@ pub(crate) async fn room_state_cache(subcommand: RoomStateCache) -> Result<RoomM
|
||||
.collect();
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::text_html(
|
||||
format!("Query completed in {query_time:?}:\n\n```\n{results:?}```"),
|
||||
format!("<p>Query completed in {query_time:?}:</p>\n<pre><code>{results:?}\n</code></pre>"),
|
||||
))
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
RoomStateCache::GetInviteCount {
|
||||
room_id,
|
||||
@@ -164,10 +154,9 @@ pub(crate) async fn room_state_cache(subcommand: RoomStateCache) -> Result<RoomM
|
||||
.get_invite_count(&room_id, &user_id);
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::text_html(
|
||||
format!("Query completed in {query_time:?}:\n\n```\n{results:?}```"),
|
||||
format!("<p>Query completed in {query_time:?}:</p>\n<pre><code>{results:?}\n</code></pre>"),
|
||||
))
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
RoomStateCache::GetLeftCount {
|
||||
room_id,
|
||||
@@ -180,10 +169,9 @@ pub(crate) async fn room_state_cache(subcommand: RoomStateCache) -> Result<RoomM
|
||||
.get_left_count(&room_id, &user_id);
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::text_html(
|
||||
format!("Query completed in {query_time:?}:\n\n```\n{results:?}```"),
|
||||
format!("<p>Query completed in {query_time:?}:</p>\n<pre><code>{results:?}\n</code></pre>"),
|
||||
))
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
RoomStateCache::RoomsJoined {
|
||||
user_id,
|
||||
@@ -196,10 +184,9 @@ pub(crate) async fn room_state_cache(subcommand: RoomStateCache) -> Result<RoomM
|
||||
.collect();
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::text_html(
|
||||
format!("Query completed in {query_time:?}:\n\n```\n{results:?}```"),
|
||||
format!("<p>Query completed in {query_time:?}:</p>\n<pre><code>{results:?}\n</code></pre>"),
|
||||
))
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
RoomStateCache::RoomsInvited {
|
||||
user_id,
|
||||
@@ -212,10 +199,9 @@ pub(crate) async fn room_state_cache(subcommand: RoomStateCache) -> Result<RoomM
|
||||
.collect();
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::text_html(
|
||||
format!("Query completed in {query_time:?}:\n\n```\n{results:?}```"),
|
||||
format!("<p>Query completed in {query_time:?}:</p>\n<pre><code>{results:?}\n</code></pre>"),
|
||||
))
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
RoomStateCache::RoomsLeft {
|
||||
user_id,
|
||||
@@ -224,10 +210,9 @@ pub(crate) async fn room_state_cache(subcommand: RoomStateCache) -> Result<RoomM
|
||||
let results: Result<Vec<_>> = services().rooms.state_cache.rooms_left(&user_id).collect();
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::text_html(
|
||||
format!("Query completed in {query_time:?}:\n\n```\n{results:?}```"),
|
||||
format!("<p>Query completed in {query_time:?}:</p>\n<pre><code>{results:?}\n</code></pre>"),
|
||||
))
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
RoomStateCache::InviteState {
|
||||
user_id,
|
||||
@@ -240,10 +225,9 @@ pub(crate) async fn room_state_cache(subcommand: RoomStateCache) -> Result<RoomM
|
||||
.invite_state(&user_id, &room_id);
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::text_html(
|
||||
format!("Query completed in {query_time:?}:\n\n```\n{results:?}```"),
|
||||
format!("<p>Query completed in {query_time:?}:</p>\n<pre><code>{results:?}\n</code></pre>"),
|
||||
))
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
+13
-17
@@ -4,7 +4,7 @@ use super::Sending;
|
||||
use crate::{service::sending::Destination, services, Result};
|
||||
|
||||
/// All the getters and iterators in key_value/sending.rs
|
||||
pub(crate) async fn sending(subcommand: Sending) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn sending(subcommand: Sending) -> Result<RoomMessageEventContent> {
|
||||
match subcommand {
|
||||
Sending::ActiveRequests => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
@@ -13,10 +13,9 @@ pub(crate) async fn sending(subcommand: Sending) -> Result<RoomMessageEventConte
|
||||
|
||||
let active_requests: Result<Vec<(_, _, _)>> = results.collect();
|
||||
|
||||
Ok(RoomMessageEventContent::text_html(
|
||||
format!("Query completed in {query_time:?}:\n\n```\n{active_requests:?}```"),
|
||||
format!("<p>Query completed in {query_time:?}:</p>\n<pre><code>{active_requests:?}\n</code></pre>"),
|
||||
))
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{active_requests:#?}\n```"
|
||||
)))
|
||||
},
|
||||
Sending::QueuedRequests {
|
||||
appservice_id,
|
||||
@@ -92,10 +91,9 @@ pub(crate) async fn sending(subcommand: Sending) -> Result<RoomMessageEventConte
|
||||
|
||||
let queued_requests = results.collect::<Result<Vec<(_, _)>>>();
|
||||
|
||||
Ok(RoomMessageEventContent::text_html(
|
||||
format!("Query completed in {query_time:?}:\n\n```\n{queued_requests:?}```"),
|
||||
format!("<p>Query completed in {query_time:?}:</p>\n<pre><code>{queued_requests:?}\n</code></pre>"),
|
||||
))
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{queued_requests:#?}\n```"
|
||||
)))
|
||||
},
|
||||
Sending::ActiveRequestsFor {
|
||||
appservice_id,
|
||||
@@ -171,10 +169,9 @@ pub(crate) async fn sending(subcommand: Sending) -> Result<RoomMessageEventConte
|
||||
|
||||
let active_requests = results.collect::<Result<Vec<(_, _)>>>();
|
||||
|
||||
Ok(RoomMessageEventContent::text_html(
|
||||
format!("Query completed in {query_time:?}:\n\n```\n{active_requests:?}```"),
|
||||
format!("<p>Query completed in {query_time:?}:</p>\n<pre><code>{active_requests:?}\n</code></pre>"),
|
||||
))
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{active_requests:#?}\n```"
|
||||
)))
|
||||
},
|
||||
Sending::GetLatestEduCount {
|
||||
server_name,
|
||||
@@ -183,10 +180,9 @@ pub(crate) async fn sending(subcommand: Sending) -> Result<RoomMessageEventConte
|
||||
let results = services().sending.db.get_latest_educount(&server_name);
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::text_html(
|
||||
format!("Query completed in {query_time:?}:\n\n```\n{results:?}```"),
|
||||
format!("<p>Query completed in {query_time:?}:</p>\n<pre><code>{results:?}\n</code></pre>"),
|
||||
))
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
)))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use super::Users;
|
||||
use crate::{services, Result};
|
||||
|
||||
/// All the getters and iterators in key_value/users.rs
|
||||
pub(crate) async fn users(subcommand: Users) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn users(subcommand: Users) -> Result<RoomMessageEventContent> {
|
||||
match subcommand {
|
||||
Users::Iter => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
@@ -13,10 +13,9 @@ pub(crate) async fn users(subcommand: Users) -> Result<RoomMessageEventContent>
|
||||
|
||||
let users = results.collect::<Vec<_>>();
|
||||
|
||||
Ok(RoomMessageEventContent::text_html(
|
||||
format!("Query completed in {query_time:?}:\n\n```\n{users:?}```"),
|
||||
format!("<p>Query completed in {query_time:?}:</p>\n<pre><code>{users:?}\n</code></pre>"),
|
||||
))
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{users:#?}\n```"
|
||||
)))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
+13
-13
@@ -1,18 +1,18 @@
|
||||
mod room_alias_commands;
|
||||
mod room_commands;
|
||||
mod room_directory_commands;
|
||||
mod room_info_commands;
|
||||
mod room_moderation_commands;
|
||||
|
||||
use clap::Subcommand;
|
||||
use conduit::Result;
|
||||
use ruma::{events::room::message::RoomMessageEventContent, RoomId, RoomOrAliasId};
|
||||
|
||||
use self::room_commands::list;
|
||||
use crate::Result;
|
||||
|
||||
pub(crate) mod room_alias_commands;
|
||||
pub(crate) mod room_commands;
|
||||
pub(crate) mod room_directory_commands;
|
||||
pub(crate) mod room_info_commands;
|
||||
pub(crate) mod room_moderation_commands;
|
||||
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
#[derive(Subcommand)]
|
||||
pub(crate) enum RoomCommand {
|
||||
pub(super) enum RoomCommand {
|
||||
/// - List all rooms the server knows about
|
||||
List {
|
||||
page: Option<usize>,
|
||||
@@ -37,7 +37,7 @@ pub(crate) enum RoomCommand {
|
||||
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
#[derive(Subcommand)]
|
||||
pub(crate) enum RoomInfoCommand {
|
||||
pub(super) enum RoomInfoCommand {
|
||||
/// - List joined members in a room
|
||||
ListJoinedMembers {
|
||||
room_id: Box<RoomId>,
|
||||
@@ -54,7 +54,7 @@ pub(crate) enum RoomInfoCommand {
|
||||
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
#[derive(Subcommand)]
|
||||
pub(crate) enum RoomAliasCommand {
|
||||
pub(super) enum RoomAliasCommand {
|
||||
/// - Make an alias point to a room.
|
||||
Set {
|
||||
#[arg(short, long)]
|
||||
@@ -90,7 +90,7 @@ pub(crate) enum RoomAliasCommand {
|
||||
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
#[derive(Subcommand)]
|
||||
pub(crate) enum RoomDirectoryCommand {
|
||||
pub(super) enum RoomDirectoryCommand {
|
||||
/// - Publish a room to the room directory
|
||||
Publish {
|
||||
/// The room id of the room to publish
|
||||
@@ -111,7 +111,7 @@ pub(crate) enum RoomDirectoryCommand {
|
||||
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
#[derive(Subcommand)]
|
||||
pub(crate) enum RoomModerationCommand {
|
||||
pub(super) enum RoomModerationCommand {
|
||||
/// - Bans a room from local users joining and evicts all our local users
|
||||
/// from the room. Also blocks any invites (local and remote) for the
|
||||
/// banned room.
|
||||
@@ -167,7 +167,7 @@ pub(crate) enum RoomModerationCommand {
|
||||
ListBannedRooms,
|
||||
}
|
||||
|
||||
pub(crate) async fn process(command: RoomCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn process(command: RoomCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
Ok(match command {
|
||||
RoomCommand::Info(command) => room_info_commands::process(command, body).await?,
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ use ruma::{events::room::message::RoomMessageEventContent, RoomAliasId};
|
||||
use super::RoomAliasCommand;
|
||||
use crate::{escape_html, services, Result};
|
||||
|
||||
pub(crate) async fn process(command: RoomAliasCommand, _body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn process(command: RoomAliasCommand, _body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
let server_user = &services().globals.server_user;
|
||||
|
||||
match command {
|
||||
|
||||
@@ -4,7 +4,7 @@ use ruma::{events::room::message::RoomMessageEventContent, OwnedRoomId};
|
||||
|
||||
use crate::{escape_html, get_room_info, handler::PAGE_SIZE, services, Result};
|
||||
|
||||
pub(crate) async fn list(_body: Vec<&str>, page: Option<usize>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn list(_body: Vec<&str>, page: Option<usize>) -> Result<RoomMessageEventContent> {
|
||||
// TODO: i know there's a way to do this with clap, but i can't seem to find it
|
||||
let page = page.unwrap_or(1);
|
||||
let mut rooms = services()
|
||||
|
||||
@@ -5,7 +5,7 @@ use ruma::{events::room::message::RoomMessageEventContent, OwnedRoomId};
|
||||
use super::RoomDirectoryCommand;
|
||||
use crate::{escape_html, get_room_info, handler::PAGE_SIZE, services, Result};
|
||||
|
||||
pub(crate) async fn process(command: RoomDirectoryCommand, _body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn process(command: RoomDirectoryCommand, _body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
match command {
|
||||
RoomDirectoryCommand::Publish {
|
||||
room_id,
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
use std::fmt::Write;
|
||||
|
||||
use ruma::{events::room::message::RoomMessageEventContent, RoomId};
|
||||
use service::services;
|
||||
|
||||
use super::RoomInfoCommand;
|
||||
use crate::{escape_html, Result};
|
||||
use crate::Result;
|
||||
|
||||
pub(crate) async fn process(command: RoomInfoCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn process(command: RoomInfoCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
match command {
|
||||
RoomInfoCommand::ListJoinedMembers {
|
||||
room_id,
|
||||
@@ -57,26 +55,7 @@ async fn list_joined_members(_body: Vec<&str>, room_id: Box<RoomId>) -> Result<R
|
||||
.join("\n")
|
||||
);
|
||||
|
||||
let output_html = format!(
|
||||
"<table><caption>{} Members in Room \"{}\" </caption>\n<tr><th>MXID</th>\t<th>Display \
|
||||
Name</th></tr>\n{}</table>",
|
||||
member_info.len(),
|
||||
room_name,
|
||||
member_info
|
||||
.iter()
|
||||
.fold(String::new(), |mut output, (mxid, displayname)| {
|
||||
writeln!(
|
||||
output,
|
||||
"<tr><td>{}</td>\t<td>{}</td></tr>",
|
||||
mxid,
|
||||
escape_html(displayname.as_ref())
|
||||
)
|
||||
.expect("should be able to write to string buffer");
|
||||
output
|
||||
})
|
||||
);
|
||||
|
||||
Ok(RoomMessageEventContent::text_html(output_plain, output_html))
|
||||
Ok(RoomMessageEventContent::notice_markdown(output_plain))
|
||||
}
|
||||
|
||||
async fn view_room_topic(_body: Vec<&str>, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
|
||||
@@ -84,10 +63,7 @@ async fn view_room_topic(_body: Vec<&str>, room_id: Box<RoomId>) -> Result<RoomM
|
||||
return Ok(RoomMessageEventContent::text_plain("Room does not have a room topic set."));
|
||||
};
|
||||
|
||||
let output_html = format!("<p>Room topic:</p>\n<hr>\n{}<hr>", escape_html(&room_topic));
|
||||
|
||||
Ok(RoomMessageEventContent::text_html(
|
||||
format!("Room topic:\n\n```{room_topic}\n```"),
|
||||
output_html,
|
||||
))
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Room topic:\n\n```{room_topic}\n```"
|
||||
)))
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use std::fmt::Write;
|
||||
|
||||
use api::client::{get_alias_helper, leave_room};
|
||||
use ruma::{
|
||||
events::room::message::RoomMessageEventContent, OwnedRoomId, OwnedUserId, RoomAliasId, RoomId, RoomOrAliasId,
|
||||
@@ -7,9 +5,9 @@ use ruma::{
|
||||
use tracing::{debug, error, info, warn};
|
||||
|
||||
use super::{super::Service, RoomModerationCommand};
|
||||
use crate::{escape_html, get_room_info, services, user_is_local, Result};
|
||||
use crate::{get_room_info, services, user_is_local, Result};
|
||||
|
||||
pub(crate) async fn process(command: RoomModerationCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn process(command: RoomModerationCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
match command {
|
||||
RoomModerationCommand::BanRoom {
|
||||
force,
|
||||
@@ -111,15 +109,15 @@ async fn ban_room(
|
||||
user.ok().filter(|local_user| {
|
||||
user_is_local(local_user)
|
||||
// additional wrapped check here is to avoid adding remote users
|
||||
// who are in the admin room to the list of local users (would fail auth check)
|
||||
// who are in the admin room to the list of local users (would
|
||||
// fail auth check)
|
||||
&& (user_is_local(local_user)
|
||||
// since this is a force operation, assume user is an admin
|
||||
// if somehow this fails
|
||||
&& services()
|
||||
.users
|
||||
.is_admin(local_user)
|
||||
.unwrap_or(true)) // since this is a force
|
||||
// operation, assume user
|
||||
// is an admin if somehow
|
||||
// this fails
|
||||
.unwrap_or(true))
|
||||
})
|
||||
})
|
||||
.collect::<Vec<OwnedUserId>>()
|
||||
@@ -311,19 +309,17 @@ async fn ban_list_of_rooms(body: Vec<&str>, force: bool, disable_federation: boo
|
||||
.filter_map(|user| {
|
||||
user.ok().filter(|local_user| {
|
||||
local_user.server_name() == services().globals.server_name()
|
||||
// additional wrapped check here is to avoid adding remote users
|
||||
// who are in the admin room to the list of local users (would fail auth check)
|
||||
// additional wrapped check here is to avoid adding remote
|
||||
// users who are in the admin room to the list of local
|
||||
// users (would fail auth check)
|
||||
&& (local_user.server_name()
|
||||
== services().globals.server_name()
|
||||
// since this is a force operation, assume user is an
|
||||
// admin if somehow this fails
|
||||
&& services()
|
||||
.users
|
||||
.is_admin(local_user)
|
||||
.unwrap_or(true)) // since this is a
|
||||
// force operation,
|
||||
// assume user is
|
||||
// an admin if
|
||||
// somehow this
|
||||
// fails
|
||||
.unwrap_or(true))
|
||||
})
|
||||
})
|
||||
.collect::<Vec<OwnedUserId>>()
|
||||
@@ -344,8 +340,9 @@ async fn ban_list_of_rooms(body: Vec<&str>, force: bool, disable_federation: boo
|
||||
.filter_map(|user| {
|
||||
user.ok().filter(|local_user| {
|
||||
local_user.server_name() == services().globals.server_name()
|
||||
// additional wrapped check here is to avoid adding remote users
|
||||
// who are in the admin room to the list of local users (would fail auth check)
|
||||
// additional wrapped check here is to avoid adding remote
|
||||
// users who are in the admin room to the list of local
|
||||
// users (would fail auth check)
|
||||
&& (local_user.server_name()
|
||||
== services().globals.server_name()
|
||||
&& !services()
|
||||
@@ -493,26 +490,7 @@ async fn list_banned_rooms(_body: Vec<&str>) -> Result<RoomMessageEventContent>
|
||||
.join("\n")
|
||||
);
|
||||
|
||||
let output_html = format!(
|
||||
"<table><caption>Rooms Banned ({}) \
|
||||
</caption>\n<tr><th>id</th>\t<th>members</th>\t<th>name</th></tr>\n{}</table>",
|
||||
rooms.len(),
|
||||
rooms
|
||||
.iter()
|
||||
.fold(String::new(), |mut output, (id, members, name)| {
|
||||
writeln!(
|
||||
output,
|
||||
"<tr><td>{}</td>\t<td>{}</td>\t<td>{}</td></tr>",
|
||||
id,
|
||||
members,
|
||||
escape_html(name.as_ref())
|
||||
)
|
||||
.expect("should be able to write to string buffer");
|
||||
output
|
||||
})
|
||||
);
|
||||
|
||||
Ok(RoomMessageEventContent::text_html(output_plain, output_html))
|
||||
Ok(RoomMessageEventContent::notice_markdown(output_plain))
|
||||
},
|
||||
Err(e) => {
|
||||
error!("Failed to list banned rooms: {}", e);
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
use conduit::{warn, Result};
|
||||
use ruma::events::room::message::RoomMessageEventContent;
|
||||
|
||||
use crate::{services, Result};
|
||||
use crate::services;
|
||||
|
||||
pub(crate) async fn uptime(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn uptime(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
let seconds = services()
|
||||
.server
|
||||
.started
|
||||
@@ -17,15 +18,15 @@ pub(crate) async fn uptime(_body: Vec<&str>) -> Result<RoomMessageEventContent>
|
||||
seconds % 60,
|
||||
);
|
||||
|
||||
Ok(RoomMessageEventContent::notice_html(String::new(), result))
|
||||
Ok(RoomMessageEventContent::notice_plain(result))
|
||||
}
|
||||
|
||||
pub(crate) async fn show_config(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn show_config(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
// Construct and send the response
|
||||
Ok(RoomMessageEventContent::text_plain(format!("{}", services().globals.config)))
|
||||
}
|
||||
|
||||
pub(crate) async fn memory_usage(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn memory_usage(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
let response0 = services().memory_usage().await;
|
||||
let response1 = services().globals.db.memory_usage();
|
||||
let response2 = conduit::alloc::memory_usage();
|
||||
@@ -40,19 +41,19 @@ pub(crate) async fn memory_usage(_body: Vec<&str>) -> Result<RoomMessageEventCon
|
||||
)))
|
||||
}
|
||||
|
||||
pub(crate) async fn clear_database_caches(_body: Vec<&str>, amount: u32) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn clear_database_caches(_body: Vec<&str>, amount: u32) -> Result<RoomMessageEventContent> {
|
||||
services().globals.db.clear_caches(amount);
|
||||
|
||||
Ok(RoomMessageEventContent::text_plain("Done."))
|
||||
}
|
||||
|
||||
pub(crate) async fn clear_service_caches(_body: Vec<&str>, amount: u32) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn clear_service_caches(_body: Vec<&str>, amount: u32) -> Result<RoomMessageEventContent> {
|
||||
services().clear_caches(amount).await;
|
||||
|
||||
Ok(RoomMessageEventContent::text_plain("Done."))
|
||||
}
|
||||
|
||||
pub(crate) async fn list_backups(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn list_backups(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
let result = services().globals.db.backup_list()?;
|
||||
|
||||
if result.is_empty() {
|
||||
@@ -62,7 +63,7 @@ pub(crate) async fn list_backups(_body: Vec<&str>) -> Result<RoomMessageEventCon
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn backup_database(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn backup_database(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
if !cfg!(feature = "rocksdb") {
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
"Only RocksDB supports online backups in conduwuit.",
|
||||
@@ -86,7 +87,7 @@ pub(crate) async fn backup_database(_body: Vec<&str>) -> Result<RoomMessageEvent
|
||||
Ok(RoomMessageEventContent::text_plain(&result))
|
||||
}
|
||||
|
||||
pub(crate) async fn list_database_files(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn list_database_files(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
if !cfg!(feature = "rocksdb") {
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
"Only RocksDB supports listing files in conduwuit.",
|
||||
@@ -94,5 +95,34 @@ pub(crate) async fn list_database_files(_body: Vec<&str>) -> Result<RoomMessageE
|
||||
}
|
||||
|
||||
let result = services().globals.db.file_list()?;
|
||||
Ok(RoomMessageEventContent::notice_html(String::new(), result))
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(result))
|
||||
}
|
||||
|
||||
pub(super) async fn admin_notice(_body: Vec<&str>, message: Vec<String>) -> Result<RoomMessageEventContent> {
|
||||
let message = message.join(" ");
|
||||
services().admin.send_text(&message).await;
|
||||
|
||||
Ok(RoomMessageEventContent::notice_plain("Notice was sent to #admins"))
|
||||
}
|
||||
|
||||
#[cfg(conduit_mods)]
|
||||
pub(super) async fn reload(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
services().server.reload()?;
|
||||
|
||||
Ok(RoomMessageEventContent::notice_plain("Reloading server..."))
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
pub(super) async fn restart(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
services().server.restart()?;
|
||||
|
||||
Ok(RoomMessageEventContent::notice_plain("Restarting server..."))
|
||||
}
|
||||
|
||||
pub(super) async fn shutdown(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
warn!("shutdown command");
|
||||
services().server.shutdown()?;
|
||||
|
||||
Ok(RoomMessageEventContent::notice_plain("Shutting down server..."))
|
||||
}
|
||||
+29
-8
@@ -1,17 +1,14 @@
|
||||
pub(crate) mod server_commands;
|
||||
mod commands;
|
||||
|
||||
use clap::Subcommand;
|
||||
use conduit::Result;
|
||||
use ruma::events::room::message::RoomMessageEventContent;
|
||||
|
||||
use self::server_commands::{
|
||||
backup_database, clear_database_caches, clear_service_caches, list_backups, list_database_files, memory_usage,
|
||||
show_config, uptime,
|
||||
};
|
||||
use crate::Result;
|
||||
use self::commands::*;
|
||||
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
#[derive(Subcommand)]
|
||||
pub(crate) enum ServerCommand {
|
||||
pub(super) enum ServerCommand {
|
||||
/// - Time elapsed since startup
|
||||
Uptime,
|
||||
|
||||
@@ -42,9 +39,25 @@ pub(crate) enum ServerCommand {
|
||||
|
||||
/// - List database files
|
||||
ListDatabaseFiles,
|
||||
|
||||
/// - Send a message to the admin room.
|
||||
AdminNotice {
|
||||
message: Vec<String>,
|
||||
},
|
||||
|
||||
#[cfg(conduit_mods)]
|
||||
/// - Hot-reload the server
|
||||
Reload,
|
||||
|
||||
#[cfg(unix)]
|
||||
/// - Restart the server
|
||||
Restart,
|
||||
|
||||
/// - Shutdown the server
|
||||
Shutdown,
|
||||
}
|
||||
|
||||
pub(crate) async fn process(command: ServerCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn process(command: ServerCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
Ok(match command {
|
||||
ServerCommand::Uptime => uptime(body).await?,
|
||||
ServerCommand::ShowConfig => show_config(body).await?,
|
||||
@@ -58,5 +71,13 @@ pub(crate) async fn process(command: ServerCommand, body: Vec<&str>) -> Result<R
|
||||
ServerCommand::ListBackups => list_backups(body).await?,
|
||||
ServerCommand::BackupDatabase => backup_database(body).await?,
|
||||
ServerCommand::ListDatabaseFiles => list_database_files(body).await?,
|
||||
ServerCommand::AdminNotice {
|
||||
message,
|
||||
} => admin_notice(body, message).await?,
|
||||
#[cfg(conduit_mods)]
|
||||
ServerCommand::Reload => reload(body).await?,
|
||||
#[cfg(unix)]
|
||||
ServerCommand::Restart => restart(body).await?,
|
||||
ServerCommand::Shutdown => shutdown(body).await?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,10 +4,10 @@ use crate::Result;
|
||||
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
#[derive(clap::Subcommand)]
|
||||
pub(crate) enum TesterCommands {
|
||||
pub(super) enum TesterCommands {
|
||||
Tester,
|
||||
}
|
||||
pub(crate) async fn process(command: TesterCommands, _body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn process(command: TesterCommands, _body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
Ok(match command {
|
||||
TesterCommands::Tester => RoomMessageEventContent::notice_plain(String::from("completed")),
|
||||
})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::{collections::BTreeMap, fmt::Write as _};
|
||||
|
||||
use api::client::{join_room_by_id_helper, leave_all_rooms, update_avatar_url, update_displayname};
|
||||
use conduit::utils;
|
||||
use conduit::{utils, Result};
|
||||
use ruma::{
|
||||
events::{
|
||||
room::message::RoomMessageEventContent,
|
||||
@@ -15,28 +15,24 @@ use tracing::{error, info, warn};
|
||||
use crate::{
|
||||
escape_html, get_room_info, services,
|
||||
utils::{parse_active_local_user_id, parse_local_user_id},
|
||||
Result,
|
||||
};
|
||||
|
||||
const AUTO_GEN_PASSWORD_LENGTH: usize = 25;
|
||||
|
||||
pub(crate) async fn list(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn list(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
match services().users.list_local_users() {
|
||||
Ok(users) => {
|
||||
let mut plain_msg = format!("Found {} local user account(s):\n```\n", users.len());
|
||||
plain_msg += &users.join("\n");
|
||||
plain_msg += "\n```";
|
||||
|
||||
let mut html_msg = format!("<p>Found {} local user account(s):</p><pre><code>", users.len());
|
||||
html_msg += &users.join("\n");
|
||||
html_msg += "\n</code></pre>";
|
||||
Ok(RoomMessageEventContent::text_html(&plain_msg, &html_msg))
|
||||
Ok(RoomMessageEventContent::notice_markdown(plain_msg))
|
||||
},
|
||||
Err(e) => Ok(RoomMessageEventContent::text_plain(e.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn create(
|
||||
pub(super) async fn create(
|
||||
_body: Vec<&str>, username: String, password: Option<String>,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
// Validate user id
|
||||
@@ -127,7 +123,7 @@ pub(crate) async fn create(
|
||||
)))
|
||||
}
|
||||
|
||||
pub(crate) async fn deactivate(
|
||||
pub(super) async fn deactivate(
|
||||
_body: Vec<&str>, no_leave_rooms: bool, user_id: String,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
// Validate user id
|
||||
@@ -166,7 +162,7 @@ pub(crate) async fn deactivate(
|
||||
)))
|
||||
}
|
||||
|
||||
pub(crate) async fn reset_password(_body: Vec<&str>, username: String) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn reset_password(_body: Vec<&str>, username: String) -> Result<RoomMessageEventContent> {
|
||||
let user_id = parse_local_user_id(&username)?;
|
||||
|
||||
if user_id == services().globals.server_user {
|
||||
@@ -190,7 +186,7 @@ pub(crate) async fn reset_password(_body: Vec<&str>, username: String) -> Result
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn deactivate_all(
|
||||
pub(super) async fn deactivate_all(
|
||||
body: Vec<&str>, no_leave_rooms: bool, force: bool,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
if body.len() < 2 || !body[0].trim().starts_with("```") || body.last().unwrap_or(&"").trim() != "```" {
|
||||
@@ -284,7 +280,7 @@ pub(crate) async fn deactivate_all(
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn list_joined_rooms(_body: Vec<&str>, user_id: String) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn list_joined_rooms(_body: Vec<&str>, user_id: String) -> Result<RoomMessageEventContent> {
|
||||
// Validate user id
|
||||
let user_id = parse_local_user_id(&user_id)?;
|
||||
|
||||
@@ -335,7 +331,7 @@ pub(crate) async fn list_joined_rooms(_body: Vec<&str>, user_id: String) -> Resu
|
||||
Ok(RoomMessageEventContent::text_html(output_plain, output_html))
|
||||
}
|
||||
|
||||
pub(crate) async fn put_room_tag(
|
||||
pub(super) async fn put_room_tag(
|
||||
_body: Vec<&str>, user_id: String, room_id: Box<RoomId>, tag: String,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
let user_id = parse_active_local_user_id(&user_id)?;
|
||||
@@ -370,7 +366,7 @@ pub(crate) async fn put_room_tag(
|
||||
)))
|
||||
}
|
||||
|
||||
pub(crate) async fn delete_room_tag(
|
||||
pub(super) async fn delete_room_tag(
|
||||
_body: Vec<&str>, user_id: String, room_id: Box<RoomId>, tag: String,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
let user_id = parse_active_local_user_id(&user_id)?;
|
||||
@@ -402,7 +398,7 @@ pub(crate) async fn delete_room_tag(
|
||||
)))
|
||||
}
|
||||
|
||||
pub(crate) async fn get_room_tags(
|
||||
pub(super) async fn get_room_tags(
|
||||
_body: Vec<&str>, user_id: String, room_id: Box<RoomId>,
|
||||
) -> Result<RoomMessageEventContent> {
|
||||
let user_id = parse_active_local_user_id(&user_id)?;
|
||||
@@ -420,8 +416,8 @@ pub(crate) async fn get_room_tags(
|
||||
|e| serde_json::from_str(e.get()).expect("Bad account data in database for user {user_id}"),
|
||||
);
|
||||
|
||||
Ok(RoomMessageEventContent::text_html(
|
||||
format!("<pre><code>\n{:?}\n</code></pre>", tags_event.content.tags),
|
||||
format!("```\n{:?}\n```", tags_event.content.tags),
|
||||
))
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"```\n{:#?}\n```",
|
||||
tags_event.content.tags
|
||||
)))
|
||||
}
|
||||
@@ -1,15 +1,14 @@
|
||||
pub(crate) mod user_commands;
|
||||
mod commands;
|
||||
|
||||
use clap::Subcommand;
|
||||
use conduit::Result;
|
||||
use ruma::{events::room::message::RoomMessageEventContent, RoomId};
|
||||
use user_commands::{delete_room_tag, get_room_tags, put_room_tag};
|
||||
|
||||
use self::user_commands::{create, deactivate, deactivate_all, list, list_joined_rooms, reset_password};
|
||||
use crate::Result;
|
||||
use self::commands::*;
|
||||
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
#[derive(Subcommand)]
|
||||
pub(crate) enum UserCommand {
|
||||
pub(super) enum UserCommand {
|
||||
/// - Create a new user
|
||||
Create {
|
||||
/// Username of the new user
|
||||
@@ -93,7 +92,7 @@ pub(crate) enum UserCommand {
|
||||
},
|
||||
}
|
||||
|
||||
pub(crate) async fn process(command: UserCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn process(command: UserCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
Ok(match command {
|
||||
UserCommand::List => list(body).await?,
|
||||
UserCommand::Create {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
pub(crate) use conduit::utils::HtmlEscape;
|
||||
use conduit_core::Error;
|
||||
use ruma::{OwnedRoomId, OwnedUserId, RoomId, UserId};
|
||||
use service::user_is_local;
|
||||
|
||||
+13
-17
@@ -40,9 +40,9 @@ const RANDOM_USER_ID_LENGTH: usize = 10;
|
||||
///
|
||||
/// Note: This will not reserve the username, so the username might become
|
||||
/// invalid when trying to register
|
||||
#[tracing::instrument(skip_all, fields(%client_ip))]
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "register_available")]
|
||||
pub(crate) async fn get_register_available_route(
|
||||
InsecureClientIp(client_ip): InsecureClientIp, body: Ruma<get_username_availability::v3::Request>,
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_username_availability::v3::Request>,
|
||||
) -> Result<get_username_availability::v3::Response> {
|
||||
// Validate user id
|
||||
let user_id = UserId::parse_with_server_name(body.username.to_lowercase(), services().globals.server_name())
|
||||
@@ -89,9 +89,9 @@ pub(crate) async fn get_register_available_route(
|
||||
/// - If `inhibit_login` is false: Creates a device and returns device id and
|
||||
/// access_token
|
||||
#[allow(clippy::doc_markdown)]
|
||||
#[tracing::instrument(skip_all, fields(%client_ip))]
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "register")]
|
||||
pub(crate) async fn register_route(
|
||||
InsecureClientIp(client_ip): InsecureClientIp, body: Ruma<register::v3::Request>,
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<register::v3::Request>,
|
||||
) -> Result<register::v3::Response> {
|
||||
if !services().globals.allow_registration() && body.appservice_info.is_none() {
|
||||
info!(
|
||||
@@ -302,7 +302,7 @@ pub(crate) async fn register_route(
|
||||
services()
|
||||
.admin
|
||||
.send_message(RoomMessageEventContent::notice_plain(format!(
|
||||
"New user \"{user_id}\" registered on this server from IP {client_ip}."
|
||||
"New user \"{user_id}\" registered on this server from IP {client}."
|
||||
)))
|
||||
.await;
|
||||
}
|
||||
@@ -321,7 +321,7 @@ pub(crate) async fn register_route(
|
||||
.admin
|
||||
.send_message(RoomMessageEventContent::notice_plain(format!(
|
||||
"Guest user \"{user_id}\" with device display name `{device_display_name}` registered on this \
|
||||
server from IP {client_ip}."
|
||||
server from IP {client}."
|
||||
)))
|
||||
.await;
|
||||
} else {
|
||||
@@ -329,7 +329,7 @@ pub(crate) async fn register_route(
|
||||
.admin
|
||||
.send_message(RoomMessageEventContent::notice_plain(format!(
|
||||
"Guest user \"{user_id}\" with no device display name registered on this server from IP \
|
||||
{client_ip}.",
|
||||
{client}.",
|
||||
)))
|
||||
.await;
|
||||
}
|
||||
@@ -337,8 +337,7 @@ pub(crate) async fn register_route(
|
||||
services()
|
||||
.admin
|
||||
.send_message(RoomMessageEventContent::notice_plain(format!(
|
||||
"Guest user \"{user_id}\" with no device display name registered on this server from IP \
|
||||
{client_ip}.",
|
||||
"Guest user \"{user_id}\" with no device display name registered on this server from IP {client}.",
|
||||
)))
|
||||
.await;
|
||||
}
|
||||
@@ -354,10 +353,7 @@ pub(crate) async fn register_route(
|
||||
.room_joined_count(&admin_room)?
|
||||
== Some(1)
|
||||
{
|
||||
services()
|
||||
.admin
|
||||
.make_user_admin(&user_id, displayname)
|
||||
.await?;
|
||||
service::admin::make_user_admin(&user_id, displayname).await?;
|
||||
|
||||
warn!("Granting {user_id} admin privileges as the first user");
|
||||
}
|
||||
@@ -423,9 +419,9 @@ pub(crate) async fn register_route(
|
||||
/// last seen ts)
|
||||
/// - Forgets to-device events
|
||||
/// - Triggers device list updates
|
||||
#[tracing::instrument(skip_all, fields(%client_ip))]
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "change_password")]
|
||||
pub(crate) async fn change_password_route(
|
||||
InsecureClientIp(client_ip): InsecureClientIp, body: Ruma<change_password::v3::Request>,
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<change_password::v3::Request>,
|
||||
) -> Result<change_password::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
||||
@@ -512,9 +508,9 @@ pub(crate) async fn whoami_route(body: Ruma<whoami::v3::Request>) -> Result<whoa
|
||||
/// - Forgets all to-device events
|
||||
/// - Triggers device list updates
|
||||
/// - Removes ability to log in again
|
||||
#[tracing::instrument(skip_all, fields(%client_ip))]
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "deactivate")]
|
||||
pub(crate) async fn deactivate_route(
|
||||
InsecureClientIp(client_ip): InsecureClientIp, body: Ruma<deactivate::v3::Request>,
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<deactivate::v3::Request>,
|
||||
) -> Result<deactivate::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
||||
|
||||
@@ -13,8 +13,7 @@ use crate::{services, Error, Result, Ruma};
|
||||
/// Allows loading room history around an event.
|
||||
///
|
||||
/// - Only works if the user is joined (TODO: always allow, but only show events
|
||||
/// if the user was
|
||||
/// joined, depending on history_visibility)
|
||||
/// if the user was joined, depending on history_visibility)
|
||||
pub(crate) async fn get_context_route(body: Ruma<get_context::v3::Request>) -> Result<get_context::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
||||
|
||||
@@ -28,9 +28,9 @@ use crate::{service::server_is_ours, services, Error, Result, Ruma};
|
||||
/// Lists the public rooms on this server.
|
||||
///
|
||||
/// - Rooms are ordered by the number of joined members
|
||||
#[tracing::instrument(skip_all, fields(%client_ip))]
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "publicrooms")]
|
||||
pub(crate) async fn get_public_rooms_filtered_route(
|
||||
InsecureClientIp(client_ip): InsecureClientIp, body: Ruma<get_public_rooms_filtered::v3::Request>,
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_public_rooms_filtered::v3::Request>,
|
||||
) -> Result<get_public_rooms_filtered::v3::Response> {
|
||||
if let Some(server) = &body.server {
|
||||
if services()
|
||||
@@ -66,9 +66,9 @@ pub(crate) async fn get_public_rooms_filtered_route(
|
||||
/// Lists the public rooms on this server.
|
||||
///
|
||||
/// - Rooms are ordered by the number of joined members
|
||||
#[tracing::instrument(skip_all, fields(%client_ip))]
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "publicrooms")]
|
||||
pub(crate) async fn get_public_rooms_route(
|
||||
InsecureClientIp(client_ip): InsecureClientIp, body: Ruma<get_public_rooms::v3::Request>,
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_public_rooms::v3::Request>,
|
||||
) -> Result<get_public_rooms::v3::Response> {
|
||||
if let Some(server) = &body.server {
|
||||
if services()
|
||||
@@ -109,9 +109,9 @@ pub(crate) async fn get_public_rooms_route(
|
||||
/// Sets the visibility of a given room in the room directory.
|
||||
///
|
||||
/// - TODO: Access control checks
|
||||
#[tracing::instrument(skip_all, fields(%client_ip))]
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "room_directory")]
|
||||
pub(crate) async fn set_room_visibility_route(
|
||||
InsecureClientIp(client_ip): InsecureClientIp, body: Ruma<set_room_visibility::v3::Request>,
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<set_room_visibility::v3::Request>,
|
||||
) -> Result<set_room_visibility::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ use std::{
|
||||
};
|
||||
|
||||
use axum_client_ip::InsecureClientIp;
|
||||
use conduit::utils::mutex_map;
|
||||
use ruma::{
|
||||
api::{
|
||||
client::{
|
||||
@@ -32,7 +33,7 @@ use ruma::{
|
||||
OwnedUserId, RoomId, RoomVersionId, ServerName, UserId,
|
||||
};
|
||||
use serde_json::value::{to_raw_value, RawValue as RawJsonValue};
|
||||
use tokio::sync::{MutexGuard, RwLock};
|
||||
use tokio::sync::RwLock;
|
||||
use tracing::{debug, error, info, trace, warn};
|
||||
|
||||
use super::get_alias_helper;
|
||||
@@ -164,7 +165,7 @@ async fn banned_room_check(
|
||||
/// rules locally
|
||||
/// - If the server does not know about the room: asks other servers over
|
||||
/// federation
|
||||
#[tracing::instrument(skip_all, fields(%client_ip))]
|
||||
#[tracing::instrument(skip_all, fields(%client_ip), name = "join")]
|
||||
pub(crate) async fn join_room_by_id_route(
|
||||
InsecureClientIp(client_ip): InsecureClientIp, body: Ruma<join_room_by_id::v3::Request>,
|
||||
) -> Result<join_room_by_id::v3::Response> {
|
||||
@@ -217,16 +218,16 @@ pub(crate) async fn join_room_by_id_route(
|
||||
/// - If the server does not know about the room: use the server name query
|
||||
/// param if specified. if not specified, asks other servers over federation
|
||||
/// via room alias server name and room ID server name
|
||||
#[tracing::instrument(skip_all, fields(%client_ip))]
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "join")]
|
||||
pub(crate) async fn join_room_by_id_or_alias_route(
|
||||
InsecureClientIp(client_ip): InsecureClientIp, body: Ruma<join_room_by_id_or_alias::v3::Request>,
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<join_room_by_id_or_alias::v3::Request>,
|
||||
) -> Result<join_room_by_id_or_alias::v3::Response> {
|
||||
let sender_user = body.sender_user.as_deref().expect("user is authenticated");
|
||||
let body = body.body;
|
||||
|
||||
let (servers, room_id) = match OwnedRoomId::try_from(body.room_id_or_alias) {
|
||||
Ok(room_id) => {
|
||||
banned_room_check(sender_user, Some(&room_id), room_id.server_name(), client_ip).await?;
|
||||
banned_room_check(sender_user, Some(&room_id), room_id.server_name(), client).await?;
|
||||
|
||||
let mut servers = body.server_name.clone();
|
||||
servers.extend(
|
||||
@@ -260,7 +261,7 @@ pub(crate) async fn join_room_by_id_or_alias_route(
|
||||
Err(room_alias) => {
|
||||
let response = get_alias_helper(room_alias.clone(), Some(body.server_name.clone())).await?;
|
||||
|
||||
banned_room_check(sender_user, Some(&response.room_id), Some(room_alias.server_name()), client_ip).await?;
|
||||
banned_room_check(sender_user, Some(&response.room_id), Some(room_alias.server_name()), client).await?;
|
||||
|
||||
let mut servers = body.server_name;
|
||||
servers.extend(response.servers);
|
||||
@@ -320,9 +321,9 @@ pub(crate) async fn leave_room_route(body: Ruma<leave_room::v3::Request>) -> Res
|
||||
/// # `POST /_matrix/client/r0/rooms/{roomId}/invite`
|
||||
///
|
||||
/// Tries to send an invite event into the room.
|
||||
#[tracing::instrument(skip_all, fields(%client_ip))]
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "invite")]
|
||||
pub(crate) async fn invite_user_route(
|
||||
InsecureClientIp(client_ip): InsecureClientIp, body: Ruma<invite_user::v3::Request>,
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<invite_user::v3::Request>,
|
||||
) -> Result<invite_user::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
@@ -337,7 +338,7 @@ pub(crate) async fn invite_user_route(
|
||||
));
|
||||
}
|
||||
|
||||
banned_room_check(sender_user, Some(&body.room_id), body.room_id.server_name(), client_ip).await?;
|
||||
banned_room_check(sender_user, Some(&body.room_id), body.room_id.server_name(), client).await?;
|
||||
|
||||
if let invite_user::v3::InvitationRecipient::UserId {
|
||||
user_id,
|
||||
@@ -373,16 +374,11 @@ pub(crate) async fn kick_user_route(body: Ruma<kick_user::v3::Request>) -> Resul
|
||||
event.membership = MembershipState::Leave;
|
||||
event.reason.clone_from(&body.reason);
|
||||
|
||||
let mutex_state = Arc::clone(
|
||||
services()
|
||||
.globals
|
||||
.roomid_mutex_state
|
||||
.write()
|
||||
.await
|
||||
.entry(body.room_id.clone())
|
||||
.or_default(),
|
||||
);
|
||||
let state_lock = mutex_state.lock().await;
|
||||
let state_lock = services()
|
||||
.globals
|
||||
.roomid_mutex_state
|
||||
.lock(&body.room_id)
|
||||
.await;
|
||||
|
||||
services()
|
||||
.rooms
|
||||
@@ -442,16 +438,11 @@ pub(crate) async fn ban_user_route(body: Ruma<ban_user::v3::Request>) -> Result<
|
||||
},
|
||||
)?;
|
||||
|
||||
let mutex_state = Arc::clone(
|
||||
services()
|
||||
.globals
|
||||
.roomid_mutex_state
|
||||
.write()
|
||||
.await
|
||||
.entry(body.room_id.clone())
|
||||
.or_default(),
|
||||
);
|
||||
let state_lock = mutex_state.lock().await;
|
||||
let state_lock = services()
|
||||
.globals
|
||||
.roomid_mutex_state
|
||||
.lock(&body.room_id)
|
||||
.await;
|
||||
|
||||
services()
|
||||
.rooms
|
||||
@@ -496,16 +487,11 @@ pub(crate) async fn unban_user_route(body: Ruma<unban_user::v3::Request>) -> Res
|
||||
event.reason.clone_from(&body.reason);
|
||||
event.join_authorized_via_users_server = None;
|
||||
|
||||
let mutex_state = Arc::clone(
|
||||
services()
|
||||
.globals
|
||||
.roomid_mutex_state
|
||||
.write()
|
||||
.await
|
||||
.entry(body.room_id.clone())
|
||||
.or_default(),
|
||||
);
|
||||
let state_lock = mutex_state.lock().await;
|
||||
let state_lock = services()
|
||||
.globals
|
||||
.roomid_mutex_state
|
||||
.lock(&body.room_id)
|
||||
.await;
|
||||
|
||||
services()
|
||||
.rooms
|
||||
@@ -670,16 +656,7 @@ pub async fn join_room_by_id_helper(
|
||||
});
|
||||
}
|
||||
|
||||
let mutex_state = Arc::clone(
|
||||
services()
|
||||
.globals
|
||||
.roomid_mutex_state
|
||||
.write()
|
||||
.await
|
||||
.entry(room_id.to_owned())
|
||||
.or_default(),
|
||||
);
|
||||
let state_lock = mutex_state.lock().await;
|
||||
let state_lock = services().globals.roomid_mutex_state.lock(room_id).await;
|
||||
|
||||
// Ask a remote server if we are not participating in this room
|
||||
if !services()
|
||||
@@ -695,7 +672,7 @@ pub async fn join_room_by_id_helper(
|
||||
|
||||
async fn join_room_by_id_helper_remote(
|
||||
sender_user: &UserId, room_id: &RoomId, reason: Option<String>, servers: &[OwnedServerName],
|
||||
_third_party_signed: Option<&ThirdPartySigned>, state_lock: MutexGuard<'_, ()>,
|
||||
_third_party_signed: Option<&ThirdPartySigned>, state_lock: mutex_map::Guard<()>,
|
||||
) -> Result<join_room_by_id::v3::Response> {
|
||||
info!("Joining {room_id} over federation.");
|
||||
|
||||
@@ -1030,7 +1007,7 @@ async fn join_room_by_id_helper_remote(
|
||||
|
||||
async fn join_room_by_id_helper_local(
|
||||
sender_user: &UserId, room_id: &RoomId, reason: Option<String>, servers: &[OwnedServerName],
|
||||
_third_party_signed: Option<&ThirdPartySigned>, state_lock: MutexGuard<'_, ()>,
|
||||
_third_party_signed: Option<&ThirdPartySigned>, state_lock: mutex_map::Guard<()>,
|
||||
) -> Result<join_room_by_id::v3::Response> {
|
||||
info!("We can join locally");
|
||||
|
||||
@@ -1413,17 +1390,7 @@ pub(crate) async fn invite_helper(
|
||||
|
||||
if !user_is_local(user_id) {
|
||||
let (pdu, pdu_json, invite_room_state) = {
|
||||
let mutex_state = Arc::clone(
|
||||
services()
|
||||
.globals
|
||||
.roomid_mutex_state
|
||||
.write()
|
||||
.await
|
||||
.entry(room_id.to_owned())
|
||||
.or_default(),
|
||||
);
|
||||
let state_lock = mutex_state.lock().await;
|
||||
|
||||
let state_lock = services().globals.roomid_mutex_state.lock(room_id).await;
|
||||
let content = to_raw_value(&RoomMemberEventContent {
|
||||
avatar_url: services().users.avatar_url(user_id)?,
|
||||
displayname: None,
|
||||
@@ -1535,16 +1502,7 @@ pub(crate) async fn invite_helper(
|
||||
));
|
||||
}
|
||||
|
||||
let mutex_state = Arc::clone(
|
||||
services()
|
||||
.globals
|
||||
.roomid_mutex_state
|
||||
.write()
|
||||
.await
|
||||
.entry(room_id.to_owned())
|
||||
.or_default(),
|
||||
);
|
||||
let state_lock = mutex_state.lock().await;
|
||||
let state_lock = services().globals.roomid_mutex_state.lock(room_id).await;
|
||||
|
||||
services()
|
||||
.rooms
|
||||
@@ -1638,16 +1596,7 @@ pub async fn leave_room(user_id: &UserId, room_id: &RoomId, reason: Option<Strin
|
||||
true,
|
||||
)?;
|
||||
} else {
|
||||
let mutex_state = Arc::clone(
|
||||
services()
|
||||
.globals
|
||||
.roomid_mutex_state
|
||||
.write()
|
||||
.await
|
||||
.entry(room_id.to_owned())
|
||||
.or_default(),
|
||||
);
|
||||
let state_lock = mutex_state.lock().await;
|
||||
let state_lock = services().globals.roomid_mutex_state.lock(room_id).await;
|
||||
|
||||
let member_event =
|
||||
services()
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
use std::{
|
||||
collections::{BTreeMap, HashSet},
|
||||
sync::Arc,
|
||||
};
|
||||
use std::collections::{BTreeMap, HashSet};
|
||||
|
||||
use conduit::PduCount;
|
||||
use ruma::{
|
||||
@@ -32,16 +29,11 @@ pub(crate) async fn send_message_event_route(
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let sender_device = body.sender_device.as_deref();
|
||||
|
||||
let mutex_state = Arc::clone(
|
||||
services()
|
||||
.globals
|
||||
.roomid_mutex_state
|
||||
.write()
|
||||
.await
|
||||
.entry(body.room_id.clone())
|
||||
.or_default(),
|
||||
);
|
||||
let state_lock = mutex_state.lock().await;
|
||||
let state_lock = services()
|
||||
.globals
|
||||
.roomid_mutex_state
|
||||
.lock(&body.room_id)
|
||||
.await;
|
||||
|
||||
// Forbid m.room.encrypted if encryption is disabled
|
||||
if MessageLikeEventType::RoomEncrypted == body.event_type && !services().globals.allow_encryption() {
|
||||
@@ -115,8 +107,7 @@ pub(crate) async fn send_message_event_route(
|
||||
/// Allows paginating through room history.
|
||||
///
|
||||
/// - Only works if the user is joined (TODO: always allow, but only show events
|
||||
/// where the user was
|
||||
/// joined, depending on `history_visibility`)
|
||||
/// where the user was joined, depending on `history_visibility`)
|
||||
pub(crate) async fn get_message_events_route(
|
||||
body: Ruma<get_message_events::v3::Request>,
|
||||
) -> Result<get_message_events::v3::Response> {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use ruma::{
|
||||
api::{
|
||||
client::{
|
||||
@@ -52,8 +50,8 @@ pub(crate) async fn set_displayname_route(
|
||||
///
|
||||
/// Returns the displayname of the user.
|
||||
///
|
||||
/// - If user is on another server and we do not have a local copy already
|
||||
/// fetch displayname over federation
|
||||
/// - If user is on another server and we do not have a local copy already fetch
|
||||
/// displayname over federation
|
||||
pub(crate) async fn get_displayname_route(
|
||||
body: Ruma<get_display_name::v3::Request>,
|
||||
) -> Result<get_display_name::v3::Response> {
|
||||
@@ -142,8 +140,8 @@ pub(crate) async fn set_avatar_url_route(
|
||||
///
|
||||
/// Returns the `avatar_url` and `blurhash` of the user.
|
||||
///
|
||||
/// - If user is on another server and we do not have a local copy already
|
||||
/// fetch `avatar_url` and blurhash over federation
|
||||
/// - If user is on another server and we do not have a local copy already fetch
|
||||
/// `avatar_url` and blurhash over federation
|
||||
pub(crate) async fn get_avatar_url_route(
|
||||
body: Ruma<get_avatar_url::v3::Request>,
|
||||
) -> Result<get_avatar_url::v3::Response> {
|
||||
@@ -201,7 +199,7 @@ pub(crate) async fn get_avatar_url_route(
|
||||
/// Returns the displayname, avatar_url and blurhash of the user.
|
||||
///
|
||||
/// - If user is on another server and we do not have a local copy already,
|
||||
/// fetch profile over federation.
|
||||
/// fetch profile over federation.
|
||||
pub(crate) async fn get_profile_route(body: Ruma<get_profile::v3::Request>) -> Result<get_profile::v3::Response> {
|
||||
if !user_is_local(&body.user_id) {
|
||||
// Create and update our local copy of the user
|
||||
@@ -355,17 +353,7 @@ pub async fn update_avatar_url(
|
||||
|
||||
pub async fn update_all_rooms(all_joined_rooms: Vec<(PduBuilder, &OwnedRoomId)>, user_id: OwnedUserId) {
|
||||
for (pdu_builder, room_id) in all_joined_rooms {
|
||||
let mutex_state = Arc::clone(
|
||||
services()
|
||||
.globals
|
||||
.roomid_mutex_state
|
||||
.write()
|
||||
.await
|
||||
.entry(room_id.clone())
|
||||
.or_default(),
|
||||
);
|
||||
let state_lock = mutex_state.lock().await;
|
||||
|
||||
let state_lock = services().globals.roomid_mutex_state.lock(room_id).await;
|
||||
if let Err(e) = services()
|
||||
.rooms
|
||||
.timeline
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use ruma::{
|
||||
api::client::redact::redact_event,
|
||||
events::{room::redaction::RoomRedactionEventContent, TimelineEventType},
|
||||
@@ -17,16 +15,11 @@ pub(crate) async fn redact_event_route(body: Ruma<redact_event::v3::Request>) ->
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let body = body.body;
|
||||
|
||||
let mutex_state = Arc::clone(
|
||||
services()
|
||||
.globals
|
||||
.roomid_mutex_state
|
||||
.write()
|
||||
.await
|
||||
.entry(body.room_id.clone())
|
||||
.or_default(),
|
||||
);
|
||||
let state_lock = mutex_state.lock().await;
|
||||
let state_lock = services()
|
||||
.globals
|
||||
.roomid_mutex_state
|
||||
.lock(&body.room_id)
|
||||
.await;
|
||||
|
||||
let event_id = services()
|
||||
.rooms
|
||||
|
||||
+15
-34
@@ -1,4 +1,4 @@
|
||||
use std::{cmp::max, collections::BTreeMap, sync::Arc};
|
||||
use std::{cmp::max, collections::BTreeMap};
|
||||
|
||||
use conduit::{debug_info, debug_warn};
|
||||
use ruma::{
|
||||
@@ -89,18 +89,8 @@ pub(crate) async fn create_room_route(body: Ruma<create_room::v3::Request>) -> R
|
||||
));
|
||||
}
|
||||
|
||||
services().rooms.short.get_or_create_shortroomid(&room_id)?;
|
||||
|
||||
let mutex_state = Arc::clone(
|
||||
services()
|
||||
.globals
|
||||
.roomid_mutex_state
|
||||
.write()
|
||||
.await
|
||||
.entry(room_id.clone())
|
||||
.or_default(),
|
||||
);
|
||||
let state_lock = mutex_state.lock().await;
|
||||
let _short_id = services().rooms.short.get_or_create_shortroomid(&room_id)?;
|
||||
let state_lock = services().globals.roomid_mutex_state.lock(&room_id).await;
|
||||
|
||||
let alias: Option<OwnedRoomAliasId> = if let Some(alias) = &body.room_alias_name {
|
||||
Some(room_alias_check(alias, &body.appservice_info).await?)
|
||||
@@ -577,21 +567,17 @@ pub(crate) async fn upgrade_room_route(body: Ruma<upgrade_room::v3::Request>) ->
|
||||
|
||||
// Create a replacement room
|
||||
let replacement_room = RoomId::new(services().globals.server_name());
|
||||
services()
|
||||
|
||||
let _short_id = services()
|
||||
.rooms
|
||||
.short
|
||||
.get_or_create_shortroomid(&replacement_room)?;
|
||||
|
||||
let mutex_state = Arc::clone(
|
||||
services()
|
||||
.globals
|
||||
.roomid_mutex_state
|
||||
.write()
|
||||
.await
|
||||
.entry(body.room_id.clone())
|
||||
.or_default(),
|
||||
);
|
||||
let state_lock = mutex_state.lock().await;
|
||||
let state_lock = services()
|
||||
.globals
|
||||
.roomid_mutex_state
|
||||
.lock(&body.room_id)
|
||||
.await;
|
||||
|
||||
// Send a m.room.tombstone event to the old room to indicate that it is not
|
||||
// intended to be used any further Fail if the sender does not have the required
|
||||
@@ -619,16 +605,11 @@ pub(crate) async fn upgrade_room_route(body: Ruma<upgrade_room::v3::Request>) ->
|
||||
|
||||
// Change lock to replacement room
|
||||
drop(state_lock);
|
||||
let mutex_state = Arc::clone(
|
||||
services()
|
||||
.globals
|
||||
.roomid_mutex_state
|
||||
.write()
|
||||
.await
|
||||
.entry(replacement_room.clone())
|
||||
.or_default(),
|
||||
);
|
||||
let state_lock = mutex_state.lock().await;
|
||||
let state_lock = services()
|
||||
.globals
|
||||
.roomid_mutex_state
|
||||
.lock(&replacement_room)
|
||||
.await;
|
||||
|
||||
// Get the old room creation event
|
||||
let mut create_event_content = serde_json::from_str::<CanonicalJsonObject>(
|
||||
|
||||
+1
-12
@@ -172,18 +172,7 @@ async fn send_state_event_for_key_helper(
|
||||
sender: &UserId, room_id: &RoomId, event_type: &StateEventType, json: &Raw<AnyStateEventContent>, state_key: String,
|
||||
) -> Result<Arc<EventId>> {
|
||||
allowed_to_send_state_event(room_id, event_type, json).await?;
|
||||
|
||||
let mutex_state = Arc::clone(
|
||||
services()
|
||||
.globals
|
||||
.roomid_mutex_state
|
||||
.write()
|
||||
.await
|
||||
.entry(room_id.to_owned())
|
||||
.or_default(),
|
||||
);
|
||||
let state_lock = mutex_state.lock().await;
|
||||
|
||||
let state_lock = services().globals.roomid_mutex_state.lock(room_id).await;
|
||||
let event_id = services()
|
||||
.rooms
|
||||
.timeline
|
||||
|
||||
+14
-51
@@ -1,7 +1,6 @@
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
collections::{hash_map::Entry, BTreeMap, BTreeSet, HashMap, HashSet},
|
||||
sync::Arc,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
@@ -37,8 +36,7 @@ use crate::{service::pdu::EventHash, services, utils, Error, PduEvent, Result, R
|
||||
/// Synchronize the client's state with the latest state on the server.
|
||||
///
|
||||
/// - This endpoint takes a `since` parameter which should be the `next_batch`
|
||||
/// value from a
|
||||
/// previous request for incremental syncs.
|
||||
/// value from a previous request for incremental syncs.
|
||||
///
|
||||
/// Calling this endpoint without a `since` parameter returns:
|
||||
/// - Some of the most recent events of each timeline
|
||||
@@ -50,11 +48,9 @@ use crate::{service::pdu::EventHash, services, utils, Error, PduEvent, Result, R
|
||||
/// returns: For joined rooms:
|
||||
/// - Some of the most recent events of each timeline that happened after since
|
||||
/// - If user joined the room after since: All state events (unless lazy loading
|
||||
/// is activated) and
|
||||
/// all device list updates in that room
|
||||
/// is activated) and all device list updates in that room
|
||||
/// - If the user was already in the room: A list of all events that are in the
|
||||
/// state now, but were
|
||||
/// not in the state at `since`
|
||||
/// state now, but were not in the state at `since`
|
||||
/// - If the state we send contains a member event: Joined and invited member
|
||||
/// counts, heroes
|
||||
/// - Device list updates that happened after `since`
|
||||
@@ -197,20 +193,9 @@ pub(crate) async fn sync_events_route(
|
||||
for result in all_invited_rooms {
|
||||
let (room_id, invite_state_events) = result?;
|
||||
|
||||
{
|
||||
// Get and drop the lock to wait for remaining operations to finish
|
||||
let mutex_insert = Arc::clone(
|
||||
services()
|
||||
.globals
|
||||
.roomid_mutex_insert
|
||||
.write()
|
||||
.await
|
||||
.entry(room_id.clone())
|
||||
.or_default(),
|
||||
);
|
||||
let insert_lock = mutex_insert.lock().await;
|
||||
drop(insert_lock);
|
||||
};
|
||||
// Get and drop the lock to wait for remaining operations to finish
|
||||
let insert_lock = services().globals.roomid_mutex_insert.lock(&room_id).await;
|
||||
drop(insert_lock);
|
||||
|
||||
let invite_count = services()
|
||||
.rooms
|
||||
@@ -327,25 +312,14 @@ pub(crate) async fn sync_events_route(
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all, fields(user_id = %sender_user, room_id = %room_id))]
|
||||
#[tracing::instrument(skip_all, fields(user_id = %sender_user, room_id = %room_id), name = "left_room")]
|
||||
async fn handle_left_room(
|
||||
since: u64, room_id: &RoomId, sender_user: &UserId, left_rooms: &mut BTreeMap<ruma::OwnedRoomId, LeftRoom>,
|
||||
next_batch_string: &str, full_state: bool, lazy_load_enabled: bool,
|
||||
) -> Result<()> {
|
||||
{
|
||||
// Get and drop the lock to wait for remaining operations to finish
|
||||
let mutex_insert = Arc::clone(
|
||||
services()
|
||||
.globals
|
||||
.roomid_mutex_insert
|
||||
.write()
|
||||
.await
|
||||
.entry(room_id.to_owned())
|
||||
.or_default(),
|
||||
);
|
||||
let insert_lock = mutex_insert.lock().await;
|
||||
drop(insert_lock);
|
||||
};
|
||||
// Get and drop the lock to wait for remaining operations to finish
|
||||
let insert_lock = services().globals.roomid_mutex_insert.lock(room_id).await;
|
||||
drop(insert_lock);
|
||||
|
||||
let left_count = services()
|
||||
.rooms
|
||||
@@ -544,21 +518,10 @@ async fn load_joined_room(
|
||||
next_batch: u64, next_batchcount: PduCount, lazy_load_enabled: bool, lazy_load_send_redundant: bool,
|
||||
full_state: bool, device_list_updates: &mut HashSet<OwnedUserId>, left_encrypted_users: &mut HashSet<OwnedUserId>,
|
||||
) -> Result<JoinedRoom> {
|
||||
{
|
||||
// Get and drop the lock to wait for remaining operations to finish
|
||||
// This will make sure the we have all events until next_batch
|
||||
let mutex_insert = Arc::clone(
|
||||
services()
|
||||
.globals
|
||||
.roomid_mutex_insert
|
||||
.write()
|
||||
.await
|
||||
.entry(room_id.to_owned())
|
||||
.or_default(),
|
||||
);
|
||||
let insert_lock = mutex_insert.lock().await;
|
||||
drop(insert_lock);
|
||||
};
|
||||
// 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().globals.roomid_mutex_insert.lock(room_id).await;
|
||||
drop(insert_lock);
|
||||
|
||||
let (timeline_pdus, limited) = load_timeline(sender_user, room_id, sincecount, 10)?;
|
||||
|
||||
|
||||
@@ -13,8 +13,7 @@ use crate::{services, Result, Ruma};
|
||||
/// Searches all known users for a match.
|
||||
///
|
||||
/// - Hides any local users that aren't in any public rooms (i.e. those that
|
||||
/// have the join rule set to public)
|
||||
/// and don't share a room with the sender
|
||||
/// have the join rule set to public) and don't share a room with the sender
|
||||
pub(crate) async fn search_users_route(body: Ruma<search_users::v3::Request>) -> Result<search_users::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let limit = usize::try_from(body.limit).unwrap_or(10); // default limit is 10
|
||||
|
||||
+4
-3
@@ -1,14 +1,15 @@
|
||||
pub mod client;
|
||||
pub mod router;
|
||||
mod ruma_wrapper;
|
||||
mod router;
|
||||
pub mod routes;
|
||||
pub mod server;
|
||||
|
||||
extern crate conduit_core as conduit;
|
||||
extern crate conduit_service as service;
|
||||
|
||||
pub(crate) use conduit::{debug_info, debug_warn, utils, Error, Result};
|
||||
pub(crate) use ruma_wrapper::{Ruma, RumaResponse};
|
||||
pub(crate) use service::{pdu::PduEvent, services, user_is_local};
|
||||
|
||||
pub(crate) use crate::router::{Ruma, RumaResponse};
|
||||
|
||||
conduit::mod_ctor! {}
|
||||
conduit::mod_dtor! {}
|
||||
|
||||
@@ -7,7 +7,7 @@ use conduit::{Error, Server};
|
||||
use http::Uri;
|
||||
use ruma::api::client::error::ErrorKind;
|
||||
|
||||
use crate::{client, ruma_wrapper::RouterExt, server};
|
||||
use crate::{client, router::RouterExt, server};
|
||||
|
||||
pub fn build(router: Router, server: &Server) -> Router {
|
||||
let config = &server.config;
|
||||
@@ -17,9 +17,9 @@ use crate::{
|
||||
/// # `PUT /_matrix/federation/v2/invite/{roomId}/{eventId}`
|
||||
///
|
||||
/// Invites a remote user to a room.
|
||||
#[tracing::instrument(skip_all, fields(%client_ip))]
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "invite")]
|
||||
pub(crate) async fn create_invite_route(
|
||||
InsecureClientIp(client_ip): InsecureClientIp, body: Ruma<create_invite::v2::Request>,
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<create_invite::v2::Request>,
|
||||
) -> Result<create_invite::v2::Response> {
|
||||
let origin = body.origin.as_ref().expect("server is authenticated");
|
||||
|
||||
|
||||
@@ -20,8 +20,7 @@ use crate::{services, Result};
|
||||
/// Gets the public signing keys of this server.
|
||||
///
|
||||
/// - Matrix does not support invalidating public keys, so the key returned by
|
||||
/// this will be valid
|
||||
/// forever.
|
||||
/// this will be valid forever.
|
||||
// Response type for this endpoint is Json because we need to calculate a
|
||||
// signature for the response
|
||||
pub(crate) async fn get_server_keys_route() -> Result<impl IntoResponse> {
|
||||
@@ -71,6 +70,5 @@ pub(crate) async fn get_server_keys_route() -> Result<impl IntoResponse> {
|
||||
/// Gets the public signing keys of this server.
|
||||
///
|
||||
/// - Matrix does not support invalidating public keys, so the key returned by
|
||||
/// this will be valid
|
||||
/// forever.
|
||||
/// this will be valid forever.
|
||||
pub(crate) async fn get_server_keys_deprecated_route() -> impl IntoResponse { get_server_keys_route().await }
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use ruma::{
|
||||
api::{client::error::ErrorKind, federation::membership::prepare_join_event},
|
||||
events::{
|
||||
@@ -74,17 +72,11 @@ pub(crate) async fn create_join_event_template_route(
|
||||
}
|
||||
}
|
||||
|
||||
let mutex_state = Arc::clone(
|
||||
services()
|
||||
.globals
|
||||
.roomid_mutex_state
|
||||
.write()
|
||||
.await
|
||||
.entry(body.room_id.clone())
|
||||
.or_default(),
|
||||
);
|
||||
let state_lock = mutex_state.lock().await;
|
||||
|
||||
let state_lock = services()
|
||||
.globals
|
||||
.roomid_mutex_state
|
||||
.lock(&body.room_id)
|
||||
.await;
|
||||
let join_rules_event =
|
||||
services()
|
||||
.rooms
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use ruma::{
|
||||
api::{client::error::ErrorKind, federation::membership::prepare_leave_event},
|
||||
events::{
|
||||
@@ -37,18 +35,11 @@ pub(crate) async fn create_leave_event_template_route(
|
||||
.acl_check(origin, &body.room_id)?;
|
||||
|
||||
let room_version_id = services().rooms.state.get_room_version(&body.room_id)?;
|
||||
|
||||
let mutex_state = Arc::clone(
|
||||
services()
|
||||
.globals
|
||||
.roomid_mutex_state
|
||||
.write()
|
||||
.await
|
||||
.entry(body.room_id.clone())
|
||||
.or_default(),
|
||||
);
|
||||
let state_lock = mutex_state.lock().await;
|
||||
|
||||
let state_lock = services()
|
||||
.globals
|
||||
.roomid_mutex_state
|
||||
.lock(&body.room_id)
|
||||
.await;
|
||||
let content = to_raw_value(&RoomMemberEventContent {
|
||||
avatar_url: None,
|
||||
blurhash: None,
|
||||
|
||||
@@ -12,9 +12,9 @@ use crate::{services, Error, Result, Ruma};
|
||||
/// # `POST /_matrix/federation/v1/publicRooms`
|
||||
///
|
||||
/// Lists the public rooms on this server.
|
||||
#[tracing::instrument(skip_all, fields(%client_ip))]
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "publicrooms")]
|
||||
pub(crate) async fn get_public_rooms_filtered_route(
|
||||
InsecureClientIp(client_ip): InsecureClientIp, body: Ruma<get_public_rooms_filtered::v1::Request>,
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_public_rooms_filtered::v1::Request>,
|
||||
) -> Result<get_public_rooms_filtered::v1::Response> {
|
||||
if !services()
|
||||
.globals
|
||||
@@ -44,9 +44,9 @@ pub(crate) async fn get_public_rooms_filtered_route(
|
||||
/// # `GET /_matrix/federation/v1/publicRooms`
|
||||
///
|
||||
/// Lists the public rooms on this server.
|
||||
#[tracing::instrument(skip_all, fields(%client_ip))]
|
||||
#[tracing::instrument(skip_all, fields(%client), "publicrooms")]
|
||||
pub(crate) async fn get_public_rooms_route(
|
||||
InsecureClientIp(client_ip): InsecureClientIp, body: Ruma<get_public_rooms::v1::Request>,
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_public_rooms::v1::Request>,
|
||||
) -> Result<get_public_rooms::v1::Response> {
|
||||
if !services()
|
||||
.globals
|
||||
|
||||
+10
-15
@@ -1,4 +1,4 @@
|
||||
use std::{collections::BTreeMap, sync::Arc, time::Instant};
|
||||
use std::{collections::BTreeMap, time::Instant};
|
||||
|
||||
use axum_client_ip::InsecureClientIp;
|
||||
use conduit::debug_warn;
|
||||
@@ -26,9 +26,9 @@ use crate::{
|
||||
/// # `PUT /_matrix/federation/v1/send/{txnId}`
|
||||
///
|
||||
/// Push EDUs and PDUs to this server.
|
||||
#[tracing::instrument(skip_all, fields(%client_ip))]
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "send")]
|
||||
pub(crate) async fn send_transaction_message_route(
|
||||
InsecureClientIp(client_ip): InsecureClientIp, body: Ruma<send_transaction_message::v1::Request>,
|
||||
InsecureClientIp(client): InsecureClientIp, body: Ruma<send_transaction_message::v1::Request>,
|
||||
) -> Result<send_transaction_message::v1::Response> {
|
||||
let origin = body.origin.as_ref().expect("server is authenticated");
|
||||
|
||||
@@ -107,16 +107,11 @@ pub(crate) async fn send_transaction_message_route(
|
||||
let mut resolved_map = BTreeMap::new();
|
||||
for (event_id, value, room_id) in parsed_pdus {
|
||||
let pdu_start_time = Instant::now();
|
||||
let mutex = Arc::clone(
|
||||
services()
|
||||
.globals
|
||||
.roomid_mutex_federation
|
||||
.write()
|
||||
.await
|
||||
.entry(room_id.clone())
|
||||
.or_default(),
|
||||
);
|
||||
let mutex_lock = mutex.lock().await;
|
||||
let mutex_lock = services()
|
||||
.globals
|
||||
.roomid_mutex_federation
|
||||
.lock(&room_id)
|
||||
.await;
|
||||
resolved_map.insert(
|
||||
event_id.clone(),
|
||||
services()
|
||||
@@ -357,8 +352,8 @@ pub(crate) async fn send_transaction_message_route(
|
||||
.add_cross_signing_keys(&user_id, &master_key, &self_signing_key, &None, true)?;
|
||||
}
|
||||
},
|
||||
Edu::_Custom(custom) => {
|
||||
debug_warn!(?custom, "received custom/unknown EDU");
|
||||
Edu::_Custom(ref _custom) => {
|
||||
debug_warn!(?edu, "received custom/unknown EDU");
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#![allow(deprecated)]
|
||||
|
||||
use std::{collections::BTreeMap, sync::Arc};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use ruma::{
|
||||
api::{client::error::ErrorKind, federation::membership::create_join_event},
|
||||
@@ -148,16 +148,11 @@ async fn create_join_event(
|
||||
.fetch_required_signing_keys([&value], &pub_key_map)
|
||||
.await?;
|
||||
|
||||
let mutex = Arc::clone(
|
||||
services()
|
||||
.globals
|
||||
.roomid_mutex_federation
|
||||
.write()
|
||||
.await
|
||||
.entry(room_id.to_owned())
|
||||
.or_default(),
|
||||
);
|
||||
let mutex_lock = mutex.lock().await;
|
||||
let mutex_lock = services()
|
||||
.globals
|
||||
.roomid_mutex_federation
|
||||
.lock(room_id)
|
||||
.await;
|
||||
let pdu_id: Vec<u8> = services()
|
||||
.rooms
|
||||
.event_handler
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#![allow(deprecated)]
|
||||
|
||||
use std::{collections::BTreeMap, sync::Arc};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use ruma::{
|
||||
api::{client::error::ErrorKind, federation::membership::create_leave_event},
|
||||
@@ -154,16 +154,11 @@ async fn create_leave_event(origin: &ServerName, room_id: &RoomId, pdu: &RawJson
|
||||
.fetch_required_signing_keys([&value], &pub_key_map)
|
||||
.await?;
|
||||
|
||||
let mutex = Arc::clone(
|
||||
services()
|
||||
.globals
|
||||
.roomid_mutex_federation
|
||||
.write()
|
||||
.await
|
||||
.entry(room_id.to_owned())
|
||||
.or_default(),
|
||||
);
|
||||
let mutex_lock = mutex.lock().await;
|
||||
let mutex_lock = services()
|
||||
.globals
|
||||
.roomid_mutex_federation
|
||||
.lock(room_id)
|
||||
.await;
|
||||
let pdu_id: Vec<u8> = services()
|
||||
.rooms
|
||||
.event_handler
|
||||
|
||||
@@ -97,6 +97,7 @@ tikv-jemalloc-ctl.workspace = true
|
||||
tikv-jemalloc-sys.optional = true
|
||||
tikv-jemalloc-sys.workspace = true
|
||||
tokio.workspace = true
|
||||
tracing-core.workspace = true
|
||||
tracing-subscriber.workspace = true
|
||||
tracing.workspace = true
|
||||
url.workspace = true
|
||||
|
||||
@@ -7,10 +7,12 @@ pub mod je;
|
||||
pub use je::{memory_stats, memory_usage};
|
||||
|
||||
// hardened_malloc
|
||||
#[cfg(all(not(target_env = "msvc"), feature = "hardened_malloc", target_os = "linux", not(feature = "jemalloc")))]
|
||||
pub mod hardened;
|
||||
#[cfg(all(not(target_env = "msvc"), feature = "hardened_malloc", target_os = "linux", not(feature = "jemalloc")))]
|
||||
pub use hardened::{memory_stats, memory_usage};
|
||||
//#[cfg(all(not(target_env = "msvc"), feature = "hardened_malloc", target_os =
|
||||
//#[cfg(all(not(target_env "linux", not(feature = "jemalloc")))]
|
||||
//pub mod hardened;
|
||||
//#[cfg(all(not(target_env = "msvc"), feature = "hardened_malloc", target_os =
|
||||
//#[cfg(all(not(target_env "linux", not(feature = "jemalloc")))]
|
||||
//pub use hardened::{memory_stats, memory_usage};
|
||||
|
||||
// default, enabled when none or multiple of the above are enabled
|
||||
#[cfg(any(
|
||||
|
||||
@@ -338,6 +338,8 @@ pub struct Config {
|
||||
|
||||
#[serde(default)]
|
||||
pub block_non_admin_invites: bool,
|
||||
#[serde(default = "true_fn")]
|
||||
pub admin_escape_commands: bool,
|
||||
|
||||
#[serde(default)]
|
||||
pub sentry: bool,
|
||||
@@ -610,6 +612,7 @@ impl fmt::Display for Config {
|
||||
"Block non-admin room invites (local and remote, admins can still send and receive invites)",
|
||||
&self.block_non_admin_invites.to_string(),
|
||||
),
|
||||
("Enable admin escape commands", &self.admin_escape_commands.to_string()),
|
||||
("Allow outgoing federated typing", &self.allow_outgoing_typing.to_string()),
|
||||
("Allow incoming federated typing", &self.allow_incoming_typing.to_string()),
|
||||
(
|
||||
|
||||
@@ -47,8 +47,8 @@ impl ProxyConfig {
|
||||
url,
|
||||
} => Some(Proxy::all(url)?),
|
||||
Self::ByDomain(proxies) => Some(Proxy::custom(move |url| {
|
||||
proxies.iter().find_map(|proxy| proxy.for_url(url)).cloned() // first matching
|
||||
// proxy
|
||||
// first matching proxy
|
||||
proxies.iter().find_map(|proxy| proxy.for_url(url)).cloned()
|
||||
})),
|
||||
})
|
||||
}
|
||||
|
||||
+3
-2
@@ -1,6 +1,6 @@
|
||||
#![allow(dead_code)] // this is a developer's toolbox
|
||||
|
||||
use std::{panic, panic::PanicInfo};
|
||||
use std::panic;
|
||||
|
||||
/// Export all of the ancillary tools from here as well.
|
||||
pub use crate::utils::debug::*;
|
||||
@@ -59,7 +59,8 @@ pub fn set_panic_trap() {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn panic_handler(info: &PanicInfo<'_>, next: &dyn Fn(&PanicInfo<'_>)) {
|
||||
#[allow(deprecated_in_future)]
|
||||
fn panic_handler(info: &panic::PanicInfo<'_>, next: &dyn Fn(&panic::PanicInfo<'_>)) {
|
||||
trap();
|
||||
next(info);
|
||||
}
|
||||
|
||||
@@ -81,8 +81,14 @@ pub enum Error {
|
||||
#[error("{0} in {1}")]
|
||||
InconsistentRoomState(&'static str, ruma::OwnedRoomId),
|
||||
#[error("{0}")]
|
||||
TracingFilter(#[from] tracing_subscriber::filter::ParseError),
|
||||
#[error("{0}")]
|
||||
AdminCommand(&'static str),
|
||||
#[error("{0}")]
|
||||
Fmt(#[from] fmt::Error),
|
||||
#[error("{0}")]
|
||||
Mxid(#[from] ruma::IdParseError),
|
||||
#[error("{0}")]
|
||||
Err(String),
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
use tracing::Level;
|
||||
use tracing_core::{span::Current, Event};
|
||||
|
||||
use super::layer::Value;
|
||||
|
||||
pub struct Data<'a> {
|
||||
pub event: &'a Event<'a>,
|
||||
pub current: &'a Current,
|
||||
pub values: Option<&'a mut [Value]>,
|
||||
}
|
||||
|
||||
impl Data<'_> {
|
||||
#[must_use]
|
||||
pub fn level(&self) -> Level { *self.event.metadata().level() }
|
||||
|
||||
#[must_use]
|
||||
pub fn mod_name(&self) -> &str { self.event.metadata().module_path().unwrap_or_default() }
|
||||
|
||||
#[must_use]
|
||||
pub fn span_name(&self) -> &str { self.current.metadata().map_or("", |s| s.name()) }
|
||||
|
||||
#[must_use]
|
||||
pub fn message(&self) -> &str {
|
||||
self.values
|
||||
.as_ref()
|
||||
.expect("values are not composed for a filter")
|
||||
.iter()
|
||||
.find(|(k, _)| *k == "message")
|
||||
.map_or("", |(_, v)| v.as_str())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::Capture;
|
||||
|
||||
/// Capture instance scope guard.
|
||||
pub struct Guard {
|
||||
pub(super) capture: Arc<Capture>,
|
||||
}
|
||||
|
||||
impl Drop for Guard {
|
||||
fn drop(&mut self) { self.capture.stop(); }
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
use std::{fmt, sync::Arc};
|
||||
|
||||
use tracing::field::{Field, Visit};
|
||||
use tracing_core::{Event, Subscriber};
|
||||
use tracing_subscriber::{layer::Context, registry::LookupSpan};
|
||||
|
||||
use super::{Capture, Data, State};
|
||||
|
||||
pub type Value = (&'static str, String);
|
||||
|
||||
pub struct Layer {
|
||||
state: Arc<State>,
|
||||
}
|
||||
|
||||
struct Visitor {
|
||||
values: Vec<Value>,
|
||||
}
|
||||
|
||||
impl Layer {
|
||||
pub fn new(state: &Arc<State>) -> Self {
|
||||
Self {
|
||||
state: state.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Layer {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
formatter.debug_struct("capture::Layer").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> tracing_subscriber::Layer<S> for Layer
|
||||
where
|
||||
S: Subscriber + for<'a> LookupSpan<'a>,
|
||||
{
|
||||
fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) {
|
||||
self.state
|
||||
.active
|
||||
.read()
|
||||
.expect("shared lock")
|
||||
.iter()
|
||||
.filter(|capture| filter(capture, event, &ctx))
|
||||
.for_each(|capture| handle(capture, event, &ctx));
|
||||
}
|
||||
}
|
||||
|
||||
fn handle<S>(capture: &Capture, event: &Event<'_>, ctx: &Context<'_, S>)
|
||||
where
|
||||
S: Subscriber + for<'a> LookupSpan<'a>,
|
||||
{
|
||||
let mut visitor = Visitor {
|
||||
values: Vec::new(),
|
||||
};
|
||||
event.record(&mut visitor);
|
||||
|
||||
let mut closure = capture.closure.lock().expect("exclusive lock");
|
||||
closure(Data {
|
||||
event,
|
||||
current: &ctx.current_span(),
|
||||
values: Some(&mut visitor.values),
|
||||
});
|
||||
}
|
||||
|
||||
fn filter<S>(capture: &Capture, event: &Event<'_>, ctx: &Context<'_, S>) -> bool
|
||||
where
|
||||
S: Subscriber + for<'a> LookupSpan<'a>,
|
||||
{
|
||||
capture.filter.as_ref().map_or(true, |filter| {
|
||||
filter(Data {
|
||||
event,
|
||||
current: &ctx.current_span(),
|
||||
values: None,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
impl Visit for Visitor {
|
||||
fn record_debug(&mut self, f: &Field, v: &dyn fmt::Debug) { self.values.push((f.name(), format!("{v:?}"))); }
|
||||
|
||||
fn record_str(&mut self, f: &Field, v: &str) { self.values.push((f.name(), v.to_owned())); }
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
pub mod data;
|
||||
mod guard;
|
||||
pub mod layer;
|
||||
pub mod state;
|
||||
pub mod util;
|
||||
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
pub use data::Data;
|
||||
use guard::Guard;
|
||||
pub use layer::{Layer, Value};
|
||||
pub use state::State;
|
||||
pub use util::*;
|
||||
|
||||
pub type Filter = dyn Fn(Data<'_>) -> bool + Send + Sync + 'static;
|
||||
pub type Closure = dyn FnMut(Data<'_>) + Send + Sync + 'static;
|
||||
|
||||
/// Capture instance state.
|
||||
pub struct Capture {
|
||||
state: Arc<State>,
|
||||
filter: Option<Box<Filter>>,
|
||||
closure: Mutex<Box<Closure>>,
|
||||
}
|
||||
|
||||
impl Capture {
|
||||
/// Construct a new capture instance. Capture does not start until the Guard
|
||||
/// is in scope.
|
||||
#[must_use]
|
||||
pub fn new<F, C>(state: &Arc<State>, filter: Option<F>, closure: C) -> Arc<Self>
|
||||
where
|
||||
F: Fn(Data<'_>) -> bool + Send + Sync + 'static,
|
||||
C: FnMut(Data<'_>) + Send + Sync + 'static,
|
||||
{
|
||||
Arc::new(Self {
|
||||
state: state.clone(),
|
||||
filter: filter.map(|p| -> Box<Filter> { Box::new(p) }),
|
||||
closure: Mutex::new(Box::new(closure)),
|
||||
})
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn start(self: &Arc<Self>) -> Guard {
|
||||
self.state.add(self);
|
||||
Guard {
|
||||
capture: self.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stop(self: &Arc<Self>) { self.state.del(self); }
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use super::Capture;
|
||||
|
||||
/// Capture layer state.
|
||||
pub struct State {
|
||||
pub(super) active: RwLock<Vec<Arc<Capture>>>,
|
||||
}
|
||||
|
||||
impl Default for State {
|
||||
fn default() -> Self { Self::new() }
|
||||
}
|
||||
|
||||
impl State {
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
active: RwLock::new(Vec::new()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn add(&self, capture: &Arc<Capture>) {
|
||||
self.active
|
||||
.write()
|
||||
.expect("locked for writing")
|
||||
.push(capture.clone());
|
||||
}
|
||||
|
||||
pub(super) fn del(&self, capture: &Arc<Capture>) {
|
||||
let mut vec = self.active.write().expect("locked for writing");
|
||||
if let Some(pos) = vec.iter().position(|v| Arc::ptr_eq(v, capture)) {
|
||||
vec.swap_remove(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use super::{
|
||||
super::{fmt, Level},
|
||||
Closure, Data,
|
||||
};
|
||||
use crate::Result;
|
||||
|
||||
pub fn fmt_html<S>(out: Arc<Mutex<S>>) -> Box<Closure>
|
||||
where
|
||||
S: std::fmt::Write + Send + 'static,
|
||||
{
|
||||
fmt(fmt::html, out)
|
||||
}
|
||||
|
||||
pub fn fmt_markdown<S>(out: Arc<Mutex<S>>) -> Box<Closure>
|
||||
where
|
||||
S: std::fmt::Write + Send + 'static,
|
||||
{
|
||||
fmt(fmt::markdown, out)
|
||||
}
|
||||
|
||||
pub fn fmt<F, S>(fun: F, out: Arc<Mutex<S>>) -> Box<Closure>
|
||||
where
|
||||
F: Fn(&mut S, &Level, &str, &str) -> Result<()> + Send + Sync + Copy + 'static,
|
||||
S: std::fmt::Write + Send + 'static,
|
||||
{
|
||||
Box::new(move |data| call(fun, &mut *out.lock().expect("locked"), &data))
|
||||
}
|
||||
|
||||
fn call<F, S>(fun: F, out: &mut S, data: &Data<'_>)
|
||||
where
|
||||
F: Fn(&mut S, &Level, &str, &str) -> Result<()>,
|
||||
S: std::fmt::Write,
|
||||
{
|
||||
fun(out, &data.level(), data.span_name(), data.message()).expect("log line appended");
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
use super::Level;
|
||||
|
||||
/// @returns (Foreground, Background)
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn html(level: &Level) -> (&'static str, &'static str) {
|
||||
match *level {
|
||||
Level::TRACE => ("#000000", "#A0A0A0"),
|
||||
Level::DEBUG => ("#000000", "#FFFFFF"),
|
||||
Level::ERROR => ("#000000", "#FF0000"),
|
||||
Level::WARN => ("#000000", "#FFFF00"),
|
||||
Level::INFO => ("#FFFFFF", "#008E00"),
|
||||
}
|
||||
}
|
||||
|
||||
/// @returns (Foreground)
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn code_tag(level: &Level) -> &'static str {
|
||||
match *level {
|
||||
Level::TRACE => "#888888",
|
||||
Level::DEBUG => "#C8C8C8",
|
||||
Level::ERROR => "#FF0000",
|
||||
Level::WARN => "#FFFF00",
|
||||
Level::INFO => "#00FF00",
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user