mirror of
https://github.com/matrix-org/dendrite.git
synced 2024-12-26 17:36:20 +00:00
Merge branch 'main' of github.com:matrix-org/dendrite into s7evink/latest-event-updater
This commit is contained in:
commit
928f3a09d9
71
.github/workflows/dendrite.yml
vendored
71
.github/workflows/dendrite.yml
vendored
|
@ -28,10 +28,10 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
if: ${{ false }} # disable for now
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: "stable"
|
||||
cache: true
|
||||
|
@ -41,7 +41,7 @@ jobs:
|
|||
with:
|
||||
node-version: 14
|
||||
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
||||
|
@ -66,11 +66,11 @@ jobs:
|
|||
name: Linting
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install libolm
|
||||
run: sudo apt-get install libolm-dev libolm3
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: "stable"
|
||||
- name: golangci-lint
|
||||
|
@ -102,14 +102,14 @@ jobs:
|
|||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install libolm
|
||||
run: sudo apt-get install libolm-dev libolm3
|
||||
- name: Setup go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: "stable"
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
# manually set up caches, as they otherwise clash with different steps using setup-go with cache=true
|
||||
with:
|
||||
path: |
|
||||
|
@ -141,12 +141,12 @@ jobs:
|
|||
goos: ["linux"]
|
||||
goarch: ["amd64", "386"]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: "stable"
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
|
@ -174,12 +174,12 @@ jobs:
|
|||
goos: ["windows"]
|
||||
goarch: ["amd64"]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: "stable"
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
|
@ -235,11 +235,11 @@ jobs:
|
|||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install libolm
|
||||
run: sudo apt-get install libolm-dev libolm3
|
||||
- name: Setup go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: "stable"
|
||||
- name: Set up gotestfmt
|
||||
|
@ -247,7 +247,7 @@ jobs:
|
|||
with:
|
||||
# Optional: pass GITHUB_TOKEN to avoid rate limiting.
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
|
@ -262,10 +262,11 @@ jobs:
|
|||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_DB: dendrite
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v3
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
flags: unittests
|
||||
fail_ci_if_error: true
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
# run database upgrade tests
|
||||
upgrade_test:
|
||||
|
@ -274,12 +275,20 @@ jobs:
|
|||
needs: initial-tests-done
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: "stable"
|
||||
cache: true
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-upgrade-test-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-upgrade-test-
|
||||
- name: Docker version
|
||||
run: docker version
|
||||
- name: Build upgrade-tests
|
||||
|
@ -296,12 +305,20 @@ jobs:
|
|||
needs: initial-tests-done
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: "stable"
|
||||
cache: true
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-upgrade-direct-test-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-upgrade-direct-test-
|
||||
- name: Docker version
|
||||
run: docker version
|
||||
- name: Build upgrade-tests
|
||||
|
@ -340,8 +357,8 @@ jobs:
|
|||
SYTEST_BRANCH: ${{ github.head_ref }}
|
||||
CGO_ENABLED: ${{ matrix.cgo && 1 }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
|
@ -364,7 +381,7 @@ jobs:
|
|||
run: /src/are-we-synapse-yet.py /logs/results.tap -v
|
||||
continue-on-error: true # not fatal
|
||||
- name: Upload Sytest logs
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
if: ${{ always() }}
|
||||
with:
|
||||
name: Sytest Logs - ${{ job.status }} - (Dendrite, ${{ join(matrix.*, ', ') }})
|
||||
|
@ -404,8 +421,8 @@ jobs:
|
|||
run: |
|
||||
sudo apt-get update && sudo apt-get install -y libolm3 libolm-dev
|
||||
go install github.com/gotesttools/gotestfmt/v2/cmd/gotestfmt@latest
|
||||
- name: Run actions/checkout@v3 for dendrite
|
||||
uses: actions/checkout@v3
|
||||
- name: Run actions/checkout@v4 for dendrite
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: dendrite
|
||||
|
||||
|
|
30
.github/workflows/docker.yml
vendored
30
.github/workflows/docker.yml
vendored
|
@ -27,22 +27,22 @@ jobs:
|
|||
security-events: write # To upload Trivy sarif files
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Get release tag & build flags
|
||||
if: github.event_name == 'release' # Only for GitHub releases
|
||||
run: |
|
||||
echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ env.DOCKER_HUB_USER }}
|
||||
password: ${{ secrets.DOCKER_TOKEN }}
|
||||
- name: Login to GitHub Containers
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
|
@ -98,22 +98,22 @@ jobs:
|
|||
packages: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Get release tag & build flags
|
||||
if: github.event_name == 'release' # Only for GitHub releases
|
||||
run: |
|
||||
echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ env.DOCKER_HUB_USER }}
|
||||
password: ${{ secrets.DOCKER_TOKEN }}
|
||||
- name: Login to GitHub Containers
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
|
@ -159,22 +159,22 @@ jobs:
|
|||
packages: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Get release tag & build flags
|
||||
if: github.event_name == 'release' # Only for GitHub releases
|
||||
run: |
|
||||
echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ env.DOCKER_HUB_USER }}
|
||||
password: ${{ secrets.DOCKER_TOKEN }}
|
||||
- name: Login to GitHub Containers
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
|
|
2
.github/workflows/gh-pages.yml
vendored
2
.github/workflows/gh-pages.yml
vendored
|
@ -28,7 +28,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v2
|
||||
- name: Build with Jekyll
|
||||
|
|
2
.github/workflows/helm.yml
vendored
2
.github/workflows/helm.yml
vendored
|
@ -17,7 +17,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
|
6
.github/workflows/k8s.yml
vendored
6
.github/workflows/k8s.yml
vendored
|
@ -17,7 +17,7 @@ jobs:
|
|||
outputs:
|
||||
changed: ${{ steps.list-changed.outputs.changed }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: azure/setup-helm@v3
|
||||
|
@ -48,7 +48,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{ inputs.checkoutCommit }}
|
||||
|
@ -66,7 +66,7 @@ jobs:
|
|||
- name: Create k3d cluster
|
||||
uses: nolar/setup-k3d-k3s@v1
|
||||
with:
|
||||
version: v1.21
|
||||
version: v1.28
|
||||
- name: Remove node taints
|
||||
run: |
|
||||
kubectl taint --all=true nodes node.cloudprovider.kubernetes.io/uninitialized- || true
|
||||
|
|
66
.github/workflows/schedules.yaml
vendored
66
.github/workflows/schedules.yaml
vendored
|
@ -10,8 +10,26 @@ concurrency:
|
|||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
check_date: # https://stackoverflow.com/questions/63014786/how-to-schedule-a-github-actions-nightly-build-but-run-it-only-when-there-where
|
||||
runs-on: ubuntu-latest
|
||||
name: Check latest commit
|
||||
outputs:
|
||||
should_run: ${{ steps.should_run.outputs.should_run }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: print latest_commit
|
||||
run: echo ${{ github.sha }}
|
||||
|
||||
- id: should_run
|
||||
continue-on-error: true
|
||||
name: check latest commit is less than a day
|
||||
if: ${{ github.event_name == 'schedule' }}
|
||||
run: test -z $(git rev-list --after="24 hours" ${{ github.sha }}) && echo "::set-output name=should_run::false"
|
||||
|
||||
# run Sytest in different variations
|
||||
sytest:
|
||||
needs: check_date
|
||||
if: ${{ needs.check_date.outputs.should_run != 'false' }}
|
||||
timeout-minutes: 60
|
||||
name: "Sytest (${{ matrix.label }})"
|
||||
runs-on: ubuntu-latest
|
||||
|
@ -38,8 +56,8 @@ jobs:
|
|||
RACE_DETECTION: 1
|
||||
COVER: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
|
@ -62,7 +80,7 @@ jobs:
|
|||
run: /src/are-we-synapse-yet.py /logs/results.tap -v
|
||||
continue-on-error: true # not fatal
|
||||
- name: Upload Sytest logs
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
if: ${{ always() }}
|
||||
with:
|
||||
name: Sytest Logs - ${{ job.status }} - (Dendrite ${{ join(matrix.*, ' ') }})
|
||||
|
@ -75,31 +93,34 @@ jobs:
|
|||
timeout-minutes: 5
|
||||
name: "Sytest Coverage"
|
||||
runs-on: ubuntu-latest
|
||||
needs: sytest # only run once Sytest is done
|
||||
if: ${{ always() }}
|
||||
needs: [ sytest, check_date ] # only run once Sytest is done and there was a commit
|
||||
if: ${{ always() && needs.check_date.outputs.should_run != 'false' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 'stable'
|
||||
cache: true
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
- name: Collect coverage
|
||||
run: |
|
||||
go tool covdata textfmt -i="$(find Sytest* -name 'covmeta*' -type f -exec dirname {} \; | uniq | paste -s -d ',' -)" -o sytest.cov
|
||||
grep -Ev 'relayapi|setup/mscs|api_trace' sytest.cov > final.cov
|
||||
go tool covdata func -i="$(find Sytest* -name 'covmeta*' -type f -exec dirname {} \; | uniq | paste -s -d ',' -)"
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v3
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
files: ./final.cov
|
||||
flags: sytest
|
||||
fail_ci_if_error: true
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
# run Complement
|
||||
complement:
|
||||
needs: check_date
|
||||
if: ${{ needs.check_date.outputs.should_run != 'false' }}
|
||||
name: "Complement (${{ matrix.label }})"
|
||||
timeout-minutes: 60
|
||||
runs-on: ubuntu-latest
|
||||
|
@ -129,8 +150,8 @@ jobs:
|
|||
run: |
|
||||
sudo apt-get update && sudo apt-get install -y libolm3 libolm-dev
|
||||
go install github.com/gotesttools/gotestfmt/v2/cmd/gotestfmt@latest
|
||||
- name: Run actions/checkout@v3 for dendrite
|
||||
uses: actions/checkout@v3
|
||||
- name: Run actions/checkout@v4 for dendrite
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: dendrite
|
||||
|
||||
|
@ -174,7 +195,7 @@ jobs:
|
|||
# Run Complement
|
||||
- run: |
|
||||
set -o pipefail &&
|
||||
go test -v -json -tags dendrite_blacklist ./tests/... 2>&1 | gotestfmt
|
||||
go test -v -json -tags dendrite_blacklist ./tests ./tests/csapi 2>&1 | gotestfmt -hide all
|
||||
shell: bash
|
||||
name: Run Complement Tests
|
||||
env:
|
||||
|
@ -185,7 +206,7 @@ jobs:
|
|||
working-directory: complement
|
||||
|
||||
- name: Upload Complement logs
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
if: ${{ always() }}
|
||||
with:
|
||||
name: Complement Logs - (Dendrite ${{ join(matrix.*, ' ') }})
|
||||
|
@ -196,30 +217,32 @@ jobs:
|
|||
timeout-minutes: 5
|
||||
name: "Complement Coverage"
|
||||
runs-on: ubuntu-latest
|
||||
needs: complement # only run once Complement is done
|
||||
if: ${{ always() }}
|
||||
needs: [ complement, check_date ] # only run once Complements is done and there was a commit
|
||||
if: ${{ always() && needs.check_date.outputs.should_run != 'false' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 'stable'
|
||||
cache: true
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
- name: Collect coverage
|
||||
run: |
|
||||
go tool covdata textfmt -i="$(find Complement* -name 'covmeta*' -type f -exec dirname {} \; | uniq | paste -s -d ',' -)" -o complement.cov
|
||||
grep -Ev 'relayapi|setup/mscs|api_trace' complement.cov > final.cov
|
||||
go tool covdata func -i="$(find Complement* -name 'covmeta*' -type f -exec dirname {} \; | uniq | paste -s -d ',' -)"
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v3
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
files: ./final.cov
|
||||
flags: complement
|
||||
fail_ci_if_error: true
|
||||
token: ${{ secrets.CODECOV_TOKEN }} # required
|
||||
|
||||
element-web:
|
||||
if: ${{ false }} # disable for now, as Cypress has been replaced by Playwright
|
||||
timeout-minutes: 120
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
@ -228,7 +251,7 @@ jobs:
|
|||
# Our test suite includes some screenshot tests with unusual diacritics, which are
|
||||
# supposed to be covered by STIXGeneral.
|
||||
tools: fonts-stix
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: matrix-org/matrix-react-sdk
|
||||
- uses: actions/setup-node@v3
|
||||
|
@ -259,6 +282,7 @@ jobs:
|
|||
TMPDIR: ${{ runner.temp }}
|
||||
|
||||
element-web-pinecone:
|
||||
if: ${{ false }} # disable for now, as Cypress has been replaced by Playwright
|
||||
timeout-minutes: 120
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
@ -267,7 +291,7 @@ jobs:
|
|||
# Our test suite includes some screenshot tests with unusual diacritics, which are
|
||||
# supposed to be covered by STIXGeneral.
|
||||
tools: fonts-stix
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: matrix-org/matrix-react-sdk
|
||||
- uses: actions/setup-node@v3
|
||||
|
|
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -77,4 +77,7 @@ media_store/
|
|||
build
|
||||
|
||||
# golang workspaces
|
||||
go.work*
|
||||
go.work*
|
||||
|
||||
# helm chart
|
||||
helm/dendrite/charts/
|
||||
|
|
|
@ -6,7 +6,7 @@ run:
|
|||
concurrency: 4
|
||||
|
||||
# timeout for analysis, e.g. 30s, 5m, default is 1m
|
||||
deadline: 30m
|
||||
timeout: 5m
|
||||
|
||||
# exit code when at least one issue was found, default is 1
|
||||
issues-exit-code: 1
|
||||
|
@ -18,24 +18,6 @@ run:
|
|||
#build-tags:
|
||||
# - mytag
|
||||
|
||||
# which dirs to skip: they won't be analyzed;
|
||||
# can use regexp here: generated.*, regexp is applied on full path;
|
||||
# default value is empty list, but next dirs are always skipped independently
|
||||
# from this option's value:
|
||||
# vendor$, third_party$, testdata$, examples$, Godeps$, builtin$
|
||||
skip-dirs:
|
||||
- bin
|
||||
- docs
|
||||
|
||||
# which files to skip: they will be analyzed, but issues from them
|
||||
# won't be reported. Default value is empty list, but there is
|
||||
# no need to include all autogenerated files, we confidently recognize
|
||||
# autogenerated files. If it's not please let us know.
|
||||
skip-files:
|
||||
- ".*\\.md$"
|
||||
- ".*\\.sh$"
|
||||
- "^cmd/syncserver-integration-tests/testdata.go$"
|
||||
|
||||
# by default isn't set. If set we pass it to "go list -mod={option}". From "go help modules":
|
||||
# If invoked with -mod=readonly, the go command is disallowed from the implicit
|
||||
# automatic updating of go.mod described above. Instead, it fails when any changes
|
||||
|
@ -50,7 +32,8 @@ run:
|
|||
# output configuration options
|
||||
output:
|
||||
# colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number"
|
||||
format: colored-line-number
|
||||
formats:
|
||||
- format: colored-line-number
|
||||
|
||||
# print lines of code with issue, default is true
|
||||
print-issued-lines: true
|
||||
|
@ -79,9 +62,8 @@ linters-settings:
|
|||
# see https://github.com/kisielk/errcheck#excluding-functions for details
|
||||
#exclude: /path/to/file.txt
|
||||
govet:
|
||||
# report about shadowed variables
|
||||
check-shadowing: true
|
||||
|
||||
enable:
|
||||
- shadow
|
||||
# settings per analyzer
|
||||
settings:
|
||||
printf: # analyzer name, run `go tool vet help` to see all analyzers
|
||||
|
@ -217,6 +199,24 @@ linters:
|
|||
|
||||
|
||||
issues:
|
||||
# which files to skip: they will be analyzed, but issues from them
|
||||
# won't be reported. Default value is empty list, but there is
|
||||
# no need to include all autogenerated files, we confidently recognize
|
||||
# autogenerated files. If it's not please let us know.
|
||||
exclude-files:
|
||||
- ".*\\.md$"
|
||||
- ".*\\.sh$"
|
||||
- "^cmd/syncserver-integration-tests/testdata.go$"
|
||||
|
||||
# which dirs to skip: they won't be analyzed;
|
||||
# can use regexp here: generated.*, regexp is applied on full path;
|
||||
# default value is empty list, but next dirs are always skipped independently
|
||||
# from this option's value:
|
||||
# vendor$, third_party$, testdata$, examples$, Godeps$, builtin$
|
||||
exclude-dirs:
|
||||
- bin
|
||||
- docs
|
||||
|
||||
# List of regexps of issue texts to exclude, empty list by default.
|
||||
# But independently from this option we use default exclude patterns,
|
||||
# it can be disabled by `exclude-use-default: false`. To list all
|
||||
|
|
38
CHANGES.md
38
CHANGES.md
|
@ -1,5 +1,43 @@
|
|||
# Changelog
|
||||
|
||||
## Dendrite 0.13.7 (2024-04-09)
|
||||
|
||||
### Fixes
|
||||
|
||||
- Fixed an issue where the displayname/avatar of an invited user was replaced with the inviter's details
|
||||
- Improved server startup performance by avoiding unnecessary room ACL queries
|
||||
- This change reduces memory footprint as it caches ACL regex patterns once instead of for each room
|
||||
- Unnecessary Relay related queries have been removed. **Note**: To use relays, you now need to explicitly enable them using the `federation_api.enable_relays` config
|
||||
- Fixed space summaries over federation
|
||||
- Improved usage of external NATS JetStream by reusing existing connections instead of opening new ones unnecessarily
|
||||
|
||||
### Features
|
||||
|
||||
- Modernized Appservices (contributed by [tulir](https://github.com/tulir))
|
||||
- Added event reporting with Synapse Admin endpoints for querying them
|
||||
- Updated dependencies
|
||||
|
||||
## Dendrite 0.13.6 (2024-01-26)
|
||||
|
||||
Upgrading to this version is **highly** recommended, as it contains several QoL improvements.
|
||||
|
||||
### Fixes
|
||||
|
||||
- Use `AckExplicitPolicy` for JetStream consumers, so messages don't pile up in NATS
|
||||
- A rare panic when assigning a state key NID has been fixed
|
||||
- A rare panic when checking powerlevels has been fixed
|
||||
- Notary keys requests for all keys now work correctly
|
||||
- Spec compliance:
|
||||
- Return `M_INVALID_PARAM` when querying room aliases
|
||||
- Handle empty `from` parameter when requesting `/messages`
|
||||
- Add CORP headers on media endpoints
|
||||
- Remove `aliases` from `/publicRooms` responses
|
||||
- Allow `+` in MXIDs (Contributed by [RosstheRoss](https://github.com/RosstheRoss))
|
||||
- Fixes membership transitions from `knock` to `join` in `knock_restricted` rooms
|
||||
- Incremental syncs now batch querying events (Contributed by [recht](https://github.com/recht))
|
||||
- Move `/joined_members` back to the clientAPI/roomserver, which should make bridges happier again
|
||||
- Backfilling from other servers now only uses at max 100 events instead of potentially thousands
|
||||
|
||||
## Dendrite 0.13.5 (2023-12-12)
|
||||
|
||||
Upgrading to this version is **highly** recommended, as it fixes several long-standing bugs in
|
||||
|
|
59
CONTRIBUTING.md
Normal file
59
CONTRIBUTING.md
Normal file
|
@ -0,0 +1,59 @@
|
|||
# Contributing to Dendrite
|
||||
|
||||
Thank you for taking the time to contribute to Matrix!
|
||||
|
||||
This is the repository for Dendrite, a second-generation Matrix homeserver written in Go.
|
||||
|
||||
## Sign off
|
||||
|
||||
We ask that everybody who contributes to this project signs off their contributions, as explained below.
|
||||
|
||||
We follow a simple 'inbound=outbound' model for contributions: the act of submitting an 'inbound' contribution means that the contributor agrees to license their contribution under the same terms as the project's overall 'outbound' license - in our case, this is Apache Software License v2 (see [LICENSE](./LICENSE)).
|
||||
|
||||
In order to have a concrete record that your contribution is intentional and you agree to license it under the same terms as the project's license, we've adopted the same lightweight approach used by the [Linux Kernel](https://www.kernel.org/doc/html/latest/process/submitting-patches.html), [Docker](https://github.com/docker/docker/blob/master/CONTRIBUTING.md), and many other projects: the [Developer Certificate of Origin](https://developercertificate.org/) (DCO). This is a simple declaration that you wrote the contribution or otherwise have the right to contribute it to Matrix:
|
||||
|
||||
```
|
||||
Developer Certificate of Origin
|
||||
Version 1.1
|
||||
|
||||
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
|
||||
660 York Street, Suite 102,
|
||||
San Francisco, CA 94110 USA
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies of this
|
||||
license document, but changing it is not allowed.
|
||||
|
||||
Developer's Certificate of Origin 1.1
|
||||
|
||||
By making a contribution to this project, I certify that:
|
||||
|
||||
(a) The contribution was created in whole or in part by me and I
|
||||
have the right to submit it under the open source license
|
||||
indicated in the file; or
|
||||
|
||||
(b) The contribution is based upon previous work that, to the best
|
||||
of my knowledge, is covered under an appropriate open source
|
||||
license and I have the right under that license to submit that
|
||||
work with modifications, whether created in whole or in part
|
||||
by me, under the same open source license (unless I am
|
||||
permitted to submit under a different license), as indicated
|
||||
in the file; or
|
||||
|
||||
(c) The contribution was provided directly to me by some other
|
||||
person who certified (a), (b) or (c) and I have not modified
|
||||
it.
|
||||
|
||||
(d) I understand and agree that this project and the contribution
|
||||
are public and that a record of the contribution (including all
|
||||
personal information I submit with it, including my sign-off) is
|
||||
maintained indefinitely and may be redistributed consistent with
|
||||
this project or the open source license(s) involved.
|
||||
```
|
||||
|
||||
If you agree to this for your contribution, then all that's needed is to include the line in your commit or pull request comment:
|
||||
|
||||
```
|
||||
Signed-off-by: Your Name <your@email.example.org>
|
||||
```
|
||||
|
||||
Git allows you to add this signoff automatically when using the `-s` flag to `git commit`, which uses the name and email set in your `user.name` and `user.email` git configs.
|
|
@ -82,9 +82,17 @@ type UserIDExistsResponse struct {
|
|||
}
|
||||
|
||||
const (
|
||||
ASProtocolPath = "/_matrix/app/unstable/thirdparty/protocol/"
|
||||
ASUserPath = "/_matrix/app/unstable/thirdparty/user"
|
||||
ASLocationPath = "/_matrix/app/unstable/thirdparty/location"
|
||||
ASProtocolLegacyPath = "/_matrix/app/unstable/thirdparty/protocol/"
|
||||
ASUserLegacyPath = "/_matrix/app/unstable/thirdparty/user"
|
||||
ASLocationLegacyPath = "/_matrix/app/unstable/thirdparty/location"
|
||||
ASRoomAliasExistsLegacyPath = "/rooms/"
|
||||
ASUserExistsLegacyPath = "/users/"
|
||||
|
||||
ASProtocolPath = "/_matrix/app/v1/thirdparty/protocol/"
|
||||
ASUserPath = "/_matrix/app/v1/thirdparty/user"
|
||||
ASLocationPath = "/_matrix/app/v1/thirdparty/location"
|
||||
ASRoomAliasExistsPath = "/_matrix/app/v1/rooms/"
|
||||
ASUserExistsPath = "/_matrix/app/v1/users/"
|
||||
)
|
||||
|
||||
type ProtocolRequest struct {
|
||||
|
|
|
@ -206,13 +206,21 @@ func (s *OutputRoomEventConsumer) sendEvents(
|
|||
}
|
||||
|
||||
// Send the transaction to the appservice.
|
||||
// https://matrix.org/docs/spec/application_service/r0.1.2#put-matrix-app-v1-transactions-txnid
|
||||
address := fmt.Sprintf("%s/transactions/%s?access_token=%s", state.RequestUrl(), txnID, url.QueryEscape(state.HSToken))
|
||||
// https://spec.matrix.org/v1.9/application-service-api/#pushing-events
|
||||
path := "_matrix/app/v1/transactions"
|
||||
if s.cfg.LegacyPaths {
|
||||
path = "transactions"
|
||||
}
|
||||
address := fmt.Sprintf("%s/%s/%s", state.RequestUrl(), path, txnID)
|
||||
if s.cfg.LegacyAuth {
|
||||
address += "?access_token=" + url.QueryEscape(state.HSToken)
|
||||
}
|
||||
req, err := http.NewRequestWithContext(ctx, "PUT", address, bytes.NewBuffer(transaction))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", state.HSToken))
|
||||
resp, err := state.HTTPClient.Do(req)
|
||||
if err != nil {
|
||||
return state.backoffAndPause(err)
|
||||
|
|
|
@ -19,10 +19,10 @@ package query
|
|||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
@ -32,9 +32,6 @@ import (
|
|||
"github.com/matrix-org/dendrite/setup/config"
|
||||
)
|
||||
|
||||
const roomAliasExistsPath = "/rooms/"
|
||||
const userIDExistsPath = "/users/"
|
||||
|
||||
// AppServiceQueryAPI is an implementation of api.AppServiceQueryAPI
|
||||
type AppServiceQueryAPI struct {
|
||||
Cfg *config.AppServiceAPI
|
||||
|
@ -55,14 +52,23 @@ func (a *AppServiceQueryAPI) RoomAliasExists(
|
|||
// Determine which application service should handle this request
|
||||
for _, appservice := range a.Cfg.Derived.ApplicationServices {
|
||||
if appservice.URL != "" && appservice.IsInterestedInRoomAlias(request.Alias) {
|
||||
path := api.ASRoomAliasExistsPath
|
||||
if a.Cfg.LegacyPaths {
|
||||
path = api.ASRoomAliasExistsLegacyPath
|
||||
}
|
||||
// The full path to the rooms API, includes hs token
|
||||
URL, err := url.Parse(appservice.RequestUrl() + roomAliasExistsPath)
|
||||
URL, err := url.Parse(appservice.RequestUrl() + path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
URL.Path += request.Alias
|
||||
apiURL := URL.String() + "?access_token=" + appservice.HSToken
|
||||
if a.Cfg.LegacyAuth {
|
||||
q := URL.Query()
|
||||
q.Set("access_token", appservice.HSToken)
|
||||
URL.RawQuery = q.Encode()
|
||||
}
|
||||
apiURL := URL.String()
|
||||
|
||||
// Send a request to each application service. If one responds that it has
|
||||
// created the room, immediately return.
|
||||
|
@ -70,6 +76,7 @@ func (a *AppServiceQueryAPI) RoomAliasExists(
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", appservice.HSToken))
|
||||
req = req.WithContext(ctx)
|
||||
|
||||
resp, err := appservice.HTTPClient.Do(req)
|
||||
|
@ -123,12 +130,21 @@ func (a *AppServiceQueryAPI) UserIDExists(
|
|||
for _, appservice := range a.Cfg.Derived.ApplicationServices {
|
||||
if appservice.URL != "" && appservice.IsInterestedInUserID(request.UserID) {
|
||||
// The full path to the rooms API, includes hs token
|
||||
URL, err := url.Parse(appservice.RequestUrl() + userIDExistsPath)
|
||||
path := api.ASUserExistsPath
|
||||
if a.Cfg.LegacyPaths {
|
||||
path = api.ASUserExistsLegacyPath
|
||||
}
|
||||
URL, err := url.Parse(appservice.RequestUrl() + path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
URL.Path += request.UserID
|
||||
apiURL := URL.String() + "?access_token=" + appservice.HSToken
|
||||
if a.Cfg.LegacyAuth {
|
||||
q := URL.Query()
|
||||
q.Set("access_token", appservice.HSToken)
|
||||
URL.RawQuery = q.Encode()
|
||||
}
|
||||
apiURL := URL.String()
|
||||
|
||||
// Send a request to each application service. If one responds that it has
|
||||
// created the user, immediately return.
|
||||
|
@ -136,6 +152,7 @@ func (a *AppServiceQueryAPI) UserIDExists(
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", appservice.HSToken))
|
||||
resp, err := appservice.HTTPClient.Do(req.WithContext(ctx))
|
||||
if resp != nil {
|
||||
defer func() {
|
||||
|
@ -176,25 +193,22 @@ type thirdpartyResponses interface {
|
|||
api.ASProtocolResponse | []api.ASUserResponse | []api.ASLocationResponse
|
||||
}
|
||||
|
||||
func requestDo[T thirdpartyResponses](client *http.Client, url string, response *T) (err error) {
|
||||
origURL := url
|
||||
// try v1 and unstable appservice endpoints
|
||||
for _, version := range []string{"v1", "unstable"} {
|
||||
var resp *http.Response
|
||||
var body []byte
|
||||
asURL := strings.Replace(origURL, "unstable", version, 1)
|
||||
resp, err = client.Get(asURL)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
defer resp.Body.Close() // nolint: errcheck
|
||||
body, err = io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
return json.Unmarshal(body, &response)
|
||||
func requestDo[T thirdpartyResponses](as *config.ApplicationService, url string, response *T) error {
|
||||
req, err := http.NewRequest(http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", as.HSToken))
|
||||
resp, err := as.HTTPClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close() // nolint: errcheck
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return json.Unmarshal(body, &response)
|
||||
}
|
||||
|
||||
func (a *AppServiceQueryAPI) Locations(
|
||||
|
@ -207,16 +221,22 @@ func (a *AppServiceQueryAPI) Locations(
|
|||
return err
|
||||
}
|
||||
|
||||
path := api.ASLocationPath
|
||||
if a.Cfg.LegacyPaths {
|
||||
path = api.ASLocationLegacyPath
|
||||
}
|
||||
for _, as := range a.Cfg.Derived.ApplicationServices {
|
||||
var asLocations []api.ASLocationResponse
|
||||
params.Set("access_token", as.HSToken)
|
||||
if a.Cfg.LegacyAuth {
|
||||
params.Set("access_token", as.HSToken)
|
||||
}
|
||||
|
||||
url := as.RequestUrl() + api.ASLocationPath
|
||||
url := as.RequestUrl() + path
|
||||
if req.Protocol != "" {
|
||||
url += "/" + req.Protocol
|
||||
}
|
||||
|
||||
if err := requestDo[[]api.ASLocationResponse](as.HTTPClient, url+"?"+params.Encode(), &asLocations); err != nil {
|
||||
if err := requestDo[[]api.ASLocationResponse](&as, url+"?"+params.Encode(), &asLocations); err != nil {
|
||||
log.WithError(err).WithField("application_service", as.ID).Error("unable to get 'locations' from application service")
|
||||
continue
|
||||
}
|
||||
|
@ -242,16 +262,22 @@ func (a *AppServiceQueryAPI) User(
|
|||
return err
|
||||
}
|
||||
|
||||
path := api.ASUserPath
|
||||
if a.Cfg.LegacyPaths {
|
||||
path = api.ASUserLegacyPath
|
||||
}
|
||||
for _, as := range a.Cfg.Derived.ApplicationServices {
|
||||
var asUsers []api.ASUserResponse
|
||||
params.Set("access_token", as.HSToken)
|
||||
if a.Cfg.LegacyAuth {
|
||||
params.Set("access_token", as.HSToken)
|
||||
}
|
||||
|
||||
url := as.RequestUrl() + api.ASUserPath
|
||||
url := as.RequestUrl() + path
|
||||
if req.Protocol != "" {
|
||||
url += "/" + req.Protocol
|
||||
}
|
||||
|
||||
if err := requestDo[[]api.ASUserResponse](as.HTTPClient, url+"?"+params.Encode(), &asUsers); err != nil {
|
||||
if err := requestDo[[]api.ASUserResponse](&as, url+"?"+params.Encode(), &asUsers); err != nil {
|
||||
log.WithError(err).WithField("application_service", as.ID).Error("unable to get 'user' from application service")
|
||||
continue
|
||||
}
|
||||
|
@ -272,6 +298,10 @@ func (a *AppServiceQueryAPI) Protocols(
|
|||
req *api.ProtocolRequest,
|
||||
resp *api.ProtocolResponse,
|
||||
) error {
|
||||
protocolPath := api.ASProtocolPath
|
||||
if a.Cfg.LegacyPaths {
|
||||
protocolPath = api.ASProtocolLegacyPath
|
||||
}
|
||||
|
||||
// get a single protocol response
|
||||
if req.Protocol != "" {
|
||||
|
@ -289,7 +319,7 @@ func (a *AppServiceQueryAPI) Protocols(
|
|||
response := api.ASProtocolResponse{}
|
||||
for _, as := range a.Cfg.Derived.ApplicationServices {
|
||||
var proto api.ASProtocolResponse
|
||||
if err := requestDo[api.ASProtocolResponse](as.HTTPClient, as.RequestUrl()+api.ASProtocolPath+req.Protocol, &proto); err != nil {
|
||||
if err := requestDo[api.ASProtocolResponse](&as, as.RequestUrl()+protocolPath+req.Protocol, &proto); err != nil {
|
||||
log.WithError(err).WithField("application_service", as.ID).Error("unable to get 'protocol' from application service")
|
||||
continue
|
||||
}
|
||||
|
@ -319,7 +349,7 @@ func (a *AppServiceQueryAPI) Protocols(
|
|||
for _, as := range a.Cfg.Derived.ApplicationServices {
|
||||
for _, p := range as.Protocols {
|
||||
var proto api.ASProtocolResponse
|
||||
if err := requestDo[api.ASProtocolResponse](as.HTTPClient, as.RequestUrl()+api.ASProtocolPath+p, &proto); err != nil {
|
||||
if err := requestDo[api.ASProtocolResponse](&as, as.RequestUrl()+protocolPath+p, &proto); err != nil {
|
||||
log.WithError(err).WithField("application_service", as.ID).Error("unable to get 'protocol' from application service")
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -2,10 +2,12 @@ package clientapi
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -1092,3 +1094,382 @@ func TestAdminMarkAsStale(t *testing.T) {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestAdminQueryEventReports(t *testing.T) {
|
||||
alice := test.NewUser(t, test.WithAccountType(uapi.AccountTypeAdmin))
|
||||
bob := test.NewUser(t)
|
||||
room := test.NewRoom(t, alice)
|
||||
room2 := test.NewRoom(t, alice)
|
||||
|
||||
// room2 has a name and canonical alias
|
||||
room2.CreateAndInsert(t, alice, spec.MRoomName, map[string]string{"name": "Testing"}, test.WithStateKey(""))
|
||||
room2.CreateAndInsert(t, alice, spec.MRoomCanonicalAlias, map[string]string{"alias": "#testing"}, test.WithStateKey(""))
|
||||
|
||||
// Join the rooms with Bob
|
||||
room.CreateAndInsert(t, bob, spec.MRoomMember, map[string]interface{}{
|
||||
"membership": "join",
|
||||
}, test.WithStateKey(bob.ID))
|
||||
room2.CreateAndInsert(t, bob, spec.MRoomMember, map[string]interface{}{
|
||||
"membership": "join",
|
||||
}, test.WithStateKey(bob.ID))
|
||||
|
||||
// Create a few events to report
|
||||
eventsToReportPerRoom := make(map[string][]string)
|
||||
for i := 0; i < 10; i++ {
|
||||
ev1 := room.CreateAndInsert(t, alice, "m.room.message", map[string]interface{}{"body": "hello world"})
|
||||
ev2 := room2.CreateAndInsert(t, alice, "m.room.message", map[string]interface{}{"body": "hello world"})
|
||||
eventsToReportPerRoom[room.ID] = append(eventsToReportPerRoom[room.ID], ev1.EventID())
|
||||
eventsToReportPerRoom[room2.ID] = append(eventsToReportPerRoom[room2.ID], ev2.EventID())
|
||||
}
|
||||
|
||||
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
|
||||
/*if dbType == test.DBTypeSQLite {
|
||||
t.Skip()
|
||||
}*/
|
||||
cfg, processCtx, close := testrig.CreateConfig(t, dbType)
|
||||
routers := httputil.NewRouters()
|
||||
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
||||
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
|
||||
defer close()
|
||||
natsInstance := jetstream.NATSInstance{}
|
||||
jsctx, _ := natsInstance.Prepare(processCtx, &cfg.Global.JetStream)
|
||||
defer jetstream.DeleteAllStreams(jsctx, &cfg.Global.JetStream)
|
||||
|
||||
// Use an actual roomserver for this
|
||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||
rsAPI.SetFederationAPI(nil, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
|
||||
if err := api.SendEvents(context.Background(), rsAPI, api.KindNew, room.Events(), "test", "test", "test", nil, false); err != nil {
|
||||
t.Fatalf("failed to send events: %v", err)
|
||||
}
|
||||
if err := api.SendEvents(context.Background(), rsAPI, api.KindNew, room2.Events(), "test", "test", "test", nil, false); err != nil {
|
||||
t.Fatalf("failed to send events: %v", err)
|
||||
}
|
||||
|
||||
// We mostly need the rsAPI for this test, so nil for other APIs/caches etc.
|
||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||
|
||||
accessTokens := map[*test.User]userDevice{
|
||||
alice: {},
|
||||
bob: {},
|
||||
}
|
||||
createAccessTokens(t, accessTokens, userAPI, processCtx.Context(), routers)
|
||||
|
||||
reqBody := map[string]any{
|
||||
"reason": "baaad",
|
||||
"score": -100,
|
||||
}
|
||||
body, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
var req *http.Request
|
||||
// Report all events
|
||||
for roomID, eventIDs := range eventsToReportPerRoom {
|
||||
for _, eventID := range eventIDs {
|
||||
req = httptest.NewRequest(http.MethodPost, fmt.Sprintf("/_matrix/client/v3/rooms/%s/report/%s", roomID, eventID), strings.NewReader(string(body)))
|
||||
req.Header.Set("Authorization", "Bearer "+accessTokens[bob].accessToken)
|
||||
|
||||
routers.Client.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("expected report to succeed, got HTTP %d instead: %s", w.Code, w.Body.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type response struct {
|
||||
EventReports []api.QueryAdminEventReportsResponse `json:"event_reports"`
|
||||
Total int64 `json:"total"`
|
||||
NextToken *int64 `json:"next_token,omitempty"`
|
||||
}
|
||||
|
||||
t.Run("Can query all reports", func(t *testing.T) {
|
||||
w = httptest.NewRecorder()
|
||||
req = httptest.NewRequest(http.MethodGet, "/_synapse/admin/v1/event_reports", strings.NewReader(string(body)))
|
||||
req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken)
|
||||
|
||||
routers.SynapseAdmin.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("expected getting reports to succeed, got HTTP %d instead: %s", w.Code, w.Body.String())
|
||||
}
|
||||
var resp response
|
||||
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
wantCount := 20
|
||||
// Only validating the count
|
||||
if len(resp.EventReports) != wantCount {
|
||||
t.Fatalf("expected %d events, got %d", wantCount, len(resp.EventReports))
|
||||
}
|
||||
if resp.Total != int64(wantCount) {
|
||||
t.Fatalf("expected total to be %d, got %d", wantCount, resp.Total)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Can filter on room", func(t *testing.T) {
|
||||
w = httptest.NewRecorder()
|
||||
req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("/_synapse/admin/v1/event_reports?room_id=%s", room.ID), strings.NewReader(string(body)))
|
||||
req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken)
|
||||
|
||||
routers.SynapseAdmin.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("expected getting reports to succeed, got HTTP %d instead: %s", w.Code, w.Body.String())
|
||||
}
|
||||
var resp response
|
||||
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
wantCount := 10
|
||||
// Only validating the count
|
||||
if len(resp.EventReports) != wantCount {
|
||||
t.Fatalf("expected %d events, got %d", wantCount, len(resp.EventReports))
|
||||
}
|
||||
if resp.Total != int64(wantCount) {
|
||||
t.Fatalf("expected total to be %d, got %d", wantCount, resp.Total)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Can filter on user_id", func(t *testing.T) {
|
||||
w = httptest.NewRecorder()
|
||||
req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("/_synapse/admin/v1/event_reports?user_id=%s", "@doesnotexist:test"), strings.NewReader(string(body)))
|
||||
req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken)
|
||||
|
||||
routers.SynapseAdmin.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("expected getting reports to succeed, got HTTP %d instead: %s", w.Code, w.Body.String())
|
||||
}
|
||||
var resp response
|
||||
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// The user does not exist, so we expect no results
|
||||
wantCount := 0
|
||||
// Only validating the count
|
||||
if len(resp.EventReports) != wantCount {
|
||||
t.Fatalf("expected %d events, got %d", wantCount, len(resp.EventReports))
|
||||
}
|
||||
if resp.Total != int64(wantCount) {
|
||||
t.Fatalf("expected total to be %d, got %d", wantCount, resp.Total)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Can set direction=f", func(t *testing.T) {
|
||||
w = httptest.NewRecorder()
|
||||
req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("/_synapse/admin/v1/event_reports?room_id=%s&dir=f", room.ID), strings.NewReader(string(body)))
|
||||
req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken)
|
||||
|
||||
routers.SynapseAdmin.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("expected getting reports to succeed, got HTTP %d instead: %s", w.Code, w.Body.String())
|
||||
}
|
||||
var resp response
|
||||
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
wantCount := 10
|
||||
// Only validating the count
|
||||
if len(resp.EventReports) != wantCount {
|
||||
t.Fatalf("expected %d events, got %d", wantCount, len(resp.EventReports))
|
||||
}
|
||||
if resp.Total != int64(wantCount) {
|
||||
t.Fatalf("expected total to be %d, got %d", wantCount, resp.Total)
|
||||
}
|
||||
// we now should have the first reported event
|
||||
wantEventID := eventsToReportPerRoom[room.ID][0]
|
||||
gotEventID := resp.EventReports[0].EventID
|
||||
if gotEventID != wantEventID {
|
||||
t.Fatalf("expected eventID to be %v, got %v", wantEventID, gotEventID)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Can limit and paginate", func(t *testing.T) {
|
||||
var from int64 = 0
|
||||
var limit int64 = 5
|
||||
var wantTotal int64 = 10 // We expect there to be 10 events in total
|
||||
var resp response
|
||||
for from+limit <= wantTotal {
|
||||
resp = response{}
|
||||
t.Logf("Getting reports starting from %d", from)
|
||||
w = httptest.NewRecorder()
|
||||
req = httptest.NewRequest(http.MethodGet, fmt.Sprintf("/_synapse/admin/v1/event_reports?room_id=%s&limit=%d&from=%d", room2.ID, limit, from), strings.NewReader(string(body)))
|
||||
req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken)
|
||||
|
||||
routers.SynapseAdmin.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("expected getting reports to succeed, got HTTP %d instead: %s", w.Code, w.Body.String())
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
wantCount := 5 // we are limited to 5
|
||||
if len(resp.EventReports) != wantCount {
|
||||
t.Fatalf("expected %d events, got %d", wantCount, len(resp.EventReports))
|
||||
}
|
||||
if resp.Total != int64(wantTotal) {
|
||||
t.Fatalf("expected total to be %d, got %d", wantCount, resp.Total)
|
||||
}
|
||||
|
||||
// We've reached the end
|
||||
if (from + int64(len(resp.EventReports))) == wantTotal {
|
||||
return
|
||||
}
|
||||
|
||||
// The next_token should be set
|
||||
if resp.NextToken == nil {
|
||||
t.Fatal("expected nextToken to be set")
|
||||
}
|
||||
from = *resp.NextToken
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestEventReportsGetDelete(t *testing.T) {
|
||||
alice := test.NewUser(t, test.WithAccountType(uapi.AccountTypeAdmin))
|
||||
bob := test.NewUser(t)
|
||||
room := test.NewRoom(t, alice)
|
||||
|
||||
// Add a name and alias
|
||||
roomName := "Testing"
|
||||
alias := "#testing"
|
||||
room.CreateAndInsert(t, alice, spec.MRoomName, map[string]string{"name": roomName}, test.WithStateKey(""))
|
||||
room.CreateAndInsert(t, alice, spec.MRoomCanonicalAlias, map[string]string{"alias": alias}, test.WithStateKey(""))
|
||||
|
||||
// Join the rooms with Bob
|
||||
room.CreateAndInsert(t, bob, spec.MRoomMember, map[string]interface{}{
|
||||
"membership": "join",
|
||||
}, test.WithStateKey(bob.ID))
|
||||
|
||||
// Create a few events to report
|
||||
|
||||
eventIDToReport := room.CreateAndInsert(t, alice, "m.room.message", map[string]interface{}{"body": "hello world"})
|
||||
|
||||
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
|
||||
cfg, processCtx, close := testrig.CreateConfig(t, dbType)
|
||||
routers := httputil.NewRouters()
|
||||
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
||||
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
|
||||
defer close()
|
||||
natsInstance := jetstream.NATSInstance{}
|
||||
jsctx, _ := natsInstance.Prepare(processCtx, &cfg.Global.JetStream)
|
||||
defer jetstream.DeleteAllStreams(jsctx, &cfg.Global.JetStream)
|
||||
|
||||
// Use an actual roomserver for this
|
||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||
rsAPI.SetFederationAPI(nil, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
|
||||
if err := api.SendEvents(context.Background(), rsAPI, api.KindNew, room.Events(), "test", "test", "test", nil, false); err != nil {
|
||||
t.Fatalf("failed to send events: %v", err)
|
||||
}
|
||||
|
||||
// We mostly need the rsAPI for this test, so nil for other APIs/caches etc.
|
||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||
|
||||
accessTokens := map[*test.User]userDevice{
|
||||
alice: {},
|
||||
bob: {},
|
||||
}
|
||||
createAccessTokens(t, accessTokens, userAPI, processCtx.Context(), routers)
|
||||
|
||||
reqBody := map[string]any{
|
||||
"reason": "baaad",
|
||||
"score": -100,
|
||||
}
|
||||
body, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
var req *http.Request
|
||||
// Report the event
|
||||
req = httptest.NewRequest(http.MethodPost, fmt.Sprintf("/_matrix/client/v3/rooms/%s/report/%s", room.ID, eventIDToReport.EventID()), strings.NewReader(string(body)))
|
||||
req.Header.Set("Authorization", "Bearer "+accessTokens[bob].accessToken)
|
||||
|
||||
routers.Client.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("expected report to succeed, got HTTP %d instead: %s", w.Code, w.Body.String())
|
||||
}
|
||||
|
||||
t.Run("Can not query with invalid ID", func(t *testing.T) {
|
||||
w = httptest.NewRecorder()
|
||||
req = httptest.NewRequest(http.MethodGet, "/_synapse/admin/v1/event_reports/abc", strings.NewReader(string(body)))
|
||||
req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken)
|
||||
|
||||
routers.SynapseAdmin.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusBadRequest {
|
||||
t.Fatalf("expected getting report to fail, got HTTP %d instead: %s", w.Code, w.Body.String())
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Can query with valid ID", func(t *testing.T) {
|
||||
w = httptest.NewRecorder()
|
||||
req = httptest.NewRequest(http.MethodGet, "/_synapse/admin/v1/event_reports/1", strings.NewReader(string(body)))
|
||||
req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken)
|
||||
|
||||
routers.SynapseAdmin.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("expected getting report to fail, got HTTP %d instead: %s", w.Code, w.Body.String())
|
||||
}
|
||||
resp := api.QueryAdminEventReportResponse{}
|
||||
if err = json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// test a few things
|
||||
if resp.EventID != eventIDToReport.EventID() {
|
||||
t.Fatalf("expected eventID to be %s, got %s instead", eventIDToReport.EventID(), resp.EventID)
|
||||
}
|
||||
if resp.RoomName != roomName {
|
||||
t.Fatalf("expected roomName to be %s, got %s instead", roomName, resp.RoomName)
|
||||
}
|
||||
if resp.CanonicalAlias != alias {
|
||||
t.Fatalf("expected alias to be %s, got %s instead", alias, resp.CanonicalAlias)
|
||||
}
|
||||
if reflect.DeepEqual(resp.EventJSON, eventIDToReport.JSON()) {
|
||||
t.Fatal("mismatching eventJSON")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Can delete with a valid ID", func(t *testing.T) {
|
||||
w = httptest.NewRecorder()
|
||||
req = httptest.NewRequest(http.MethodDelete, "/_synapse/admin/v1/event_reports/1", strings.NewReader(string(body)))
|
||||
req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken)
|
||||
|
||||
routers.SynapseAdmin.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("expected getting report to fail, got HTTP %d instead: %s", w.Code, w.Body.String())
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Can not query deleted report", func(t *testing.T) {
|
||||
w = httptest.NewRecorder()
|
||||
req = httptest.NewRequest(http.MethodGet, "/_synapse/admin/v1/event_reports/1", strings.NewReader(string(body)))
|
||||
req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken)
|
||||
|
||||
routers.SynapseAdmin.ServeHTTP(w, req)
|
||||
|
||||
if w.Code == http.StatusOK {
|
||||
t.Fatalf("expected getting report to fail, got HTTP %d instead: %s", w.Code, w.Body.String())
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -958,7 +958,8 @@ func TestCapabilities(t *testing.T) {
|
|||
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
||||
|
||||
// Needed to create accounts
|
||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, nil, caching.DisableMetrics)
|
||||
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
|
||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||
rsAPI.SetFederationAPI(nil, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
// We mostly need the rsAPI/userAPI for this test, so nil for other APIs etc.
|
||||
|
@ -1005,7 +1006,8 @@ func TestTurnserver(t *testing.T) {
|
|||
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
||||
|
||||
// Needed to create accounts
|
||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, nil, caching.DisableMetrics)
|
||||
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
|
||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||
rsAPI.SetFederationAPI(nil, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
//rsAPI.SetUserAPI(userAPI)
|
||||
|
@ -1103,7 +1105,8 @@ func Test3PID(t *testing.T) {
|
|||
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
||||
|
||||
// Needed to create accounts
|
||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, nil, caching.DisableMetrics)
|
||||
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
|
||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||
rsAPI.SetFederationAPI(nil, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
// We mostly need the rsAPI/userAPI for this test, so nil for other APIs etc.
|
||||
|
@ -2151,3 +2154,284 @@ func TestKeyBackup(t *testing.T) {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetMembership(t *testing.T) {
|
||||
alice := test.NewUser(t)
|
||||
bob := test.NewUser(t)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
roomID string
|
||||
user *test.User
|
||||
additionalEvents func(t *testing.T, room *test.Room)
|
||||
request func(t *testing.T, room *test.Room, accessToken string) *http.Request
|
||||
wantOK bool
|
||||
wantMemberCount int
|
||||
}{
|
||||
|
||||
{
|
||||
name: "/joined_members - Bob never joined",
|
||||
user: bob,
|
||||
request: func(t *testing.T, room *test.Room, accessToken string) *http.Request {
|
||||
return test.NewRequest(t, "GET", fmt.Sprintf("/_matrix/client/v3/rooms/%s/joined_members", room.ID), test.WithQueryParams(map[string]string{
|
||||
"access_token": accessToken,
|
||||
}))
|
||||
},
|
||||
wantOK: false,
|
||||
},
|
||||
{
|
||||
name: "/joined_members - Alice joined",
|
||||
user: alice,
|
||||
request: func(t *testing.T, room *test.Room, accessToken string) *http.Request {
|
||||
return test.NewRequest(t, "GET", fmt.Sprintf("/_matrix/client/v3/rooms/%s/joined_members", room.ID), test.WithQueryParams(map[string]string{
|
||||
"access_token": accessToken,
|
||||
}))
|
||||
},
|
||||
wantOK: true,
|
||||
wantMemberCount: 1,
|
||||
},
|
||||
{
|
||||
name: "/joined_members - Alice leaves, shouldn't be able to see members ",
|
||||
user: alice,
|
||||
request: func(t *testing.T, room *test.Room, accessToken string) *http.Request {
|
||||
return test.NewRequest(t, "GET", fmt.Sprintf("/_matrix/client/v3/rooms/%s/joined_members", room.ID), test.WithQueryParams(map[string]string{
|
||||
"access_token": accessToken,
|
||||
}))
|
||||
},
|
||||
additionalEvents: func(t *testing.T, room *test.Room) {
|
||||
room.CreateAndInsert(t, alice, spec.MRoomMember, map[string]interface{}{
|
||||
"membership": "leave",
|
||||
}, test.WithStateKey(alice.ID))
|
||||
},
|
||||
wantOK: false,
|
||||
},
|
||||
{
|
||||
name: "/joined_members - Bob joins, Alice sees two members",
|
||||
user: alice,
|
||||
request: func(t *testing.T, room *test.Room, accessToken string) *http.Request {
|
||||
return test.NewRequest(t, "GET", fmt.Sprintf("/_matrix/client/v3/rooms/%s/joined_members", room.ID), test.WithQueryParams(map[string]string{
|
||||
"access_token": accessToken,
|
||||
}))
|
||||
},
|
||||
additionalEvents: func(t *testing.T, room *test.Room) {
|
||||
room.CreateAndInsert(t, bob, spec.MRoomMember, map[string]interface{}{
|
||||
"membership": "join",
|
||||
}, test.WithStateKey(bob.ID))
|
||||
},
|
||||
wantOK: true,
|
||||
wantMemberCount: 2,
|
||||
},
|
||||
}
|
||||
|
||||
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
|
||||
|
||||
cfg, processCtx, close := testrig.CreateConfig(t, dbType)
|
||||
routers := httputil.NewRouters()
|
||||
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
||||
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
|
||||
defer close()
|
||||
natsInstance := jetstream.NATSInstance{}
|
||||
jsctx, _ := natsInstance.Prepare(processCtx, &cfg.Global.JetStream)
|
||||
defer jetstream.DeleteAllStreams(jsctx, &cfg.Global.JetStream)
|
||||
|
||||
// Use an actual roomserver for this
|
||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||
rsAPI.SetFederationAPI(nil, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
|
||||
// We mostly need the rsAPI for this test, so nil for other APIs/caches etc.
|
||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||
|
||||
accessTokens := map[*test.User]userDevice{
|
||||
alice: {},
|
||||
bob: {},
|
||||
}
|
||||
createAccessTokens(t, accessTokens, userAPI, processCtx.Context(), routers)
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
room := test.NewRoom(t, alice)
|
||||
t.Cleanup(func() {
|
||||
t.Logf("running cleanup for %s", tc.name)
|
||||
})
|
||||
// inject additional events
|
||||
if tc.additionalEvents != nil {
|
||||
tc.additionalEvents(t, room)
|
||||
}
|
||||
if err := api.SendEvents(context.Background(), rsAPI, api.KindNew, room.Events(), "test", "test", "test", nil, false); err != nil {
|
||||
t.Fatalf("failed to send events: %v", err)
|
||||
}
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
routers.Client.ServeHTTP(w, tc.request(t, room, accessTokens[tc.user].accessToken))
|
||||
if w.Code != 200 && tc.wantOK {
|
||||
t.Logf("%s", w.Body.String())
|
||||
t.Fatalf("got HTTP %d want %d", w.Code, 200)
|
||||
}
|
||||
t.Logf("[%s] Resp: %s", tc.name, w.Body.String())
|
||||
|
||||
// check we got the expected events
|
||||
if tc.wantOK {
|
||||
memberCount := len(gjson.GetBytes(w.Body.Bytes(), "joined").Map())
|
||||
if memberCount != tc.wantMemberCount {
|
||||
t.Fatalf("expected %d members, got %d", tc.wantMemberCount, memberCount)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestCreateRoomInvite(t *testing.T) {
|
||||
alice := test.NewUser(t)
|
||||
bob := test.NewUser(t)
|
||||
|
||||
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
|
||||
|
||||
cfg, processCtx, close := testrig.CreateConfig(t, dbType)
|
||||
routers := httputil.NewRouters()
|
||||
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
||||
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
|
||||
defer close()
|
||||
natsInstance := jetstream.NATSInstance{}
|
||||
jsctx, _ := natsInstance.Prepare(processCtx, &cfg.Global.JetStream)
|
||||
defer jetstream.DeleteAllStreams(jsctx, &cfg.Global.JetStream)
|
||||
|
||||
// Use an actual roomserver for this
|
||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||
rsAPI.SetFederationAPI(nil, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
|
||||
// We mostly need the rsAPI for this test, so nil for other APIs/caches etc.
|
||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||
|
||||
accessTokens := map[*test.User]userDevice{
|
||||
alice: {},
|
||||
}
|
||||
createAccessTokens(t, accessTokens, userAPI, processCtx.Context(), routers)
|
||||
|
||||
reqBody := map[string]any{
|
||||
"invite": []string{bob.ID},
|
||||
}
|
||||
body, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
req := httptest.NewRequest(http.MethodPost, "/_matrix/client/v3/createRoom", strings.NewReader(string(body)))
|
||||
req.Header.Set("Authorization", "Bearer "+accessTokens[alice].accessToken)
|
||||
|
||||
routers.Client.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("expected room creation to be successful, got HTTP %d instead: %s", w.Code, w.Body.String())
|
||||
}
|
||||
|
||||
roomID := gjson.GetBytes(w.Body.Bytes(), "room_id").Str
|
||||
validRoomID, _ := spec.NewRoomID(roomID)
|
||||
// Now ask the roomserver about the membership event of Bob
|
||||
ev, err := rsAPI.CurrentStateEvent(context.Background(), *validRoomID, spec.MRoomMember, bob.ID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if ev == nil {
|
||||
t.Fatal("Membership event for Bob does not exist")
|
||||
}
|
||||
|
||||
// Validate that there is NO displayname in content
|
||||
if gjson.GetBytes(ev.Content(), "displayname").Exists() {
|
||||
t.Fatal("Found displayname in invite")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestReportEvent(t *testing.T) {
|
||||
alice := test.NewUser(t)
|
||||
bob := test.NewUser(t)
|
||||
charlie := test.NewUser(t)
|
||||
room := test.NewRoom(t, alice)
|
||||
|
||||
room.CreateAndInsert(t, charlie, spec.MRoomMember, map[string]interface{}{
|
||||
"membership": "join",
|
||||
}, test.WithStateKey(charlie.ID))
|
||||
eventToReport := room.CreateAndInsert(t, alice, "m.room.message", map[string]interface{}{"body": "hello world"})
|
||||
|
||||
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
|
||||
cfg, processCtx, close := testrig.CreateConfig(t, dbType)
|
||||
routers := httputil.NewRouters()
|
||||
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
||||
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
|
||||
defer close()
|
||||
natsInstance := jetstream.NATSInstance{}
|
||||
jsctx, _ := natsInstance.Prepare(processCtx, &cfg.Global.JetStream)
|
||||
defer jetstream.DeleteAllStreams(jsctx, &cfg.Global.JetStream)
|
||||
|
||||
// Use an actual roomserver for this
|
||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
|
||||
rsAPI.SetFederationAPI(nil, nil)
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||
|
||||
if err := api.SendEvents(context.Background(), rsAPI, api.KindNew, room.Events(), "test", "test", "test", nil, false); err != nil {
|
||||
t.Fatalf("failed to send events: %v", err)
|
||||
}
|
||||
|
||||
// We mostly need the rsAPI for this test, so nil for other APIs/caches etc.
|
||||
AddPublicRoutes(processCtx, routers, cfg, &natsInstance, nil, rsAPI, nil, nil, nil, userAPI, nil, nil, caching.DisableMetrics)
|
||||
|
||||
accessTokens := map[*test.User]userDevice{
|
||||
alice: {},
|
||||
bob: {},
|
||||
charlie: {},
|
||||
}
|
||||
createAccessTokens(t, accessTokens, userAPI, processCtx.Context(), routers)
|
||||
|
||||
reqBody := map[string]any{
|
||||
"reason": "baaad",
|
||||
"score": -100,
|
||||
}
|
||||
body, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
var req *http.Request
|
||||
t.Run("Bob is not joined and should not be able to report the event", func(t *testing.T) {
|
||||
req = httptest.NewRequest(http.MethodPost, fmt.Sprintf("/_matrix/client/v3/rooms/%s/report/%s", room.ID, eventToReport.EventID()), strings.NewReader(string(body)))
|
||||
req.Header.Set("Authorization", "Bearer "+accessTokens[bob].accessToken)
|
||||
|
||||
routers.Client.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusNotFound {
|
||||
t.Fatalf("expected report to fail, got HTTP %d instead: %s", w.Code, w.Body.String())
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Charlie is joined but the event does not exist", func(t *testing.T) {
|
||||
w = httptest.NewRecorder()
|
||||
req = httptest.NewRequest(http.MethodPost, fmt.Sprintf("/_matrix/client/v3/rooms/%s/report/$doesNotExist", room.ID), strings.NewReader(string(body)))
|
||||
req.Header.Set("Authorization", "Bearer "+accessTokens[charlie].accessToken)
|
||||
|
||||
routers.Client.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusNotFound {
|
||||
t.Fatalf("expected report to fail, got HTTP %d instead: %s", w.Code, w.Body.String())
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Charlie is joined and allowed to report the event", func(t *testing.T) {
|
||||
w = httptest.NewRecorder()
|
||||
req = httptest.NewRequest(http.MethodPost, fmt.Sprintf("/_matrix/client/v3/rooms/%s/report/%s", room.ID, eventToReport.EventID()), strings.NewReader(string(body)))
|
||||
req.Header.Set("Authorization", "Bearer "+accessTokens[charlie].accessToken)
|
||||
|
||||
routers.Client.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("expected report to be successful, got HTTP %d instead: %s", w.Code, w.Body.String())
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -35,5 +35,5 @@ func ParseTSParam(req *http.Request) (time.Time, error) {
|
|||
return time.Time{}, fmt.Errorf("param 'ts' is no valid int (%s)", err.Error())
|
||||
}
|
||||
|
||||
return time.Unix(ts/1000, 0), nil
|
||||
return time.UnixMilli(ts), nil
|
||||
}
|
||||
|
|
|
@ -328,7 +328,7 @@ func AdminPurgeRoom(req *http.Request, rsAPI roomserverAPI.ClientRoomserverAPI)
|
|||
}
|
||||
}
|
||||
|
||||
func AdminResetPassword(req *http.Request, cfg *config.ClientAPI, device *api.Device, userAPI api.ClientUserAPI) util.JSONResponse {
|
||||
func AdminResetPassword(req *http.Request, cfg *config.ClientAPI, device *api.Device, userAPI userapi.ClientUserAPI) util.JSONResponse {
|
||||
if req.Body == nil {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
|
@ -423,7 +423,7 @@ func AdminReindex(req *http.Request, cfg *config.ClientAPI, device *api.Device,
|
|||
}
|
||||
}
|
||||
|
||||
func AdminMarkAsStale(req *http.Request, cfg *config.ClientAPI, keyAPI api.ClientKeyAPI) util.JSONResponse {
|
||||
func AdminMarkAsStale(req *http.Request, cfg *config.ClientAPI, keyAPI userapi.ClientKeyAPI) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
|
@ -495,3 +495,93 @@ func AdminDownloadState(req *http.Request, device *api.Device, rsAPI roomserverA
|
|||
JSON: struct{}{},
|
||||
}
|
||||
}
|
||||
|
||||
// GetEventReports returns reported events for a given user/room.
|
||||
func GetEventReports(
|
||||
req *http.Request,
|
||||
rsAPI roomserverAPI.ClientRoomserverAPI,
|
||||
from, limit uint64,
|
||||
backwards bool,
|
||||
userID, roomID string,
|
||||
) util.JSONResponse {
|
||||
|
||||
eventReports, count, err := rsAPI.QueryAdminEventReports(req.Context(), from, limit, backwards, userID, roomID)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Error("failed to query event reports")
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusInternalServerError,
|
||||
JSON: spec.InternalServerError{},
|
||||
}
|
||||
}
|
||||
|
||||
resp := map[string]any{
|
||||
"event_reports": eventReports,
|
||||
"total": count,
|
||||
}
|
||||
|
||||
// Add a next_token if there are still reports
|
||||
if int64(from+limit) < count {
|
||||
resp["next_token"] = int(from) + len(eventReports)
|
||||
}
|
||||
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusOK,
|
||||
JSON: resp,
|
||||
}
|
||||
}
|
||||
|
||||
func GetEventReport(req *http.Request, rsAPI roomserverAPI.ClientRoomserverAPI, reportID string) util.JSONResponse {
|
||||
parsedReportID, err := strconv.ParseUint(reportID, 10, 64)
|
||||
if err != nil {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
// Given this is an admin endpoint, let them know what didn't work.
|
||||
JSON: spec.InvalidParam(err.Error()),
|
||||
}
|
||||
}
|
||||
|
||||
report, err := rsAPI.QueryAdminEventReport(req.Context(), parsedReportID)
|
||||
if err != nil {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusInternalServerError,
|
||||
JSON: spec.Unknown(err.Error()),
|
||||
}
|
||||
}
|
||||
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusOK,
|
||||
JSON: report,
|
||||
}
|
||||
}
|
||||
|
||||
func DeleteEventReport(req *http.Request, rsAPI roomserverAPI.ClientRoomserverAPI, reportID string) util.JSONResponse {
|
||||
parsedReportID, err := strconv.ParseUint(reportID, 10, 64)
|
||||
if err != nil {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
// Given this is an admin endpoint, let them know what didn't work.
|
||||
JSON: spec.InvalidParam(err.Error()),
|
||||
}
|
||||
}
|
||||
|
||||
err = rsAPI.PerformAdminDeleteEventReport(req.Context(), parsedReportID)
|
||||
if err != nil {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusInternalServerError,
|
||||
JSON: spec.Unknown(err.Error()),
|
||||
}
|
||||
}
|
||||
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusOK,
|
||||
JSON: struct{}{},
|
||||
}
|
||||
}
|
||||
|
||||
func parseUint64OrDefault(input string, defaultValue uint64) uint64 {
|
||||
v, err := strconv.ParseUint(input, 10, 64)
|
||||
if err != nil {
|
||||
return defaultValue
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
|
|
@ -93,7 +93,6 @@ func UploadKeys(req *http.Request, keyAPI api.ClientKeyAPI, device *api.Device)
|
|||
|
||||
type queryKeysRequest struct {
|
||||
Timeout int `json:"timeout"`
|
||||
Token string `json:"token"`
|
||||
DeviceKeys map[string][]string `json:"device_keys"`
|
||||
}
|
||||
|
||||
|
@ -119,7 +118,6 @@ func QueryKeys(req *http.Request, keyAPI api.ClientKeyAPI, device *api.Device) u
|
|||
UserID: device.UserID,
|
||||
UserToDevices: r.DeviceKeys,
|
||||
Timeout: r.GetTimeout(),
|
||||
// TODO: Token?
|
||||
}, &queryRes)
|
||||
return util.JSONResponse{
|
||||
Code: 200,
|
||||
|
|
|
@ -324,19 +324,18 @@ func SendInvite(
|
|||
}
|
||||
|
||||
// We already received the return value, so no need to check for an error here.
|
||||
response, _ := sendInvite(req.Context(), profileAPI, device, roomID, body.UserID, body.Reason, cfg, rsAPI, asAPI, evTime)
|
||||
response, _ := sendInvite(req.Context(), device, roomID, body.UserID, body.Reason, cfg, rsAPI, evTime)
|
||||
return response
|
||||
}
|
||||
|
||||
// sendInvite sends an invitation to a user. Returns a JSONResponse and an error
|
||||
func sendInvite(
|
||||
ctx context.Context,
|
||||
profileAPI userapi.ClientUserAPI,
|
||||
device *userapi.Device,
|
||||
roomID, userID, reason string,
|
||||
cfg *config.ClientAPI,
|
||||
rsAPI roomserverAPI.ClientRoomserverAPI,
|
||||
asAPI appserviceAPI.AppServiceInternalAPI, evTime time.Time,
|
||||
evTime time.Time,
|
||||
) (util.JSONResponse, error) {
|
||||
validRoomID, err := spec.NewRoomID(roomID)
|
||||
if err != nil {
|
||||
|
@ -359,13 +358,7 @@ func sendInvite(
|
|||
JSON: spec.InvalidParam("UserID is invalid"),
|
||||
}, err
|
||||
}
|
||||
profile, err := loadProfile(ctx, userID, cfg, profileAPI, asAPI)
|
||||
if err != nil {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusInternalServerError,
|
||||
JSON: spec.InternalServerError{},
|
||||
}, err
|
||||
}
|
||||
|
||||
identity, err := cfg.Matrix.SigningIdentityFor(device.UserDomain())
|
||||
if err != nil {
|
||||
return util.JSONResponse{
|
||||
|
@ -375,16 +368,14 @@ func sendInvite(
|
|||
}
|
||||
err = rsAPI.PerformInvite(ctx, &api.PerformInviteRequest{
|
||||
InviteInput: roomserverAPI.InviteInput{
|
||||
RoomID: *validRoomID,
|
||||
Inviter: *inviter,
|
||||
Invitee: *invitee,
|
||||
DisplayName: profile.DisplayName,
|
||||
AvatarURL: profile.AvatarURL,
|
||||
Reason: reason,
|
||||
IsDirect: false,
|
||||
KeyID: identity.KeyID,
|
||||
PrivateKey: identity.PrivateKey,
|
||||
EventTime: evTime,
|
||||
RoomID: *validRoomID,
|
||||
Inviter: *inviter,
|
||||
Invitee: *invitee,
|
||||
Reason: reason,
|
||||
IsDirect: false,
|
||||
KeyID: identity.KeyID,
|
||||
PrivateKey: identity.PrivateKey,
|
||||
EventTime: evTime,
|
||||
},
|
||||
InviteRoomState: nil, // ask the roomserver to draw up invite room state for us
|
||||
SendAsServer: string(device.UserDomain()),
|
||||
|
|
139
clientapi/routing/memberships.go
Normal file
139
clientapi/routing/memberships.go
Normal file
|
@ -0,0 +1,139 @@
|
|||
// Copyright 2024 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package routing
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/matrix-org/dendrite/roomserver/api"
|
||||
userapi "github.com/matrix-org/dendrite/userapi/api"
|
||||
"github.com/matrix-org/gomatrixserverlib/spec"
|
||||
"github.com/matrix-org/util"
|
||||
)
|
||||
|
||||
// https://matrix.org/docs/spec/client_server/r0.6.0#get-matrix-client-r0-rooms-roomid-joined-members
|
||||
type getJoinedMembersResponse struct {
|
||||
Joined map[string]joinedMember `json:"joined"`
|
||||
}
|
||||
|
||||
type joinedMember struct {
|
||||
DisplayName string `json:"display_name"`
|
||||
AvatarURL string `json:"avatar_url"`
|
||||
}
|
||||
|
||||
// The database stores 'displayname' without an underscore.
|
||||
// Deserialize into this and then change to the actual API response
|
||||
type databaseJoinedMember struct {
|
||||
DisplayName string `json:"displayname"`
|
||||
AvatarURL string `json:"avatar_url"`
|
||||
}
|
||||
|
||||
// GetJoinedMembers implements
|
||||
//
|
||||
// GET /rooms/{roomId}/joined_members
|
||||
func GetJoinedMembers(
|
||||
req *http.Request, device *userapi.Device, roomID string,
|
||||
rsAPI api.ClientRoomserverAPI,
|
||||
) util.JSONResponse {
|
||||
// Validate the userID
|
||||
userID, err := spec.NewUserID(device.UserID, true)
|
||||
if err != nil {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
JSON: spec.InvalidParam("Device UserID is invalid"),
|
||||
}
|
||||
}
|
||||
|
||||
// Validate the roomID
|
||||
validRoomID, err := spec.NewRoomID(roomID)
|
||||
if err != nil {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
JSON: spec.InvalidParam("RoomID is invalid"),
|
||||
}
|
||||
}
|
||||
|
||||
// Get the current memberships for the requesting user to determine
|
||||
// if they are allowed to query this endpoint.
|
||||
queryReq := api.QueryMembershipForUserRequest{
|
||||
RoomID: validRoomID.String(),
|
||||
UserID: *userID,
|
||||
}
|
||||
|
||||
var queryRes api.QueryMembershipForUserResponse
|
||||
if queryErr := rsAPI.QueryMembershipForUser(req.Context(), &queryReq, &queryRes); queryErr != nil {
|
||||
util.GetLogger(req.Context()).WithError(queryErr).Error("rsAPI.QueryMembershipsForRoom failed")
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusInternalServerError,
|
||||
JSON: spec.InternalServerError{},
|
||||
}
|
||||
}
|
||||
|
||||
if !queryRes.HasBeenInRoom {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusForbidden,
|
||||
JSON: spec.Forbidden("You aren't a member of the room and weren't previously a member of the room."),
|
||||
}
|
||||
}
|
||||
|
||||
if !queryRes.IsInRoom {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusForbidden,
|
||||
JSON: spec.Forbidden("You aren't a member of the room and weren't previously a member of the room."),
|
||||
}
|
||||
}
|
||||
|
||||
// Get the current membership events
|
||||
var membershipsForRoomResp api.QueryMembershipsForRoomResponse
|
||||
if err = rsAPI.QueryMembershipsForRoom(req.Context(), &api.QueryMembershipsForRoomRequest{
|
||||
JoinedOnly: true,
|
||||
RoomID: validRoomID.String(),
|
||||
}, &membershipsForRoomResp); err != nil {
|
||||
util.GetLogger(req.Context()).WithError(err).Error("rsAPI.QueryEventsByID failed")
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusInternalServerError,
|
||||
JSON: spec.InternalServerError{},
|
||||
}
|
||||
}
|
||||
|
||||
var res getJoinedMembersResponse
|
||||
res.Joined = make(map[string]joinedMember)
|
||||
for _, ev := range membershipsForRoomResp.JoinEvents {
|
||||
var content databaseJoinedMember
|
||||
if err := json.Unmarshal(ev.Content, &content); err != nil {
|
||||
util.GetLogger(req.Context()).WithError(err).Error("failed to unmarshal event content")
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusInternalServerError,
|
||||
JSON: spec.InternalServerError{},
|
||||
}
|
||||
}
|
||||
|
||||
userID, err := rsAPI.QueryUserIDForSender(req.Context(), *validRoomID, spec.SenderID(ev.Sender))
|
||||
if err != nil || userID == nil {
|
||||
util.GetLogger(req.Context()).WithError(err).Error("rsAPI.QueryUserIDForSender failed")
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusInternalServerError,
|
||||
JSON: spec.InternalServerError{},
|
||||
}
|
||||
}
|
||||
|
||||
res.Joined[userID.String()] = joinedMember(content)
|
||||
}
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusOK,
|
||||
JSON: res,
|
||||
}
|
||||
}
|
|
@ -70,7 +70,7 @@ func GetPushRulesByKind(ctx context.Context, scope, kind string, device *userapi
|
|||
}
|
||||
rulesPtr := pushRuleSetKindPointer(ruleSet, pushrules.Kind(kind))
|
||||
// Even if rulesPtr is not nil, there may not be any rules for this kind
|
||||
if rulesPtr == nil || (rulesPtr != nil && len(*rulesPtr) == 0) {
|
||||
if rulesPtr == nil || len(*rulesPtr) == 0 {
|
||||
return errorResponse(ctx, spec.InvalidParam("invalid push rules kind"), "pushRuleSetKindPointer failed")
|
||||
}
|
||||
return util.JSONResponse{
|
||||
|
|
93
clientapi/routing/report_event.go
Normal file
93
clientapi/routing/report_event.go
Normal file
|
@ -0,0 +1,93 @@
|
|||
// Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package routing
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/matrix-org/dendrite/clientapi/httputil"
|
||||
"github.com/matrix-org/dendrite/roomserver/api"
|
||||
userAPI "github.com/matrix-org/dendrite/userapi/api"
|
||||
"github.com/matrix-org/gomatrixserverlib/spec"
|
||||
"github.com/matrix-org/util"
|
||||
)
|
||||
|
||||
type reportEventRequest struct {
|
||||
Reason string `json:"reason"`
|
||||
Score int64 `json:"score"`
|
||||
}
|
||||
|
||||
func ReportEvent(
|
||||
req *http.Request,
|
||||
device *userAPI.Device,
|
||||
roomID, eventID string,
|
||||
rsAPI api.ClientRoomserverAPI,
|
||||
) util.JSONResponse {
|
||||
defer req.Body.Close() // nolint: errcheck
|
||||
|
||||
deviceUserID, err := spec.NewUserID(device.UserID, true)
|
||||
if err != nil {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusForbidden,
|
||||
JSON: spec.NotFound("You don't have permission to report this event, bad userID"),
|
||||
}
|
||||
}
|
||||
// The requesting user must be a member of the room
|
||||
errRes := checkMemberInRoom(req.Context(), rsAPI, *deviceUserID, roomID)
|
||||
if errRes != nil {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusNotFound, // Spec demands this...
|
||||
JSON: spec.NotFound("The event was not found or you are not joined to the room."),
|
||||
}
|
||||
}
|
||||
|
||||
// Parse the request
|
||||
report := reportEventRequest{}
|
||||
if resErr := httputil.UnmarshalJSONRequest(req, &report); resErr != nil {
|
||||
return *resErr
|
||||
}
|
||||
|
||||
queryRes := &api.QueryEventsByIDResponse{}
|
||||
if err = rsAPI.QueryEventsByID(req.Context(), &api.QueryEventsByIDRequest{
|
||||
RoomID: roomID,
|
||||
EventIDs: []string{eventID},
|
||||
}, queryRes); err != nil {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusInternalServerError,
|
||||
JSON: spec.InternalServerError{Err: err.Error()},
|
||||
}
|
||||
}
|
||||
|
||||
// No event was found or it was already redacted
|
||||
if len(queryRes.Events) == 0 || queryRes.Events[0].Redacted() {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusNotFound,
|
||||
JSON: spec.NotFound("The event was not found or you are not joined to the room."),
|
||||
}
|
||||
}
|
||||
|
||||
_, err = rsAPI.InsertReportedEvent(req.Context(), roomID, eventID, device.UserID, report.Reason, report.Score)
|
||||
if err != nil {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusInternalServerError,
|
||||
JSON: spec.InternalServerError{Err: err.Error()},
|
||||
}
|
||||
}
|
||||
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusOK,
|
||||
JSON: struct{}{},
|
||||
}
|
||||
}
|
|
@ -138,7 +138,7 @@ func QueryRoomHierarchy(req *http.Request, device *userapi.Device, roomIDStr str
|
|||
walker = *cachedWalker
|
||||
}
|
||||
|
||||
discoveredRooms, nextWalker, err := rsAPI.QueryNextRoomHierarchyPage(req.Context(), walker, limit)
|
||||
discoveredRooms, _, nextWalker, err := rsAPI.QueryNextRoomHierarchyPage(req.Context(), walker, limit)
|
||||
|
||||
if err != nil {
|
||||
switch err.(type) {
|
||||
|
|
|
@ -255,7 +255,7 @@ func Setup(
|
|||
logrus.Info("Enabling server notices at /_synapse/admin/v1/send_server_notice")
|
||||
serverNotificationSender, err := getSenderDevice(context.Background(), rsAPI, userAPI, cfg)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatal("unable to get account for sending sending server notices")
|
||||
logrus.WithError(err).Fatal("unable to get account for sending server notices")
|
||||
}
|
||||
|
||||
synapseAdminRouter.Handle("/admin/v1/send_server_notice/{txnID}",
|
||||
|
@ -1513,4 +1513,58 @@ func Setup(
|
|||
return GetPresence(req, device, natsClient, cfg.Matrix.JetStream.Prefixed(jetstream.RequestPresence), vars["userId"])
|
||||
}),
|
||||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/rooms/{roomID}/joined_members",
|
||||
httputil.MakeAuthAPI("rooms_members", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
}
|
||||
return GetJoinedMembers(req, device, vars["roomID"], rsAPI)
|
||||
}),
|
||||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/rooms/{roomID}/report/{eventID}",
|
||||
httputil.MakeAuthAPI("report_event", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
}
|
||||
return ReportEvent(req, device, vars["roomID"], vars["eventID"], rsAPI)
|
||||
}),
|
||||
).Methods(http.MethodPost, http.MethodOptions)
|
||||
|
||||
synapseAdminRouter.Handle("/admin/v1/event_reports",
|
||||
httputil.MakeAdminAPI("admin_report_events", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
from := parseUint64OrDefault(req.URL.Query().Get("from"), 0)
|
||||
limit := parseUint64OrDefault(req.URL.Query().Get("limit"), 100)
|
||||
dir := req.URL.Query().Get("dir")
|
||||
userID := req.URL.Query().Get("user_id")
|
||||
roomID := req.URL.Query().Get("room_id")
|
||||
|
||||
// Go backwards if direction is empty or "b"
|
||||
backwards := dir == "" || dir == "b"
|
||||
return GetEventReports(req, rsAPI, from, limit, backwards, userID, roomID)
|
||||
}),
|
||||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
synapseAdminRouter.Handle("/admin/v1/event_reports/{reportID}",
|
||||
httputil.MakeAdminAPI("admin_report_event", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
}
|
||||
return GetEventReport(req, rsAPI, vars["reportID"])
|
||||
}),
|
||||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
synapseAdminRouter.Handle("/admin/v1/event_reports/{reportID}",
|
||||
httputil.MakeAdminAPI("admin_report_event_delete", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
}
|
||||
return DeleteEventReport(req, rsAPI, vars["reportID"])
|
||||
}),
|
||||
).Methods(http.MethodDelete, http.MethodOptions)
|
||||
}
|
||||
|
|
|
@ -265,7 +265,7 @@ func createEvents(eventsJSON []string, roomVer gomatrixserverlib.RoomVersion) ([
|
|||
for i, eventJSON := range eventsJSON {
|
||||
pdu, evErr := roomVerImpl.NewEventFromTrustedJSON([]byte(eventJSON), false)
|
||||
if evErr != nil {
|
||||
return nil, fmt.Errorf("failed to make event: %s", err.Error())
|
||||
return nil, fmt.Errorf("failed to make event: %s", evErr.Error())
|
||||
}
|
||||
ev := types.HeaderedEvent{PDU: pdu}
|
||||
events[i] = &ev
|
||||
|
|
|
@ -215,7 +215,7 @@ func SendServerNotice(
|
|||
}
|
||||
if !membershipRes.IsInRoom {
|
||||
// re-invite the user
|
||||
res, err := sendInvite(ctx, userAPI, senderDevice, roomID, r.UserID, "Server notice room", cfgClient, rsAPI, asAPI, time.Now())
|
||||
res, err := sendInvite(ctx, senderDevice, roomID, r.UserID, "Server notice room", cfgClient, rsAPI, time.Now())
|
||||
if err != nil {
|
||||
return res
|
||||
}
|
||||
|
|
|
@ -17,13 +17,13 @@ package relay
|
|||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
federationAPI "github.com/matrix-org/dendrite/federationapi/api"
|
||||
relayServerAPI "github.com/matrix-org/dendrite/relayapi/api"
|
||||
"github.com/matrix-org/gomatrixserverlib/spec"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.uber.org/atomic"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -54,7 +54,7 @@ func NewRelayServerRetriever(
|
|||
federationAPI: federationAPI,
|
||||
relayAPI: relayAPI,
|
||||
relayServersQueried: make(map[spec.ServerName]bool),
|
||||
running: *atomic.NewBool(false),
|
||||
running: atomic.Bool{},
|
||||
quit: quit,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -154,6 +154,13 @@ app_service_api:
|
|||
# to be sent to an insecure endpoint.
|
||||
disable_tls_validation: false
|
||||
|
||||
# Send the access_token query parameter with appservice requests in addition
|
||||
# to the Authorization header. This can cause hs_tokens to be saved to logs,
|
||||
# so it should not be enabled unless absolutely necessary.
|
||||
legacy_auth: false
|
||||
# Use the legacy unprefixed paths for appservice requests.
|
||||
legacy_paths: false
|
||||
|
||||
# Appservice configuration files to load into this homeserver.
|
||||
config_files:
|
||||
# - /path/to/appservice_registration.yaml
|
||||
|
|
|
@ -117,6 +117,7 @@ The list of files that need to be stored is:
|
|||
- matrix-key.pem
|
||||
- dendrite.yaml
|
||||
- the postgres or sqlite DB
|
||||
- the jetstream directory
|
||||
- the media store
|
||||
- the search index (although this can be regenerated)
|
||||
|
||||
|
|
|
@ -231,9 +231,9 @@ GEM
|
|||
jekyll-seo-tag (~> 2.1)
|
||||
minitest (5.17.0)
|
||||
multipart-post (2.1.1)
|
||||
nokogiri (1.14.3-arm64-darwin)
|
||||
nokogiri (1.16.2-arm64-darwin)
|
||||
racc (~> 1.4)
|
||||
nokogiri (1.14.3-x86_64-linux)
|
||||
nokogiri (1.16.2-x86_64-linux)
|
||||
racc (~> 1.4)
|
||||
octokit (4.22.0)
|
||||
faraday (>= 0.9)
|
||||
|
@ -241,11 +241,12 @@ GEM
|
|||
pathutil (0.16.2)
|
||||
forwardable-extended (~> 2.6)
|
||||
public_suffix (4.0.7)
|
||||
racc (1.6.2)
|
||||
racc (1.7.3)
|
||||
rb-fsevent (0.11.1)
|
||||
rb-inotify (0.10.1)
|
||||
ffi (~> 1.0)
|
||||
rexml (3.2.5)
|
||||
rexml (3.3.2)
|
||||
strscan
|
||||
rouge (3.26.0)
|
||||
ruby2_keywords (0.0.5)
|
||||
rubyzip (2.3.2)
|
||||
|
@ -260,6 +261,7 @@ GEM
|
|||
faraday (> 0.8, < 2.0)
|
||||
simpleidn (0.2.1)
|
||||
unf (~> 0.1.4)
|
||||
strscan (3.1.0)
|
||||
terminal-table (1.8.0)
|
||||
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||
thread_safe (0.3.6)
|
||||
|
|
|
@ -113,10 +113,7 @@ func NewInternalAPI(
|
|||
_ = federationDB.RemoveAllServersFromBlacklist()
|
||||
}
|
||||
|
||||
stats := statistics.NewStatistics(
|
||||
federationDB,
|
||||
cfg.FederationMaxRetries+1,
|
||||
cfg.P2PFederationRetriesUntilAssumedOffline+1)
|
||||
stats := statistics.NewStatistics(federationDB, cfg.FederationMaxRetries+1, cfg.P2PFederationRetriesUntilAssumedOffline+1, cfg.EnableRelays)
|
||||
|
||||
js, nats := natsInstance.Prepare(processContext, &cfg.Matrix.JetStream)
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@ func TestFederationClientQueryKeys(t *testing.T) {
|
|||
},
|
||||
}
|
||||
fedClient := &testFedClient{}
|
||||
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline)
|
||||
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline, false)
|
||||
queues := queue.NewOutgoingQueues(
|
||||
testDB, process.NewProcessContext(),
|
||||
false,
|
||||
|
@ -92,7 +92,7 @@ func TestFederationClientQueryKeysBlacklisted(t *testing.T) {
|
|||
},
|
||||
}
|
||||
fedClient := &testFedClient{}
|
||||
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline)
|
||||
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline, false)
|
||||
queues := queue.NewOutgoingQueues(
|
||||
testDB, process.NewProcessContext(),
|
||||
false,
|
||||
|
@ -122,7 +122,7 @@ func TestFederationClientQueryKeysFailure(t *testing.T) {
|
|||
},
|
||||
}
|
||||
fedClient := &testFedClient{shouldFail: true}
|
||||
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline)
|
||||
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline, false)
|
||||
queues := queue.NewOutgoingQueues(
|
||||
testDB, process.NewProcessContext(),
|
||||
false,
|
||||
|
@ -152,7 +152,7 @@ func TestFederationClientClaimKeys(t *testing.T) {
|
|||
},
|
||||
}
|
||||
fedClient := &testFedClient{}
|
||||
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline)
|
||||
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline, false)
|
||||
queues := queue.NewOutgoingQueues(
|
||||
testDB, process.NewProcessContext(),
|
||||
false,
|
||||
|
@ -183,7 +183,7 @@ func TestFederationClientClaimKeysBlacklisted(t *testing.T) {
|
|||
},
|
||||
}
|
||||
fedClient := &testFedClient{}
|
||||
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline)
|
||||
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline, false)
|
||||
queues := queue.NewOutgoingQueues(
|
||||
testDB, process.NewProcessContext(),
|
||||
false,
|
||||
|
|
|
@ -66,7 +66,7 @@ func TestPerformWakeupServers(t *testing.T) {
|
|||
},
|
||||
}
|
||||
fedClient := &testFedClient{}
|
||||
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline)
|
||||
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline, true)
|
||||
queues := queue.NewOutgoingQueues(
|
||||
testDB, process.NewProcessContext(),
|
||||
false,
|
||||
|
@ -112,7 +112,7 @@ func TestQueryRelayServers(t *testing.T) {
|
|||
},
|
||||
}
|
||||
fedClient := &testFedClient{}
|
||||
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline)
|
||||
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline, false)
|
||||
queues := queue.NewOutgoingQueues(
|
||||
testDB, process.NewProcessContext(),
|
||||
false,
|
||||
|
@ -153,7 +153,7 @@ func TestRemoveRelayServers(t *testing.T) {
|
|||
},
|
||||
}
|
||||
fedClient := &testFedClient{}
|
||||
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline)
|
||||
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline, false)
|
||||
queues := queue.NewOutgoingQueues(
|
||||
testDB, process.NewProcessContext(),
|
||||
false,
|
||||
|
@ -193,7 +193,7 @@ func TestPerformDirectoryLookup(t *testing.T) {
|
|||
},
|
||||
}
|
||||
fedClient := &testFedClient{}
|
||||
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline)
|
||||
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline, false)
|
||||
queues := queue.NewOutgoingQueues(
|
||||
testDB, process.NewProcessContext(),
|
||||
false,
|
||||
|
@ -232,7 +232,7 @@ func TestPerformDirectoryLookupRelaying(t *testing.T) {
|
|||
},
|
||||
}
|
||||
fedClient := &testFedClient{}
|
||||
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline)
|
||||
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline, true)
|
||||
queues := queue.NewOutgoingQueues(
|
||||
testDB, process.NewProcessContext(),
|
||||
false,
|
||||
|
|
|
@ -19,6 +19,7 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/matrix-org/gomatrix"
|
||||
|
@ -26,7 +27,6 @@ import (
|
|||
"github.com/matrix-org/gomatrixserverlib/fclient"
|
||||
"github.com/matrix-org/gomatrixserverlib/spec"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.uber.org/atomic"
|
||||
|
||||
"github.com/matrix-org/dendrite/federationapi/statistics"
|
||||
"github.com/matrix-org/dendrite/federationapi/storage"
|
||||
|
@ -294,6 +294,10 @@ func (oq *destinationQueue) checkNotificationsOnClose() {
|
|||
|
||||
// backgroundSend is the worker goroutine for sending events.
|
||||
func (oq *destinationQueue) backgroundSend() {
|
||||
// Don't try to send transactions if we are shutting down.
|
||||
if oq.process.Context().Err() != nil {
|
||||
return
|
||||
}
|
||||
// Check if a worker is already running, and if it isn't, then
|
||||
// mark it as started.
|
||||
if !oq.running.CompareAndSwap(false, true) {
|
||||
|
|
|
@ -18,6 +18,7 @@ import (
|
|||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -26,7 +27,6 @@ import (
|
|||
"github.com/matrix-org/dendrite/test/testrig"
|
||||
"github.com/matrix-org/gomatrixserverlib/fclient"
|
||||
"github.com/matrix-org/gomatrixserverlib/spec"
|
||||
"go.uber.org/atomic"
|
||||
"gotest.tools/v3/poll"
|
||||
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
|
@ -113,11 +113,11 @@ func testSetup(failuresUntilBlacklist uint32, failuresUntilAssumedOffline uint32
|
|||
fc := &stubFederationClient{
|
||||
shouldTxSucceed: shouldTxSucceed,
|
||||
shouldTxRelaySucceed: shouldTxRelaySucceed,
|
||||
txCount: *atomic.NewUint32(0),
|
||||
txRelayCount: *atomic.NewUint32(0),
|
||||
txCount: atomic.Uint32{},
|
||||
txRelayCount: atomic.Uint32{},
|
||||
}
|
||||
|
||||
stats := statistics.NewStatistics(db, failuresUntilBlacklist, failuresUntilAssumedOffline)
|
||||
stats := statistics.NewStatistics(db, failuresUntilBlacklist, failuresUntilAssumedOffline, false)
|
||||
signingInfo := []*fclient.SigningIdentity{
|
||||
{
|
||||
KeyID: "ed21019:auto",
|
||||
|
|
|
@ -146,7 +146,7 @@ func QueryRoomHierarchy(httpReq *http.Request, request *fclient.FederationReques
|
|||
}
|
||||
|
||||
walker := roomserverAPI.NewRoomHierarchyWalker(types.NewServerNameNotDevice(request.Origin()), roomID, suggestedOnly, 1)
|
||||
discoveredRooms, _, err := rsAPI.QueryNextRoomHierarchyPage(httpReq.Context(), walker, -1)
|
||||
discoveredRooms, inaccessibleRooms, _, err := rsAPI.QueryNextRoomHierarchyPage(httpReq.Context(), walker, -1)
|
||||
|
||||
if err != nil {
|
||||
switch err.(type) {
|
||||
|
@ -175,8 +175,9 @@ func QueryRoomHierarchy(httpReq *http.Request, request *fclient.FederationReques
|
|||
return util.JSONResponse{
|
||||
Code: 200,
|
||||
JSON: fclient.RoomHierarchyResponse{
|
||||
Room: discoveredRooms[0],
|
||||
Children: discoveredRooms[1:],
|
||||
Room: discoveredRooms[0],
|
||||
Children: discoveredRooms[1:],
|
||||
InaccessibleChildren: inaccessibleRooms,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -647,6 +647,8 @@ func MakeFedAPI(
|
|||
// add the user to Sentry, if enabled
|
||||
hub := sentry.GetHubFromContext(req.Context())
|
||||
if hub != nil {
|
||||
// clone the hub, so we don't send garbage events with e.g. mismatching rooms/event_ids
|
||||
hub = hub.Clone()
|
||||
hub.Scope().SetTag("origin", string(fedReq.Origin()))
|
||||
hub.Scope().SetTag("uri", fedReq.RequestURI())
|
||||
}
|
||||
|
|
|
@ -5,10 +5,10 @@ import (
|
|||
"math"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.uber.org/atomic"
|
||||
|
||||
"github.com/matrix-org/dendrite/federationapi/storage"
|
||||
"github.com/matrix-org/gomatrixserverlib/spec"
|
||||
|
@ -34,12 +34,15 @@ type Statistics struct {
|
|||
// mark the destination as offline. At this point we should attempt
|
||||
// to send messages to the user's async relay servers if we know them.
|
||||
FailuresUntilAssumedOffline uint32
|
||||
|
||||
enableRelays bool
|
||||
}
|
||||
|
||||
func NewStatistics(
|
||||
db storage.Database,
|
||||
failuresUntilBlacklist uint32,
|
||||
failuresUntilAssumedOffline uint32,
|
||||
enableRelays bool,
|
||||
) Statistics {
|
||||
return Statistics{
|
||||
DB: db,
|
||||
|
@ -47,6 +50,7 @@ func NewStatistics(
|
|||
FailuresUntilAssumedOffline: failuresUntilAssumedOffline,
|
||||
backoffTimers: make(map[spec.ServerName]*time.Timer),
|
||||
servers: make(map[spec.ServerName]*ServerStatistics),
|
||||
enableRelays: enableRelays,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -73,6 +77,13 @@ func (s *Statistics) ForServer(serverName spec.ServerName) *ServerStatistics {
|
|||
} else {
|
||||
server.blacklisted.Store(blacklisted)
|
||||
}
|
||||
|
||||
// Don't bother hitting the database 2 additional times
|
||||
// if we don't want to use relays.
|
||||
if !s.enableRelays {
|
||||
return server
|
||||
}
|
||||
|
||||
assumedOffline, err := s.DB.IsServerAssumedOffline(context.Background(), serverName)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Errorf("Failed to get assumed offline entry %q", serverName)
|
||||
|
@ -158,7 +169,7 @@ func (s *ServerStatistics) Success(method SendMethod) {
|
|||
// NOTE : Sending to the final destination vs. a relay server has
|
||||
// slightly different semantics.
|
||||
if method == SendDirect {
|
||||
s.successCounter.Inc()
|
||||
s.successCounter.Add(1)
|
||||
if s.blacklisted.Load() && s.statistics.DB != nil {
|
||||
if err := s.statistics.DB.RemoveServerFromBlacklist(s.serverName); err != nil {
|
||||
logrus.WithError(err).Errorf("Failed to remove %q from blacklist", s.serverName)
|
||||
|
@ -184,7 +195,7 @@ func (s *ServerStatistics) Failure() (time.Time, bool) {
|
|||
// start a goroutine which will wait out the backoff and
|
||||
// unset the backoffStarted flag when done.
|
||||
if s.backoffStarted.CompareAndSwap(false, true) {
|
||||
backoffCount := s.backoffCount.Inc()
|
||||
backoffCount := s.backoffCount.Add(1)
|
||||
|
||||
if backoffCount >= s.statistics.FailuresUntilAssumedOffline {
|
||||
s.assumedOffline.CompareAndSwap(false, true)
|
||||
|
|
|
@ -16,7 +16,7 @@ const (
|
|||
)
|
||||
|
||||
func TestBackoff(t *testing.T) {
|
||||
stats := NewStatistics(nil, FailuresUntilBlacklist, FailuresUntilAssumedOffline)
|
||||
stats := NewStatistics(nil, FailuresUntilBlacklist, FailuresUntilAssumedOffline, false)
|
||||
server := ServerStatistics{
|
||||
statistics: &stats,
|
||||
serverName: "test.com",
|
||||
|
@ -106,7 +106,7 @@ func TestBackoff(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestRelayServersListing(t *testing.T) {
|
||||
stats := NewStatistics(test.NewInMemoryFederationDatabase(), FailuresUntilBlacklist, FailuresUntilAssumedOffline)
|
||||
stats := NewStatistics(test.NewInMemoryFederationDatabase(), FailuresUntilBlacklist, FailuresUntilAssumedOffline, false)
|
||||
server := ServerStatistics{statistics: &stats}
|
||||
server.AddRelayServers([]spec.ServerName{"relay1", "relay1", "relay2"})
|
||||
relayServers := server.KnownRelayServers()
|
||||
|
|
4
federationapi/storage/cache/keydb.go
vendored
4
federationapi/storage/cache/keydb.go
vendored
|
@ -46,6 +46,10 @@ func (d *KeyDatabase) FetchKeys(
|
|||
delete(requests, req)
|
||||
}
|
||||
}
|
||||
// Don't bother hitting the DB if we got everything from cache.
|
||||
if len(requests) == 0 {
|
||||
return results, nil
|
||||
}
|
||||
fromDB, err := d.inner.FetchKeys(ctx, requests)
|
||||
if err != nil {
|
||||
return results, err
|
||||
|
|
96
go.mod
96
go.mod
|
@ -6,14 +6,14 @@ require (
|
|||
github.com/DATA-DOG/go-sqlmock v1.5.0
|
||||
github.com/MFAshby/stdemuxerhook v1.0.0
|
||||
github.com/Masterminds/semver/v3 v3.1.1
|
||||
github.com/blevesearch/bleve/v2 v2.3.8
|
||||
github.com/blevesearch/bleve/v2 v2.4.0
|
||||
github.com/codeclysm/extract v2.2.0+incompatible
|
||||
github.com/dgraph-io/ristretto v0.1.1
|
||||
github.com/docker/docker v24.0.7+incompatible
|
||||
github.com/docker/docker v24.0.9+incompatible
|
||||
github.com/docker/go-connections v0.4.0
|
||||
github.com/getsentry/sentry-go v0.14.0
|
||||
github.com/gologme/log v1.3.0
|
||||
github.com/google/go-cmp v0.5.9
|
||||
github.com/google/go-cmp v0.6.0
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
|
@ -22,10 +22,10 @@ require (
|
|||
github.com/matrix-org/dugong v0.0.0-20210921133753-66e6b1c67e2e
|
||||
github.com/matrix-org/go-sqlite3-js v0.0.0-20220419092513-28aa791a1c91
|
||||
github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530
|
||||
github.com/matrix-org/gomatrixserverlib v0.0.0-20240109180417-3495e573f2b7
|
||||
github.com/matrix-org/gomatrixserverlib v0.0.0-20240328203753-c2391f7113a5
|
||||
github.com/matrix-org/pinecone v0.11.1-0.20230810010612-ea4c33717fd7
|
||||
github.com/matrix-org/util v0.0.0-20221111132719-399730281e66
|
||||
github.com/mattn/go-sqlite3 v1.14.17
|
||||
github.com/mattn/go-sqlite3 v1.14.22
|
||||
github.com/nats-io/nats-server/v2 v2.10.7
|
||||
github.com/nats-io/nats.go v1.31.0
|
||||
github.com/neilalexander/utp v0.1.1-0.20210727203401-54ae7b1cd5f9
|
||||
|
@ -33,26 +33,26 @@ require (
|
|||
github.com/opentracing/opentracing-go v1.2.0
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/prometheus/client_golang v1.16.0
|
||||
github.com/prometheus/client_golang v1.19.0
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/stretchr/testify v1.8.2
|
||||
github.com/tidwall/gjson v1.17.0
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/tidwall/gjson v1.17.1
|
||||
github.com/tidwall/sjson v1.2.5
|
||||
github.com/uber/jaeger-client-go v2.30.0+incompatible
|
||||
github.com/uber/jaeger-lib v2.4.1+incompatible
|
||||
github.com/yggdrasil-network/yggdrasil-go v0.4.6
|
||||
go.uber.org/atomic v1.10.0
|
||||
golang.org/x/crypto v0.17.0
|
||||
golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819
|
||||
golang.org/x/image v0.10.0
|
||||
go.uber.org/atomic v1.11.0
|
||||
golang.org/x/crypto v0.23.0
|
||||
golang.org/x/exp v0.0.0-20231108232855-2478ac86f678
|
||||
golang.org/x/image v0.18.0
|
||||
golang.org/x/mobile v0.0.0-20221020085226-b36e6246172e
|
||||
golang.org/x/sync v0.3.0
|
||||
golang.org/x/term v0.15.0
|
||||
golang.org/x/sync v0.7.0
|
||||
golang.org/x/term v0.20.0
|
||||
gopkg.in/h2non/bimg.v1 v1.1.9
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
gotest.tools/v3 v3.4.0
|
||||
maunium.net/go/mautrix v0.15.1
|
||||
modernc.org/sqlite v1.23.1
|
||||
modernc.org/sqlite v1.29.5
|
||||
nhooyr.io/websocket v1.8.7
|
||||
)
|
||||
|
||||
|
@ -62,21 +62,23 @@ require (
|
|||
github.com/RoaringBitmap/roaring v1.2.3 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.5.0 // indirect
|
||||
github.com/blevesearch/bleve_index_api v1.0.5 // indirect
|
||||
github.com/blevesearch/geo v0.1.17 // indirect
|
||||
github.com/blevesearch/bleve_index_api v1.1.6 // indirect
|
||||
github.com/blevesearch/geo v0.1.20 // indirect
|
||||
github.com/blevesearch/go-faiss v1.0.13 // indirect
|
||||
github.com/blevesearch/go-porterstemmer v1.0.3 // indirect
|
||||
github.com/blevesearch/gtreap v0.1.1 // indirect
|
||||
github.com/blevesearch/mmap-go v1.0.4 // indirect
|
||||
github.com/blevesearch/scorch_segment_api/v2 v2.1.4 // indirect
|
||||
github.com/blevesearch/scorch_segment_api/v2 v2.2.9 // indirect
|
||||
github.com/blevesearch/segment v0.9.1 // indirect
|
||||
github.com/blevesearch/snowballstem v0.9.0 // indirect
|
||||
github.com/blevesearch/upsidedown_store_api v1.0.2 // indirect
|
||||
github.com/blevesearch/vellum v1.0.9 // indirect
|
||||
github.com/blevesearch/zapx/v11 v11.3.7 // indirect
|
||||
github.com/blevesearch/zapx/v12 v12.3.7 // indirect
|
||||
github.com/blevesearch/zapx/v13 v13.3.7 // indirect
|
||||
github.com/blevesearch/zapx/v14 v14.3.7 // indirect
|
||||
github.com/blevesearch/zapx/v15 v15.3.10 // indirect
|
||||
github.com/blevesearch/vellum v1.0.10 // indirect
|
||||
github.com/blevesearch/zapx/v11 v11.3.10 // indirect
|
||||
github.com/blevesearch/zapx/v12 v12.3.10 // indirect
|
||||
github.com/blevesearch/zapx/v13 v13.3.10 // indirect
|
||||
github.com/blevesearch/zapx/v14 v14.3.10 // indirect
|
||||
github.com/blevesearch/zapx/v15 v15.3.13 // indirect
|
||||
github.com/blevesearch/zapx/v16 v16.0.12 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/docker/distribution v2.8.2+incompatible // indirect
|
||||
|
@ -91,56 +93,52 @@ require (
|
|||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/pprof v0.0.0-20230808223545-4887780b67fb // indirect
|
||||
github.com/h2non/filetype v1.1.3 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/juju/errors v1.0.0 // indirect
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/klauspost/compress v1.17.4 // indirect
|
||||
github.com/klauspost/compress v1.17.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/minio/highwayhash v1.0.2 // indirect
|
||||
github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/mschoch/smat v0.2.0 // indirect
|
||||
github.com/nats-io/jwt/v2 v2.5.3 // indirect
|
||||
github.com/nats-io/nkeys v0.4.6 // indirect
|
||||
github.com/nats-io/jwt/v2 v2.5.5 // indirect
|
||||
github.com/nats-io/nkeys v0.4.7 // indirect
|
||||
github.com/nats-io/nuid v1.0.1 // indirect
|
||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.11.0 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_model v0.3.0 // indirect
|
||||
github.com/prometheus/common v0.42.0 // indirect
|
||||
github.com/prometheus/procfs v0.10.1 // indirect
|
||||
github.com/prometheus/client_model v0.5.0 // indirect
|
||||
github.com/prometheus/common v0.48.0 // indirect
|
||||
github.com/prometheus/procfs v0.12.0 // indirect
|
||||
github.com/quic-go/qtls-go1-20 v0.3.2 // indirect
|
||||
github.com/quic-go/quic-go v0.37.7 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/rogpeppe/go-internal v1.9.0 // indirect
|
||||
github.com/rs/zerolog v1.29.1 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
go.etcd.io/bbolt v1.3.6 // indirect
|
||||
golang.org/x/mod v0.12.0 // indirect
|
||||
golang.org/x/net v0.17.0 // indirect
|
||||
golang.org/x/sys v0.15.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
go.etcd.io/bbolt v1.3.7 // indirect
|
||||
golang.org/x/mod v0.17.0 // indirect
|
||||
golang.org/x/net v0.25.0 // indirect
|
||||
golang.org/x/sys v0.20.0 // indirect
|
||||
golang.org/x/text v0.16.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/tools v0.12.0 // indirect
|
||||
google.golang.org/protobuf v1.30.0 // indirect
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
|
||||
google.golang.org/protobuf v1.33.0 // indirect
|
||||
gopkg.in/macaroon.v2 v2.1.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
lukechampine.com/uint128 v1.2.0 // indirect
|
||||
maunium.net/go/maulogger/v2 v2.4.1 // indirect
|
||||
modernc.org/cc/v3 v3.40.0 // indirect
|
||||
modernc.org/ccgo/v3 v3.16.13 // indirect
|
||||
modernc.org/libc v1.22.5 // indirect
|
||||
modernc.org/mathutil v1.5.0 // indirect
|
||||
modernc.org/memory v1.5.0 // indirect
|
||||
modernc.org/opt v0.1.3 // indirect
|
||||
modernc.org/strutil v1.1.3 // indirect
|
||||
modernc.org/token v1.0.1 // indirect
|
||||
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect
|
||||
modernc.org/libc v1.41.0 // indirect
|
||||
modernc.org/mathutil v1.6.0 // indirect
|
||||
modernc.org/memory v1.7.2 // indirect
|
||||
modernc.org/strutil v1.2.0 // indirect
|
||||
modernc.org/token v1.1.0 // indirect
|
||||
)
|
||||
|
||||
go 1.20
|
||||
|
|
226
go.sum
226
go.sum
|
@ -36,38 +36,42 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
|
|||
github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
|
||||
github.com/bits-and-blooms/bitset v1.5.0 h1:NpE8frKRLGHIcEzkR+gZhiioW1+WbYV6fKwD6ZIpQT8=
|
||||
github.com/bits-and-blooms/bitset v1.5.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
|
||||
github.com/blevesearch/bleve/v2 v2.3.8 h1:IqFyMJ73n4gY8AmVqM8Sa6EtAZ5beE8yramVqCvs2kQ=
|
||||
github.com/blevesearch/bleve/v2 v2.3.8/go.mod h1:Lh9aZEHrLKxwPnW4z4lsBEGnflZQ1V/aWP/t+htsiDw=
|
||||
github.com/blevesearch/bleve_index_api v1.0.5 h1:Lc986kpC4Z0/n1g3gg8ul7H+lxgOQPcXb9SxvQGu+tw=
|
||||
github.com/blevesearch/bleve_index_api v1.0.5/go.mod h1:YXMDwaXFFXwncRS8UobWs7nvo0DmusriM1nztTlj1ms=
|
||||
github.com/blevesearch/geo v0.1.17 h1:AguzI6/5mHXapzB0gE9IKWo+wWPHZmXZoscHcjFgAFA=
|
||||
github.com/blevesearch/geo v0.1.17/go.mod h1:uRMGWG0HJYfWfFJpK3zTdnnr1K+ksZTuWKhXeSokfnM=
|
||||
github.com/blevesearch/bleve/v2 v2.4.0 h1:2xyg+Wv60CFHYccXc+moGxbL+8QKT/dZK09AewHgKsg=
|
||||
github.com/blevesearch/bleve/v2 v2.4.0/go.mod h1:IhQHoFAbHgWKYavb9rQgQEJJVMuY99cKdQ0wPpst2aY=
|
||||
github.com/blevesearch/bleve_index_api v1.1.6 h1:orkqDFCBuNU2oHW9hN2YEJmet+TE9orml3FCGbl1cKk=
|
||||
github.com/blevesearch/bleve_index_api v1.1.6/go.mod h1:PbcwjIcRmjhGbkS/lJCpfgVSMROV6TRubGGAODaK1W8=
|
||||
github.com/blevesearch/geo v0.1.20 h1:paaSpu2Ewh/tn5DKn/FB5SzvH0EWupxHEIwbCk/QPqM=
|
||||
github.com/blevesearch/geo v0.1.20/go.mod h1:DVG2QjwHNMFmjo+ZgzrIq2sfCh6rIHzy9d9d0B59I6w=
|
||||
github.com/blevesearch/go-faiss v1.0.13 h1:zfFs7ZYD0NqXVSY37j0JZjZT1BhE9AE4peJfcx/NB4A=
|
||||
github.com/blevesearch/go-faiss v1.0.13/go.mod h1:jrxHrbl42X/RnDPI+wBoZU8joxxuRwedrxqswQ3xfU8=
|
||||
github.com/blevesearch/go-porterstemmer v1.0.3 h1:GtmsqID0aZdCSNiY8SkuPJ12pD4jI+DdXTAn4YRcHCo=
|
||||
github.com/blevesearch/go-porterstemmer v1.0.3/go.mod h1:angGc5Ht+k2xhJdZi511LtmxuEf0OVpvUUNrwmM1P7M=
|
||||
github.com/blevesearch/gtreap v0.1.1 h1:2JWigFrzDMR+42WGIN/V2p0cUvn4UP3C4Q5nmaZGW8Y=
|
||||
github.com/blevesearch/gtreap v0.1.1/go.mod h1:QaQyDRAT51sotthUWAH4Sj08awFSSWzgYICSZ3w0tYk=
|
||||
github.com/blevesearch/mmap-go v1.0.4 h1:OVhDhT5B/M1HNPpYPBKIEJaD0F3Si+CrEKULGCDPWmc=
|
||||
github.com/blevesearch/mmap-go v1.0.4/go.mod h1:EWmEAOmdAS9z/pi/+Toxu99DnsbhG1TIxUoRmJw/pSs=
|
||||
github.com/blevesearch/scorch_segment_api/v2 v2.1.4 h1:LmGmo5twU3gV+natJbKmOktS9eMhokPGKWuR+jX84vk=
|
||||
github.com/blevesearch/scorch_segment_api/v2 v2.1.4/go.mod h1:PgVnbbg/t1UkgezPDu8EHLi1BHQ17xUwsFdU6NnOYS0=
|
||||
github.com/blevesearch/scorch_segment_api/v2 v2.2.9 h1:3nBaSBRFokjE4FtPW3eUDgcAu3KphBg1GP07zy/6Uyk=
|
||||
github.com/blevesearch/scorch_segment_api/v2 v2.2.9/go.mod h1:ckbeb7knyOOvAdZinn/ASbB7EA3HoagnJkmEV3J7+sg=
|
||||
github.com/blevesearch/segment v0.9.1 h1:+dThDy+Lvgj5JMxhmOVlgFfkUtZV2kw49xax4+jTfSU=
|
||||
github.com/blevesearch/segment v0.9.1/go.mod h1:zN21iLm7+GnBHWTao9I+Au/7MBiL8pPFtJBJTsk6kQw=
|
||||
github.com/blevesearch/snowballstem v0.9.0 h1:lMQ189YspGP6sXvZQ4WZ+MLawfV8wOmPoD/iWeNXm8s=
|
||||
github.com/blevesearch/snowballstem v0.9.0/go.mod h1:PivSj3JMc8WuaFkTSRDW2SlrulNWPl4ABg1tC/hlgLs=
|
||||
github.com/blevesearch/upsidedown_store_api v1.0.2 h1:U53Q6YoWEARVLd1OYNc9kvhBMGZzVrdmaozG2MfoB+A=
|
||||
github.com/blevesearch/upsidedown_store_api v1.0.2/go.mod h1:M01mh3Gpfy56Ps/UXHjEO/knbqyQ1Oamg8If49gRwrQ=
|
||||
github.com/blevesearch/vellum v1.0.9 h1:PL+NWVk3dDGPCV0hoDu9XLLJgqU4E5s/dOeEJByQ2uQ=
|
||||
github.com/blevesearch/vellum v1.0.9/go.mod h1:ul1oT0FhSMDIExNjIxHqJoGpVrBpKCdgDQNxfqgJt7k=
|
||||
github.com/blevesearch/zapx/v11 v11.3.7 h1:Y6yIAF/DVPiqZUA/jNgSLXmqewfzwHzuwfKyfdG+Xaw=
|
||||
github.com/blevesearch/zapx/v11 v11.3.7/go.mod h1:Xk9Z69AoAWIOvWudNDMlxJDqSYGf90LS0EfnaAIvXCA=
|
||||
github.com/blevesearch/zapx/v12 v12.3.7 h1:DfQ6rsmZfEK4PzzJJRXjiM6AObG02+HWvprlXQ1Y7eI=
|
||||
github.com/blevesearch/zapx/v12 v12.3.7/go.mod h1:SgEtYIBGvM0mgIBn2/tQE/5SdrPXaJUaT/kVqpAPxm0=
|
||||
github.com/blevesearch/zapx/v13 v13.3.7 h1:igIQg5eKmjw168I7av0Vtwedf7kHnQro/M+ubM4d2l8=
|
||||
github.com/blevesearch/zapx/v13 v13.3.7/go.mod h1:yyrB4kJ0OT75UPZwT/zS+Ru0/jYKorCOOSY5dBzAy+s=
|
||||
github.com/blevesearch/zapx/v14 v14.3.7 h1:gfe+fbWslDWP/evHLtp/GOvmNM3sw1BbqD7LhycBX20=
|
||||
github.com/blevesearch/zapx/v14 v14.3.7/go.mod h1:9J/RbOkqZ1KSjmkOes03AkETX7hrXT0sFMpWH4ewC4w=
|
||||
github.com/blevesearch/zapx/v15 v15.3.10 h1:bQ9ZxJCj6rKp873EuVJu2JPxQ+EWQZI1cjJGeroovaQ=
|
||||
github.com/blevesearch/zapx/v15 v15.3.10/go.mod h1:m7Y6m8soYUvS7MjN9eKlz1xrLCcmqfFadmu7GhWIrLY=
|
||||
github.com/blevesearch/vellum v1.0.10 h1:HGPJDT2bTva12hrHepVT3rOyIKFFF4t7Gf6yMxyMIPI=
|
||||
github.com/blevesearch/vellum v1.0.10/go.mod h1:ul1oT0FhSMDIExNjIxHqJoGpVrBpKCdgDQNxfqgJt7k=
|
||||
github.com/blevesearch/zapx/v11 v11.3.10 h1:hvjgj9tZ9DeIqBCxKhi70TtSZYMdcFn7gDb71Xo/fvk=
|
||||
github.com/blevesearch/zapx/v11 v11.3.10/go.mod h1:0+gW+FaE48fNxoVtMY5ugtNHHof/PxCqh7CnhYdnMzQ=
|
||||
github.com/blevesearch/zapx/v12 v12.3.10 h1:yHfj3vXLSYmmsBleJFROXuO08mS3L1qDCdDK81jDl8s=
|
||||
github.com/blevesearch/zapx/v12 v12.3.10/go.mod h1:0yeZg6JhaGxITlsS5co73aqPtM04+ycnI6D1v0mhbCs=
|
||||
github.com/blevesearch/zapx/v13 v13.3.10 h1:0KY9tuxg06rXxOZHg3DwPJBjniSlqEgVpxIqMGahDE8=
|
||||
github.com/blevesearch/zapx/v13 v13.3.10/go.mod h1:w2wjSDQ/WBVeEIvP0fvMJZAzDwqwIEzVPnCPrz93yAk=
|
||||
github.com/blevesearch/zapx/v14 v14.3.10 h1:SG6xlsL+W6YjhX5N3aEiL/2tcWh3DO75Bnz77pSwwKU=
|
||||
github.com/blevesearch/zapx/v14 v14.3.10/go.mod h1:qqyuR0u230jN1yMmE4FIAuCxmahRQEOehF78m6oTgns=
|
||||
github.com/blevesearch/zapx/v15 v15.3.13 h1:6EkfaZiPlAxqXz0neniq35my6S48QI94W/wyhnpDHHQ=
|
||||
github.com/blevesearch/zapx/v15 v15.3.13/go.mod h1:Turk/TNRKj9es7ZpKK95PS7f6D44Y7fAFy8F4LXQtGg=
|
||||
github.com/blevesearch/zapx/v16 v16.0.12 h1:Uccxvjmn+hQ6ywQP+wIiTpdq9LnAviGoryJOmGwAo/I=
|
||||
github.com/blevesearch/zapx/v16 v16.0.12/go.mod h1:MYnOshRfSm4C4drxx1LGRI+MVFByykJ2anDY1fxdk9Q=
|
||||
github.com/bradfitz/iter v0.0.0-20140124041915-454541ec3da2/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo=
|
||||
github.com/bradfitz/iter v0.0.0-20190303215204-33e6a9893b0c/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo=
|
||||
github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8 h1:GKTyiRCL6zVf5wWaqKnf+7Qs6GbEPfd4iMOitWzXJx8=
|
||||
|
@ -89,8 +93,8 @@ github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczC
|
|||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
|
||||
github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM=
|
||||
github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v24.0.9+incompatible h1:HPGzNmwfLZWdxHqK9/II92pyi1EpYKsAqcl4G0Of9v0=
|
||||
github.com/docker/docker v24.0.9+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||
|
@ -141,7 +145,6 @@ github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ=
|
|||
github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
|
||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
|
@ -158,8 +161,8 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
|
|||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20230808223545-4887780b67fb h1:oqpb3Cwpc7EOml5PVGMYbSGmwNui2R7i8IW83gs4W0c=
|
||||
github.com/google/pprof v0.0.0-20230808223545-4887780b67fb/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA=
|
||||
|
@ -174,6 +177,8 @@ github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad
|
|||
github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg=
|
||||
github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/huandu/xstrings v1.0.0 h1:pO2K/gKgKaat5LdpAhxhluX2GPQMaI3W5FUz/I/UnWk=
|
||||
github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
|
@ -185,13 +190,11 @@ github.com/juju/errors v1.0.0/go.mod h1:B5x9thDqx0wIMH3+aLIMP9HjItInYWObRovoCFM5
|
|||
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
|
||||
github.com/kardianos/minwinsvc v1.0.2 h1:JmZKFJQrmTGa/WiW+vkJXKmfzdjabuEW4Tirj5lLdR0=
|
||||
github.com/kardianos/minwinsvc v1.0.2/go.mod h1:LUZNYhNmxujx2tR7FbdxqYJ9XDDoCd3MQcl1o//FWl4=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
|
||||
github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
|
||||
github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg=
|
||||
github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
|
@ -208,8 +211,8 @@ github.com/matrix-org/go-sqlite3-js v0.0.0-20220419092513-28aa791a1c91 h1:s7fexw
|
|||
github.com/matrix-org/go-sqlite3-js v0.0.0-20220419092513-28aa791a1c91/go.mod h1:e+cg2q7C7yE5QnAXgzo512tgFh1RbQLC0+jozuegKgo=
|
||||
github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530 h1:kHKxCOLcHH8r4Fzarl4+Y3K5hjothkVW5z7T1dUM11U=
|
||||
github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530/go.mod h1:/gBX06Kw0exX1HrwmoBibFA98yBk/jxKpGVeyQbff+s=
|
||||
github.com/matrix-org/gomatrixserverlib v0.0.0-20240109180417-3495e573f2b7 h1:EaUvK2ay6cxMxeshC1p6QswS9+rQFbUc2YerkRFyVXQ=
|
||||
github.com/matrix-org/gomatrixserverlib v0.0.0-20240109180417-3495e573f2b7/go.mod h1:HZGsVJ3bUE+DkZtufkH9H0mlsvbhEGK5CpX0Zlavylg=
|
||||
github.com/matrix-org/gomatrixserverlib v0.0.0-20240328203753-c2391f7113a5 h1:GuxmpyjZQoqb6UFQgKq8Td3wIITlXln/sItqp1jbTTA=
|
||||
github.com/matrix-org/gomatrixserverlib v0.0.0-20240328203753-c2391f7113a5/go.mod h1:HZGsVJ3bUE+DkZtufkH9H0mlsvbhEGK5CpX0Zlavylg=
|
||||
github.com/matrix-org/pinecone v0.11.1-0.20230810010612-ea4c33717fd7 h1:6t8kJr8i1/1I5nNttw6nn1ryQJgzVlBmSGgPiiaTdw4=
|
||||
github.com/matrix-org/pinecone v0.11.1-0.20230810010612-ea4c33717fd7/go.mod h1:ReWMS/LoVnOiRAdq9sNUC2NZnd1mZkMNB52QhpTRWjg=
|
||||
github.com/matrix-org/util v0.0.0-20221111132719-399730281e66 h1:6z4KxomXSIGWqhHcfzExgkH3Z3UkIXry4ibJS4Aqz2Y=
|
||||
|
@ -222,10 +225,8 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k
|
|||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
||||
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM=
|
||||
github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
|
||||
github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g=
|
||||
github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY=
|
||||
|
@ -242,16 +243,18 @@ github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7P
|
|||
github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg=
|
||||
github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM=
|
||||
github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw=
|
||||
github.com/nats-io/jwt/v2 v2.5.3 h1:/9SWvzc6hTfamcgXJ3uYRpgj+QuY2aLNqRiqrKcrpEo=
|
||||
github.com/nats-io/jwt/v2 v2.5.3/go.mod h1:iysuPemFcc7p4IoYots3IuELSI4EDe9Y0bQMe+I3Bf4=
|
||||
github.com/nats-io/jwt/v2 v2.5.5 h1:ROfXb50elFq5c9+1ztaUbdlrArNFl2+fQWP6B8HGEq4=
|
||||
github.com/nats-io/jwt/v2 v2.5.5/go.mod h1:ZdWS1nZa6WMZfFwwgpEaqBV8EPGVgOTDHN/wTbz0Y5A=
|
||||
github.com/nats-io/nats-server/v2 v2.10.7 h1:f5VDy+GMu7JyuFA0Fef+6TfulfCs5nBTgq7MMkFJx5Y=
|
||||
github.com/nats-io/nats-server/v2 v2.10.7/go.mod h1:V2JHOvPiPdtfDXTuEUsthUnCvSDeFrK4Xn9hRo6du7c=
|
||||
github.com/nats-io/nats.go v1.31.0 h1:/WFBHEc/dOKBF6qf1TZhrdEfTmOZ5JzdJ+Y3m6Y/p7E=
|
||||
github.com/nats-io/nats.go v1.31.0/go.mod h1:di3Bm5MLsoB4Bx61CBTsxuarI36WbhAwOm8QrW39+i8=
|
||||
github.com/nats-io/nkeys v0.4.6 h1:IzVe95ru2CT6ta874rt9saQRkWfe2nFj1NtvYSLqMzY=
|
||||
github.com/nats-io/nkeys v0.4.6/go.mod h1:4DxZNzenSVd1cYQoAa8948QY3QDjrHfcfVADymtkpts=
|
||||
github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI=
|
||||
github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc=
|
||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/neilalexander/utp v0.1.1-0.20210727203401-54ae7b1cd5f9 h1:lrVQzBtkeQEGGYUHwSX1XPe1E5GL6U3KYCNe2G4bncQ=
|
||||
github.com/neilalexander/utp v0.1.1-0.20210727203401-54ae7b1cd5f9/go.mod h1:NPHGhPc0/wudcaCqL/H5AOddkRf8GPRhzOujuUKGQu8=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||
|
@ -276,23 +279,21 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
|||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8=
|
||||
github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc=
|
||||
github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
|
||||
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
|
||||
github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM=
|
||||
github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=
|
||||
github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg=
|
||||
github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM=
|
||||
github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
|
||||
github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
|
||||
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
|
||||
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
|
||||
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
|
||||
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
|
||||
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
|
||||
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
|
||||
github.com/quic-go/qtls-go1-20 v0.3.2 h1:rRgN3WfnKbyik4dBV8A6girlJVxGand/d+jVKbQq5GI=
|
||||
github.com/quic-go/qtls-go1-20 v0.3.2/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
|
||||
github.com/quic-go/quic-go v0.37.7 h1:AgKsQLZ1+YCwZd2GYhBUsJDYZwEkA5gENtAjb+MxONU=
|
||||
github.com/quic-go/quic-go v0.37.7/go.mod h1:YsbH1r4mSHPJcLF4k4zruUkLBqctEMBDR6VPvcYjIsU=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.29.1 h1:cO+d60CHkknCbvzEWxP0S9K6KqyTjrCNUy1LdQLCGPc=
|
||||
github.com/rs/zerolog v1.29.1/go.mod h1:Le6ESbR7hc+DP6Lt1THiV8CQSdkkNrd3R0XbEgp3ZBU=
|
||||
|
@ -304,22 +305,18 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1
|
|||
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM=
|
||||
github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U=
|
||||
github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
|
@ -342,32 +339,30 @@ github.com/yggdrasil-network/yggdrasil-go v0.4.6/go.mod h1:PBMoAOvQjA9geNEeGyMXA
|
|||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
|
||||
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
|
||||
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
||||
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ=
|
||||
go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
|
||||
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
golang.org/x/crypto v0.0.0-20180723164146-c126467f60eb/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
|
||||
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819 h1:EDuYyU/MkFXllv9QF9819VlI9a4tzGuCbhG0ExK9o1U=
|
||||
golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
|
||||
golang.org/x/exp v0.0.0-20231108232855-2478ac86f678 h1:mchzmB1XO2pMaKFRqk/+MV3mgGG96aqaPXaMifQU47w=
|
||||
golang.org/x/exp v0.0.0-20231108232855-2478ac86f678/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
|
||||
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.10.0 h1:gXjUUtwtx5yOE0VKWq1CH4IJAClq4UGgUA3i+rpON9M=
|
||||
golang.org/x/image v0.10.0/go.mod h1:jtrku+n79PfroUbvDdeUWMAI+heR786BofxrbiSF+J0=
|
||||
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
|
||||
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mobile v0.0.0-20221020085226-b36e6246172e h1:zSgtO19fpg781xknwqiQPmOHaASr6E7ZVlTseLd9Fx4=
|
||||
golang.org/x/mobile v0.0.0-20221020085226-b36e6246172e/go.mod h1:aAjjkJNdrh3PMckS4B10TGS2nag27cbKR1y2BpUxsiY=
|
||||
|
@ -375,10 +370,8 @@ golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
|||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
|
@ -386,57 +379,43 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL
|
|||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
|
||||
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
||||
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
|
@ -450,10 +429,8 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY
|
|||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss=
|
||||
golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
@ -464,8 +441,8 @@ gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6d
|
|||
gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
@ -485,34 +462,25 @@ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|||
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
|
||||
gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o=
|
||||
gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g=
|
||||
lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
|
||||
lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
||||
maunium.net/go/maulogger/v2 v2.4.1 h1:N7zSdd0mZkB2m2JtFUsiGTQQAdP0YeFWT7YMc80yAL8=
|
||||
maunium.net/go/maulogger/v2 v2.4.1/go.mod h1:omPuYwYBILeVQobz8uO3XC8DIRuEb5rXYlQSuqrbCho=
|
||||
maunium.net/go/mautrix v0.15.1 h1:pmCtMjYRpd83+2UL+KTRFYQo5to0373yulimvLK+1k0=
|
||||
maunium.net/go/mautrix v0.15.1/go.mod h1:icQIrvz2NldkRLTuzSGzmaeuMUmw+fzO7UVycPeauN8=
|
||||
modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw=
|
||||
modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0=
|
||||
modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw=
|
||||
modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY=
|
||||
modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk=
|
||||
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
|
||||
modernc.org/libc v1.22.5 h1:91BNch/e5B0uPbJFgqbxXuOnxBQjlS//icfQEGmvyjE=
|
||||
modernc.org/libc v1.22.5/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY=
|
||||
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
|
||||
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds=
|
||||
modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
||||
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
|
||||
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||
modernc.org/sqlite v1.23.1 h1:nrSBg4aRQQwq59JpvGEQ15tNxoO5pX/kUjcRNwSAGQM=
|
||||
modernc.org/sqlite v1.23.1/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk=
|
||||
modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY=
|
||||
modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
|
||||
modernc.org/tcl v1.15.2 h1:C4ybAYCGJw968e+Me18oW55kD/FexcHbqH2xak1ROSY=
|
||||
modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg=
|
||||
modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
modernc.org/z v1.7.3 h1:zDJf6iHjrnB+WRD88stbXokugjyc0/pB91ri1gO6LZY=
|
||||
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
|
||||
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI=
|
||||
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4=
|
||||
modernc.org/libc v1.41.0 h1:g9YAc6BkKlgORsUWj+JwqoB1wU3o4DE3bM3yvA3k+Gk=
|
||||
modernc.org/libc v1.41.0/go.mod h1:w0eszPsiXoOnoMJgrXjglgLuDy/bt5RR4y3QzUUeodY=
|
||||
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
|
||||
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
|
||||
modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E=
|
||||
modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E=
|
||||
modernc.org/sqlite v1.29.5 h1:8l/SQKAjDtZFo9lkJLdk8g9JEOeYRG4/ghStDCCTiTE=
|
||||
modernc.org/sqlite v1.29.5/go.mod h1:S02dvcmm7TnTRvGhv8IGYyLnIt7AS2KPaB1F/71p75U=
|
||||
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
|
||||
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g=
|
||||
nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
apiVersion: v2
|
||||
name: dendrite
|
||||
version: "0.13.6"
|
||||
appVersion: "0.13.5"
|
||||
version: "0.14.1"
|
||||
appVersion: "0.13.7"
|
||||
description: Dendrite Matrix Homeserver
|
||||
type: application
|
||||
icon: https://avatars.githubusercontent.com/u/8418310?s=48&v=4
|
||||
keywords:
|
||||
- matrix
|
||||
- chat
|
||||
|
@ -13,7 +14,7 @@ home: https://github.com/matrix-org/dendrite
|
|||
sources:
|
||||
- https://github.com/matrix-org/dendrite
|
||||
dependencies:
|
||||
- name: postgresql
|
||||
version: 12.1.7
|
||||
repository: https://charts.bitnami.com/bitnami
|
||||
condition: postgresql.enabled
|
||||
- name: postgresql
|
||||
version: 14.2.3
|
||||
repository: https://charts.bitnami.com/bitnami
|
||||
condition: postgresql.enabled
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
# dendrite
|
||||
|
||||
![Version: 0.13.6](https://img.shields.io/badge/Version-0.13.6-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.13.5](https://img.shields.io/badge/AppVersion-0.13.5-informational?style=flat-square)
|
||||
![Version: 0.14.0](https://img.shields.io/badge/Version-0.14.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.13.7](https://img.shields.io/badge/AppVersion-0.13.7-informational?style=flat-square)
|
||||
Dendrite Matrix Homeserver
|
||||
|
||||
Status: **NOT PRODUCTION READY**
|
||||
|
@ -37,7 +37,7 @@ Create a folder `appservices` and place your configurations in there. The confi
|
|||
|
||||
| Repository | Name | Version |
|
||||
|------------|------|---------|
|
||||
| https://charts.bitnami.com/bitnami | postgresql | 12.1.7 |
|
||||
| https://charts.bitnami.com/bitnami | postgresql | 14.2.3 |
|
||||
## Values
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|
@ -48,16 +48,19 @@ Create a folder `appservices` and place your configurations in there. The confi
|
|||
| signing_key.create | bool | `true` | Create a new signing key, if not exists |
|
||||
| signing_key.existingSecret | string | `""` | Use an existing secret |
|
||||
| resources | object | sets some sane default values | Default resource requests/limits. |
|
||||
| persistence.jetstream | object | `{"capacity":"1Gi","existingClaim":""}` | The storage class to use for volume claims. Used unless specified at the specific component. Defaults to the cluster default storage class. # If defined, storageClassName: <storageClass> # If set to "-", storageClassName: "", which disables dynamic provisioning # If undefined (the default) or set to null, no storageClassName spec is # set, choosing the default provisioner. (gp2 on AWS, standard on # GKE, AWS & OpenStack) # storageClass: "" |
|
||||
| persistence.storageClass | string | `nil` | The storage class to use for volume claims. Used unless specified at the specific component. Defaults to the cluster default storage class. If defined, storageClassName: <storageClass> If set to "-", storageClassName: "", which disables dynamic provisioning If undefined (the default) or set to null, no storageClassName spec is set, choosing the default provisioner. (gp2 on AWS, standard on GKE, AWS & OpenStack) |
|
||||
| persistence.jetstream.existingClaim | string | `""` | Use an existing volume claim for jetstream |
|
||||
| persistence.jetstream.capacity | string | `"1Gi"` | PVC Storage Request for the jetstream volume |
|
||||
| persistence.jetstream.storageClass | string | `nil` | The storage class to use for volume claims. Defaults to persistence.storageClass If defined, storageClassName: <storageClass> If set to "-", storageClassName: "", which disables dynamic provisioning If undefined (the default) or set to null, no storageClassName spec is set, choosing the default provisioner. (gp2 on AWS, standard on GKE, AWS & OpenStack) |
|
||||
| persistence.media.existingClaim | string | `""` | Use an existing volume claim for media files |
|
||||
| persistence.media.capacity | string | `"1Gi"` | PVC Storage Request for the media volume |
|
||||
| persistence.media.storageClass | string | `nil` | The storage class to use for volume claims. Defaults to persistence.storageClass If defined, storageClassName: <storageClass> If set to "-", storageClassName: "", which disables dynamic provisioning If undefined (the default) or set to null, no storageClassName spec is set, choosing the default provisioner. (gp2 on AWS, standard on GKE, AWS & OpenStack) |
|
||||
| persistence.search.existingClaim | string | `""` | Use an existing volume claim for the fulltext search index |
|
||||
| persistence.search.capacity | string | `"1Gi"` | PVC Storage Request for the search volume |
|
||||
| persistence.search.storageClass | string | `nil` | The storage class to use for volume claims. Defaults to persistence.storageClass If defined, storageClassName: <storageClass> If set to "-", storageClassName: "", which disables dynamic provisioning If undefined (the default) or set to null, no storageClassName spec is set, choosing the default provisioner. (gp2 on AWS, standard on GKE, AWS & OpenStack) |
|
||||
| extraVolumes | list | `[]` | Add additional volumes to the Dendrite Pod |
|
||||
| extraVolumeMounts | list | `[]` | Configure additional mount points volumes in the Dendrite Pod |
|
||||
| strategy.type | string | `"RollingUpdate"` | Strategy to use for rolling updates (e.g. Recreate, RollingUpdate) If you are using ReadWriteOnce volumes, you should probably use Recreate |
|
||||
| strategy.type | string | `"Recreate"` | Strategy to use for rolling updates (e.g. Recreate, RollingUpdate) If you are using ReadWriteOnce volumes, you should probably use Recreate |
|
||||
| strategy.rollingUpdate.maxUnavailable | string | `"25%"` | Maximum number of pods that can be unavailable during the update process |
|
||||
| strategy.rollingUpdate.maxSurge | string | `"25%"` | Maximum number of pods that can be scheduled above the desired number of pods |
|
||||
| dendrite_config.version | int | `2` | |
|
||||
|
@ -139,7 +142,7 @@ Create a folder `appservices` and place your configurations in there. The confi
|
|||
| dendrite_config.logging | list | `[{"level":"info","type":"std"}]` | Default logging configuration |
|
||||
| postgresql.enabled | bool | See value.yaml | Enable and configure postgres as the database for dendrite. |
|
||||
| postgresql.image.repository | string | `"bitnami/postgresql"` | |
|
||||
| postgresql.image.tag | string | `"15.1.0"` | |
|
||||
| postgresql.image.tag | string | `"16.2.0"` | |
|
||||
| postgresql.auth.username | string | `"dendrite"` | |
|
||||
| postgresql.auth.password | string | `"changeme"` | |
|
||||
| postgresql.auth.database | string | `"dendrite"` | |
|
||||
|
@ -186,3 +189,5 @@ grafana:
|
|||
```
|
||||
PS: The label `release=kube-prometheus-stack` is setup with the helmchart of the Prometheus Operator. For Grafana Dashboards it may be necessary to enable scanning in the correct namespaces (or ALL), enabled by `sidecar.dashboards.searchNamespace` in [Helmchart of grafana](https://artifacthub.io/packages/helm/grafana/grafana) (which is part of PrometheusOperator, so `grafana.sidecar.dashboards.searchNamespace`)
|
||||
|
||||
----------------------------------------------
|
||||
Autogenerated from chart metadata using [helm-docs v1.13.1](https://github.com/norwoodj/helm-docs/releases/v1.13.1)
|
|
@ -4,6 +4,7 @@
|
|||
{{- $wellKnownServerHost := default $serverNameHost (regexFind "^(\\[.+\\])?[^:]*" .Values.dendrite_config.global.well_known_server_name) -}}
|
||||
{{- $wellKnownClientHost := default $serverNameHost (regexFind "//(\\[.+\\])?[^:/]*" .Values.dendrite_config.global.well_known_client_name | trimAll "/") -}}
|
||||
{{- $allHosts := list $serverNameHost $wellKnownServerHost $wellKnownClientHost | uniq -}}
|
||||
|
||||
{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}}
|
||||
apiVersion: networking.k8s.io/v1
|
||||
{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
|
||||
|
@ -56,7 +57,7 @@ spec:
|
|||
service:
|
||||
name: {{ $fullName }}
|
||||
port:
|
||||
name: http
|
||||
number: {{ $.Values.service.port }}
|
||||
{{- else }}
|
||||
serviceName: {{ $fullName }}
|
||||
servicePort: http
|
||||
|
@ -72,7 +73,7 @@ spec:
|
|||
service:
|
||||
name: {{ $fullName }}
|
||||
port:
|
||||
name: http
|
||||
number: {{ $.Values.service.port }}
|
||||
{{- else }}
|
||||
serviceName: {{ $fullName }}
|
||||
servicePort: http
|
||||
|
@ -88,7 +89,7 @@ spec:
|
|||
service:
|
||||
name: {{ $fullName }}
|
||||
port:
|
||||
name: http
|
||||
number: {{ $.Values.service.port }}
|
||||
{{- else }}
|
||||
serviceName: {{ $fullName }}
|
||||
servicePort: http
|
||||
|
@ -105,7 +106,7 @@ spec:
|
|||
service:
|
||||
name: {{ $fullName }}
|
||||
port:
|
||||
name: http
|
||||
number: {{ $.Values.service.port }}
|
||||
{{- else }}
|
||||
serviceName: {{ $fullName }}
|
||||
servicePort: http
|
||||
|
|
|
@ -14,4 +14,4 @@ spec:
|
|||
- name: http
|
||||
protocol: TCP
|
||||
port: {{ .Values.service.port }}
|
||||
targetPort: 8008
|
||||
targetPort: http
|
|
@ -26,13 +26,13 @@ persistence:
|
|||
# -- The storage class to use for volume claims.
|
||||
# Used unless specified at the specific component.
|
||||
# Defaults to the cluster default storage class.
|
||||
## If defined, storageClassName: <storageClass>
|
||||
## If set to "-", storageClassName: "", which disables dynamic provisioning
|
||||
## If undefined (the default) or set to null, no storageClassName spec is
|
||||
## set, choosing the default provisioner. (gp2 on AWS, standard on
|
||||
## GKE, AWS & OpenStack)
|
||||
##
|
||||
# storageClass: ""
|
||||
# If defined, storageClassName: <storageClass>
|
||||
# If set to "-", storageClassName: "", which disables dynamic provisioning
|
||||
# If undefined (the default) or set to null, no storageClassName spec is
|
||||
# set, choosing the default provisioner. (gp2 on AWS, standard on
|
||||
# GKE, AWS & OpenStack)
|
||||
#
|
||||
storageClass:
|
||||
jetstream:
|
||||
# -- Use an existing volume claim for jetstream
|
||||
existingClaim: ""
|
||||
|
@ -40,13 +40,12 @@ persistence:
|
|||
capacity: "1Gi"
|
||||
# -- The storage class to use for volume claims.
|
||||
# Defaults to persistence.storageClass
|
||||
## If defined, storageClassName: <storageClass>
|
||||
## If set to "-", storageClassName: "", which disables dynamic provisioning
|
||||
## If undefined (the default) or set to null, no storageClassName spec is
|
||||
## set, choosing the default provisioner. (gp2 on AWS, standard on
|
||||
## GKE, AWS & OpenStack)
|
||||
##
|
||||
# storageClass: ""
|
||||
# If defined, storageClassName: <storageClass>
|
||||
# If set to "-", storageClassName: "", which disables dynamic provisioning
|
||||
# If undefined (the default) or set to null, no storageClassName spec is
|
||||
# set, choosing the default provisioner. (gp2 on AWS, standard on
|
||||
# GKE, AWS & OpenStack)
|
||||
storageClass:
|
||||
media:
|
||||
# -- Use an existing volume claim for media files
|
||||
existingClaim: ""
|
||||
|
@ -54,13 +53,12 @@ persistence:
|
|||
capacity: "1Gi"
|
||||
# -- The storage class to use for volume claims.
|
||||
# Defaults to persistence.storageClass
|
||||
## If defined, storageClassName: <storageClass>
|
||||
## If set to "-", storageClassName: "", which disables dynamic provisioning
|
||||
## If undefined (the default) or set to null, no storageClassName spec is
|
||||
## set, choosing the default provisioner. (gp2 on AWS, standard on
|
||||
## GKE, AWS & OpenStack)
|
||||
##
|
||||
# storageClass: ""
|
||||
# If defined, storageClassName: <storageClass>
|
||||
# If set to "-", storageClassName: "", which disables dynamic provisioning
|
||||
# If undefined (the default) or set to null, no storageClassName spec is
|
||||
# set, choosing the default provisioner. (gp2 on AWS, standard on
|
||||
# GKE, AWS & OpenStack)
|
||||
storageClass:
|
||||
search:
|
||||
# -- Use an existing volume claim for the fulltext search index
|
||||
existingClaim: ""
|
||||
|
@ -68,13 +66,12 @@ persistence:
|
|||
capacity: "1Gi"
|
||||
# -- The storage class to use for volume claims.
|
||||
# Defaults to persistence.storageClass
|
||||
## If defined, storageClassName: <storageClass>
|
||||
## If set to "-", storageClassName: "", which disables dynamic provisioning
|
||||
## If undefined (the default) or set to null, no storageClassName spec is
|
||||
## set, choosing the default provisioner. (gp2 on AWS, standard on
|
||||
## GKE, AWS & OpenStack)
|
||||
##
|
||||
# storageClass: ""
|
||||
# If defined, storageClassName: <storageClass>
|
||||
# If set to "-", storageClassName: "", which disables dynamic provisioning
|
||||
# If undefined (the default) or set to null, no storageClassName spec is
|
||||
# set, choosing the default provisioner. (gp2 on AWS, standard on
|
||||
# GKE, AWS & OpenStack)
|
||||
storageClass:
|
||||
|
||||
# -- Add additional volumes to the Dendrite Pod
|
||||
extraVolumes: []
|
||||
|
@ -92,7 +89,7 @@ extraVolumeMounts: []
|
|||
strategy:
|
||||
# -- Strategy to use for rolling updates (e.g. Recreate, RollingUpdate)
|
||||
# If you are using ReadWriteOnce volumes, you should probably use Recreate
|
||||
type: RollingUpdate
|
||||
type: Recreate
|
||||
rollingUpdate:
|
||||
# -- Maximum number of pods that can be unavailable during the update process
|
||||
maxUnavailable: 25%
|
||||
|
@ -378,7 +375,7 @@ postgresql:
|
|||
enabled: false
|
||||
image:
|
||||
repository: bitnami/postgresql
|
||||
tag: "15.1.0"
|
||||
tag: "16.2.0"
|
||||
auth:
|
||||
username: dendrite
|
||||
password: changeme
|
||||
|
|
|
@ -76,6 +76,8 @@ func MakeAuthAPI(
|
|||
// add the user to Sentry, if enabled
|
||||
hub := sentry.GetHubFromContext(req.Context())
|
||||
if hub != nil {
|
||||
// clone the hub, so we don't send garbage events with e.g. mismatching rooms/event_ids
|
||||
hub = hub.Clone()
|
||||
hub.Scope().SetUser(sentry.User{
|
||||
Username: device.UserID,
|
||||
})
|
||||
|
|
|
@ -66,15 +66,15 @@ func NewRouters() Routers {
|
|||
}
|
||||
|
||||
var NotAllowedHandler = WrapHandlerInCORS(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
unrecognizedErr, _ := json.Marshal(spec.Unrecognized("Unrecognized request")) // nolint:misspell
|
||||
_, _ = w.Write(unrecognizedErr) // nolint:misspell
|
||||
}))
|
||||
|
||||
var NotFoundCORSHandler = WrapHandlerInCORS(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
unrecognizedErr, _ := json.Marshal(spec.Unrecognized("Unrecognized request")) // nolint:misspell
|
||||
_, _ = w.Write(unrecognizedErr) // nolint:misspell
|
||||
}))
|
||||
|
|
|
@ -17,7 +17,7 @@ func TestRoutersError(t *testing.T) {
|
|||
if rec.Code != http.StatusNotFound {
|
||||
t.Fatalf("unexpected status code: %d - %s", rec.Code, rec.Body.String())
|
||||
}
|
||||
if ct := rec.Header().Get("Content-Type"); ct != "application/json" {
|
||||
if ct := rec.Result().Header.Get("Content-Type"); ct != "application/json" {
|
||||
t.Fatalf("unexpected content-type: %s", ct)
|
||||
}
|
||||
|
||||
|
@ -32,7 +32,7 @@ func TestRoutersError(t *testing.T) {
|
|||
if rec.Code != http.StatusMethodNotAllowed {
|
||||
t.Fatalf("unexpected status code: %d - %s", rec.Code, rec.Body.String())
|
||||
}
|
||||
if ct := rec.Header().Get("Content-Type"); ct != "application/json" {
|
||||
if ct := rec.Result().Header.Get("Content-Type"); ct != "application/json" {
|
||||
t.Fatalf("unexpected content-type: %s", ct)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,8 +3,7 @@ package sqlutil
|
|||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
|
||||
"go.uber.org/atomic"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// ExclusiveWriter implements sqlutil.Writer.
|
||||
|
|
|
@ -19,6 +19,7 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -26,7 +27,6 @@ import (
|
|||
"github.com/matrix-org/gomatrixserverlib/spec"
|
||||
"github.com/nats-io/nats.go"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.uber.org/atomic"
|
||||
"gotest.tools/v3/poll"
|
||||
|
||||
"github.com/matrix-org/dendrite/federationapi/producers"
|
||||
|
@ -228,7 +228,7 @@ func TestProcessTransactionRequestEDUTyping(t *testing.T) {
|
|||
ctx := process.NewProcessContext()
|
||||
defer ctx.ShutdownDendrite()
|
||||
txn, js, cfg := createTransactionWithEDU(ctx, edus)
|
||||
received := atomic.NewBool(false)
|
||||
received := atomic.Bool{}
|
||||
onMessage := func(ctx context.Context, msgs []*nats.Msg) bool {
|
||||
msg := msgs[0] // Guaranteed to exist if onMessage is called
|
||||
room := msg.Header.Get(jetstream.RoomID)
|
||||
|
@ -294,7 +294,7 @@ func TestProcessTransactionRequestEDUToDevice(t *testing.T) {
|
|||
ctx := process.NewProcessContext()
|
||||
defer ctx.ShutdownDendrite()
|
||||
txn, js, cfg := createTransactionWithEDU(ctx, edus)
|
||||
received := atomic.NewBool(false)
|
||||
received := atomic.Bool{}
|
||||
onMessage := func(ctx context.Context, msgs []*nats.Msg) bool {
|
||||
msg := msgs[0] // Guaranteed to exist if onMessage is called
|
||||
|
||||
|
@ -371,7 +371,7 @@ func TestProcessTransactionRequestEDUDeviceListUpdate(t *testing.T) {
|
|||
ctx := process.NewProcessContext()
|
||||
defer ctx.ShutdownDendrite()
|
||||
txn, js, cfg := createTransactionWithEDU(ctx, edus)
|
||||
received := atomic.NewBool(false)
|
||||
received := atomic.Bool{}
|
||||
onMessage := func(ctx context.Context, msgs []*nats.Msg) bool {
|
||||
msg := msgs[0] // Guaranteed to exist if onMessage is called
|
||||
|
||||
|
@ -468,7 +468,7 @@ func TestProcessTransactionRequestEDUReceipt(t *testing.T) {
|
|||
ctx := process.NewProcessContext()
|
||||
defer ctx.ShutdownDendrite()
|
||||
txn, js, cfg := createTransactionWithEDU(ctx, edus)
|
||||
received := atomic.NewBool(false)
|
||||
received := atomic.Bool{}
|
||||
onMessage := func(ctx context.Context, msgs []*nats.Msg) bool {
|
||||
msg := msgs[0] // Guaranteed to exist if onMessage is called
|
||||
|
||||
|
@ -512,7 +512,7 @@ func TestProcessTransactionRequestEDUSigningKeyUpdate(t *testing.T) {
|
|||
ctx := process.NewProcessContext()
|
||||
defer ctx.ShutdownDendrite()
|
||||
txn, js, cfg := createTransactionWithEDU(ctx, edus)
|
||||
received := atomic.NewBool(false)
|
||||
received := atomic.Bool{}
|
||||
onMessage := func(ctx context.Context, msgs []*nats.Msg) bool {
|
||||
msg := msgs[0] // Guaranteed to exist if onMessage is called
|
||||
|
||||
|
@ -569,7 +569,7 @@ func TestProcessTransactionRequestEDUPresence(t *testing.T) {
|
|||
ctx := process.NewProcessContext()
|
||||
defer ctx.ShutdownDendrite()
|
||||
txn, js, cfg := createTransactionWithEDU(ctx, edus)
|
||||
received := atomic.NewBool(false)
|
||||
received := atomic.Bool{}
|
||||
onMessage := func(ctx context.Context, msgs []*nats.Msg) bool {
|
||||
msg := msgs[0] // Guaranteed to exist if onMessage is called
|
||||
|
||||
|
|
|
@ -38,9 +38,9 @@ var (
|
|||
ErrPasswordTooLong = fmt.Errorf("password too long: max %d characters", maxPasswordLength)
|
||||
ErrPasswordWeak = fmt.Errorf("password too weak: min %d characters", minPasswordLength)
|
||||
ErrUsernameTooLong = fmt.Errorf("username exceeds the maximum length of %d characters", maxUsernameLength)
|
||||
ErrUsernameInvalid = errors.New("username can only contain characters a-z, 0-9, or '_-./='")
|
||||
ErrUsernameInvalid = errors.New("username can only contain characters a-z, 0-9, or '_+-./='")
|
||||
ErrUsernameUnderscore = errors.New("username cannot start with a '_'")
|
||||
validUsernameRegex = regexp.MustCompile(`^[0-9a-z_\-=./]+$`)
|
||||
validUsernameRegex = regexp.MustCompile(`^[0-9a-z_\-+=./]+$`)
|
||||
)
|
||||
|
||||
// ValidatePassword returns an error if the password is invalid
|
||||
|
|
|
@ -129,6 +129,11 @@ func Test_validateUsername(t *testing.T) {
|
|||
localpart: "i_am_allowed=1",
|
||||
domain: "localhost",
|
||||
},
|
||||
{
|
||||
name: "special characters are allowed 3",
|
||||
localpart: "+55555555555",
|
||||
domain: "localhost",
|
||||
},
|
||||
{
|
||||
name: "not all special characters are allowed",
|
||||
localpart: "notallowed#", // contains #
|
||||
|
@ -139,6 +144,16 @@ func Test_validateUsername(t *testing.T) {
|
|||
JSON: spec.InvalidUsername(ErrUsernameInvalid.Error()),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "not all special characters are allowed 2",
|
||||
localpart: "<notallowed", // contains <
|
||||
domain: "localhost",
|
||||
wantErr: ErrUsernameInvalid,
|
||||
wantJSON: &util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
JSON: spec.InvalidUsername(ErrUsernameInvalid.Error()),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "username containing numbers",
|
||||
localpart: "hello1337",
|
||||
|
|
|
@ -18,7 +18,7 @@ var build string
|
|||
const (
|
||||
VersionMajor = 0
|
||||
VersionMinor = 13
|
||||
VersionPatch = 5
|
||||
VersionPatch = 7
|
||||
VersionTag = "" // example: "rc1"
|
||||
|
||||
gitRevLen = 7 // 7 matches the displayed characters on github.com
|
||||
|
|
|
@ -17,6 +17,7 @@ package shared
|
|||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
|
||||
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||
"github.com/matrix-org/dendrite/mediaapi/storage/tables"
|
||||
|
@ -33,7 +34,7 @@ type Database struct {
|
|||
|
||||
// StoreMediaMetadata inserts the metadata about the uploaded media into the database.
|
||||
// Returns an error if the combination of MediaID and Origin are not unique in the table.
|
||||
func (d Database) StoreMediaMetadata(ctx context.Context, mediaMetadata *types.MediaMetadata) error {
|
||||
func (d *Database) StoreMediaMetadata(ctx context.Context, mediaMetadata *types.MediaMetadata) error {
|
||||
return d.Writer.Do(d.DB, nil, func(txn *sql.Tx) error {
|
||||
return d.MediaRepository.InsertMedia(ctx, txn, mediaMetadata)
|
||||
})
|
||||
|
@ -42,9 +43,9 @@ func (d Database) StoreMediaMetadata(ctx context.Context, mediaMetadata *types.M
|
|||
// GetMediaMetadata returns metadata about media stored on this server.
|
||||
// The media could have been uploaded to this server or fetched from another server and cached here.
|
||||
// Returns nil metadata if there is no metadata associated with this media.
|
||||
func (d Database) GetMediaMetadata(ctx context.Context, mediaID types.MediaID, mediaOrigin spec.ServerName) (*types.MediaMetadata, error) {
|
||||
func (d *Database) GetMediaMetadata(ctx context.Context, mediaID types.MediaID, mediaOrigin spec.ServerName) (*types.MediaMetadata, error) {
|
||||
mediaMetadata, err := d.MediaRepository.SelectMedia(ctx, nil, mediaID, mediaOrigin)
|
||||
if err != nil && err == sql.ErrNoRows {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, nil
|
||||
}
|
||||
return mediaMetadata, err
|
||||
|
@ -53,9 +54,9 @@ func (d Database) GetMediaMetadata(ctx context.Context, mediaID types.MediaID, m
|
|||
// GetMediaMetadataByHash returns metadata about media stored on this server.
|
||||
// The media could have been uploaded to this server or fetched from another server and cached here.
|
||||
// Returns nil metadata if there is no metadata associated with this media.
|
||||
func (d Database) GetMediaMetadataByHash(ctx context.Context, mediaHash types.Base64Hash, mediaOrigin spec.ServerName) (*types.MediaMetadata, error) {
|
||||
func (d *Database) GetMediaMetadataByHash(ctx context.Context, mediaHash types.Base64Hash, mediaOrigin spec.ServerName) (*types.MediaMetadata, error) {
|
||||
mediaMetadata, err := d.MediaRepository.SelectMediaByHash(ctx, nil, mediaHash, mediaOrigin)
|
||||
if err != nil && err == sql.ErrNoRows {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, nil
|
||||
}
|
||||
return mediaMetadata, err
|
||||
|
@ -63,7 +64,7 @@ func (d Database) GetMediaMetadataByHash(ctx context.Context, mediaHash types.Ba
|
|||
|
||||
// StoreThumbnail inserts the metadata about the thumbnail into the database.
|
||||
// Returns an error if the combination of MediaID and Origin are not unique in the table.
|
||||
func (d Database) StoreThumbnail(ctx context.Context, thumbnailMetadata *types.ThumbnailMetadata) error {
|
||||
func (d *Database) StoreThumbnail(ctx context.Context, thumbnailMetadata *types.ThumbnailMetadata) error {
|
||||
return d.Writer.Do(d.DB, nil, func(txn *sql.Tx) error {
|
||||
return d.Thumbnails.InsertThumbnail(ctx, txn, thumbnailMetadata)
|
||||
})
|
||||
|
@ -72,13 +73,10 @@ func (d Database) StoreThumbnail(ctx context.Context, thumbnailMetadata *types.T
|
|||
// GetThumbnail returns metadata about a specific thumbnail.
|
||||
// The media could have been uploaded to this server or fetched from another server and cached here.
|
||||
// Returns nil metadata if there is no metadata associated with this thumbnail.
|
||||
func (d Database) GetThumbnail(ctx context.Context, mediaID types.MediaID, mediaOrigin spec.ServerName, width, height int, resizeMethod string) (*types.ThumbnailMetadata, error) {
|
||||
func (d *Database) GetThumbnail(ctx context.Context, mediaID types.MediaID, mediaOrigin spec.ServerName, width, height int, resizeMethod string) (*types.ThumbnailMetadata, error) {
|
||||
metadata, err := d.Thumbnails.SelectThumbnail(ctx, nil, mediaID, mediaOrigin, width, height, resizeMethod)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, nil
|
||||
}
|
||||
return metadata, err
|
||||
}
|
||||
|
@ -86,13 +84,10 @@ func (d Database) GetThumbnail(ctx context.Context, mediaID types.MediaID, media
|
|||
// GetThumbnails returns metadata about all thumbnails for a specific media stored on this server.
|
||||
// The media could have been uploaded to this server or fetched from another server and cached here.
|
||||
// Returns nil metadata if there are no thumbnails associated with this media.
|
||||
func (d Database) GetThumbnails(ctx context.Context, mediaID types.MediaID, mediaOrigin spec.ServerName) ([]*types.ThumbnailMetadata, error) {
|
||||
func (d *Database) GetThumbnails(ctx context.Context, mediaID types.MediaID, mediaOrigin spec.ServerName) ([]*types.ThumbnailMetadata, error) {
|
||||
metadatas, err := d.Thumbnails.SelectThumbnails(ctx, nil, mediaID, mediaOrigin)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, nil
|
||||
}
|
||||
return metadatas, err
|
||||
}
|
||||
|
|
|
@ -108,6 +108,8 @@ func MakeRelayAPI(
|
|||
// add the user to Sentry, if enabled
|
||||
hub := sentry.GetHubFromContext(req.Context())
|
||||
if hub != nil {
|
||||
// clone the hub, so we don't send garbage events with e.g. mismatching rooms/event_ids
|
||||
hub = hub.Clone()
|
||||
hub.Scope().SetTag("origin", string(fedReq.Origin()))
|
||||
hub.Scope().SetTag("uri", fedReq.RequestURI())
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ import (
|
|||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/matrix-org/dendrite/roomserver/types"
|
||||
"github.com/matrix-org/dendrite/roomserver/storage/tables"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/matrix-org/gomatrixserverlib/spec"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
@ -32,42 +32,48 @@ import (
|
|||
const MRoomServerACL = "m.room.server_acl"
|
||||
|
||||
type ServerACLDatabase interface {
|
||||
// GetKnownRooms returns a list of all rooms we know about.
|
||||
GetKnownRooms(ctx context.Context) ([]string, error)
|
||||
// GetStateEvent returns the state event of a given type for a given room with a given state key
|
||||
// If no event could be found, returns nil
|
||||
// If there was an issue during the retrieval, returns an error
|
||||
GetStateEvent(ctx context.Context, roomID, evType, stateKey string) (*types.HeaderedEvent, error)
|
||||
// RoomsWithACLs returns all room IDs for rooms with ACLs
|
||||
RoomsWithACLs(ctx context.Context) ([]string, error)
|
||||
|
||||
// GetBulkStateContent returns all state events which match a given room ID and a given state key tuple. Both must be satisfied for a match.
|
||||
// If a tuple has the StateKey of '*' and allowWildcards=true then all state events with the EventType should be returned.
|
||||
GetBulkStateContent(ctx context.Context, roomIDs []string, tuples []gomatrixserverlib.StateKeyTuple, allowWildcards bool) ([]tables.StrippedEvent, error)
|
||||
}
|
||||
|
||||
type ServerACLs struct {
|
||||
acls map[string]*serverACL // room ID -> ACL
|
||||
aclsMutex sync.RWMutex // protects the above
|
||||
acls map[string]*serverACL // room ID -> ACL
|
||||
aclsMutex sync.RWMutex // protects the above
|
||||
aclRegexCache map[string]**regexp.Regexp // Cache from "serverName" -> pointer to a regex
|
||||
aclRegexCacheMutex sync.RWMutex // protects the above
|
||||
}
|
||||
|
||||
func NewServerACLs(db ServerACLDatabase) *ServerACLs {
|
||||
ctx := context.TODO()
|
||||
acls := &ServerACLs{
|
||||
acls: make(map[string]*serverACL),
|
||||
// Be generous when creating the cache, as in reality
|
||||
// there are hundreds of servers in an ACL.
|
||||
aclRegexCache: make(map[string]**regexp.Regexp, 100),
|
||||
}
|
||||
|
||||
// Look up all of the rooms that the current state server knows about.
|
||||
rooms, err := db.GetKnownRooms(ctx)
|
||||
rooms, err := db.RoomsWithACLs(ctx)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatalf("Failed to get known rooms")
|
||||
}
|
||||
// For each room, let's see if we have a server ACL state event. If we
|
||||
// do then we'll process it into memory so that we have the regexes to
|
||||
// hand.
|
||||
for _, room := range rooms {
|
||||
state, err := db.GetStateEvent(ctx, room, MRoomServerACL, "")
|
||||
if err != nil {
|
||||
logrus.WithError(err).Errorf("Failed to get server ACLs for room %q", room)
|
||||
continue
|
||||
}
|
||||
if state != nil {
|
||||
acls.OnServerACLUpdate(state.PDU)
|
||||
}
|
||||
|
||||
events, err := db.GetBulkStateContent(ctx, rooms, []gomatrixserverlib.StateKeyTuple{{EventType: MRoomServerACL, StateKey: ""}}, false)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Errorf("Failed to get server ACLs for all rooms: %q", err)
|
||||
}
|
||||
|
||||
for _, event := range events {
|
||||
acls.OnServerACLUpdate(event)
|
||||
}
|
||||
|
||||
return acls
|
||||
}
|
||||
|
||||
|
@ -79,8 +85,8 @@ type ServerACL struct {
|
|||
|
||||
type serverACL struct {
|
||||
ServerACL
|
||||
allowedRegexes []*regexp.Regexp
|
||||
deniedRegexes []*regexp.Regexp
|
||||
allowedRegexes []**regexp.Regexp
|
||||
deniedRegexes []**regexp.Regexp
|
||||
}
|
||||
|
||||
func compileACLRegex(orig string) (*regexp.Regexp, error) {
|
||||
|
@ -90,9 +96,28 @@ func compileACLRegex(orig string) (*regexp.Regexp, error) {
|
|||
return regexp.Compile(escaped)
|
||||
}
|
||||
|
||||
func (s *ServerACLs) OnServerACLUpdate(state gomatrixserverlib.PDU) {
|
||||
// cachedCompileACLRegex is a wrapper around compileACLRegex with added caching
|
||||
func (s *ServerACLs) cachedCompileACLRegex(orig string) (**regexp.Regexp, error) {
|
||||
s.aclRegexCacheMutex.RLock()
|
||||
re, ok := s.aclRegexCache[orig]
|
||||
if ok {
|
||||
s.aclRegexCacheMutex.RUnlock()
|
||||
return re, nil
|
||||
}
|
||||
s.aclRegexCacheMutex.RUnlock()
|
||||
compiled, err := compileACLRegex(orig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.aclRegexCacheMutex.Lock()
|
||||
defer s.aclRegexCacheMutex.Unlock()
|
||||
s.aclRegexCache[orig] = &compiled
|
||||
return &compiled, nil
|
||||
}
|
||||
|
||||
func (s *ServerACLs) OnServerACLUpdate(strippedEvent tables.StrippedEvent) {
|
||||
acls := &serverACL{}
|
||||
if err := json.Unmarshal(state.Content(), &acls.ServerACL); err != nil {
|
||||
if err := json.Unmarshal([]byte(strippedEvent.ContentValue), &acls.ServerACL); err != nil {
|
||||
logrus.WithError(err).Errorf("Failed to unmarshal state content for server ACLs")
|
||||
return
|
||||
}
|
||||
|
@ -101,14 +126,14 @@ func (s *ServerACLs) OnServerACLUpdate(state gomatrixserverlib.PDU) {
|
|||
// special characters and then replace * and ? with their regex counterparts.
|
||||
// https://matrix.org/docs/spec/client_server/r0.6.1#m-room-server-acl
|
||||
for _, orig := range acls.Allowed {
|
||||
if expr, err := compileACLRegex(orig); err != nil {
|
||||
if expr, err := s.cachedCompileACLRegex(orig); err != nil {
|
||||
logrus.WithError(err).Errorf("Failed to compile allowed regex")
|
||||
} else {
|
||||
acls.allowedRegexes = append(acls.allowedRegexes, expr)
|
||||
}
|
||||
}
|
||||
for _, orig := range acls.Denied {
|
||||
if expr, err := compileACLRegex(orig); err != nil {
|
||||
if expr, err := s.cachedCompileACLRegex(orig); err != nil {
|
||||
logrus.WithError(err).Errorf("Failed to compile denied regex")
|
||||
} else {
|
||||
acls.deniedRegexes = append(acls.deniedRegexes, expr)
|
||||
|
@ -118,10 +143,15 @@ func (s *ServerACLs) OnServerACLUpdate(state gomatrixserverlib.PDU) {
|
|||
"allow_ip_literals": acls.AllowIPLiterals,
|
||||
"num_allowed": len(acls.allowedRegexes),
|
||||
"num_denied": len(acls.deniedRegexes),
|
||||
}).Debugf("Updating server ACLs for %q", state.RoomID())
|
||||
}).Debugf("Updating server ACLs for %q", strippedEvent.RoomID)
|
||||
|
||||
// Clear out Denied and Allowed, now that we have the compiled regexes.
|
||||
// They are not needed anymore from this point on.
|
||||
acls.Denied = nil
|
||||
acls.Allowed = nil
|
||||
s.aclsMutex.Lock()
|
||||
defer s.aclsMutex.Unlock()
|
||||
s.acls[state.RoomID().String()] = acls
|
||||
s.acls[strippedEvent.RoomID] = acls
|
||||
}
|
||||
|
||||
func (s *ServerACLs) IsServerBannedFromRoom(serverName spec.ServerName, roomID string) bool {
|
||||
|
@ -151,14 +181,14 @@ func (s *ServerACLs) IsServerBannedFromRoom(serverName spec.ServerName, roomID s
|
|||
// Check if the hostname matches one of the denied regexes. If it does then
|
||||
// the server is banned from the room.
|
||||
for _, expr := range acls.deniedRegexes {
|
||||
if expr.MatchString(string(serverName)) {
|
||||
if (*expr).MatchString(string(serverName)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
// Check if the hostname matches one of the allowed regexes. If it does then
|
||||
// the server is NOT banned from the room.
|
||||
for _, expr := range acls.allowedRegexes {
|
||||
if expr.MatchString(string(serverName)) {
|
||||
if (*expr).MatchString(string(serverName)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,8 +15,14 @@
|
|||
package acls
|
||||
|
||||
import (
|
||||
"context"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"github.com/matrix-org/dendrite/roomserver/storage/tables"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/matrix-org/gomatrixserverlib/spec"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestOpenACLsWithBlacklist(t *testing.T) {
|
||||
|
@ -38,8 +44,8 @@ func TestOpenACLsWithBlacklist(t *testing.T) {
|
|||
ServerACL: ServerACL{
|
||||
AllowIPLiterals: true,
|
||||
},
|
||||
allowedRegexes: []*regexp.Regexp{allowRegex},
|
||||
deniedRegexes: []*regexp.Regexp{denyRegex},
|
||||
allowedRegexes: []**regexp.Regexp{&allowRegex},
|
||||
deniedRegexes: []**regexp.Regexp{&denyRegex},
|
||||
}
|
||||
|
||||
if acls.IsServerBannedFromRoom("1.2.3.4", roomID) {
|
||||
|
@ -77,8 +83,8 @@ func TestDefaultACLsWithWhitelist(t *testing.T) {
|
|||
ServerACL: ServerACL{
|
||||
AllowIPLiterals: false,
|
||||
},
|
||||
allowedRegexes: []*regexp.Regexp{allowRegex},
|
||||
deniedRegexes: []*regexp.Regexp{},
|
||||
allowedRegexes: []**regexp.Regexp{&allowRegex},
|
||||
deniedRegexes: []**regexp.Regexp{},
|
||||
}
|
||||
|
||||
if !acls.IsServerBannedFromRoom("1.2.3.4", roomID) {
|
||||
|
@ -103,3 +109,45 @@ func TestDefaultACLsWithWhitelist(t *testing.T) {
|
|||
t.Fatal("Expected qux.com:4567 to be allowed but wasn't")
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
content1 = `{"allow":["*"],"allow_ip_literals":false,"deny":["hello.world", "*.hello.world"]}`
|
||||
)
|
||||
|
||||
type dummyACLDB struct{}
|
||||
|
||||
func (d dummyACLDB) RoomsWithACLs(ctx context.Context) ([]string, error) {
|
||||
return []string{"1", "2"}, nil
|
||||
}
|
||||
|
||||
func (d dummyACLDB) GetBulkStateContent(ctx context.Context, roomIDs []string, tuples []gomatrixserverlib.StateKeyTuple, allowWildcards bool) ([]tables.StrippedEvent, error) {
|
||||
return []tables.StrippedEvent{
|
||||
{
|
||||
RoomID: "1",
|
||||
ContentValue: content1,
|
||||
},
|
||||
{
|
||||
RoomID: "2",
|
||||
ContentValue: content1,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func TestCachedRegex(t *testing.T) {
|
||||
db := dummyACLDB{}
|
||||
wantBannedServer := spec.ServerName("hello.world")
|
||||
|
||||
acls := NewServerACLs(db)
|
||||
|
||||
// Check that hello.world is banned in room 1
|
||||
banned := acls.IsServerBannedFromRoom(wantBannedServer, "1")
|
||||
assert.True(t, banned)
|
||||
|
||||
// Check that hello.world is banned in room 2
|
||||
banned = acls.IsServerBannedFromRoom(wantBannedServer, "2")
|
||||
assert.True(t, banned)
|
||||
|
||||
// Check that matrix.hello.world is banned in room 2
|
||||
banned = acls.IsServerBannedFromRoom("matrix."+wantBannedServer, "2")
|
||||
assert.True(t, banned)
|
||||
}
|
||||
|
|
|
@ -86,6 +86,9 @@ type RoomserverInternalAPI interface {
|
|||
req *QueryAuthChainRequest,
|
||||
res *QueryAuthChainResponse,
|
||||
) error
|
||||
|
||||
// RoomsWithACLs returns all room IDs for rooms with ACLs
|
||||
RoomsWithACLs(ctx context.Context) ([]string, error)
|
||||
}
|
||||
|
||||
type UserRoomPrivateKeyCreator interface {
|
||||
|
@ -138,7 +141,12 @@ type QueryRoomHierarchyAPI interface {
|
|||
//
|
||||
// If returned walker is nil, then there are no more rooms left to traverse. This method does not modify the provided walker, so it
|
||||
// can be cached.
|
||||
QueryNextRoomHierarchyPage(ctx context.Context, walker RoomHierarchyWalker, limit int) ([]fclient.RoomHierarchyRoom, *RoomHierarchyWalker, error)
|
||||
QueryNextRoomHierarchyPage(ctx context.Context, walker RoomHierarchyWalker, limit int) (
|
||||
hierarchyRooms []fclient.RoomHierarchyRoom,
|
||||
inaccessibleRooms []string,
|
||||
hierarchyWalker *RoomHierarchyWalker,
|
||||
err error,
|
||||
)
|
||||
}
|
||||
|
||||
type QueryMembershipAPI interface {
|
||||
|
@ -220,6 +228,7 @@ type ClientRoomserverAPI interface {
|
|||
UserRoomPrivateKeyCreator
|
||||
QueryRoomHierarchyAPI
|
||||
DefaultRoomVersionAPI
|
||||
|
||||
QueryMembershipForUser(ctx context.Context, req *QueryMembershipForUserRequest, res *QueryMembershipForUserResponse) error
|
||||
QueryMembershipsForRoom(ctx context.Context, req *QueryMembershipsForRoomRequest, res *QueryMembershipsForRoomResponse) error
|
||||
QueryRoomsForUser(ctx context.Context, userID spec.UserID, desiredMembership string) ([]spec.RoomID, error)
|
||||
|
@ -261,6 +270,15 @@ type ClientRoomserverAPI interface {
|
|||
RemoveRoomAlias(ctx context.Context, senderID spec.SenderID, alias string) (aliasFound bool, aliasRemoved bool, err error)
|
||||
|
||||
SigningIdentityFor(ctx context.Context, roomID spec.RoomID, senderID spec.UserID) (fclient.SigningIdentity, error)
|
||||
|
||||
InsertReportedEvent(
|
||||
ctx context.Context,
|
||||
roomID, eventID, reportingUserID, reason string,
|
||||
score int64,
|
||||
) (int64, error)
|
||||
QueryAdminEventReports(ctx context.Context, from, limit uint64, backwards bool, userID, roomID string) ([]QueryAdminEventReportsResponse, int64, error)
|
||||
QueryAdminEventReport(ctx context.Context, reportID uint64) (QueryAdminEventReportResponse, error)
|
||||
PerformAdminDeleteEventReport(ctx context.Context, reportID uint64) error
|
||||
}
|
||||
|
||||
type UserRoomserverAPI interface {
|
||||
|
|
|
@ -50,16 +50,14 @@ type PerformLeaveResponse struct {
|
|||
}
|
||||
|
||||
type InviteInput struct {
|
||||
RoomID spec.RoomID
|
||||
Inviter spec.UserID
|
||||
Invitee spec.UserID
|
||||
DisplayName string
|
||||
AvatarURL string
|
||||
Reason string
|
||||
IsDirect bool
|
||||
KeyID gomatrixserverlib.KeyID
|
||||
PrivateKey ed25519.PrivateKey
|
||||
EventTime time.Time
|
||||
RoomID spec.RoomID
|
||||
Inviter spec.UserID
|
||||
Invitee spec.UserID
|
||||
Reason string
|
||||
IsDirect bool
|
||||
KeyID gomatrixserverlib.KeyID
|
||||
PrivateKey ed25519.PrivateKey
|
||||
EventTime time.Time
|
||||
}
|
||||
|
||||
type PerformInviteRequest struct {
|
||||
|
|
|
@ -346,6 +346,28 @@ type QueryServerBannedFromRoomResponse struct {
|
|||
Banned bool `json:"banned"`
|
||||
}
|
||||
|
||||
type QueryAdminEventReportsResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
Score int64 `json:"score"`
|
||||
EventNID types.EventNID `json:"-"` // only used to query the state
|
||||
RoomNID types.RoomNID `json:"-"` // only used to query the state
|
||||
ReportingUserNID types.EventStateKeyNID `json:"-"` // only used in the DB
|
||||
SenderNID types.EventStateKeyNID `json:"-"` // only used in the DB
|
||||
RoomID string `json:"room_id"`
|
||||
EventID string `json:"event_id"`
|
||||
UserID string `json:"user_id"` // the user reporting the event
|
||||
Reason string `json:"reason"`
|
||||
Sender string `json:"sender"` // the user sending the reported event
|
||||
CanonicalAlias string `json:"canonical_alias"`
|
||||
RoomName string `json:"name"`
|
||||
ReceivedTS spec.Timestamp `json:"received_ts"`
|
||||
}
|
||||
|
||||
type QueryAdminEventReportResponse struct {
|
||||
QueryAdminEventReportsResponse
|
||||
EventJSON json.RawMessage `json:"event_json"`
|
||||
}
|
||||
|
||||
// MarshalJSON stringifies the room ID and StateKeyTuple keys so they can be sent over the wire in HTTP API mode.
|
||||
func (r *QueryBulkStateContentResponse) MarshalJSON() ([]byte, error) {
|
||||
se := make(map[string]string)
|
||||
|
|
|
@ -189,7 +189,7 @@ func PopulatePublicRooms(ctx context.Context, roomIDs []string, rsAPI QueryBulkS
|
|||
RoomID: roomID,
|
||||
}
|
||||
joinCount := 0
|
||||
var joinRule, guestAccess string
|
||||
var guestAccess string
|
||||
for tuple, contentVal := range data {
|
||||
if tuple.EventType == spec.MRoomMember && contentVal == "join" {
|
||||
joinCount++
|
||||
|
@ -210,12 +210,12 @@ func PopulatePublicRooms(ctx context.Context, roomIDs []string, rsAPI QueryBulkS
|
|||
pub.WorldReadable = contentVal == "world_readable"
|
||||
// need both of these to determine whether guests can join
|
||||
case joinRuleTuple:
|
||||
joinRule = contentVal
|
||||
pub.JoinRule = contentVal
|
||||
case guestTuple:
|
||||
guestAccess = contentVal
|
||||
}
|
||||
}
|
||||
if joinRule == spec.Public && guestAccess == "can_join" {
|
||||
if pub.JoinRule == spec.Public && guestAccess == "can_join" {
|
||||
pub.GuestCanJoin = true
|
||||
}
|
||||
pub.JoinedMembersCount = joinCount
|
||||
|
|
|
@ -340,3 +340,11 @@ func (r *RoomserverInternalAPI) SigningIdentityFor(ctx context.Context, roomID s
|
|||
func (r *RoomserverInternalAPI) AssignRoomNID(ctx context.Context, roomID spec.RoomID, roomVersion gomatrixserverlib.RoomVersion) (roomNID types.RoomNID, err error) {
|
||||
return r.DB.AssignRoomNID(ctx, roomID, roomVersion)
|
||||
}
|
||||
|
||||
func (r *RoomserverInternalAPI) InsertReportedEvent(
|
||||
ctx context.Context,
|
||||
roomID, eventID, reportingUserID, reason string,
|
||||
score int64,
|
||||
) (int64, error) {
|
||||
return r.DB.InsertReportedEvent(ctx, roomID, eventID, reportingUserID, reason, score)
|
||||
}
|
||||
|
|
|
@ -108,12 +108,14 @@ type worker struct {
|
|||
r *Inputer
|
||||
roomID string
|
||||
subscription *nats.Subscription
|
||||
sentryHub *sentry.Hub
|
||||
}
|
||||
|
||||
func (r *Inputer) startWorkerForRoom(roomID string) {
|
||||
v, loaded := r.workers.LoadOrStore(roomID, &worker{
|
||||
r: r,
|
||||
roomID: roomID,
|
||||
r: r,
|
||||
roomID: roomID,
|
||||
sentryHub: sentry.CurrentHub().Clone(),
|
||||
})
|
||||
w := v.(*worker)
|
||||
w.Lock()
|
||||
|
@ -265,9 +267,9 @@ func (w *worker) _next() {
|
|||
// Look up what the next event is that's waiting to be processed.
|
||||
ctx, cancel := context.WithTimeout(w.r.ProcessContext.Context(), time.Minute)
|
||||
defer cancel()
|
||||
if scope := sentry.CurrentHub().Scope(); scope != nil {
|
||||
w.sentryHub.ConfigureScope(func(scope *sentry.Scope) {
|
||||
scope.SetTag("room_id", w.roomID)
|
||||
}
|
||||
})
|
||||
msgs, err := w.subscription.Fetch(1, nats.Context(ctx))
|
||||
switch err {
|
||||
case nil:
|
||||
|
@ -323,9 +325,9 @@ func (w *worker) _next() {
|
|||
return
|
||||
}
|
||||
|
||||
if scope := sentry.CurrentHub().Scope(); scope != nil {
|
||||
w.sentryHub.ConfigureScope(func(scope *sentry.Scope) {
|
||||
scope.SetTag("event_id", inputRoomEvent.Event.EventID())
|
||||
}
|
||||
})
|
||||
|
||||
// Process the room event. If something goes wrong then we'll tell
|
||||
// NATS to terminate the message. We'll store the error result as
|
||||
|
@ -347,7 +349,7 @@ func (w *worker) _next() {
|
|||
}).Warn("Roomserver rejected event")
|
||||
default:
|
||||
if !errors.Is(err, context.DeadlineExceeded) && !errors.Is(err, context.Canceled) {
|
||||
sentry.CaptureException(err)
|
||||
w.sentryHub.CaptureException(err)
|
||||
}
|
||||
logrus.WithError(err).WithFields(logrus.Fields{
|
||||
"room_id": w.roomID,
|
||||
|
|
|
@ -24,6 +24,7 @@ import (
|
|||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/matrix-org/dendrite/roomserver/storage/tables"
|
||||
"github.com/tidwall/gjson"
|
||||
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
|
@ -509,7 +510,13 @@ func (r *Inputer) processRoomEvent(
|
|||
logrus.WithError(err).Error("failed to get server ACLs")
|
||||
}
|
||||
if aclEvent != nil {
|
||||
r.ACLs.OnServerACLUpdate(aclEvent)
|
||||
strippedEvent := tables.StrippedEvent{
|
||||
RoomID: aclEvent.RoomID().String(),
|
||||
EventType: aclEvent.Type(),
|
||||
StateKey: *aclEvent.StateKey(),
|
||||
ContentValue: string(aclEvent.Content()),
|
||||
}
|
||||
r.ACLs.OnServerACLUpdate(strippedEvent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -290,6 +290,7 @@ func (u *latestEventsUpdater) latestState(ctx context.Context, roomInfo *types.R
|
|||
}).Warnf("State reset detected (removing %d events)", removed)
|
||||
sentry.WithScope(func(scope *sentry.Scope) {
|
||||
scope.SetLevel("warning")
|
||||
scope.SetTag("room_id", u.event.RoomID().String())
|
||||
scope.SetContext("State reset", map[string]interface{}{
|
||||
"Event ID": u.event.EventID(),
|
||||
"Old state NID": fmt.Sprintf("%d", u.oldStateNID),
|
||||
|
|
|
@ -354,3 +354,7 @@ func (r *Admin) PerformAdminDownloadState(
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Admin) PerformAdminDeleteEventReport(ctx context.Context, reportID uint64) error {
|
||||
return r.DB.AdminDeleteEventReport(ctx, reportID)
|
||||
}
|
||||
|
|
|
@ -503,16 +503,14 @@ func (c *Creator) PerformCreateRoom(ctx context.Context, userID spec.UserID, roo
|
|||
|
||||
err = c.RSAPI.PerformInvite(ctx, &api.PerformInviteRequest{
|
||||
InviteInput: api.InviteInput{
|
||||
RoomID: roomID,
|
||||
Inviter: userID,
|
||||
Invitee: *inviteeUserID,
|
||||
DisplayName: createRequest.UserDisplayName,
|
||||
AvatarURL: createRequest.UserAvatarURL,
|
||||
Reason: "",
|
||||
IsDirect: createRequest.IsDirect,
|
||||
KeyID: createRequest.KeyID,
|
||||
PrivateKey: createRequest.PrivateKey,
|
||||
EventTime: createRequest.EventTime,
|
||||
RoomID: roomID,
|
||||
Inviter: userID,
|
||||
Invitee: *inviteeUserID,
|
||||
Reason: "",
|
||||
IsDirect: createRequest.IsDirect,
|
||||
KeyID: createRequest.KeyID,
|
||||
PrivateKey: createRequest.PrivateKey,
|
||||
EventTime: createRequest.EventTime,
|
||||
},
|
||||
InviteRoomState: globalStrippedState,
|
||||
SendAsServer: string(userID.Domain()),
|
||||
|
|
|
@ -144,11 +144,9 @@ func (r *Inviter) PerformInvite(
|
|||
}
|
||||
|
||||
content := gomatrixserverlib.MemberContent{
|
||||
Membership: spec.Invite,
|
||||
DisplayName: req.InviteInput.DisplayName,
|
||||
AvatarURL: req.InviteInput.AvatarURL,
|
||||
Reason: req.InviteInput.Reason,
|
||||
IsDirect: req.InviteInput.IsDirect,
|
||||
Membership: spec.Invite,
|
||||
Reason: req.InviteInput.Reason,
|
||||
IsDirect: req.InviteInput.IsDirect,
|
||||
}
|
||||
|
||||
if err = proto.SetContent(content); err != nil {
|
||||
|
|
|
@ -1099,3 +1099,18 @@ func (r *Queryer) QueryUserIDForSender(ctx context.Context, roomID spec.RoomID,
|
|||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// RoomsWithACLs returns all room IDs for rooms with ACLs
|
||||
func (r *Queryer) RoomsWithACLs(ctx context.Context) ([]string, error) {
|
||||
return r.DB.RoomsWithACLs(ctx)
|
||||
}
|
||||
|
||||
// QueryAdminEventReports returns event reports given a filter.
|
||||
func (r *Queryer) QueryAdminEventReports(ctx context.Context, from uint64, limit uint64, backwards bool, userID, roomID string) ([]api.QueryAdminEventReportsResponse, int64, error) {
|
||||
return r.DB.QueryAdminEventReports(ctx, from, limit, backwards, userID, roomID)
|
||||
}
|
||||
|
||||
// QueryAdminEventReport returns a single event report.
|
||||
func (r *Queryer) QueryAdminEventReport(ctx context.Context, reportID uint64) (api.QueryAdminEventReportResponse, error) {
|
||||
return r.DB.QueryAdminEventReport(ctx, reportID)
|
||||
}
|
||||
|
|
|
@ -39,9 +39,14 @@ import (
|
|||
//
|
||||
// If returned walker is nil, then there are no more rooms left to traverse. This method does not modify the provided walker, so it
|
||||
// can be cached.
|
||||
func (querier *Queryer) QueryNextRoomHierarchyPage(ctx context.Context, walker roomserver.RoomHierarchyWalker, limit int) ([]fclient.RoomHierarchyRoom, *roomserver.RoomHierarchyWalker, error) {
|
||||
if authorised, _ := authorised(ctx, querier, walker.Caller, walker.RootRoomID, nil); !authorised {
|
||||
return nil, nil, roomserver.ErrRoomUnknownOrNotAllowed{Err: fmt.Errorf("room is unknown/forbidden")}
|
||||
func (querier *Queryer) QueryNextRoomHierarchyPage(ctx context.Context, walker roomserver.RoomHierarchyWalker, limit int) (
|
||||
[]fclient.RoomHierarchyRoom,
|
||||
[]string,
|
||||
*roomserver.RoomHierarchyWalker,
|
||||
error,
|
||||
) {
|
||||
if authorised, _, _ := authorised(ctx, querier, walker.Caller, walker.RootRoomID, nil); !authorised {
|
||||
return nil, []string{walker.RootRoomID.String()}, nil, roomserver.ErrRoomUnknownOrNotAllowed{Err: fmt.Errorf("room is unknown/forbidden")}
|
||||
}
|
||||
|
||||
discoveredRooms := []fclient.RoomHierarchyRoom{}
|
||||
|
@ -50,6 +55,7 @@ func (querier *Queryer) QueryNextRoomHierarchyPage(ctx context.Context, walker r
|
|||
unvisited := make([]roomserver.RoomHierarchyWalkerQueuedRoom, len(walker.Unvisited))
|
||||
copy(unvisited, walker.Unvisited)
|
||||
processed := walker.Processed.Copy()
|
||||
inaccessible := []string{}
|
||||
|
||||
// Depth first -> stack data structure
|
||||
for len(unvisited) > 0 {
|
||||
|
@ -108,7 +114,7 @@ func (querier *Queryer) QueryNextRoomHierarchyPage(ctx context.Context, walker r
|
|||
// as these children may be rooms we do know about.
|
||||
roomType = spec.MSpace
|
||||
}
|
||||
} else if authorised, isJoinedOrInvited := authorised(ctx, querier, walker.Caller, queuedRoom.RoomID, queuedRoom.ParentRoomID); authorised {
|
||||
} else if authorised, isJoinedOrInvited, allowedRoomIDs := authorised(ctx, querier, walker.Caller, queuedRoom.RoomID, queuedRoom.ParentRoomID); authorised {
|
||||
// Get all `m.space.child` state events for this room
|
||||
events, err := childReferences(ctx, querier, walker.SuggestedOnly, queuedRoom.RoomID)
|
||||
if err != nil {
|
||||
|
@ -125,14 +131,18 @@ func (querier *Queryer) QueryNextRoomHierarchyPage(ctx context.Context, walker r
|
|||
}
|
||||
|
||||
discoveredRooms = append(discoveredRooms, fclient.RoomHierarchyRoom{
|
||||
PublicRoom: *pubRoom,
|
||||
RoomType: roomType,
|
||||
ChildrenState: events,
|
||||
PublicRoom: *pubRoom,
|
||||
RoomType: roomType,
|
||||
ChildrenState: events,
|
||||
AllowedRoomIDs: allowedRoomIDs,
|
||||
})
|
||||
// don't walk children if the user is not joined/invited to the space
|
||||
if !isJoinedOrInvited {
|
||||
continue
|
||||
}
|
||||
} else if !authorised {
|
||||
inaccessible = append(inaccessible, queuedRoom.RoomID.String())
|
||||
continue
|
||||
} else {
|
||||
// room exists but user is not authorised
|
||||
continue
|
||||
|
@ -149,6 +159,7 @@ func (querier *Queryer) QueryNextRoomHierarchyPage(ctx context.Context, walker r
|
|||
// We need to invert the order here because the child events are lo->hi on the timestamp,
|
||||
// so we need to ensure we pop in the same lo->hi order, which won't be the case if we
|
||||
// insert the highest timestamp last in a stack.
|
||||
extendQueueLoop:
|
||||
for i := len(discoveredChildEvents) - 1; i >= 0; i-- {
|
||||
spaceContent := struct {
|
||||
Via []string `json:"via"`
|
||||
|
@ -161,6 +172,12 @@ func (querier *Queryer) QueryNextRoomHierarchyPage(ctx context.Context, walker r
|
|||
if err != nil {
|
||||
util.GetLogger(ctx).WithError(err).WithField("invalid_room_id", ev.StateKey).WithField("parent_room_id", queuedRoom.RoomID).Warn("Invalid room ID in m.space.child state event")
|
||||
} else {
|
||||
// Make sure not to queue inaccessible rooms
|
||||
for _, inaccessibleRoomID := range inaccessible {
|
||||
if inaccessibleRoomID == childRoomID.String() {
|
||||
continue extendQueueLoop
|
||||
}
|
||||
}
|
||||
unvisited = append(unvisited, roomserver.RoomHierarchyWalkerQueuedRoom{
|
||||
RoomID: *childRoomID,
|
||||
ParentRoomID: &queuedRoom.RoomID,
|
||||
|
@ -173,7 +190,7 @@ func (querier *Queryer) QueryNextRoomHierarchyPage(ctx context.Context, walker r
|
|||
|
||||
if len(unvisited) == 0 {
|
||||
// If no more rooms to walk, then don't return a walker for future pages
|
||||
return discoveredRooms, nil, nil
|
||||
return discoveredRooms, inaccessible, nil, nil
|
||||
} else {
|
||||
// If there are more rooms to walk, then return a new walker to resume walking from (for querying more pages)
|
||||
newWalker := roomserver.RoomHierarchyWalker{
|
||||
|
@ -185,22 +202,25 @@ func (querier *Queryer) QueryNextRoomHierarchyPage(ctx context.Context, walker r
|
|||
Processed: processed,
|
||||
}
|
||||
|
||||
return discoveredRooms, &newWalker, nil
|
||||
return discoveredRooms, inaccessible, &newWalker, nil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// authorised returns true iff the user is joined this room or the room is world_readable
|
||||
func authorised(ctx context.Context, querier *Queryer, caller types.DeviceOrServerName, roomID spec.RoomID, parentRoomID *spec.RoomID) (authed, isJoinedOrInvited bool) {
|
||||
func authorised(ctx context.Context, querier *Queryer, caller types.DeviceOrServerName, roomID spec.RoomID, parentRoomID *spec.RoomID) (authed, isJoinedOrInvited bool, resultAllowedRoomIDs []string) {
|
||||
if clientCaller := caller.Device(); clientCaller != nil {
|
||||
return authorisedUser(ctx, querier, clientCaller, roomID, parentRoomID)
|
||||
} else {
|
||||
return authorisedServer(ctx, querier, roomID, *caller.ServerName()), false
|
||||
}
|
||||
if serverCaller := caller.ServerName(); serverCaller != nil {
|
||||
authed, resultAllowedRoomIDs = authorisedServer(ctx, querier, roomID, *serverCaller)
|
||||
return authed, false, resultAllowedRoomIDs
|
||||
}
|
||||
return false, false, resultAllowedRoomIDs
|
||||
}
|
||||
|
||||
// authorisedServer returns true iff the server is joined this room or the room is world_readable, public, or knockable
|
||||
func authorisedServer(ctx context.Context, querier *Queryer, roomID spec.RoomID, callerServerName spec.ServerName) bool {
|
||||
func authorisedServer(ctx context.Context, querier *Queryer, roomID spec.RoomID, callerServerName spec.ServerName) (bool, []string) {
|
||||
// Check history visibility / join rules first
|
||||
hisVisTuple := gomatrixserverlib.StateKeyTuple{
|
||||
EventType: spec.MRoomHistoryVisibility,
|
||||
|
@ -219,13 +239,13 @@ func authorisedServer(ctx context.Context, querier *Queryer, roomID spec.RoomID,
|
|||
}, &queryRoomRes)
|
||||
if err != nil {
|
||||
util.GetLogger(ctx).WithError(err).Error("failed to QueryCurrentState")
|
||||
return false
|
||||
return false, []string{}
|
||||
}
|
||||
hisVisEv := queryRoomRes.StateEvents[hisVisTuple]
|
||||
if hisVisEv != nil {
|
||||
hisVis, _ := hisVisEv.HistoryVisibility()
|
||||
if hisVis == "world_readable" {
|
||||
return true
|
||||
return true, []string{}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -238,19 +258,23 @@ func authorisedServer(ctx context.Context, querier *Queryer, roomID spec.RoomID,
|
|||
rule, ruleErr := joinRuleEv.JoinRule()
|
||||
if ruleErr != nil {
|
||||
util.GetLogger(ctx).WithError(ruleErr).WithField("parent_room_id", roomID).Warn("failed to get join rule")
|
||||
return false
|
||||
return false, []string{}
|
||||
}
|
||||
|
||||
if rule == spec.Public || rule == spec.Knock {
|
||||
return true
|
||||
return true, []string{}
|
||||
}
|
||||
|
||||
if rule == spec.Restricted {
|
||||
if rule == spec.Restricted || rule == spec.KnockRestricted {
|
||||
allowJoinedToRoomIDs = append(allowJoinedToRoomIDs, restrictedJoinRuleAllowedRooms(ctx, joinRuleEv)...)
|
||||
}
|
||||
}
|
||||
|
||||
// check if server is joined to any allowed room
|
||||
resultAllowedRoomIDs := make([]string, 0, len(allowJoinedToRoomIDs))
|
||||
for _, allowedRoomID := range allowJoinedToRoomIDs {
|
||||
resultAllowedRoomIDs = append(resultAllowedRoomIDs, allowedRoomID.String())
|
||||
}
|
||||
for _, allowedRoomID := range allowJoinedToRoomIDs {
|
||||
var queryRes fs.QueryJoinedHostServerNamesInRoomResponse
|
||||
err = querier.FSAPI.QueryJoinedHostServerNamesInRoom(ctx, &fs.QueryJoinedHostServerNamesInRoomRequest{
|
||||
|
@ -262,18 +286,18 @@ func authorisedServer(ctx context.Context, querier *Queryer, roomID spec.RoomID,
|
|||
}
|
||||
for _, srv := range queryRes.ServerNames {
|
||||
if srv == callerServerName {
|
||||
return true
|
||||
return true, resultAllowedRoomIDs[1:]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
return false, resultAllowedRoomIDs[1:]
|
||||
}
|
||||
|
||||
// authorisedUser returns true iff the user is invited/joined this room or the room is world_readable
|
||||
// or if the room has a public or knock join rule.
|
||||
// Failing that, if the room has a restricted join rule and belongs to the space parent listed, it will return true.
|
||||
func authorisedUser(ctx context.Context, querier *Queryer, clientCaller *userapi.Device, roomID spec.RoomID, parentRoomID *spec.RoomID) (authed bool, isJoinedOrInvited bool) {
|
||||
func authorisedUser(ctx context.Context, querier *Queryer, clientCaller *userapi.Device, roomID spec.RoomID, parentRoomID *spec.RoomID) (authed bool, isJoinedOrInvited bool, resultAllowedRoomIDs []string) {
|
||||
hisVisTuple := gomatrixserverlib.StateKeyTuple{
|
||||
EventType: spec.MRoomHistoryVisibility,
|
||||
StateKey: "",
|
||||
|
@ -295,20 +319,20 @@ func authorisedUser(ctx context.Context, querier *Queryer, clientCaller *userapi
|
|||
}, &queryRes)
|
||||
if err != nil {
|
||||
util.GetLogger(ctx).WithError(err).Error("failed to QueryCurrentState")
|
||||
return false, false
|
||||
return false, false, resultAllowedRoomIDs
|
||||
}
|
||||
memberEv := queryRes.StateEvents[roomMemberTuple]
|
||||
if memberEv != nil {
|
||||
membership, _ := memberEv.Membership()
|
||||
if membership == spec.Join || membership == spec.Invite {
|
||||
return true, true
|
||||
return true, true, resultAllowedRoomIDs
|
||||
}
|
||||
}
|
||||
hisVisEv := queryRes.StateEvents[hisVisTuple]
|
||||
if hisVisEv != nil {
|
||||
hisVis, _ := hisVisEv.HistoryVisibility()
|
||||
if hisVis == "world_readable" {
|
||||
return true, false
|
||||
return true, false, resultAllowedRoomIDs
|
||||
}
|
||||
}
|
||||
joinRuleEv := queryRes.StateEvents[joinRuleTuple]
|
||||
|
@ -323,6 +347,7 @@ func authorisedUser(ctx context.Context, querier *Queryer, clientCaller *userapi
|
|||
allowedRoomIDs := restrictedJoinRuleAllowedRooms(ctx, joinRuleEv)
|
||||
// check parent is in the allowed set
|
||||
for _, a := range allowedRoomIDs {
|
||||
resultAllowedRoomIDs = append(resultAllowedRoomIDs, a.String())
|
||||
if *parentRoomID == a {
|
||||
allowed = true
|
||||
break
|
||||
|
@ -345,13 +370,13 @@ func authorisedUser(ctx context.Context, querier *Queryer, clientCaller *userapi
|
|||
if memberEv != nil {
|
||||
membership, _ := memberEv.Membership()
|
||||
if membership == spec.Join {
|
||||
return true, false
|
||||
return true, false, resultAllowedRoomIDs
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, false
|
||||
return false, false, resultAllowedRoomIDs
|
||||
}
|
||||
|
||||
// helper function to fetch a state event
|
||||
|
|
|
@ -17,6 +17,7 @@ package producers
|
|||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/matrix-org/dendrite/roomserver/storage/tables"
|
||||
"github.com/nats-io/nats.go"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/tidwall/gjson"
|
||||
|
@ -75,7 +76,13 @@ func (r *RoomEventProducer) ProduceRoomEvents(roomID string, updates []api.Outpu
|
|||
|
||||
if eventType == acls.MRoomServerACL && update.NewRoomEvent.Event.StateKeyEquals("") {
|
||||
ev := update.NewRoomEvent.Event.PDU
|
||||
defer r.ACLs.OnServerACLUpdate(ev)
|
||||
strippedEvent := tables.StrippedEvent{
|
||||
RoomID: ev.RoomID().String(),
|
||||
EventType: ev.Type(),
|
||||
StateKey: *ev.StateKey(),
|
||||
ContentValue: string(ev.Content()),
|
||||
}
|
||||
defer r.ACLs.OnServerACLUpdate(strippedEvent)
|
||||
}
|
||||
}
|
||||
logger.Tracef("Producing to topic '%s'", r.Topic)
|
||||
|
|
|
@ -1284,3 +1284,38 @@ func TestRoomConsumerRecreation(t *testing.T) {
|
|||
wantAckWait := input.MaximumMissingProcessingTime + (time.Second * 10)
|
||||
assert.Equal(t, wantAckWait, info.Config.AckWait)
|
||||
}
|
||||
|
||||
func TestRoomsWithACLs(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
alice := test.NewUser(t)
|
||||
noACLRoom := test.NewRoom(t, alice)
|
||||
aclRoom := test.NewRoom(t, alice)
|
||||
|
||||
aclRoom.CreateAndInsert(t, alice, "m.room.server_acl", map[string]any{
|
||||
"deny": []string{"evilhost.test"},
|
||||
"allow": []string{"*"},
|
||||
}, test.WithStateKey(""))
|
||||
|
||||
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
|
||||
cfg, processCtx, closeDB := testrig.CreateConfig(t, dbType)
|
||||
defer closeDB()
|
||||
|
||||
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
||||
natsInstance := &jetstream.NATSInstance{}
|
||||
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
|
||||
// start JetStream listeners
|
||||
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, natsInstance, caches, caching.DisableMetrics)
|
||||
rsAPI.SetFederationAPI(nil, nil)
|
||||
|
||||
for _, room := range []*test.Room{noACLRoom, aclRoom} {
|
||||
// Create the rooms
|
||||
err := api.SendEvents(ctx, rsAPI, api.KindNew, room.Events(), "test", "test", "test", nil, false)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
// Validate that we only have one ACLd room.
|
||||
roomsWithACLs, err := rsAPI.RoomsWithACLs(ctx)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []string{aclRoom.ID}, roomsWithACLs)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ import (
|
|||
|
||||
type Database interface {
|
||||
UserRoomKeys
|
||||
ReportedEvents
|
||||
// Do we support processing input events for more than one room at a time?
|
||||
SupportsConcurrentRoomInputs() bool
|
||||
AssignRoomNID(ctx context.Context, roomID spec.RoomID, roomVersion gomatrixserverlib.RoomVersion) (roomNID types.RoomNID, err error)
|
||||
|
@ -170,8 +171,6 @@ type Database interface {
|
|||
GetServerInRoom(ctx context.Context, roomNID types.RoomNID, serverName spec.ServerName) (bool, error)
|
||||
// GetKnownUsers searches all users that userID knows about.
|
||||
GetKnownUsers(ctx context.Context, userID, searchString string, limit int) ([]string, error)
|
||||
// GetKnownRooms returns a list of all rooms we know about.
|
||||
GetKnownRooms(ctx context.Context) ([]string, error)
|
||||
// ForgetRoom sets a flag in the membership table, that the user wishes to forget a specific room
|
||||
ForgetRoom(ctx context.Context, userID, roomID string, forget bool) error
|
||||
|
||||
|
@ -193,6 +192,12 @@ type Database interface {
|
|||
MaybeRedactEvent(
|
||||
ctx context.Context, roomInfo *types.RoomInfo, eventNID types.EventNID, event gomatrixserverlib.PDU, plResolver state.PowerLevelResolver, querier api.QuerySenderIDAPI,
|
||||
) (gomatrixserverlib.PDU, gomatrixserverlib.PDU, error)
|
||||
|
||||
// RoomsWithACLs returns all room IDs for rooms with ACLs
|
||||
RoomsWithACLs(ctx context.Context) ([]string, error)
|
||||
QueryAdminEventReports(ctx context.Context, from uint64, limit uint64, backwards bool, userID string, roomID string) ([]api.QueryAdminEventReportsResponse, int64, error)
|
||||
QueryAdminEventReport(ctx context.Context, reportID uint64) (api.QueryAdminEventReportResponse, error)
|
||||
AdminDeleteEventReport(ctx context.Context, reportID uint64) error
|
||||
}
|
||||
|
||||
type UserRoomKeys interface {
|
||||
|
@ -256,3 +261,11 @@ type EventDatabase interface {
|
|||
) (gomatrixserverlib.PDU, gomatrixserverlib.PDU, error)
|
||||
StoreEvent(ctx context.Context, event gomatrixserverlib.PDU, roomInfo *types.RoomInfo, eventTypeNID types.EventTypeNID, eventStateKeyNID types.EventStateKeyNID, authEventNIDs []types.EventNID, isRejected bool) (types.EventNID, types.StateAtEvent, error)
|
||||
}
|
||||
|
||||
type ReportedEvents interface {
|
||||
InsertReportedEvent(
|
||||
ctx context.Context,
|
||||
roomID, eventID, reportingUserID, reason string,
|
||||
score int64,
|
||||
) (int64, error)
|
||||
}
|
||||
|
|
|
@ -68,6 +68,10 @@ CREATE TABLE IF NOT EXISTS roomserver_events (
|
|||
|
||||
-- Create an index which helps in resolving membership events (event_type_nid = 5) - (used for history visibility)
|
||||
CREATE INDEX IF NOT EXISTS roomserver_events_memberships_idx ON roomserver_events (room_nid, event_state_key_nid) WHERE (event_type_nid = 5);
|
||||
|
||||
-- The following indexes are used by bulkSelectStateEventByNIDSQL
|
||||
CREATE INDEX IF NOT EXISTS roomserver_event_event_type_nid_idx ON roomserver_events (event_type_nid);
|
||||
CREATE INDEX IF NOT EXISTS roomserver_event_state_key_nid_idx ON roomserver_events (event_state_key_nid);
|
||||
`
|
||||
|
||||
const insertEventSQL = "" +
|
||||
|
@ -147,6 +151,8 @@ const selectRoomNIDsForEventNIDsSQL = "" +
|
|||
const selectEventRejectedSQL = "" +
|
||||
"SELECT is_rejected FROM roomserver_events WHERE room_nid = $1 AND event_id = $2"
|
||||
|
||||
const selectRoomsWithEventTypeNIDSQL = `SELECT DISTINCT room_nid FROM roomserver_events WHERE event_type_nid = $1`
|
||||
|
||||
type eventStatements struct {
|
||||
insertEventStmt *sql.Stmt
|
||||
selectEventStmt *sql.Stmt
|
||||
|
@ -166,6 +172,7 @@ type eventStatements struct {
|
|||
selectMaxEventDepthStmt *sql.Stmt
|
||||
selectRoomNIDsForEventNIDsStmt *sql.Stmt
|
||||
selectEventRejectedStmt *sql.Stmt
|
||||
selectRoomsWithEventTypeNIDStmt *sql.Stmt
|
||||
}
|
||||
|
||||
func CreateEventsTable(db *sql.DB) error {
|
||||
|
@ -206,6 +213,7 @@ func PrepareEventsTable(db *sql.DB) (tables.Events, error) {
|
|||
{&s.selectMaxEventDepthStmt, selectMaxEventDepthSQL},
|
||||
{&s.selectRoomNIDsForEventNIDsStmt, selectRoomNIDsForEventNIDsSQL},
|
||||
{&s.selectEventRejectedStmt, selectEventRejectedSQL},
|
||||
{&s.selectRoomsWithEventTypeNIDStmt, selectRoomsWithEventTypeNIDSQL},
|
||||
}.Prepare(db)
|
||||
}
|
||||
|
||||
|
@ -582,3 +590,25 @@ func (s *eventStatements) SelectEventRejected(
|
|||
err = stmt.QueryRowContext(ctx, roomNID, eventID).Scan(&rejected)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *eventStatements) SelectRoomsWithEventTypeNID(
|
||||
ctx context.Context, txn *sql.Tx, eventTypeNID types.EventTypeNID,
|
||||
) ([]types.RoomNID, error) {
|
||||
stmt := sqlutil.TxStmt(txn, s.selectRoomsWithEventTypeNIDStmt)
|
||||
rows, err := stmt.QueryContext(ctx, eventTypeNID)
|
||||
defer internal.CloseAndLogIfError(ctx, rows, "SelectRoomsWithEventTypeNID: rows.close() failed")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var roomNIDs []types.RoomNID
|
||||
var roomNID types.RoomNID
|
||||
for rows.Next() {
|
||||
if err := rows.Scan(&roomNID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
roomNIDs = append(roomNIDs, roomNID)
|
||||
}
|
||||
|
||||
return roomNIDs, rows.Err()
|
||||
}
|
||||
|
|
221
roomserver/storage/postgres/reported_events_table.go
Normal file
221
roomserver/storage/postgres/reported_events_table.go
Normal file
|
@ -0,0 +1,221 @@
|
|||
// Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package postgres
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"github.com/matrix-org/dendrite/internal"
|
||||
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||
"github.com/matrix-org/dendrite/roomserver/api"
|
||||
"github.com/matrix-org/dendrite/roomserver/storage/tables"
|
||||
"github.com/matrix-org/dendrite/roomserver/types"
|
||||
"github.com/matrix-org/gomatrixserverlib/spec"
|
||||
)
|
||||
|
||||
const reportedEventsScheme = `
|
||||
CREATE SEQUENCE IF NOT EXISTS roomserver_reported_events_id_seq;
|
||||
CREATE TABLE IF NOT EXISTS roomserver_reported_events
|
||||
(
|
||||
id BIGINT PRIMARY KEY DEFAULT nextval('roomserver_reported_events_id_seq'),
|
||||
room_nid BIGINT NOT NULL,
|
||||
event_nid BIGINT NOT NULL,
|
||||
reporting_user_nid BIGINT NOT NULL, -- the user reporting the event
|
||||
event_sender_nid BIGINT NOT NULL, -- the user who sent the reported event
|
||||
reason TEXT,
|
||||
score INTEGER,
|
||||
received_ts BIGINT NOT NULL
|
||||
);`
|
||||
|
||||
const insertReportedEventSQL = `
|
||||
INSERT INTO roomserver_reported_events (room_nid, event_nid, reporting_user_nid, event_sender_nid, reason, score, received_ts)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||
RETURNING id
|
||||
`
|
||||
|
||||
const selectReportedEventsDescSQL = `
|
||||
WITH countReports AS (
|
||||
SELECT count(*) as report_count
|
||||
FROM roomserver_reported_events
|
||||
WHERE ($1::BIGINT IS NULL OR room_nid = $1::BIGINT) AND ($2::TEXT IS NULL OR reporting_user_nid = $2::BIGINT)
|
||||
)
|
||||
SELECT report_count, id, room_nid, event_nid, reporting_user_nid, event_sender_nid, reason, score, received_ts
|
||||
FROM roomserver_reported_events, countReports
|
||||
WHERE ($1::BIGINT IS NULL OR room_nid = $1::BIGINT) AND ($2::TEXT IS NULL OR reporting_user_nid = $2::BIGINT)
|
||||
ORDER BY received_ts DESC
|
||||
OFFSET $3
|
||||
LIMIT $4
|
||||
`
|
||||
|
||||
const selectReportedEventsAscSQL = `
|
||||
WITH countReports AS (
|
||||
SELECT count(*) as report_count
|
||||
FROM roomserver_reported_events
|
||||
WHERE ($1::BIGINT IS NULL OR room_nid = $1::BIGINT) AND ($2::TEXT IS NULL OR reporting_user_nid = $2::BIGINT)
|
||||
)
|
||||
SELECT report_count, id, room_nid, event_nid, reporting_user_nid, event_sender_nid, reason, score, received_ts
|
||||
FROM roomserver_reported_events, countReports
|
||||
WHERE ($1::BIGINT IS NULL OR room_nid = $1::BIGINT) AND ($2::TEXT IS NULL OR reporting_user_nid = $2::BIGINT)
|
||||
ORDER BY received_ts ASC
|
||||
OFFSET $3
|
||||
LIMIT $4
|
||||
`
|
||||
|
||||
const selectReportedEventSQL = `
|
||||
SELECT id, room_nid, event_nid, reporting_user_nid, event_sender_nid, reason, score, received_ts
|
||||
FROM roomserver_reported_events
|
||||
WHERE id = $1
|
||||
`
|
||||
|
||||
const deleteReportedEventSQL = `DELETE FROM roomserver_reported_events WHERE id = $1`
|
||||
|
||||
type reportedEventsStatements struct {
|
||||
insertReportedEventsStmt *sql.Stmt
|
||||
selectReportedEventsDescStmt *sql.Stmt
|
||||
selectReportedEventsAscStmt *sql.Stmt
|
||||
selectReportedEventStmt *sql.Stmt
|
||||
deleteReportedEventStmt *sql.Stmt
|
||||
}
|
||||
|
||||
func CreateReportedEventsTable(db *sql.DB) error {
|
||||
_, err := db.Exec(reportedEventsScheme)
|
||||
return err
|
||||
}
|
||||
|
||||
func PrepareReportedEventsTable(db *sql.DB) (tables.ReportedEvents, error) {
|
||||
s := &reportedEventsStatements{}
|
||||
|
||||
return s, sqlutil.StatementList{
|
||||
{&s.insertReportedEventsStmt, insertReportedEventSQL},
|
||||
{&s.selectReportedEventsDescStmt, selectReportedEventsDescSQL},
|
||||
{&s.selectReportedEventsAscStmt, selectReportedEventsAscSQL},
|
||||
{&s.selectReportedEventStmt, selectReportedEventSQL},
|
||||
{&s.deleteReportedEventStmt, deleteReportedEventSQL},
|
||||
}.Prepare(db)
|
||||
}
|
||||
|
||||
func (r *reportedEventsStatements) InsertReportedEvent(
|
||||
ctx context.Context,
|
||||
txn *sql.Tx,
|
||||
roomNID types.RoomNID,
|
||||
eventNID types.EventNID,
|
||||
reportingUserID types.EventStateKeyNID,
|
||||
eventSenderID types.EventStateKeyNID,
|
||||
reason string,
|
||||
score int64,
|
||||
) (int64, error) {
|
||||
stmt := sqlutil.TxStmt(txn, r.insertReportedEventsStmt)
|
||||
|
||||
var reportID int64
|
||||
err := stmt.QueryRowContext(ctx,
|
||||
roomNID,
|
||||
eventNID,
|
||||
reportingUserID,
|
||||
eventSenderID,
|
||||
reason,
|
||||
score,
|
||||
spec.AsTimestamp(time.Now()),
|
||||
).Scan(&reportID)
|
||||
return reportID, err
|
||||
}
|
||||
|
||||
func (r *reportedEventsStatements) SelectReportedEvents(
|
||||
ctx context.Context,
|
||||
txn *sql.Tx,
|
||||
from, limit uint64,
|
||||
backwards bool,
|
||||
reportingUserID types.EventStateKeyNID,
|
||||
roomNID types.RoomNID,
|
||||
) ([]api.QueryAdminEventReportsResponse, int64, error) {
|
||||
var stmt *sql.Stmt
|
||||
if backwards {
|
||||
stmt = sqlutil.TxStmt(txn, r.selectReportedEventsDescStmt)
|
||||
} else {
|
||||
stmt = sqlutil.TxStmt(txn, r.selectReportedEventsAscStmt)
|
||||
}
|
||||
|
||||
var qryRoomNID *types.RoomNID
|
||||
if roomNID > 0 {
|
||||
qryRoomNID = &roomNID
|
||||
}
|
||||
var qryReportingUser *types.EventStateKeyNID
|
||||
if reportingUserID > 0 {
|
||||
qryReportingUser = &reportingUserID
|
||||
}
|
||||
|
||||
rows, err := stmt.QueryContext(ctx,
|
||||
qryRoomNID,
|
||||
qryReportingUser,
|
||||
from,
|
||||
limit,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
defer internal.CloseAndLogIfError(ctx, rows, "SelectReportedEvents: failed to close rows")
|
||||
|
||||
var result []api.QueryAdminEventReportsResponse
|
||||
var row api.QueryAdminEventReportsResponse
|
||||
var count int64
|
||||
for rows.Next() {
|
||||
if err = rows.Scan(
|
||||
&count,
|
||||
&row.ID,
|
||||
&row.RoomNID,
|
||||
&row.EventNID,
|
||||
&row.ReportingUserNID,
|
||||
&row.SenderNID,
|
||||
&row.Reason,
|
||||
&row.Score,
|
||||
&row.ReceivedTS,
|
||||
); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
result = append(result, row)
|
||||
}
|
||||
|
||||
return result, count, rows.Err()
|
||||
}
|
||||
|
||||
func (r *reportedEventsStatements) SelectReportedEvent(
|
||||
ctx context.Context,
|
||||
txn *sql.Tx,
|
||||
reportID uint64,
|
||||
) (api.QueryAdminEventReportResponse, error) {
|
||||
stmt := sqlutil.TxStmt(txn, r.selectReportedEventStmt)
|
||||
|
||||
var row api.QueryAdminEventReportResponse
|
||||
if err := stmt.QueryRowContext(ctx, reportID).Scan(
|
||||
&row.ID,
|
||||
&row.RoomNID,
|
||||
&row.EventNID,
|
||||
&row.ReportingUserNID,
|
||||
&row.SenderNID,
|
||||
&row.Reason,
|
||||
&row.Score,
|
||||
&row.ReceivedTS,
|
||||
); err != nil {
|
||||
return api.QueryAdminEventReportResponse{}, err
|
||||
}
|
||||
return row, nil
|
||||
}
|
||||
|
||||
func (r *reportedEventsStatements) DeleteReportedEvent(ctx context.Context, txn *sql.Tx, reportID uint64) error {
|
||||
stmt := sqlutil.TxStmt(txn, r.deleteReportedEventStmt)
|
||||
_, err := stmt.ExecContext(ctx, reportID)
|
||||
return err
|
||||
}
|
|
@ -76,9 +76,6 @@ const selectRoomVersionsForRoomNIDsSQL = "" +
|
|||
const selectRoomInfoSQL = "" +
|
||||
"SELECT room_version, room_nid, state_snapshot_nid, latest_event_nids FROM roomserver_rooms WHERE room_id = $1"
|
||||
|
||||
const selectRoomIDsSQL = "" +
|
||||
"SELECT room_id FROM roomserver_rooms WHERE array_length(latest_event_nids, 1) > 0"
|
||||
|
||||
const bulkSelectRoomIDsSQL = "" +
|
||||
"SELECT room_id FROM roomserver_rooms WHERE room_nid = ANY($1)"
|
||||
|
||||
|
@ -94,7 +91,6 @@ type roomStatements struct {
|
|||
updateLatestEventNIDsStmt *sql.Stmt
|
||||
selectRoomVersionsForRoomNIDsStmt *sql.Stmt
|
||||
selectRoomInfoStmt *sql.Stmt
|
||||
selectRoomIDsStmt *sql.Stmt
|
||||
bulkSelectRoomIDsStmt *sql.Stmt
|
||||
bulkSelectRoomNIDsStmt *sql.Stmt
|
||||
}
|
||||
|
@ -116,29 +112,11 @@ func PrepareRoomsTable(db *sql.DB) (tables.Rooms, error) {
|
|||
{&s.updateLatestEventNIDsStmt, updateLatestEventNIDsSQL},
|
||||
{&s.selectRoomVersionsForRoomNIDsStmt, selectRoomVersionsForRoomNIDsSQL},
|
||||
{&s.selectRoomInfoStmt, selectRoomInfoSQL},
|
||||
{&s.selectRoomIDsStmt, selectRoomIDsSQL},
|
||||
{&s.bulkSelectRoomIDsStmt, bulkSelectRoomIDsSQL},
|
||||
{&s.bulkSelectRoomNIDsStmt, bulkSelectRoomNIDsSQL},
|
||||
}.Prepare(db)
|
||||
}
|
||||
|
||||
func (s *roomStatements) SelectRoomIDsWithEvents(ctx context.Context, txn *sql.Tx) ([]string, error) {
|
||||
stmt := sqlutil.TxStmt(txn, s.selectRoomIDsStmt)
|
||||
rows, err := stmt.QueryContext(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer internal.CloseAndLogIfError(ctx, rows, "selectRoomIDsStmt: rows.close() failed")
|
||||
var roomIDs []string
|
||||
var roomID string
|
||||
for rows.Next() {
|
||||
if err = rows.Scan(&roomID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
roomIDs = append(roomIDs, roomID)
|
||||
}
|
||||
return roomIDs, rows.Err()
|
||||
}
|
||||
func (s *roomStatements) InsertRoomNID(
|
||||
ctx context.Context, txn *sql.Tx,
|
||||
roomID string, roomVersion gomatrixserverlib.RoomVersion,
|
||||
|
|
|
@ -134,6 +134,9 @@ func (d *Database) create(db *sql.DB) error {
|
|||
if err := CreateUserRoomKeysTable(db); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := CreateReportedEventsTable(db); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -199,6 +202,10 @@ func (d *Database) prepare(db *sql.DB, writer sqlutil.Writer, cache caching.Room
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
reportedEvents, err := PrepareReportedEventsTable(db)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.Database = shared.Database{
|
||||
DB: db,
|
||||
|
@ -212,6 +219,7 @@ func (d *Database) prepare(db *sql.DB, writer sqlutil.Writer, cache caching.Room
|
|||
EventStateKeysTable: eventStateKeys,
|
||||
PrevEventsTable: prevEvents,
|
||||
RedactionsTable: redactions,
|
||||
ReportedEventsTable: reportedEvents,
|
||||
},
|
||||
Cache: cache,
|
||||
Writer: writer,
|
||||
|
|
|
@ -162,6 +162,9 @@ func (s *userRoomKeysStatements) SelectAllPublicKeysForUser(ctx context.Context,
|
|||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer internal.CloseAndLogIfError(ctx, rows, "SelectAllPublicKeysForUser: failed to close rows")
|
||||
|
||||
resultMap := make(map[types.RoomNID]ed25519.PublicKey)
|
||||
|
|
|
@ -61,6 +61,7 @@ type EventDatabase struct {
|
|||
EventStateKeysTable tables.EventStateKeys
|
||||
PrevEventsTable tables.PreviousEvents
|
||||
RedactionsTable tables.Redactions
|
||||
ReportedEventsTable tables.ReportedEvents
|
||||
}
|
||||
|
||||
func (d *Database) SupportsConcurrentRoomInputs() bool {
|
||||
|
@ -1625,9 +1626,24 @@ func (d *Database) GetKnownUsers(ctx context.Context, userID, searchString strin
|
|||
return d.MembershipTable.SelectKnownUsers(ctx, nil, stateKeyNID, searchString, limit)
|
||||
}
|
||||
|
||||
// GetKnownRooms returns a list of all rooms we know about.
|
||||
func (d *Database) GetKnownRooms(ctx context.Context) ([]string, error) {
|
||||
return d.RoomsTable.SelectRoomIDsWithEvents(ctx, nil)
|
||||
func (d *Database) RoomsWithACLs(ctx context.Context) ([]string, error) {
|
||||
|
||||
eventTypeNID, err := d.GetOrCreateEventTypeNID(ctx, "m.room.server_acl")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
roomNIDs, err := d.EventsTable.SelectRoomsWithEventTypeNID(ctx, nil, eventTypeNID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
roomIDs, err := d.RoomsTable.BulkSelectRoomIDs(ctx, nil, roomNIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return roomIDs, nil
|
||||
}
|
||||
|
||||
// ForgetRoom sets a users room to forgotten
|
||||
|
@ -1867,6 +1883,252 @@ func (d *Database) SelectUserIDsForPublicKeys(ctx context.Context, publicKeys ma
|
|||
return result, err
|
||||
}
|
||||
|
||||
// InsertReportedEvent stores a reported event.
|
||||
func (d *Database) InsertReportedEvent(
|
||||
ctx context.Context,
|
||||
roomID, eventID, reportingUserID, reason string,
|
||||
score int64,
|
||||
) (int64, error) {
|
||||
roomInfo, err := d.roomInfo(ctx, nil, roomID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if roomInfo == nil {
|
||||
return 0, fmt.Errorf("room does not exist")
|
||||
}
|
||||
|
||||
events, err := d.eventsFromIDs(ctx, nil, roomInfo, []string{eventID}, NoFilter)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if len(events) == 0 {
|
||||
return 0, fmt.Errorf("unable to find requested event")
|
||||
}
|
||||
|
||||
stateKeyNIDs, err := d.EventStateKeyNIDs(ctx, []string{reportingUserID, events[0].SenderID().ToUserID().String()})
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to query eventStateKeyNIDs: %w", err)
|
||||
}
|
||||
|
||||
// We expect exactly 2 stateKeyNIDs
|
||||
if len(stateKeyNIDs) != 2 {
|
||||
return 0, fmt.Errorf("expected 2 stateKeyNIDs, received %d", len(stateKeyNIDs))
|
||||
}
|
||||
|
||||
var reportID int64
|
||||
err = d.Writer.Do(d.DB, nil, func(txn *sql.Tx) error {
|
||||
reportID, err = d.ReportedEventsTable.InsertReportedEvent(
|
||||
ctx,
|
||||
txn,
|
||||
roomInfo.RoomNID,
|
||||
events[0].EventNID,
|
||||
stateKeyNIDs[reportingUserID],
|
||||
stateKeyNIDs[events[0].SenderID().ToUserID().String()],
|
||||
reason,
|
||||
score,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return reportID, err
|
||||
}
|
||||
|
||||
// QueryAdminEventReports returns event reports given a filter.
|
||||
func (d *Database) QueryAdminEventReports(ctx context.Context, from uint64, limit uint64, backwards bool, userID string, roomID string) ([]api.QueryAdminEventReportsResponse, int64, error) {
|
||||
// Filter on roomID, if requested
|
||||
var roomNID types.RoomNID
|
||||
if roomID != "" {
|
||||
roomInfo, err := d.RoomInfo(ctx, roomID)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
roomNID = roomInfo.RoomNID
|
||||
}
|
||||
|
||||
// Same as above, but for userID
|
||||
var userNID types.EventStateKeyNID
|
||||
if userID != "" {
|
||||
stateKeysMap, err := d.EventStateKeyNIDs(ctx, []string{userID})
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
if len(stateKeysMap) != 1 {
|
||||
return nil, 0, fmt.Errorf("failed to get eventStateKeyNID for %s", userID)
|
||||
}
|
||||
userNID = stateKeysMap[userID]
|
||||
}
|
||||
|
||||
// Query all reported events matching the filters
|
||||
reports, count, err := d.ReportedEventsTable.SelectReportedEvents(ctx, nil, from, limit, backwards, userNID, roomNID)
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("failed to SelectReportedEvents: %w", err)
|
||||
}
|
||||
|
||||
// TODO: The below code may be inefficient due to many DB round trips and needs to be revisited.
|
||||
// For the time being, this is "good enough".
|
||||
qryRoomNIDs := make([]types.RoomNID, 0, len(reports))
|
||||
qryEventNIDs := make([]types.EventNID, 0, len(reports))
|
||||
qryStateKeyNIDs := make([]types.EventStateKeyNID, 0, len(reports))
|
||||
for _, report := range reports {
|
||||
qryRoomNIDs = append(qryRoomNIDs, report.RoomNID)
|
||||
qryEventNIDs = append(qryEventNIDs, report.EventNID)
|
||||
qryStateKeyNIDs = append(qryStateKeyNIDs, report.ReportingUserNID, report.SenderNID)
|
||||
}
|
||||
|
||||
// This also de-dupes the roomIDs, otherwise we would query the same
|
||||
// roomIDs in GetBulkStateContent multiple times
|
||||
roomIDs, err := d.RoomsTable.BulkSelectRoomIDs(ctx, nil, qryRoomNIDs)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
// TODO: replace this with something more efficient, as it loads the entire state snapshot.
|
||||
stateContent, err := d.GetBulkStateContent(ctx, roomIDs, []gomatrixserverlib.StateKeyTuple{
|
||||
{EventType: spec.MRoomName, StateKey: ""},
|
||||
{EventType: spec.MRoomCanonicalAlias, StateKey: ""},
|
||||
}, false)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
eventIDMap, err := d.EventIDs(ctx, qryEventNIDs)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Error("unable to map eventNIDs to eventIDs")
|
||||
return nil, 0, err
|
||||
}
|
||||
if len(eventIDMap) != len(qryEventNIDs) {
|
||||
return nil, 0, fmt.Errorf("expected %d eventIDs, got %d", len(qryEventNIDs), len(eventIDMap))
|
||||
}
|
||||
|
||||
// Get a map from EventStateKeyNID to userID
|
||||
userNIDMap, err := d.EventStateKeys(ctx, qryStateKeyNIDs)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Error("unable to map userNIDs to userIDs")
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
// Create a cache from roomNID to roomID to avoid hitting the DB again
|
||||
roomNIDIDCache := make(map[types.RoomNID]string, len(roomIDs))
|
||||
for i := 0; i < len(reports); i++ {
|
||||
cachedRoomID := roomNIDIDCache[reports[i].RoomNID]
|
||||
if cachedRoomID == "" {
|
||||
// We need to query this again, as we otherwise don't have a way to match roomNID -> roomID
|
||||
roomIDs, err = d.RoomsTable.BulkSelectRoomIDs(ctx, nil, []types.RoomNID{reports[i].RoomNID})
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
if len(roomIDs) == 0 || len(roomIDs) > 1 {
|
||||
logrus.Warnf("unable to map roomNID %d to a roomID, was this room deleted?", roomNID)
|
||||
continue
|
||||
}
|
||||
roomNIDIDCache[reports[i].RoomNID] = roomIDs[0]
|
||||
cachedRoomID = roomIDs[0]
|
||||
}
|
||||
|
||||
reports[i].EventID = eventIDMap[reports[i].EventNID]
|
||||
reports[i].RoomID = cachedRoomID
|
||||
roomName, canonicalAlias := findRoomNameAndCanonicalAlias(stateContent, cachedRoomID)
|
||||
reports[i].RoomName = roomName
|
||||
reports[i].CanonicalAlias = canonicalAlias
|
||||
reports[i].Sender = userNIDMap[reports[i].SenderNID]
|
||||
reports[i].UserID = userNIDMap[reports[i].ReportingUserNID]
|
||||
}
|
||||
|
||||
return reports, count, nil
|
||||
}
|
||||
|
||||
func (d *Database) QueryAdminEventReport(ctx context.Context, reportID uint64) (api.QueryAdminEventReportResponse, error) {
|
||||
|
||||
report, err := d.ReportedEventsTable.SelectReportedEvent(ctx, nil, reportID)
|
||||
if err != nil {
|
||||
return api.QueryAdminEventReportResponse{}, err
|
||||
}
|
||||
|
||||
// Get a map from EventStateKeyNID to userID
|
||||
userNIDMap, err := d.EventStateKeys(ctx, []types.EventStateKeyNID{report.ReportingUserNID, report.SenderNID})
|
||||
if err != nil {
|
||||
logrus.WithError(err).Error("unable to map userNIDs to userIDs")
|
||||
return report, err
|
||||
}
|
||||
|
||||
roomIDs, err := d.RoomsTable.BulkSelectRoomIDs(ctx, nil, []types.RoomNID{report.RoomNID})
|
||||
if err != nil {
|
||||
return report, err
|
||||
}
|
||||
|
||||
if len(roomIDs) != 1 {
|
||||
return report, fmt.Errorf("expected one roomID, got %d", len(roomIDs))
|
||||
}
|
||||
|
||||
// TODO: replace this with something more efficient, as it loads the entire state snapshot.
|
||||
stateContent, err := d.GetBulkStateContent(ctx, roomIDs, []gomatrixserverlib.StateKeyTuple{
|
||||
{EventType: spec.MRoomName, StateKey: ""},
|
||||
{EventType: spec.MRoomCanonicalAlias, StateKey: ""},
|
||||
}, false)
|
||||
if err != nil {
|
||||
return report, err
|
||||
}
|
||||
|
||||
eventIDMap, err := d.EventIDs(ctx, []types.EventNID{report.EventNID})
|
||||
if err != nil {
|
||||
logrus.WithError(err).Error("unable to map eventNIDs to eventIDs")
|
||||
return report, err
|
||||
}
|
||||
if len(eventIDMap) != 1 {
|
||||
return report, fmt.Errorf("expected %d eventIDs, got %d", 1, len(eventIDMap))
|
||||
}
|
||||
|
||||
eventJSONs, err := d.EventJSONTable.BulkSelectEventJSON(ctx, nil, []types.EventNID{report.EventNID})
|
||||
if err != nil {
|
||||
return report, err
|
||||
}
|
||||
if len(eventJSONs) != 1 {
|
||||
return report, fmt.Errorf("expected %d eventJSONs, got %d", 1, len(eventJSONs))
|
||||
}
|
||||
|
||||
roomName, canonicalAlias := findRoomNameAndCanonicalAlias(stateContent, roomIDs[0])
|
||||
|
||||
report.Sender = userNIDMap[report.SenderNID]
|
||||
report.UserID = userNIDMap[report.ReportingUserNID]
|
||||
report.RoomID = roomIDs[0]
|
||||
report.RoomName = roomName
|
||||
report.CanonicalAlias = canonicalAlias
|
||||
report.EventID = eventIDMap[report.EventNID]
|
||||
report.EventJSON = eventJSONs[0].EventJSON
|
||||
|
||||
return report, nil
|
||||
}
|
||||
|
||||
func (d *Database) AdminDeleteEventReport(ctx context.Context, reportID uint64) error {
|
||||
return d.Writer.Do(d.DB, nil, func(txn *sql.Tx) error {
|
||||
return d.ReportedEventsTable.DeleteReportedEvent(ctx, txn, reportID)
|
||||
})
|
||||
}
|
||||
|
||||
// findRoomNameAndCanonicalAlias loops over events to find the corresponding room name and canonicalAlias
|
||||
// for a given roomID.
|
||||
func findRoomNameAndCanonicalAlias(events []tables.StrippedEvent, roomID string) (name, canonicalAlias string) {
|
||||
for _, ev := range events {
|
||||
if ev.RoomID != roomID {
|
||||
continue
|
||||
}
|
||||
if ev.EventType == spec.MRoomName {
|
||||
name = ev.ContentValue
|
||||
}
|
||||
if ev.EventType == spec.MRoomCanonicalAlias {
|
||||
canonicalAlias = ev.ContentValue
|
||||
}
|
||||
// We found both wanted values, break the loop
|
||||
if name != "" && canonicalAlias != "" {
|
||||
break
|
||||
}
|
||||
}
|
||||
return name, canonicalAlias
|
||||
}
|
||||
|
||||
// FIXME TODO: Remove all this - horrible dupe with roomserver/state. Can't use the original impl because of circular loops
|
||||
// it should live in this package!
|
||||
|
||||
|
|
|
@ -44,6 +44,14 @@ const eventsSchema = `
|
|||
auth_event_nids TEXT NOT NULL DEFAULT '[]',
|
||||
is_rejected BOOLEAN NOT NULL DEFAULT FALSE
|
||||
);
|
||||
|
||||
-- Create an index which helps in resolving membership events (event_type_nid = 5) - (used for history visibility)
|
||||
CREATE INDEX IF NOT EXISTS roomserver_events_memberships_idx ON roomserver_events (room_nid, event_state_key_nid) WHERE (event_type_nid = 5);
|
||||
|
||||
-- The following indexes are used by bulkSelectStateEventByNIDSQL
|
||||
CREATE INDEX IF NOT EXISTS roomserver_event_event_type_nid_idx ON roomserver_events (event_type_nid);
|
||||
CREATE INDEX IF NOT EXISTS roomserver_event_state_key_nid_idx ON roomserver_events (event_state_key_nid);
|
||||
|
||||
`
|
||||
|
||||
const insertEventSQL = `
|
||||
|
@ -120,6 +128,8 @@ const selectRoomNIDsForEventNIDsSQL = "" +
|
|||
const selectEventRejectedSQL = "" +
|
||||
"SELECT is_rejected FROM roomserver_events WHERE room_nid = $1 AND event_id = $2"
|
||||
|
||||
const selectRoomsWithEventTypeNIDSQL = `SELECT DISTINCT room_nid FROM roomserver_events WHERE event_type_nid = $1`
|
||||
|
||||
type eventStatements struct {
|
||||
db *sql.DB
|
||||
insertEventStmt *sql.Stmt
|
||||
|
@ -135,6 +145,7 @@ type eventStatements struct {
|
|||
bulkSelectStateAtEventAndReferenceStmt *sql.Stmt
|
||||
bulkSelectEventIDStmt *sql.Stmt
|
||||
selectEventRejectedStmt *sql.Stmt
|
||||
selectRoomsWithEventTypeNIDStmt *sql.Stmt
|
||||
//bulkSelectEventNIDStmt *sql.Stmt
|
||||
//bulkSelectUnsentEventNIDStmt *sql.Stmt
|
||||
//selectRoomNIDsForEventNIDsStmt *sql.Stmt
|
||||
|
@ -192,6 +203,7 @@ func PrepareEventsTable(db *sql.DB) (tables.Events, error) {
|
|||
//{&s.bulkSelectUnsentEventNIDStmt, bulkSelectUnsentEventNIDSQL},
|
||||
//{&s.selectRoomNIDForEventNIDStmt, selectRoomNIDForEventNIDSQL},
|
||||
{&s.selectEventRejectedStmt, selectEventRejectedSQL},
|
||||
{&s.selectRoomsWithEventTypeNIDStmt, selectRoomsWithEventTypeNIDSQL},
|
||||
}.Prepare(db)
|
||||
}
|
||||
|
||||
|
@ -682,3 +694,25 @@ func (s *eventStatements) SelectEventRejected(
|
|||
err = stmt.QueryRowContext(ctx, roomNID, eventID).Scan(&rejected)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *eventStatements) SelectRoomsWithEventTypeNID(
|
||||
ctx context.Context, txn *sql.Tx, eventTypeNID types.EventTypeNID,
|
||||
) ([]types.RoomNID, error) {
|
||||
stmt := sqlutil.TxStmt(txn, s.selectRoomsWithEventTypeNIDStmt)
|
||||
rows, err := stmt.QueryContext(ctx, eventTypeNID)
|
||||
defer internal.CloseAndLogIfError(ctx, rows, "SelectRoomsWithEventTypeNID: rows.close() failed")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var roomNIDs []types.RoomNID
|
||||
var roomNID types.RoomNID
|
||||
for rows.Next() {
|
||||
if err := rows.Scan(&roomNID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
roomNIDs = append(roomNIDs, roomNID)
|
||||
}
|
||||
|
||||
return roomNIDs, rows.Err()
|
||||
}
|
||||
|
|
221
roomserver/storage/sqlite3/reported_events_table.go
Normal file
221
roomserver/storage/sqlite3/reported_events_table.go
Normal file
|
@ -0,0 +1,221 @@
|
|||
// Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package sqlite3
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"github.com/matrix-org/dendrite/internal"
|
||||
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||
"github.com/matrix-org/dendrite/roomserver/api"
|
||||
"github.com/matrix-org/dendrite/roomserver/storage/tables"
|
||||
"github.com/matrix-org/dendrite/roomserver/types"
|
||||
"github.com/matrix-org/gomatrixserverlib/spec"
|
||||
)
|
||||
|
||||
const reportedEventsScheme = `
|
||||
CREATE TABLE IF NOT EXISTS roomserver_reported_events
|
||||
(
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
room_nid INTEGER NOT NULL,
|
||||
event_nid INTEGER NOT NULL,
|
||||
reporting_user_nid INTEGER NOT NULL, -- the user reporting the event
|
||||
event_sender_nid INTEGER NOT NULL, -- the user who sent the reported event
|
||||
reason TEXT,
|
||||
score INTEGER,
|
||||
received_ts INTEGER NOT NULL
|
||||
);`
|
||||
|
||||
const insertReportedEventSQL = `
|
||||
INSERT INTO roomserver_reported_events (room_nid, event_nid, reporting_user_nid, event_sender_nid, reason, score, received_ts)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||
RETURNING id
|
||||
`
|
||||
|
||||
const selectReportedEventsDescSQL = `
|
||||
WITH countReports AS (
|
||||
SELECT count(*) as report_count
|
||||
FROM roomserver_reported_events
|
||||
WHERE ($1 IS NULL OR room_nid = $1) AND ($2 IS NULL OR reporting_user_nid = $2)
|
||||
)
|
||||
SELECT report_count, id, room_nid, event_nid, reporting_user_nid, event_sender_nid, reason, score, received_ts
|
||||
FROM roomserver_reported_events, countReports
|
||||
WHERE ($1 IS NULL OR room_nid = $1) AND ($2 IS NULL OR reporting_user_nid = $2)
|
||||
ORDER BY received_ts DESC
|
||||
LIMIT $3
|
||||
OFFSET $4
|
||||
`
|
||||
|
||||
const selectReportedEventsAscSQL = `
|
||||
WITH countReports AS (
|
||||
SELECT count(*) as report_count
|
||||
FROM roomserver_reported_events
|
||||
WHERE ($1 IS NULL OR room_nid = $1) AND ($2 IS NULL OR reporting_user_nid = $2)
|
||||
)
|
||||
SELECT report_count, id, room_nid, event_nid, reporting_user_nid, event_sender_nid, reason, score, received_ts
|
||||
FROM roomserver_reported_events, countReports
|
||||
WHERE ($1 IS NULL OR room_nid = $1) AND ($2 IS NULL OR reporting_user_nid = $2)
|
||||
ORDER BY received_ts ASC
|
||||
LIMIT $3
|
||||
OFFSET $4
|
||||
`
|
||||
|
||||
const selectReportedEventSQL = `
|
||||
SELECT id, room_nid, event_nid, reporting_user_nid, event_sender_nid, reason, score, received_ts
|
||||
FROM roomserver_reported_events
|
||||
WHERE id = $1
|
||||
`
|
||||
|
||||
const deleteReportedEventSQL = `DELETE FROM roomserver_reported_events WHERE id = $1`
|
||||
|
||||
type reportedEventsStatements struct {
|
||||
insertReportedEventsStmt *sql.Stmt
|
||||
selectReportedEventsDescStmt *sql.Stmt
|
||||
selectReportedEventsAscStmt *sql.Stmt
|
||||
selectReportedEventStmt *sql.Stmt
|
||||
deleteReportedEventStmt *sql.Stmt
|
||||
}
|
||||
|
||||
func CreateReportedEventsTable(db *sql.DB) error {
|
||||
_, err := db.Exec(reportedEventsScheme)
|
||||
return err
|
||||
}
|
||||
|
||||
func PrepareReportedEventsTable(db *sql.DB) (tables.ReportedEvents, error) {
|
||||
s := &reportedEventsStatements{}
|
||||
|
||||
return s, sqlutil.StatementList{
|
||||
{&s.insertReportedEventsStmt, insertReportedEventSQL},
|
||||
{&s.selectReportedEventsDescStmt, selectReportedEventsDescSQL},
|
||||
{&s.selectReportedEventsAscStmt, selectReportedEventsAscSQL},
|
||||
{&s.selectReportedEventStmt, selectReportedEventSQL},
|
||||
{&s.deleteReportedEventStmt, deleteReportedEventSQL},
|
||||
}.Prepare(db)
|
||||
}
|
||||
|
||||
func (r *reportedEventsStatements) InsertReportedEvent(
|
||||
ctx context.Context,
|
||||
txn *sql.Tx,
|
||||
roomNID types.RoomNID,
|
||||
eventNID types.EventNID,
|
||||
reportingUserID types.EventStateKeyNID,
|
||||
eventSenderID types.EventStateKeyNID,
|
||||
reason string,
|
||||
score int64,
|
||||
) (int64, error) {
|
||||
stmt := sqlutil.TxStmt(txn, r.insertReportedEventsStmt)
|
||||
|
||||
var reportID int64
|
||||
err := stmt.QueryRowContext(ctx,
|
||||
roomNID,
|
||||
eventNID,
|
||||
reportingUserID,
|
||||
eventSenderID,
|
||||
reason,
|
||||
score,
|
||||
spec.AsTimestamp(time.Now()),
|
||||
).Scan(&reportID)
|
||||
return reportID, err
|
||||
}
|
||||
|
||||
func (r *reportedEventsStatements) SelectReportedEvents(
|
||||
ctx context.Context,
|
||||
txn *sql.Tx,
|
||||
from, limit uint64,
|
||||
backwards bool,
|
||||
reportingUserID types.EventStateKeyNID,
|
||||
roomNID types.RoomNID,
|
||||
) ([]api.QueryAdminEventReportsResponse, int64, error) {
|
||||
|
||||
var stmt *sql.Stmt
|
||||
if backwards {
|
||||
stmt = sqlutil.TxStmt(txn, r.selectReportedEventsDescStmt)
|
||||
} else {
|
||||
stmt = sqlutil.TxStmt(txn, r.selectReportedEventsAscStmt)
|
||||
}
|
||||
|
||||
var qryRoomNID *types.RoomNID
|
||||
if roomNID > 0 {
|
||||
qryRoomNID = &roomNID
|
||||
}
|
||||
var qryReportingUser *types.EventStateKeyNID
|
||||
if reportingUserID > 0 {
|
||||
qryReportingUser = &reportingUserID
|
||||
}
|
||||
|
||||
rows, err := stmt.QueryContext(ctx,
|
||||
qryRoomNID,
|
||||
qryReportingUser,
|
||||
limit,
|
||||
from,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
defer internal.CloseAndLogIfError(ctx, rows, "SelectReportedEvents: failed to close rows")
|
||||
|
||||
var result []api.QueryAdminEventReportsResponse
|
||||
var row api.QueryAdminEventReportsResponse
|
||||
var count int64
|
||||
for rows.Next() {
|
||||
if err = rows.Scan(
|
||||
&count,
|
||||
&row.ID,
|
||||
&row.RoomNID,
|
||||
&row.EventNID,
|
||||
&row.ReportingUserNID,
|
||||
&row.SenderNID,
|
||||
&row.Reason,
|
||||
&row.Score,
|
||||
&row.ReceivedTS,
|
||||
); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
result = append(result, row)
|
||||
}
|
||||
|
||||
return result, count, rows.Err()
|
||||
}
|
||||
|
||||
func (r *reportedEventsStatements) SelectReportedEvent(
|
||||
ctx context.Context,
|
||||
txn *sql.Tx,
|
||||
reportID uint64,
|
||||
) (api.QueryAdminEventReportResponse, error) {
|
||||
stmt := sqlutil.TxStmt(txn, r.selectReportedEventStmt)
|
||||
|
||||
var row api.QueryAdminEventReportResponse
|
||||
if err := stmt.QueryRowContext(ctx, reportID).Scan(
|
||||
&row.ID,
|
||||
&row.RoomNID,
|
||||
&row.EventNID,
|
||||
&row.ReportingUserNID,
|
||||
&row.SenderNID,
|
||||
&row.Reason,
|
||||
&row.Score,
|
||||
&row.ReceivedTS,
|
||||
); err != nil {
|
||||
return api.QueryAdminEventReportResponse{}, err
|
||||
}
|
||||
return row, nil
|
||||
}
|
||||
|
||||
func (r *reportedEventsStatements) DeleteReportedEvent(ctx context.Context, txn *sql.Tx, reportID uint64) error {
|
||||
stmt := sqlutil.TxStmt(txn, r.deleteReportedEventStmt)
|
||||
_, err := stmt.ExecContext(ctx, reportID)
|
||||
return err
|
||||
}
|
|
@ -65,9 +65,6 @@ const selectRoomVersionsForRoomNIDsSQL = "" +
|
|||
const selectRoomInfoSQL = "" +
|
||||
"SELECT room_version, room_nid, state_snapshot_nid, latest_event_nids FROM roomserver_rooms WHERE room_id = $1"
|
||||
|
||||
const selectRoomIDsSQL = "" +
|
||||
"SELECT room_id FROM roomserver_rooms WHERE latest_event_nids != '[]'"
|
||||
|
||||
const bulkSelectRoomIDsSQL = "" +
|
||||
"SELECT room_id FROM roomserver_rooms WHERE room_nid IN ($1)"
|
||||
|
||||
|
@ -87,7 +84,6 @@ type roomStatements struct {
|
|||
updateLatestEventNIDsStmt *sql.Stmt
|
||||
//selectRoomVersionForRoomNIDStmt *sql.Stmt
|
||||
selectRoomInfoStmt *sql.Stmt
|
||||
selectRoomIDsStmt *sql.Stmt
|
||||
}
|
||||
|
||||
func CreateRoomsTable(db *sql.DB) error {
|
||||
|
@ -108,29 +104,10 @@ func PrepareRoomsTable(db *sql.DB) (tables.Rooms, error) {
|
|||
{&s.updateLatestEventNIDsStmt, updateLatestEventNIDsSQL},
|
||||
//{&s.selectRoomVersionForRoomNIDsStmt, selectRoomVersionForRoomNIDsSQL},
|
||||
{&s.selectRoomInfoStmt, selectRoomInfoSQL},
|
||||
{&s.selectRoomIDsStmt, selectRoomIDsSQL},
|
||||
{&s.selectRoomNIDForUpdateStmt, selectRoomNIDForUpdateSQL},
|
||||
}.Prepare(db)
|
||||
}
|
||||
|
||||
func (s *roomStatements) SelectRoomIDsWithEvents(ctx context.Context, txn *sql.Tx) ([]string, error) {
|
||||
stmt := sqlutil.TxStmt(txn, s.selectRoomIDsStmt)
|
||||
rows, err := stmt.QueryContext(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer internal.CloseAndLogIfError(ctx, rows, "selectRoomIDsStmt: rows.close() failed")
|
||||
var roomIDs []string
|
||||
var roomID string
|
||||
for rows.Next() {
|
||||
if err = rows.Scan(&roomID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
roomIDs = append(roomIDs, roomID)
|
||||
}
|
||||
return roomIDs, rows.Err()
|
||||
}
|
||||
|
||||
func (s *roomStatements) SelectRoomInfo(ctx context.Context, txn *sql.Tx, roomID string) (*types.RoomInfo, error) {
|
||||
var info types.RoomInfo
|
||||
var latestNIDsJSON string
|
||||
|
|
|
@ -141,7 +141,9 @@ func (d *Database) create(db *sql.DB) error {
|
|||
if err := CreateUserRoomKeysTable(db); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := CreateReportedEventsTable(db); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -206,6 +208,10 @@ func (d *Database) prepare(db *sql.DB, writer sqlutil.Writer, cache caching.Room
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
reportedEvents, err := PrepareReportedEventsTable(db)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.Database = shared.Database{
|
||||
DB: db,
|
||||
|
@ -219,6 +225,7 @@ func (d *Database) prepare(db *sql.DB, writer sqlutil.Writer, cache caching.Room
|
|||
EventJSONTable: eventJSON,
|
||||
PrevEventsTable: prevEvents,
|
||||
RedactionsTable: redactions,
|
||||
ReportedEventsTable: reportedEvents,
|
||||
},
|
||||
Cache: cache,
|
||||
Writer: writer,
|
||||
|
|
|
@ -177,6 +177,9 @@ func (s *userRoomKeysStatements) SelectAllPublicKeysForUser(ctx context.Context,
|
|||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer internal.CloseAndLogIfError(ctx, rows, "SelectAllPublicKeysForUser: failed to close rows")
|
||||
|
||||
resultMap := make(map[types.RoomNID]ed25519.PublicKey)
|
||||
|
|
|
@ -2,6 +2,7 @@ package tables_test
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||
|
@ -147,3 +148,38 @@ func Test_EventsTable(t *testing.T) {
|
|||
assert.Equal(t, int64(len(room.Events())+1), maxDepth)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRoomsWithACL(t *testing.T) {
|
||||
|
||||
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
|
||||
eventStateKeys, closeEventStateKeys := mustCreateEventTypesTable(t, dbType)
|
||||
defer closeEventStateKeys()
|
||||
|
||||
eventsTable, closeEventsTable := mustCreateEventsTable(t, dbType)
|
||||
defer closeEventsTable()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// insert the m.room.server_acl event type
|
||||
eventTypeNID, err := eventStateKeys.InsertEventTypeNID(ctx, nil, "m.room.server_acl")
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Create ACL'd rooms
|
||||
var wantRoomNIDs []types.RoomNID
|
||||
for i := 0; i < 10; i++ {
|
||||
_, _, err = eventsTable.InsertEvent(ctx, nil, types.RoomNID(i), eventTypeNID, types.EmptyStateKeyNID, fmt.Sprintf("$1337+%d", i), nil, 0, false)
|
||||
assert.Nil(t, err)
|
||||
wantRoomNIDs = append(wantRoomNIDs, types.RoomNID(i))
|
||||
}
|
||||
|
||||
// Create non-ACL'd rooms (eventTypeNID+1)
|
||||
for i := 10; i < 20; i++ {
|
||||
_, _, err = eventsTable.InsertEvent(ctx, nil, types.RoomNID(i), eventTypeNID+1, types.EmptyStateKeyNID, fmt.Sprintf("$1337+%d", i), nil, 0, false)
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
gotRoomNIDs, err := eventsTable.SelectRoomsWithEventTypeNID(ctx, nil, eventTypeNID)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, wantRoomNIDs, gotRoomNIDs)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"database/sql"
|
||||
"errors"
|
||||
|
||||
"github.com/matrix-org/dendrite/roomserver/api"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/matrix-org/gomatrixserverlib/spec"
|
||||
"github.com/tidwall/gjson"
|
||||
|
@ -69,6 +70,8 @@ type Events interface {
|
|||
SelectMaxEventDepth(ctx context.Context, txn *sql.Tx, eventNIDs []types.EventNID) (int64, error)
|
||||
SelectRoomNIDsForEventNIDs(ctx context.Context, txn *sql.Tx, eventNIDs []types.EventNID) (roomNIDs map[types.EventNID]types.RoomNID, err error)
|
||||
SelectEventRejected(ctx context.Context, txn *sql.Tx, roomNID types.RoomNID, eventID string) (rejected bool, err error)
|
||||
|
||||
SelectRoomsWithEventTypeNID(ctx context.Context, txn *sql.Tx, eventTypeNID types.EventTypeNID) ([]types.RoomNID, error)
|
||||
}
|
||||
|
||||
type Rooms interface {
|
||||
|
@ -80,7 +83,6 @@ type Rooms interface {
|
|||
UpdateLatestEventNIDs(ctx context.Context, txn *sql.Tx, roomNID types.RoomNID, eventNIDs []types.EventNID, lastEventSentNID types.EventNID, stateSnapshotNID types.StateSnapshotNID) error
|
||||
SelectRoomVersionsForRoomNIDs(ctx context.Context, txn *sql.Tx, roomNID []types.RoomNID) (map[types.RoomNID]gomatrixserverlib.RoomVersion, error)
|
||||
SelectRoomInfo(ctx context.Context, txn *sql.Tx, roomID string) (*types.RoomInfo, error)
|
||||
SelectRoomIDsWithEvents(ctx context.Context, txn *sql.Tx) ([]string, error)
|
||||
BulkSelectRoomIDs(ctx context.Context, txn *sql.Tx, roomNIDs []types.RoomNID) ([]string, error)
|
||||
BulkSelectRoomNIDs(ctx context.Context, txn *sql.Tx, roomIDs []string) ([]types.RoomNID, error)
|
||||
}
|
||||
|
@ -126,6 +128,33 @@ type Invites interface {
|
|||
SelectInviteActiveForUserInRoom(ctx context.Context, txn *sql.Tx, targetUserNID types.EventStateKeyNID, roomNID types.RoomNID) ([]types.EventStateKeyNID, []string, []byte, error)
|
||||
}
|
||||
|
||||
type ReportedEvents interface {
|
||||
InsertReportedEvent(
|
||||
ctx context.Context,
|
||||
txn *sql.Tx,
|
||||
roomNID types.RoomNID,
|
||||
eventNID types.EventNID,
|
||||
reportingUserID types.EventStateKeyNID,
|
||||
eventSenderID types.EventStateKeyNID,
|
||||
reason string,
|
||||
score int64,
|
||||
) (int64, error)
|
||||
SelectReportedEvents(
|
||||
ctx context.Context,
|
||||
txn *sql.Tx,
|
||||
from, limit uint64,
|
||||
backwards bool,
|
||||
reportingUserID types.EventStateKeyNID,
|
||||
roomNID types.RoomNID,
|
||||
) ([]api.QueryAdminEventReportsResponse, int64, error)
|
||||
SelectReportedEvent(
|
||||
ctx context.Context,
|
||||
txn *sql.Tx,
|
||||
reportID uint64,
|
||||
) (api.QueryAdminEventReportResponse, error)
|
||||
DeleteReportedEvent(ctx context.Context, txn *sql.Tx, reportID uint64) error
|
||||
}
|
||||
|
||||
type MembershipState int64
|
||||
|
||||
const (
|
||||
|
@ -235,6 +264,10 @@ func ExtractContentValue(ev *types.HeaderedEvent) string {
|
|||
key = "topic"
|
||||
case "m.room.guest_access":
|
||||
key = "guest_access"
|
||||
case "m.room.server_acl":
|
||||
// We need the entire content and not only one key, so we can use it
|
||||
// on startup to generate the ACLs. This is merely a workaround.
|
||||
return string(content)
|
||||
}
|
||||
result := gjson.GetBytes(content, key)
|
||||
if !result.Exists() {
|
||||
|
|
|
@ -74,11 +74,6 @@ func TestRoomsTable(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
assert.Nil(t, roomInfo)
|
||||
|
||||
// There are no rooms with latestEventNIDs yet
|
||||
roomIDs, err := tab.SelectRoomIDsWithEvents(ctx, nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 0, len(roomIDs))
|
||||
|
||||
roomVersions, err := tab.SelectRoomVersionsForRoomNIDs(ctx, nil, []types.RoomNID{wantRoomNID, 1337})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, roomVersions[wantRoomNID], room.Version)
|
||||
|
@ -86,7 +81,7 @@ func TestRoomsTable(t *testing.T) {
|
|||
_, ok := roomVersions[1337]
|
||||
assert.False(t, ok)
|
||||
|
||||
roomIDs, err = tab.BulkSelectRoomIDs(ctx, nil, []types.RoomNID{wantRoomNID, 1337})
|
||||
roomIDs, err := tab.BulkSelectRoomIDs(ctx, nil, []types.RoomNID{wantRoomNID, 1337})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []string{room.ID}, roomIDs)
|
||||
|
||||
|
|
|
@ -28,13 +28,13 @@ import (
|
|||
_ "net/http/pprof"
|
||||
"os"
|
||||
"os/signal"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
sentryhttp "github.com/getsentry/sentry-go/http"
|
||||
"github.com/matrix-org/gomatrixserverlib/fclient"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"go.uber.org/atomic"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/kardianos/minwinsvc"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//go:build !linux && !darwin && !netbsd && !freebsd && !openbsd && !solaris && !dragonfly && !aix
|
||||
// +build !linux,!darwin,!netbsd,!freebsd,!openbsd,!solaris,!dragonfly,!aix
|
||||
//go:build !unix
|
||||
// +build !unix
|
||||
|
||||
package base
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//go:build linux || darwin || netbsd || freebsd || openbsd || solaris || dragonfly || aix
|
||||
// +build linux darwin netbsd freebsd openbsd solaris dragonfly aix
|
||||
//go:build unix
|
||||
// +build unix
|
||||
|
||||
package base
|
||||
|
||||
|
|
|
@ -40,6 +40,9 @@ type AppServiceAPI struct {
|
|||
// on appservice endpoints. This is not recommended in production!
|
||||
DisableTLSValidation bool `yaml:"disable_tls_validation"`
|
||||
|
||||
LegacyAuth bool `yaml:"legacy_auth"`
|
||||
LegacyPaths bool `yaml:"legacy_paths"`
|
||||
|
||||
ConfigFiles []string `yaml:"config_files"`
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,13 @@ type FederationAPI struct {
|
|||
// The default value is 16 if not specified, which is circa 18 hours.
|
||||
FederationMaxRetries uint32 `yaml:"send_max_retries"`
|
||||
|
||||
// P2P Feature: Whether relaying to specific nodes should be enabled.
|
||||
// Defaults to false.
|
||||
// Note: Enabling relays introduces a huge startup delay, if you are not using
|
||||
// relays and have many servers to re-hydrate on start. Only enable this
|
||||
// if you are using relays!
|
||||
EnableRelays bool `yaml:"enable_relays"`
|
||||
|
||||
// P2P Feature: How many consecutive failures that we should tolerate when
|
||||
// sending federation requests to a specific server until we should assume they
|
||||
// are offline. If we assume they are offline then we will attempt to send
|
||||
|
|
|
@ -38,7 +38,12 @@ func (s *NATSInstance) Prepare(process *process.ProcessContext, cfg *config.JetS
|
|||
defer natsLock.Unlock()
|
||||
// check if we need an in-process NATS Server
|
||||
if len(cfg.Addresses) != 0 {
|
||||
return setupNATS(process, cfg, nil)
|
||||
// reuse existing connections
|
||||
if s.nc != nil {
|
||||
return s.js, s.nc
|
||||
}
|
||||
s.js, s.nc = setupNATS(process, cfg, nil)
|
||||
return s.js, s.nc
|
||||
}
|
||||
if s.Server == nil {
|
||||
var err error
|
||||
|
|
|
@ -601,9 +601,11 @@ func (s *OutputRoomEventConsumer) writeFTS(ev *rstypes.HeaderedEvent, pduPositio
|
|||
}
|
||||
e.SetContentType(ev.Type())
|
||||
|
||||
var relatesTo gjson.Result
|
||||
switch ev.Type() {
|
||||
case "m.room.message":
|
||||
e.Content = gjson.GetBytes(ev.Content(), "body").String()
|
||||
relatesTo = gjson.GetBytes(ev.Content(), "m\\.relates_to")
|
||||
case spec.MRoomName:
|
||||
e.Content = gjson.GetBytes(ev.Content(), "name").String()
|
||||
case spec.MRoomTopic:
|
||||
|
@ -622,6 +624,22 @@ func (s *OutputRoomEventConsumer) writeFTS(ev *rstypes.HeaderedEvent, pduPositio
|
|||
if err := s.fts.Index(e); err != nil {
|
||||
return err
|
||||
}
|
||||
// If the event is an edited message we remove the original event from the index
|
||||
// to avoid duplicates in the search results.
|
||||
if relatesTo.Exists() {
|
||||
relatedData := relatesTo.Map()
|
||||
if _, ok := relatedData["rel_type"]; ok && relatedData["rel_type"].Str == "m.replace" {
|
||||
// We remove the original event from the index
|
||||
if srcEventID, ok := relatedData["event_id"]; ok {
|
||||
if err := s.fts.Delete(srcEventID.Str); err != nil {
|
||||
log.WithFields(log.Fields{
|
||||
"event_id": ev.EventID(),
|
||||
"src_id": srcEventID.Str,
|
||||
}).WithError(err).Error("Failed to delete edited message from the fulltext index")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -110,6 +110,7 @@ func Context(
|
|||
}
|
||||
|
||||
stateFilter := synctypes.StateFilter{
|
||||
Limit: filter.Limit,
|
||||
NotSenders: filter.NotSenders,
|
||||
NotTypes: filter.NotTypes,
|
||||
Senders: filter.Senders,
|
||||
|
@ -157,6 +158,11 @@ func Context(
|
|||
}
|
||||
}
|
||||
|
||||
// Limit is split up for before/after events
|
||||
if filter.Limit > 1 {
|
||||
filter.Limit = filter.Limit / 2
|
||||
}
|
||||
|
||||
eventsBefore, err := snapshot.SelectContextBeforeEvent(ctx, id, roomID, filter)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
logrus.WithError(err).Error("unable to fetch before events")
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
package routing
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math"
|
||||
"net/http"
|
||||
|
||||
|
@ -33,31 +32,13 @@ type getMembershipResponse struct {
|
|||
Chunk []synctypes.ClientEvent `json:"chunk"`
|
||||
}
|
||||
|
||||
// https://matrix.org/docs/spec/client_server/r0.6.0#get-matrix-client-r0-rooms-roomid-joined-members
|
||||
type getJoinedMembersResponse struct {
|
||||
Joined map[string]joinedMember `json:"joined"`
|
||||
}
|
||||
|
||||
type joinedMember struct {
|
||||
DisplayName string `json:"display_name"`
|
||||
AvatarURL string `json:"avatar_url"`
|
||||
}
|
||||
|
||||
// The database stores 'displayname' without an underscore.
|
||||
// Deserialize into this and then change to the actual API response
|
||||
type databaseJoinedMember struct {
|
||||
DisplayName string `json:"displayname"`
|
||||
AvatarURL string `json:"avatar_url"`
|
||||
}
|
||||
|
||||
// GetMemberships implements
|
||||
//
|
||||
// GET /rooms/{roomId}/members
|
||||
// GET /rooms/{roomId}/joined_members
|
||||
func GetMemberships(
|
||||
req *http.Request, device *userapi.Device, roomID string,
|
||||
syncDB storage.Database, rsAPI api.SyncRoomserverAPI,
|
||||
joinedOnly bool, membership, notMembership *string, at string,
|
||||
membership, notMembership *string, at string,
|
||||
) util.JSONResponse {
|
||||
userID, err := spec.NewUserID(device.UserID, true)
|
||||
if err != nil {
|
||||
|
@ -87,13 +68,6 @@ func GetMemberships(
|
|||
}
|
||||
}
|
||||
|
||||
if joinedOnly && !queryRes.IsInRoom {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusForbidden,
|
||||
JSON: spec.Forbidden("You aren't a member of the room and weren't previously a member of the room."),
|
||||
}
|
||||
}
|
||||
|
||||
db, err := syncDB.NewDatabaseSnapshot(req.Context())
|
||||
if err != nil {
|
||||
return util.JSONResponse{
|
||||
|
@ -139,40 +113,6 @@ func GetMemberships(
|
|||
|
||||
result := qryRes.Events
|
||||
|
||||
if joinedOnly {
|
||||
var res getJoinedMembersResponse
|
||||
res.Joined = make(map[string]joinedMember)
|
||||
for _, ev := range result {
|
||||
var content databaseJoinedMember
|
||||
if err := json.Unmarshal(ev.Content(), &content); err != nil {
|
||||
util.GetLogger(req.Context()).WithError(err).Error("failed to unmarshal event content")
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusInternalServerError,
|
||||
JSON: spec.InternalServerError{},
|
||||
}
|
||||
}
|
||||
|
||||
userID, err := rsAPI.QueryUserIDForSender(req.Context(), ev.RoomID(), ev.SenderID())
|
||||
if err != nil || userID == nil {
|
||||
util.GetLogger(req.Context()).WithError(err).Error("rsAPI.QueryUserIDForSender failed")
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusInternalServerError,
|
||||
JSON: spec.InternalServerError{},
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusForbidden,
|
||||
JSON: spec.Forbidden("You don't have permission to kick this user, unknown senderID"),
|
||||
}
|
||||
}
|
||||
res.Joined[userID.String()] = joinedMember(content)
|
||||
}
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusOK,
|
||||
JSON: res,
|
||||
}
|
||||
}
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusOK,
|
||||
JSON: getMembershipResponse{synctypes.ToClientEvents(gomatrixserverlib.ToPDUs(result), synctypes.FormatAll, func(roomID spec.RoomID, senderID spec.SenderID) (*spec.UserID, error) {
|
||||
|
|
|
@ -197,19 +197,7 @@ func Setup(
|
|||
}
|
||||
|
||||
at := req.URL.Query().Get("at")
|
||||
return GetMemberships(req, device, vars["roomID"], syncDB, rsAPI, false, membership, notMembership, at)
|
||||
return GetMemberships(req, device, vars["roomID"], syncDB, rsAPI, membership, notMembership, at)
|
||||
}, httputil.WithAllowGuests()),
|
||||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
|
||||
v3mux.Handle("/rooms/{roomID}/joined_members",
|
||||
httputil.MakeAuthAPI("rooms_members", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||
if err != nil {
|
||||
return util.ErrorResponse(err)
|
||||
}
|
||||
at := req.URL.Query().Get("at")
|
||||
membership := spec.Join
|
||||
return GetMemberships(req, device, vars["roomID"], syncDB, rsAPI, true, &membership, nil, at)
|
||||
}),
|
||||
).Methods(http.MethodGet, http.MethodOptions)
|
||||
}
|
||||
|
|
|
@ -203,6 +203,12 @@ func (p *PDUStreamProvider) IncrementalSync(
|
|||
req.Log.WithError(err).Error("unable to update event filter with ignored users")
|
||||
}
|
||||
|
||||
dbEvents, err := p.getRecentEvents(ctx, stateDeltas, r, eventFilter, snapshot)
|
||||
if err != nil {
|
||||
req.Log.WithError(err).Error("unable to get recent events")
|
||||
return r.From
|
||||
}
|
||||
|
||||
newPos = from
|
||||
for _, delta := range stateDeltas {
|
||||
newRange := r
|
||||
|
@ -218,7 +224,7 @@ func (p *PDUStreamProvider) IncrementalSync(
|
|||
}
|
||||
}
|
||||
var pos types.StreamPosition
|
||||
if pos, err = p.addRoomDeltaToResponse(ctx, snapshot, req.Device, newRange, delta, &eventFilter, &stateFilter, req); err != nil {
|
||||
if pos, err = p.addRoomDeltaToResponse(ctx, snapshot, req.Device, newRange, delta, &eventFilter, &stateFilter, req, dbEvents); err != nil {
|
||||
req.Log.WithError(err).Error("d.addRoomDeltaToResponse failed")
|
||||
if err == context.DeadlineExceeded || err == context.Canceled || err == sql.ErrTxDone {
|
||||
return newPos
|
||||
|
@ -240,6 +246,66 @@ func (p *PDUStreamProvider) IncrementalSync(
|
|||
return newPos
|
||||
}
|
||||
|
||||
func (p *PDUStreamProvider) getRecentEvents(ctx context.Context, stateDeltas []types.StateDelta, r types.Range, eventFilter synctypes.RoomEventFilter, snapshot storage.DatabaseTransaction) (map[string]types.RecentEvents, error) {
|
||||
var roomIDs []string
|
||||
var newlyJoinedRoomIDs []string
|
||||
for _, delta := range stateDeltas {
|
||||
if delta.NewlyJoined {
|
||||
newlyJoinedRoomIDs = append(newlyJoinedRoomIDs, delta.RoomID)
|
||||
} else {
|
||||
roomIDs = append(roomIDs, delta.RoomID)
|
||||
}
|
||||
}
|
||||
dbEvents := make(map[string]types.RecentEvents)
|
||||
if len(roomIDs) > 0 {
|
||||
events, err := snapshot.RecentEvents(
|
||||
ctx, roomIDs, r,
|
||||
&eventFilter, true, true,
|
||||
)
|
||||
if err != nil {
|
||||
if err != sql.ErrNoRows {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
for k, v := range events {
|
||||
dbEvents[k] = v
|
||||
}
|
||||
}
|
||||
if len(newlyJoinedRoomIDs) > 0 {
|
||||
// For rooms that were joined in this sync, try to fetch
|
||||
// as much timeline events as allowed by the filter.
|
||||
|
||||
filter := eventFilter
|
||||
// If we're going backwards, grep at least X events, this is mostly to satisfy Sytest
|
||||
if eventFilter.Limit < recentEventBackwardsLimit {
|
||||
filter.Limit = recentEventBackwardsLimit // TODO: Figure out a better way
|
||||
diff := r.From - r.To
|
||||
if diff > 0 && diff < recentEventBackwardsLimit {
|
||||
filter.Limit = int(diff)
|
||||
}
|
||||
}
|
||||
|
||||
events, err := snapshot.RecentEvents(
|
||||
ctx, newlyJoinedRoomIDs, types.Range{
|
||||
From: r.To,
|
||||
To: 0,
|
||||
Backwards: true,
|
||||
},
|
||||
&filter, true, true,
|
||||
)
|
||||
if err != nil {
|
||||
if err != sql.ErrNoRows {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
for k, v := range events {
|
||||
dbEvents[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
return dbEvents, nil
|
||||
}
|
||||
|
||||
// Limit the recent events to X when going backwards
|
||||
const recentEventBackwardsLimit = 100
|
||||
|
||||
|
@ -253,29 +319,9 @@ func (p *PDUStreamProvider) addRoomDeltaToResponse(
|
|||
eventFilter *synctypes.RoomEventFilter,
|
||||
stateFilter *synctypes.StateFilter,
|
||||
req *types.SyncRequest,
|
||||
dbEvents map[string]types.RecentEvents,
|
||||
) (types.StreamPosition, error) {
|
||||
var err error
|
||||
originalLimit := eventFilter.Limit
|
||||
// If we're going backwards, grep at least X events, this is mostly to satisfy Sytest
|
||||
if r.Backwards && originalLimit < recentEventBackwardsLimit {
|
||||
eventFilter.Limit = recentEventBackwardsLimit // TODO: Figure out a better way
|
||||
diff := r.From - r.To
|
||||
if diff > 0 && diff < recentEventBackwardsLimit {
|
||||
eventFilter.Limit = int(diff)
|
||||
}
|
||||
}
|
||||
|
||||
dbEvents, err := snapshot.RecentEvents(
|
||||
ctx, []string{delta.RoomID}, r,
|
||||
eventFilter, true, true,
|
||||
)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return r.To, nil
|
||||
}
|
||||
return r.From, fmt.Errorf("p.DB.RecentEvents: %w", err)
|
||||
}
|
||||
|
||||
recentStreamEvents := dbEvents[delta.RoomID].Events
|
||||
limited := dbEvents[delta.RoomID].Limited
|
||||
|
||||
|
@ -337,9 +383,9 @@ func (p *PDUStreamProvider) addRoomDeltaToResponse(
|
|||
logrus.WithError(err).Error("unable to apply history visibility filter")
|
||||
}
|
||||
|
||||
if r.Backwards && len(events) > originalLimit {
|
||||
if r.Backwards && len(events) > eventFilter.Limit {
|
||||
// We're going backwards and the events are ordered chronologically, so take the last `limit` events
|
||||
events = events[len(events)-originalLimit:]
|
||||
events = events[len(events)-eventFilter.Limit:]
|
||||
limited = true
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue