mirror of
https://github.com/matrix-org/dendrite.git
synced 2024-11-26 00:01:55 -06: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
|
runs-on: ubuntu-latest
|
||||||
if: ${{ false }} # disable for now
|
if: ${{ false }} # disable for now
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: "stable"
|
go-version: "stable"
|
||||||
cache: true
|
cache: true
|
||||||
|
@ -41,7 +41,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
node-version: 14
|
node-version: 14
|
||||||
|
|
||||||
- uses: actions/cache@v3
|
- uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: ~/.npm
|
path: ~/.npm
|
||||||
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
||||||
|
@ -66,11 +66,11 @@ jobs:
|
||||||
name: Linting
|
name: Linting
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Install libolm
|
- name: Install libolm
|
||||||
run: sudo apt-get install libolm-dev libolm3
|
run: sudo apt-get install libolm-dev libolm3
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: "stable"
|
go-version: "stable"
|
||||||
- name: golangci-lint
|
- name: golangci-lint
|
||||||
|
@ -102,14 +102,14 @@ jobs:
|
||||||
--health-timeout 5s
|
--health-timeout 5s
|
||||||
--health-retries 5
|
--health-retries 5
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Install libolm
|
- name: Install libolm
|
||||||
run: sudo apt-get install libolm-dev libolm3
|
run: sudo apt-get install libolm-dev libolm3
|
||||||
- name: Setup go
|
- name: Setup go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: "stable"
|
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
|
# manually set up caches, as they otherwise clash with different steps using setup-go with cache=true
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
|
@ -141,12 +141,12 @@ jobs:
|
||||||
goos: ["linux"]
|
goos: ["linux"]
|
||||||
goarch: ["amd64", "386"]
|
goarch: ["amd64", "386"]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Setup go
|
- name: Setup go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: "stable"
|
go-version: "stable"
|
||||||
- uses: actions/cache@v3
|
- uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/.cache/go-build
|
~/.cache/go-build
|
||||||
|
@ -174,12 +174,12 @@ jobs:
|
||||||
goos: ["windows"]
|
goos: ["windows"]
|
||||||
goarch: ["amd64"]
|
goarch: ["amd64"]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: "stable"
|
go-version: "stable"
|
||||||
- uses: actions/cache@v3
|
- uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/.cache/go-build
|
~/.cache/go-build
|
||||||
|
@ -235,11 +235,11 @@ jobs:
|
||||||
--health-timeout 5s
|
--health-timeout 5s
|
||||||
--health-retries 5
|
--health-retries 5
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Install libolm
|
- name: Install libolm
|
||||||
run: sudo apt-get install libolm-dev libolm3
|
run: sudo apt-get install libolm-dev libolm3
|
||||||
- name: Setup go
|
- name: Setup go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: "stable"
|
go-version: "stable"
|
||||||
- name: Set up gotestfmt
|
- name: Set up gotestfmt
|
||||||
|
@ -247,7 +247,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
# Optional: pass GITHUB_TOKEN to avoid rate limiting.
|
# Optional: pass GITHUB_TOKEN to avoid rate limiting.
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- uses: actions/cache@v3
|
- uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/.cache/go-build
|
~/.cache/go-build
|
||||||
|
@ -262,10 +262,11 @@ jobs:
|
||||||
POSTGRES_PASSWORD: postgres
|
POSTGRES_PASSWORD: postgres
|
||||||
POSTGRES_DB: dendrite
|
POSTGRES_DB: dendrite
|
||||||
- name: Upload coverage to Codecov
|
- name: Upload coverage to Codecov
|
||||||
uses: codecov/codecov-action@v3
|
uses: codecov/codecov-action@v4
|
||||||
with:
|
with:
|
||||||
flags: unittests
|
flags: unittests
|
||||||
fail_ci_if_error: true
|
fail_ci_if_error: true
|
||||||
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
|
||||||
# run database upgrade tests
|
# run database upgrade tests
|
||||||
upgrade_test:
|
upgrade_test:
|
||||||
|
@ -274,12 +275,20 @@ jobs:
|
||||||
needs: initial-tests-done
|
needs: initial-tests-done
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Setup go
|
- name: Setup go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: "stable"
|
go-version: "stable"
|
||||||
cache: true
|
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
|
- name: Docker version
|
||||||
run: docker version
|
run: docker version
|
||||||
- name: Build upgrade-tests
|
- name: Build upgrade-tests
|
||||||
|
@ -296,12 +305,20 @@ jobs:
|
||||||
needs: initial-tests-done
|
needs: initial-tests-done
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Setup go
|
- name: Setup go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: "stable"
|
go-version: "stable"
|
||||||
cache: true
|
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
|
- name: Docker version
|
||||||
run: docker version
|
run: docker version
|
||||||
- name: Build upgrade-tests
|
- name: Build upgrade-tests
|
||||||
|
@ -340,8 +357,8 @@ jobs:
|
||||||
SYTEST_BRANCH: ${{ github.head_ref }}
|
SYTEST_BRANCH: ${{ github.head_ref }}
|
||||||
CGO_ENABLED: ${{ matrix.cgo && 1 }}
|
CGO_ENABLED: ${{ matrix.cgo && 1 }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/cache@v3
|
- uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/.cache/go-build
|
~/.cache/go-build
|
||||||
|
@ -364,7 +381,7 @@ jobs:
|
||||||
run: /src/are-we-synapse-yet.py /logs/results.tap -v
|
run: /src/are-we-synapse-yet.py /logs/results.tap -v
|
||||||
continue-on-error: true # not fatal
|
continue-on-error: true # not fatal
|
||||||
- name: Upload Sytest logs
|
- name: Upload Sytest logs
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v4
|
||||||
if: ${{ always() }}
|
if: ${{ always() }}
|
||||||
with:
|
with:
|
||||||
name: Sytest Logs - ${{ job.status }} - (Dendrite, ${{ join(matrix.*, ', ') }})
|
name: Sytest Logs - ${{ job.status }} - (Dendrite, ${{ join(matrix.*, ', ') }})
|
||||||
|
@ -404,8 +421,8 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update && sudo apt-get install -y libolm3 libolm-dev
|
sudo apt-get update && sudo apt-get install -y libolm3 libolm-dev
|
||||||
go install github.com/gotesttools/gotestfmt/v2/cmd/gotestfmt@latest
|
go install github.com/gotesttools/gotestfmt/v2/cmd/gotestfmt@latest
|
||||||
- name: Run actions/checkout@v3 for dendrite
|
- name: Run actions/checkout@v4 for dendrite
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
path: dendrite
|
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
|
security-events: write # To upload Trivy sarif files
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Get release tag & build flags
|
- name: Get release tag & build flags
|
||||||
if: github.event_name == 'release' # Only for GitHub releases
|
if: github.event_name == 'release' # Only for GitHub releases
|
||||||
run: |
|
run: |
|
||||||
echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v1
|
uses: docker/setup-qemu-action@v3
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v3
|
||||||
- name: Login to Docker Hub
|
- name: Login to Docker Hub
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
username: ${{ env.DOCKER_HUB_USER }}
|
username: ${{ env.DOCKER_HUB_USER }}
|
||||||
password: ${{ secrets.DOCKER_TOKEN }}
|
password: ${{ secrets.DOCKER_TOKEN }}
|
||||||
- name: Login to GitHub Containers
|
- name: Login to GitHub Containers
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
|
@ -98,22 +98,22 @@ jobs:
|
||||||
packages: write
|
packages: write
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Get release tag & build flags
|
- name: Get release tag & build flags
|
||||||
if: github.event_name == 'release' # Only for GitHub releases
|
if: github.event_name == 'release' # Only for GitHub releases
|
||||||
run: |
|
run: |
|
||||||
echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v1
|
uses: docker/setup-qemu-action@v3
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v3
|
||||||
- name: Login to Docker Hub
|
- name: Login to Docker Hub
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
username: ${{ env.DOCKER_HUB_USER }}
|
username: ${{ env.DOCKER_HUB_USER }}
|
||||||
password: ${{ secrets.DOCKER_TOKEN }}
|
password: ${{ secrets.DOCKER_TOKEN }}
|
||||||
- name: Login to GitHub Containers
|
- name: Login to GitHub Containers
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
|
@ -159,22 +159,22 @@ jobs:
|
||||||
packages: write
|
packages: write
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Get release tag & build flags
|
- name: Get release tag & build flags
|
||||||
if: github.event_name == 'release' # Only for GitHub releases
|
if: github.event_name == 'release' # Only for GitHub releases
|
||||||
run: |
|
run: |
|
||||||
echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v1
|
uses: docker/setup-qemu-action@v3
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v3
|
||||||
- name: Login to Docker Hub
|
- name: Login to Docker Hub
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
username: ${{ env.DOCKER_HUB_USER }}
|
username: ${{ env.DOCKER_HUB_USER }}
|
||||||
password: ${{ secrets.DOCKER_TOKEN }}
|
password: ${{ secrets.DOCKER_TOKEN }}
|
||||||
- name: Login to GitHub Containers
|
- name: Login to GitHub Containers
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.repository_owner }}
|
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
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Setup Pages
|
- name: Setup Pages
|
||||||
uses: actions/configure-pages@v2
|
uses: actions/configure-pages@v2
|
||||||
- name: Build with Jekyll
|
- 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
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
|
|
6
.github/workflows/k8s.yml
vendored
6
.github/workflows/k8s.yml
vendored
|
@ -17,7 +17,7 @@ jobs:
|
||||||
outputs:
|
outputs:
|
||||||
changed: ${{ steps.list-changed.outputs.changed }}
|
changed: ${{ steps.list-changed.outputs.changed }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- uses: azure/setup-helm@v3
|
- uses: azure/setup-helm@v3
|
||||||
|
@ -48,7 +48,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
ref: ${{ inputs.checkoutCommit }}
|
ref: ${{ inputs.checkoutCommit }}
|
||||||
|
@ -66,7 +66,7 @@ jobs:
|
||||||
- name: Create k3d cluster
|
- name: Create k3d cluster
|
||||||
uses: nolar/setup-k3d-k3s@v1
|
uses: nolar/setup-k3d-k3s@v1
|
||||||
with:
|
with:
|
||||||
version: v1.21
|
version: v1.28
|
||||||
- name: Remove node taints
|
- name: Remove node taints
|
||||||
run: |
|
run: |
|
||||||
kubectl taint --all=true nodes node.cloudprovider.kubernetes.io/uninitialized- || true
|
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
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
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
|
# run Sytest in different variations
|
||||||
sytest:
|
sytest:
|
||||||
|
needs: check_date
|
||||||
|
if: ${{ needs.check_date.outputs.should_run != 'false' }}
|
||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
name: "Sytest (${{ matrix.label }})"
|
name: "Sytest (${{ matrix.label }})"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
@ -38,8 +56,8 @@ jobs:
|
||||||
RACE_DETECTION: 1
|
RACE_DETECTION: 1
|
||||||
COVER: 1
|
COVER: 1
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/cache@v3
|
- uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/.cache/go-build
|
~/.cache/go-build
|
||||||
|
@ -62,7 +80,7 @@ jobs:
|
||||||
run: /src/are-we-synapse-yet.py /logs/results.tap -v
|
run: /src/are-we-synapse-yet.py /logs/results.tap -v
|
||||||
continue-on-error: true # not fatal
|
continue-on-error: true # not fatal
|
||||||
- name: Upload Sytest logs
|
- name: Upload Sytest logs
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v4
|
||||||
if: ${{ always() }}
|
if: ${{ always() }}
|
||||||
with:
|
with:
|
||||||
name: Sytest Logs - ${{ job.status }} - (Dendrite ${{ join(matrix.*, ' ') }})
|
name: Sytest Logs - ${{ job.status }} - (Dendrite ${{ join(matrix.*, ' ') }})
|
||||||
|
@ -75,31 +93,34 @@ jobs:
|
||||||
timeout-minutes: 5
|
timeout-minutes: 5
|
||||||
name: "Sytest Coverage"
|
name: "Sytest Coverage"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: sytest # only run once Sytest is done
|
needs: [ sytest, check_date ] # only run once Sytest is done and there was a commit
|
||||||
if: ${{ always() }}
|
if: ${{ always() && needs.check_date.outputs.should_run != 'false' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: 'stable'
|
go-version: 'stable'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Download all artifacts
|
- name: Download all artifacts
|
||||||
uses: actions/download-artifact@v3
|
uses: actions/download-artifact@v4
|
||||||
- name: Collect coverage
|
- name: Collect coverage
|
||||||
run: |
|
run: |
|
||||||
go tool covdata textfmt -i="$(find Sytest* -name 'covmeta*' -type f -exec dirname {} \; | uniq | paste -s -d ',' -)" -o sytest.cov
|
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
|
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 ',' -)"
|
go tool covdata func -i="$(find Sytest* -name 'covmeta*' -type f -exec dirname {} \; | uniq | paste -s -d ',' -)"
|
||||||
- name: Upload coverage to Codecov
|
- name: Upload coverage to Codecov
|
||||||
uses: codecov/codecov-action@v3
|
uses: codecov/codecov-action@v4
|
||||||
with:
|
with:
|
||||||
files: ./final.cov
|
files: ./final.cov
|
||||||
flags: sytest
|
flags: sytest
|
||||||
fail_ci_if_error: true
|
fail_ci_if_error: true
|
||||||
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
|
||||||
# run Complement
|
# run Complement
|
||||||
complement:
|
complement:
|
||||||
|
needs: check_date
|
||||||
|
if: ${{ needs.check_date.outputs.should_run != 'false' }}
|
||||||
name: "Complement (${{ matrix.label }})"
|
name: "Complement (${{ matrix.label }})"
|
||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
@ -129,8 +150,8 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update && sudo apt-get install -y libolm3 libolm-dev
|
sudo apt-get update && sudo apt-get install -y libolm3 libolm-dev
|
||||||
go install github.com/gotesttools/gotestfmt/v2/cmd/gotestfmt@latest
|
go install github.com/gotesttools/gotestfmt/v2/cmd/gotestfmt@latest
|
||||||
- name: Run actions/checkout@v3 for dendrite
|
- name: Run actions/checkout@v4 for dendrite
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
path: dendrite
|
path: dendrite
|
||||||
|
|
||||||
|
@ -174,7 +195,7 @@ jobs:
|
||||||
# Run Complement
|
# Run Complement
|
||||||
- run: |
|
- run: |
|
||||||
set -o pipefail &&
|
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
|
shell: bash
|
||||||
name: Run Complement Tests
|
name: Run Complement Tests
|
||||||
env:
|
env:
|
||||||
|
@ -185,7 +206,7 @@ jobs:
|
||||||
working-directory: complement
|
working-directory: complement
|
||||||
|
|
||||||
- name: Upload Complement logs
|
- name: Upload Complement logs
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v4
|
||||||
if: ${{ always() }}
|
if: ${{ always() }}
|
||||||
with:
|
with:
|
||||||
name: Complement Logs - (Dendrite ${{ join(matrix.*, ' ') }})
|
name: Complement Logs - (Dendrite ${{ join(matrix.*, ' ') }})
|
||||||
|
@ -196,30 +217,32 @@ jobs:
|
||||||
timeout-minutes: 5
|
timeout-minutes: 5
|
||||||
name: "Complement Coverage"
|
name: "Complement Coverage"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: complement # only run once Complement is done
|
needs: [ complement, check_date ] # only run once Complements is done and there was a commit
|
||||||
if: ${{ always() }}
|
if: ${{ always() && needs.check_date.outputs.should_run != 'false' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: 'stable'
|
go-version: 'stable'
|
||||||
cache: true
|
cache: true
|
||||||
- name: Download all artifacts
|
- name: Download all artifacts
|
||||||
uses: actions/download-artifact@v3
|
uses: actions/download-artifact@v4
|
||||||
- name: Collect coverage
|
- name: Collect coverage
|
||||||
run: |
|
run: |
|
||||||
go tool covdata textfmt -i="$(find Complement* -name 'covmeta*' -type f -exec dirname {} \; | uniq | paste -s -d ',' -)" -o complement.cov
|
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
|
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 ',' -)"
|
go tool covdata func -i="$(find Complement* -name 'covmeta*' -type f -exec dirname {} \; | uniq | paste -s -d ',' -)"
|
||||||
- name: Upload coverage to Codecov
|
- name: Upload coverage to Codecov
|
||||||
uses: codecov/codecov-action@v3
|
uses: codecov/codecov-action@v4
|
||||||
with:
|
with:
|
||||||
files: ./final.cov
|
files: ./final.cov
|
||||||
flags: complement
|
flags: complement
|
||||||
fail_ci_if_error: true
|
fail_ci_if_error: true
|
||||||
|
token: ${{ secrets.CODECOV_TOKEN }} # required
|
||||||
|
|
||||||
element-web:
|
element-web:
|
||||||
|
if: ${{ false }} # disable for now, as Cypress has been replaced by Playwright
|
||||||
timeout-minutes: 120
|
timeout-minutes: 120
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
@ -228,7 +251,7 @@ jobs:
|
||||||
# Our test suite includes some screenshot tests with unusual diacritics, which are
|
# Our test suite includes some screenshot tests with unusual diacritics, which are
|
||||||
# supposed to be covered by STIXGeneral.
|
# supposed to be covered by STIXGeneral.
|
||||||
tools: fonts-stix
|
tools: fonts-stix
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
repository: matrix-org/matrix-react-sdk
|
repository: matrix-org/matrix-react-sdk
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v3
|
||||||
|
@ -259,6 +282,7 @@ jobs:
|
||||||
TMPDIR: ${{ runner.temp }}
|
TMPDIR: ${{ runner.temp }}
|
||||||
|
|
||||||
element-web-pinecone:
|
element-web-pinecone:
|
||||||
|
if: ${{ false }} # disable for now, as Cypress has been replaced by Playwright
|
||||||
timeout-minutes: 120
|
timeout-minutes: 120
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
@ -267,7 +291,7 @@ jobs:
|
||||||
# Our test suite includes some screenshot tests with unusual diacritics, which are
|
# Our test suite includes some screenshot tests with unusual diacritics, which are
|
||||||
# supposed to be covered by STIXGeneral.
|
# supposed to be covered by STIXGeneral.
|
||||||
tools: fonts-stix
|
tools: fonts-stix
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
repository: matrix-org/matrix-react-sdk
|
repository: matrix-org/matrix-react-sdk
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v3
|
||||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -78,3 +78,6 @@ build
|
||||||
|
|
||||||
# golang workspaces
|
# golang workspaces
|
||||||
go.work*
|
go.work*
|
||||||
|
|
||||||
|
# helm chart
|
||||||
|
helm/dendrite/charts/
|
||||||
|
|
|
@ -6,7 +6,7 @@ run:
|
||||||
concurrency: 4
|
concurrency: 4
|
||||||
|
|
||||||
# timeout for analysis, e.g. 30s, 5m, default is 1m
|
# 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
|
# exit code when at least one issue was found, default is 1
|
||||||
issues-exit-code: 1
|
issues-exit-code: 1
|
||||||
|
@ -18,24 +18,6 @@ run:
|
||||||
#build-tags:
|
#build-tags:
|
||||||
# - mytag
|
# - 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":
|
# 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
|
# 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
|
# automatic updating of go.mod described above. Instead, it fails when any changes
|
||||||
|
@ -50,7 +32,8 @@ run:
|
||||||
# output configuration options
|
# output configuration options
|
||||||
output:
|
output:
|
||||||
# colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number"
|
# 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 lines of code with issue, default is true
|
||||||
print-issued-lines: true
|
print-issued-lines: true
|
||||||
|
@ -79,9 +62,8 @@ linters-settings:
|
||||||
# see https://github.com/kisielk/errcheck#excluding-functions for details
|
# see https://github.com/kisielk/errcheck#excluding-functions for details
|
||||||
#exclude: /path/to/file.txt
|
#exclude: /path/to/file.txt
|
||||||
govet:
|
govet:
|
||||||
# report about shadowed variables
|
enable:
|
||||||
check-shadowing: true
|
- shadow
|
||||||
|
|
||||||
# settings per analyzer
|
# settings per analyzer
|
||||||
settings:
|
settings:
|
||||||
printf: # analyzer name, run `go tool vet help` to see all analyzers
|
printf: # analyzer name, run `go tool vet help` to see all analyzers
|
||||||
|
@ -217,6 +199,24 @@ linters:
|
||||||
|
|
||||||
|
|
||||||
issues:
|
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.
|
# List of regexps of issue texts to exclude, empty list by default.
|
||||||
# But independently from this option we use default exclude patterns,
|
# But independently from this option we use default exclude patterns,
|
||||||
# it can be disabled by `exclude-use-default: false`. To list all
|
# it can be disabled by `exclude-use-default: false`. To list all
|
||||||
|
|
38
CHANGES.md
38
CHANGES.md
|
@ -1,5 +1,43 @@
|
||||||
# Changelog
|
# 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)
|
## Dendrite 0.13.5 (2023-12-12)
|
||||||
|
|
||||||
Upgrading to this version is **highly** recommended, as it fixes several long-standing bugs in
|
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 (
|
const (
|
||||||
ASProtocolPath = "/_matrix/app/unstable/thirdparty/protocol/"
|
ASProtocolLegacyPath = "/_matrix/app/unstable/thirdparty/protocol/"
|
||||||
ASUserPath = "/_matrix/app/unstable/thirdparty/user"
|
ASUserLegacyPath = "/_matrix/app/unstable/thirdparty/user"
|
||||||
ASLocationPath = "/_matrix/app/unstable/thirdparty/location"
|
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 {
|
type ProtocolRequest struct {
|
||||||
|
|
|
@ -206,13 +206,21 @@ func (s *OutputRoomEventConsumer) sendEvents(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the transaction to the appservice.
|
// Send the transaction to the appservice.
|
||||||
// https://matrix.org/docs/spec/application_service/r0.1.2#put-matrix-app-v1-transactions-txnid
|
// https://spec.matrix.org/v1.9/application-service-api/#pushing-events
|
||||||
address := fmt.Sprintf("%s/transactions/%s?access_token=%s", state.RequestUrl(), txnID, url.QueryEscape(state.HSToken))
|
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))
|
req, err := http.NewRequestWithContext(ctx, "PUT", address, bytes.NewBuffer(transaction))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", state.HSToken))
|
||||||
resp, err := state.HTTPClient.Do(req)
|
resp, err := state.HTTPClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return state.backoffAndPause(err)
|
return state.backoffAndPause(err)
|
||||||
|
|
|
@ -19,10 +19,10 @@ package query
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
@ -32,9 +32,6 @@ import (
|
||||||
"github.com/matrix-org/dendrite/setup/config"
|
"github.com/matrix-org/dendrite/setup/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
const roomAliasExistsPath = "/rooms/"
|
|
||||||
const userIDExistsPath = "/users/"
|
|
||||||
|
|
||||||
// AppServiceQueryAPI is an implementation of api.AppServiceQueryAPI
|
// AppServiceQueryAPI is an implementation of api.AppServiceQueryAPI
|
||||||
type AppServiceQueryAPI struct {
|
type AppServiceQueryAPI struct {
|
||||||
Cfg *config.AppServiceAPI
|
Cfg *config.AppServiceAPI
|
||||||
|
@ -55,14 +52,23 @@ func (a *AppServiceQueryAPI) RoomAliasExists(
|
||||||
// Determine which application service should handle this request
|
// Determine which application service should handle this request
|
||||||
for _, appservice := range a.Cfg.Derived.ApplicationServices {
|
for _, appservice := range a.Cfg.Derived.ApplicationServices {
|
||||||
if appservice.URL != "" && appservice.IsInterestedInRoomAlias(request.Alias) {
|
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
|
// 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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
URL.Path += request.Alias
|
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
|
// Send a request to each application service. If one responds that it has
|
||||||
// created the room, immediately return.
|
// created the room, immediately return.
|
||||||
|
@ -70,6 +76,7 @@ func (a *AppServiceQueryAPI) RoomAliasExists(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", appservice.HSToken))
|
||||||
req = req.WithContext(ctx)
|
req = req.WithContext(ctx)
|
||||||
|
|
||||||
resp, err := appservice.HTTPClient.Do(req)
|
resp, err := appservice.HTTPClient.Do(req)
|
||||||
|
@ -123,12 +130,21 @@ func (a *AppServiceQueryAPI) UserIDExists(
|
||||||
for _, appservice := range a.Cfg.Derived.ApplicationServices {
|
for _, appservice := range a.Cfg.Derived.ApplicationServices {
|
||||||
if appservice.URL != "" && appservice.IsInterestedInUserID(request.UserID) {
|
if appservice.URL != "" && appservice.IsInterestedInUserID(request.UserID) {
|
||||||
// The full path to the rooms API, includes hs token
|
// 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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
URL.Path += request.UserID
|
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
|
// Send a request to each application service. If one responds that it has
|
||||||
// created the user, immediately return.
|
// created the user, immediately return.
|
||||||
|
@ -136,6 +152,7 @@ func (a *AppServiceQueryAPI) UserIDExists(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", appservice.HSToken))
|
||||||
resp, err := appservice.HTTPClient.Do(req.WithContext(ctx))
|
resp, err := appservice.HTTPClient.Do(req.WithContext(ctx))
|
||||||
if resp != nil {
|
if resp != nil {
|
||||||
defer func() {
|
defer func() {
|
||||||
|
@ -176,25 +193,22 @@ type thirdpartyResponses interface {
|
||||||
api.ASProtocolResponse | []api.ASUserResponse | []api.ASLocationResponse
|
api.ASProtocolResponse | []api.ASUserResponse | []api.ASLocationResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
func requestDo[T thirdpartyResponses](client *http.Client, url string, response *T) (err error) {
|
func requestDo[T thirdpartyResponses](as *config.ApplicationService, url string, response *T) error {
|
||||||
origURL := url
|
req, err := http.NewRequest(http.MethodGet, url, nil)
|
||||||
// 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 {
|
if err != nil {
|
||||||
continue
|
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
|
defer resp.Body.Close() // nolint: errcheck
|
||||||
body, err = io.ReadAll(resp.Body)
|
body, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
return err
|
||||||
}
|
}
|
||||||
return json.Unmarshal(body, &response)
|
return json.Unmarshal(body, &response)
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AppServiceQueryAPI) Locations(
|
func (a *AppServiceQueryAPI) Locations(
|
||||||
|
@ -207,16 +221,22 @@ func (a *AppServiceQueryAPI) Locations(
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
path := api.ASLocationPath
|
||||||
|
if a.Cfg.LegacyPaths {
|
||||||
|
path = api.ASLocationLegacyPath
|
||||||
|
}
|
||||||
for _, as := range a.Cfg.Derived.ApplicationServices {
|
for _, as := range a.Cfg.Derived.ApplicationServices {
|
||||||
var asLocations []api.ASLocationResponse
|
var asLocations []api.ASLocationResponse
|
||||||
|
if a.Cfg.LegacyAuth {
|
||||||
params.Set("access_token", as.HSToken)
|
params.Set("access_token", as.HSToken)
|
||||||
|
}
|
||||||
|
|
||||||
url := as.RequestUrl() + api.ASLocationPath
|
url := as.RequestUrl() + path
|
||||||
if req.Protocol != "" {
|
if req.Protocol != "" {
|
||||||
url += "/" + 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")
|
log.WithError(err).WithField("application_service", as.ID).Error("unable to get 'locations' from application service")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -242,16 +262,22 @@ func (a *AppServiceQueryAPI) User(
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
path := api.ASUserPath
|
||||||
|
if a.Cfg.LegacyPaths {
|
||||||
|
path = api.ASUserLegacyPath
|
||||||
|
}
|
||||||
for _, as := range a.Cfg.Derived.ApplicationServices {
|
for _, as := range a.Cfg.Derived.ApplicationServices {
|
||||||
var asUsers []api.ASUserResponse
|
var asUsers []api.ASUserResponse
|
||||||
|
if a.Cfg.LegacyAuth {
|
||||||
params.Set("access_token", as.HSToken)
|
params.Set("access_token", as.HSToken)
|
||||||
|
}
|
||||||
|
|
||||||
url := as.RequestUrl() + api.ASUserPath
|
url := as.RequestUrl() + path
|
||||||
if req.Protocol != "" {
|
if req.Protocol != "" {
|
||||||
url += "/" + 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")
|
log.WithError(err).WithField("application_service", as.ID).Error("unable to get 'user' from application service")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -272,6 +298,10 @@ func (a *AppServiceQueryAPI) Protocols(
|
||||||
req *api.ProtocolRequest,
|
req *api.ProtocolRequest,
|
||||||
resp *api.ProtocolResponse,
|
resp *api.ProtocolResponse,
|
||||||
) error {
|
) error {
|
||||||
|
protocolPath := api.ASProtocolPath
|
||||||
|
if a.Cfg.LegacyPaths {
|
||||||
|
protocolPath = api.ASProtocolLegacyPath
|
||||||
|
}
|
||||||
|
|
||||||
// get a single protocol response
|
// get a single protocol response
|
||||||
if req.Protocol != "" {
|
if req.Protocol != "" {
|
||||||
|
@ -289,7 +319,7 @@ func (a *AppServiceQueryAPI) Protocols(
|
||||||
response := api.ASProtocolResponse{}
|
response := api.ASProtocolResponse{}
|
||||||
for _, as := range a.Cfg.Derived.ApplicationServices {
|
for _, as := range a.Cfg.Derived.ApplicationServices {
|
||||||
var proto api.ASProtocolResponse
|
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")
|
log.WithError(err).WithField("application_service", as.ID).Error("unable to get 'protocol' from application service")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -319,7 +349,7 @@ func (a *AppServiceQueryAPI) Protocols(
|
||||||
for _, as := range a.Cfg.Derived.ApplicationServices {
|
for _, as := range a.Cfg.Derived.ApplicationServices {
|
||||||
for _, p := range as.Protocols {
|
for _, p := range as.Protocols {
|
||||||
var proto api.ASProtocolResponse
|
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")
|
log.WithError(err).WithField("application_service", as.ID).Error("unable to get 'protocol' from application service")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,12 @@ package clientapi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"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)
|
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
||||||
|
|
||||||
// Needed to create accounts
|
// 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)
|
rsAPI.SetFederationAPI(nil, nil)
|
||||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
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.
|
// 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)
|
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
||||||
|
|
||||||
// Needed to create accounts
|
// 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)
|
rsAPI.SetFederationAPI(nil, nil)
|
||||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
||||||
//rsAPI.SetUserAPI(userAPI)
|
//rsAPI.SetUserAPI(userAPI)
|
||||||
|
@ -1103,7 +1105,8 @@ func Test3PID(t *testing.T) {
|
||||||
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
|
||||||
|
|
||||||
// Needed to create accounts
|
// 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)
|
rsAPI.SetFederationAPI(nil, nil)
|
||||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil, caching.DisableMetrics, testIsBlacklistedOrBackingOff)
|
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.
|
// 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.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 {
|
if req.Body == nil {
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
Code: http.StatusBadRequest,
|
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))
|
vars, err := httputil.URLDecodeMapValues(mux.Vars(req))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.ErrorResponse(err)
|
return util.ErrorResponse(err)
|
||||||
|
@ -495,3 +495,93 @@ func AdminDownloadState(req *http.Request, device *api.Device, rsAPI roomserverA
|
||||||
JSON: struct{}{},
|
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 {
|
type queryKeysRequest struct {
|
||||||
Timeout int `json:"timeout"`
|
Timeout int `json:"timeout"`
|
||||||
Token string `json:"token"`
|
|
||||||
DeviceKeys map[string][]string `json:"device_keys"`
|
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,
|
UserID: device.UserID,
|
||||||
UserToDevices: r.DeviceKeys,
|
UserToDevices: r.DeviceKeys,
|
||||||
Timeout: r.GetTimeout(),
|
Timeout: r.GetTimeout(),
|
||||||
// TODO: Token?
|
|
||||||
}, &queryRes)
|
}, &queryRes)
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
Code: 200,
|
Code: 200,
|
||||||
|
|
|
@ -324,19 +324,18 @@ func SendInvite(
|
||||||
}
|
}
|
||||||
|
|
||||||
// We already received the return value, so no need to check for an error here.
|
// 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
|
return response
|
||||||
}
|
}
|
||||||
|
|
||||||
// sendInvite sends an invitation to a user. Returns a JSONResponse and an error
|
// sendInvite sends an invitation to a user. Returns a JSONResponse and an error
|
||||||
func sendInvite(
|
func sendInvite(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
profileAPI userapi.ClientUserAPI,
|
|
||||||
device *userapi.Device,
|
device *userapi.Device,
|
||||||
roomID, userID, reason string,
|
roomID, userID, reason string,
|
||||||
cfg *config.ClientAPI,
|
cfg *config.ClientAPI,
|
||||||
rsAPI roomserverAPI.ClientRoomserverAPI,
|
rsAPI roomserverAPI.ClientRoomserverAPI,
|
||||||
asAPI appserviceAPI.AppServiceInternalAPI, evTime time.Time,
|
evTime time.Time,
|
||||||
) (util.JSONResponse, error) {
|
) (util.JSONResponse, error) {
|
||||||
validRoomID, err := spec.NewRoomID(roomID)
|
validRoomID, err := spec.NewRoomID(roomID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -359,13 +358,7 @@ func sendInvite(
|
||||||
JSON: spec.InvalidParam("UserID is invalid"),
|
JSON: spec.InvalidParam("UserID is invalid"),
|
||||||
}, err
|
}, 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())
|
identity, err := cfg.Matrix.SigningIdentityFor(device.UserDomain())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
|
@ -378,8 +371,6 @@ func sendInvite(
|
||||||
RoomID: *validRoomID,
|
RoomID: *validRoomID,
|
||||||
Inviter: *inviter,
|
Inviter: *inviter,
|
||||||
Invitee: *invitee,
|
Invitee: *invitee,
|
||||||
DisplayName: profile.DisplayName,
|
|
||||||
AvatarURL: profile.AvatarURL,
|
|
||||||
Reason: reason,
|
Reason: reason,
|
||||||
IsDirect: false,
|
IsDirect: false,
|
||||||
KeyID: identity.KeyID,
|
KeyID: identity.KeyID,
|
||||||
|
|
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))
|
rulesPtr := pushRuleSetKindPointer(ruleSet, pushrules.Kind(kind))
|
||||||
// Even if rulesPtr is not nil, there may not be any rules for this 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 errorResponse(ctx, spec.InvalidParam("invalid push rules kind"), "pushRuleSetKindPointer failed")
|
||||||
}
|
}
|
||||||
return util.JSONResponse{
|
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
|
walker = *cachedWalker
|
||||||
}
|
}
|
||||||
|
|
||||||
discoveredRooms, nextWalker, err := rsAPI.QueryNextRoomHierarchyPage(req.Context(), walker, limit)
|
discoveredRooms, _, nextWalker, err := rsAPI.QueryNextRoomHierarchyPage(req.Context(), walker, limit)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
switch err.(type) {
|
switch err.(type) {
|
||||||
|
|
|
@ -255,7 +255,7 @@ func Setup(
|
||||||
logrus.Info("Enabling server notices at /_synapse/admin/v1/send_server_notice")
|
logrus.Info("Enabling server notices at /_synapse/admin/v1/send_server_notice")
|
||||||
serverNotificationSender, err := getSenderDevice(context.Background(), rsAPI, userAPI, cfg)
|
serverNotificationSender, err := getSenderDevice(context.Background(), rsAPI, userAPI, cfg)
|
||||||
if err != nil {
|
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}",
|
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"])
|
return GetPresence(req, device, natsClient, cfg.Matrix.JetStream.Prefixed(jetstream.RequestPresence), vars["userId"])
|
||||||
}),
|
}),
|
||||||
).Methods(http.MethodGet, http.MethodOptions)
|
).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 {
|
for i, eventJSON := range eventsJSON {
|
||||||
pdu, evErr := roomVerImpl.NewEventFromTrustedJSON([]byte(eventJSON), false)
|
pdu, evErr := roomVerImpl.NewEventFromTrustedJSON([]byte(eventJSON), false)
|
||||||
if evErr != nil {
|
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}
|
ev := types.HeaderedEvent{PDU: pdu}
|
||||||
events[i] = &ev
|
events[i] = &ev
|
||||||
|
|
|
@ -215,7 +215,7 @@ func SendServerNotice(
|
||||||
}
|
}
|
||||||
if !membershipRes.IsInRoom {
|
if !membershipRes.IsInRoom {
|
||||||
// re-invite the user
|
// 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 {
|
if err != nil {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,13 +17,13 @@ package relay
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
federationAPI "github.com/matrix-org/dendrite/federationapi/api"
|
federationAPI "github.com/matrix-org/dendrite/federationapi/api"
|
||||||
relayServerAPI "github.com/matrix-org/dendrite/relayapi/api"
|
relayServerAPI "github.com/matrix-org/dendrite/relayapi/api"
|
||||||
"github.com/matrix-org/gomatrixserverlib/spec"
|
"github.com/matrix-org/gomatrixserverlib/spec"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"go.uber.org/atomic"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -54,7 +54,7 @@ func NewRelayServerRetriever(
|
||||||
federationAPI: federationAPI,
|
federationAPI: federationAPI,
|
||||||
relayAPI: relayAPI,
|
relayAPI: relayAPI,
|
||||||
relayServersQueried: make(map[spec.ServerName]bool),
|
relayServersQueried: make(map[spec.ServerName]bool),
|
||||||
running: *atomic.NewBool(false),
|
running: atomic.Bool{},
|
||||||
quit: quit,
|
quit: quit,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -154,6 +154,13 @@ app_service_api:
|
||||||
# to be sent to an insecure endpoint.
|
# to be sent to an insecure endpoint.
|
||||||
disable_tls_validation: false
|
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.
|
# Appservice configuration files to load into this homeserver.
|
||||||
config_files:
|
config_files:
|
||||||
# - /path/to/appservice_registration.yaml
|
# - /path/to/appservice_registration.yaml
|
||||||
|
|
|
@ -117,6 +117,7 @@ The list of files that need to be stored is:
|
||||||
- matrix-key.pem
|
- matrix-key.pem
|
||||||
- dendrite.yaml
|
- dendrite.yaml
|
||||||
- the postgres or sqlite DB
|
- the postgres or sqlite DB
|
||||||
|
- the jetstream directory
|
||||||
- the media store
|
- the media store
|
||||||
- the search index (although this can be regenerated)
|
- the search index (although this can be regenerated)
|
||||||
|
|
||||||
|
|
|
@ -231,9 +231,9 @@ GEM
|
||||||
jekyll-seo-tag (~> 2.1)
|
jekyll-seo-tag (~> 2.1)
|
||||||
minitest (5.17.0)
|
minitest (5.17.0)
|
||||||
multipart-post (2.1.1)
|
multipart-post (2.1.1)
|
||||||
nokogiri (1.14.3-arm64-darwin)
|
nokogiri (1.16.2-arm64-darwin)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.14.3-x86_64-linux)
|
nokogiri (1.16.2-x86_64-linux)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
octokit (4.22.0)
|
octokit (4.22.0)
|
||||||
faraday (>= 0.9)
|
faraday (>= 0.9)
|
||||||
|
@ -241,11 +241,12 @@ GEM
|
||||||
pathutil (0.16.2)
|
pathutil (0.16.2)
|
||||||
forwardable-extended (~> 2.6)
|
forwardable-extended (~> 2.6)
|
||||||
public_suffix (4.0.7)
|
public_suffix (4.0.7)
|
||||||
racc (1.6.2)
|
racc (1.7.3)
|
||||||
rb-fsevent (0.11.1)
|
rb-fsevent (0.11.1)
|
||||||
rb-inotify (0.10.1)
|
rb-inotify (0.10.1)
|
||||||
ffi (~> 1.0)
|
ffi (~> 1.0)
|
||||||
rexml (3.2.5)
|
rexml (3.3.2)
|
||||||
|
strscan
|
||||||
rouge (3.26.0)
|
rouge (3.26.0)
|
||||||
ruby2_keywords (0.0.5)
|
ruby2_keywords (0.0.5)
|
||||||
rubyzip (2.3.2)
|
rubyzip (2.3.2)
|
||||||
|
@ -260,6 +261,7 @@ GEM
|
||||||
faraday (> 0.8, < 2.0)
|
faraday (> 0.8, < 2.0)
|
||||||
simpleidn (0.2.1)
|
simpleidn (0.2.1)
|
||||||
unf (~> 0.1.4)
|
unf (~> 0.1.4)
|
||||||
|
strscan (3.1.0)
|
||||||
terminal-table (1.8.0)
|
terminal-table (1.8.0)
|
||||||
unicode-display_width (~> 1.1, >= 1.1.1)
|
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||||
thread_safe (0.3.6)
|
thread_safe (0.3.6)
|
||||||
|
|
|
@ -113,10 +113,7 @@ func NewInternalAPI(
|
||||||
_ = federationDB.RemoveAllServersFromBlacklist()
|
_ = federationDB.RemoveAllServersFromBlacklist()
|
||||||
}
|
}
|
||||||
|
|
||||||
stats := statistics.NewStatistics(
|
stats := statistics.NewStatistics(federationDB, cfg.FederationMaxRetries+1, cfg.P2PFederationRetriesUntilAssumedOffline+1, cfg.EnableRelays)
|
||||||
federationDB,
|
|
||||||
cfg.FederationMaxRetries+1,
|
|
||||||
cfg.P2PFederationRetriesUntilAssumedOffline+1)
|
|
||||||
|
|
||||||
js, nats := natsInstance.Prepare(processContext, &cfg.Matrix.JetStream)
|
js, nats := natsInstance.Prepare(processContext, &cfg.Matrix.JetStream)
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,7 @@ func TestFederationClientQueryKeys(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
fedClient := &testFedClient{}
|
fedClient := &testFedClient{}
|
||||||
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline)
|
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline, false)
|
||||||
queues := queue.NewOutgoingQueues(
|
queues := queue.NewOutgoingQueues(
|
||||||
testDB, process.NewProcessContext(),
|
testDB, process.NewProcessContext(),
|
||||||
false,
|
false,
|
||||||
|
@ -92,7 +92,7 @@ func TestFederationClientQueryKeysBlacklisted(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
fedClient := &testFedClient{}
|
fedClient := &testFedClient{}
|
||||||
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline)
|
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline, false)
|
||||||
queues := queue.NewOutgoingQueues(
|
queues := queue.NewOutgoingQueues(
|
||||||
testDB, process.NewProcessContext(),
|
testDB, process.NewProcessContext(),
|
||||||
false,
|
false,
|
||||||
|
@ -122,7 +122,7 @@ func TestFederationClientQueryKeysFailure(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
fedClient := &testFedClient{shouldFail: true}
|
fedClient := &testFedClient{shouldFail: true}
|
||||||
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline)
|
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline, false)
|
||||||
queues := queue.NewOutgoingQueues(
|
queues := queue.NewOutgoingQueues(
|
||||||
testDB, process.NewProcessContext(),
|
testDB, process.NewProcessContext(),
|
||||||
false,
|
false,
|
||||||
|
@ -152,7 +152,7 @@ func TestFederationClientClaimKeys(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
fedClient := &testFedClient{}
|
fedClient := &testFedClient{}
|
||||||
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline)
|
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline, false)
|
||||||
queues := queue.NewOutgoingQueues(
|
queues := queue.NewOutgoingQueues(
|
||||||
testDB, process.NewProcessContext(),
|
testDB, process.NewProcessContext(),
|
||||||
false,
|
false,
|
||||||
|
@ -183,7 +183,7 @@ func TestFederationClientClaimKeysBlacklisted(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
fedClient := &testFedClient{}
|
fedClient := &testFedClient{}
|
||||||
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline)
|
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline, false)
|
||||||
queues := queue.NewOutgoingQueues(
|
queues := queue.NewOutgoingQueues(
|
||||||
testDB, process.NewProcessContext(),
|
testDB, process.NewProcessContext(),
|
||||||
false,
|
false,
|
||||||
|
|
|
@ -66,7 +66,7 @@ func TestPerformWakeupServers(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
fedClient := &testFedClient{}
|
fedClient := &testFedClient{}
|
||||||
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline)
|
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline, true)
|
||||||
queues := queue.NewOutgoingQueues(
|
queues := queue.NewOutgoingQueues(
|
||||||
testDB, process.NewProcessContext(),
|
testDB, process.NewProcessContext(),
|
||||||
false,
|
false,
|
||||||
|
@ -112,7 +112,7 @@ func TestQueryRelayServers(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
fedClient := &testFedClient{}
|
fedClient := &testFedClient{}
|
||||||
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline)
|
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline, false)
|
||||||
queues := queue.NewOutgoingQueues(
|
queues := queue.NewOutgoingQueues(
|
||||||
testDB, process.NewProcessContext(),
|
testDB, process.NewProcessContext(),
|
||||||
false,
|
false,
|
||||||
|
@ -153,7 +153,7 @@ func TestRemoveRelayServers(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
fedClient := &testFedClient{}
|
fedClient := &testFedClient{}
|
||||||
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline)
|
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline, false)
|
||||||
queues := queue.NewOutgoingQueues(
|
queues := queue.NewOutgoingQueues(
|
||||||
testDB, process.NewProcessContext(),
|
testDB, process.NewProcessContext(),
|
||||||
false,
|
false,
|
||||||
|
@ -193,7 +193,7 @@ func TestPerformDirectoryLookup(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
fedClient := &testFedClient{}
|
fedClient := &testFedClient{}
|
||||||
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline)
|
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline, false)
|
||||||
queues := queue.NewOutgoingQueues(
|
queues := queue.NewOutgoingQueues(
|
||||||
testDB, process.NewProcessContext(),
|
testDB, process.NewProcessContext(),
|
||||||
false,
|
false,
|
||||||
|
@ -232,7 +232,7 @@ func TestPerformDirectoryLookupRelaying(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
fedClient := &testFedClient{}
|
fedClient := &testFedClient{}
|
||||||
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline)
|
stats := statistics.NewStatistics(testDB, FailuresUntilBlacklist, FailuresUntilAssumedOffline, true)
|
||||||
queues := queue.NewOutgoingQueues(
|
queues := queue.NewOutgoingQueues(
|
||||||
testDB, process.NewProcessContext(),
|
testDB, process.NewProcessContext(),
|
||||||
false,
|
false,
|
||||||
|
|
|
@ -19,6 +19,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/matrix-org/gomatrix"
|
"github.com/matrix-org/gomatrix"
|
||||||
|
@ -26,7 +27,6 @@ import (
|
||||||
"github.com/matrix-org/gomatrixserverlib/fclient"
|
"github.com/matrix-org/gomatrixserverlib/fclient"
|
||||||
"github.com/matrix-org/gomatrixserverlib/spec"
|
"github.com/matrix-org/gomatrixserverlib/spec"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"go.uber.org/atomic"
|
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/federationapi/statistics"
|
"github.com/matrix-org/dendrite/federationapi/statistics"
|
||||||
"github.com/matrix-org/dendrite/federationapi/storage"
|
"github.com/matrix-org/dendrite/federationapi/storage"
|
||||||
|
@ -294,6 +294,10 @@ func (oq *destinationQueue) checkNotificationsOnClose() {
|
||||||
|
|
||||||
// backgroundSend is the worker goroutine for sending events.
|
// backgroundSend is the worker goroutine for sending events.
|
||||||
func (oq *destinationQueue) backgroundSend() {
|
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
|
// Check if a worker is already running, and if it isn't, then
|
||||||
// mark it as started.
|
// mark it as started.
|
||||||
if !oq.running.CompareAndSwap(false, true) {
|
if !oq.running.CompareAndSwap(false, true) {
|
||||||
|
|
|
@ -18,6 +18,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sync/atomic"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -26,7 +27,6 @@ import (
|
||||||
"github.com/matrix-org/dendrite/test/testrig"
|
"github.com/matrix-org/dendrite/test/testrig"
|
||||||
"github.com/matrix-org/gomatrixserverlib/fclient"
|
"github.com/matrix-org/gomatrixserverlib/fclient"
|
||||||
"github.com/matrix-org/gomatrixserverlib/spec"
|
"github.com/matrix-org/gomatrixserverlib/spec"
|
||||||
"go.uber.org/atomic"
|
|
||||||
"gotest.tools/v3/poll"
|
"gotest.tools/v3/poll"
|
||||||
|
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
|
@ -113,11 +113,11 @@ func testSetup(failuresUntilBlacklist uint32, failuresUntilAssumedOffline uint32
|
||||||
fc := &stubFederationClient{
|
fc := &stubFederationClient{
|
||||||
shouldTxSucceed: shouldTxSucceed,
|
shouldTxSucceed: shouldTxSucceed,
|
||||||
shouldTxRelaySucceed: shouldTxRelaySucceed,
|
shouldTxRelaySucceed: shouldTxRelaySucceed,
|
||||||
txCount: *atomic.NewUint32(0),
|
txCount: atomic.Uint32{},
|
||||||
txRelayCount: *atomic.NewUint32(0),
|
txRelayCount: atomic.Uint32{},
|
||||||
}
|
}
|
||||||
|
|
||||||
stats := statistics.NewStatistics(db, failuresUntilBlacklist, failuresUntilAssumedOffline)
|
stats := statistics.NewStatistics(db, failuresUntilBlacklist, failuresUntilAssumedOffline, false)
|
||||||
signingInfo := []*fclient.SigningIdentity{
|
signingInfo := []*fclient.SigningIdentity{
|
||||||
{
|
{
|
||||||
KeyID: "ed21019:auto",
|
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)
|
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 {
|
if err != nil {
|
||||||
switch err.(type) {
|
switch err.(type) {
|
||||||
|
@ -177,6 +177,7 @@ func QueryRoomHierarchy(httpReq *http.Request, request *fclient.FederationReques
|
||||||
JSON: fclient.RoomHierarchyResponse{
|
JSON: fclient.RoomHierarchyResponse{
|
||||||
Room: discoveredRooms[0],
|
Room: discoveredRooms[0],
|
||||||
Children: discoveredRooms[1:],
|
Children: discoveredRooms[1:],
|
||||||
|
InaccessibleChildren: inaccessibleRooms,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -647,6 +647,8 @@ func MakeFedAPI(
|
||||||
// add the user to Sentry, if enabled
|
// add the user to Sentry, if enabled
|
||||||
hub := sentry.GetHubFromContext(req.Context())
|
hub := sentry.GetHubFromContext(req.Context())
|
||||||
if hub != nil {
|
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("origin", string(fedReq.Origin()))
|
||||||
hub.Scope().SetTag("uri", fedReq.RequestURI())
|
hub.Scope().SetTag("uri", fedReq.RequestURI())
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,10 +5,10 @@ import (
|
||||||
"math"
|
"math"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"go.uber.org/atomic"
|
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/federationapi/storage"
|
"github.com/matrix-org/dendrite/federationapi/storage"
|
||||||
"github.com/matrix-org/gomatrixserverlib/spec"
|
"github.com/matrix-org/gomatrixserverlib/spec"
|
||||||
|
@ -34,12 +34,15 @@ type Statistics struct {
|
||||||
// mark the destination as offline. At this point we should attempt
|
// 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.
|
// to send messages to the user's async relay servers if we know them.
|
||||||
FailuresUntilAssumedOffline uint32
|
FailuresUntilAssumedOffline uint32
|
||||||
|
|
||||||
|
enableRelays bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewStatistics(
|
func NewStatistics(
|
||||||
db storage.Database,
|
db storage.Database,
|
||||||
failuresUntilBlacklist uint32,
|
failuresUntilBlacklist uint32,
|
||||||
failuresUntilAssumedOffline uint32,
|
failuresUntilAssumedOffline uint32,
|
||||||
|
enableRelays bool,
|
||||||
) Statistics {
|
) Statistics {
|
||||||
return Statistics{
|
return Statistics{
|
||||||
DB: db,
|
DB: db,
|
||||||
|
@ -47,6 +50,7 @@ func NewStatistics(
|
||||||
FailuresUntilAssumedOffline: failuresUntilAssumedOffline,
|
FailuresUntilAssumedOffline: failuresUntilAssumedOffline,
|
||||||
backoffTimers: make(map[spec.ServerName]*time.Timer),
|
backoffTimers: make(map[spec.ServerName]*time.Timer),
|
||||||
servers: make(map[spec.ServerName]*ServerStatistics),
|
servers: make(map[spec.ServerName]*ServerStatistics),
|
||||||
|
enableRelays: enableRelays,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,6 +77,13 @@ func (s *Statistics) ForServer(serverName spec.ServerName) *ServerStatistics {
|
||||||
} else {
|
} else {
|
||||||
server.blacklisted.Store(blacklisted)
|
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)
|
assumedOffline, err := s.DB.IsServerAssumedOffline(context.Background(), serverName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.WithError(err).Errorf("Failed to get assumed offline entry %q", serverName)
|
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
|
// NOTE : Sending to the final destination vs. a relay server has
|
||||||
// slightly different semantics.
|
// slightly different semantics.
|
||||||
if method == SendDirect {
|
if method == SendDirect {
|
||||||
s.successCounter.Inc()
|
s.successCounter.Add(1)
|
||||||
if s.blacklisted.Load() && s.statistics.DB != nil {
|
if s.blacklisted.Load() && s.statistics.DB != nil {
|
||||||
if err := s.statistics.DB.RemoveServerFromBlacklist(s.serverName); err != nil {
|
if err := s.statistics.DB.RemoveServerFromBlacklist(s.serverName); err != nil {
|
||||||
logrus.WithError(err).Errorf("Failed to remove %q from blacklist", s.serverName)
|
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
|
// start a goroutine which will wait out the backoff and
|
||||||
// unset the backoffStarted flag when done.
|
// unset the backoffStarted flag when done.
|
||||||
if s.backoffStarted.CompareAndSwap(false, true) {
|
if s.backoffStarted.CompareAndSwap(false, true) {
|
||||||
backoffCount := s.backoffCount.Inc()
|
backoffCount := s.backoffCount.Add(1)
|
||||||
|
|
||||||
if backoffCount >= s.statistics.FailuresUntilAssumedOffline {
|
if backoffCount >= s.statistics.FailuresUntilAssumedOffline {
|
||||||
s.assumedOffline.CompareAndSwap(false, true)
|
s.assumedOffline.CompareAndSwap(false, true)
|
||||||
|
|
|
@ -16,7 +16,7 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestBackoff(t *testing.T) {
|
func TestBackoff(t *testing.T) {
|
||||||
stats := NewStatistics(nil, FailuresUntilBlacklist, FailuresUntilAssumedOffline)
|
stats := NewStatistics(nil, FailuresUntilBlacklist, FailuresUntilAssumedOffline, false)
|
||||||
server := ServerStatistics{
|
server := ServerStatistics{
|
||||||
statistics: &stats,
|
statistics: &stats,
|
||||||
serverName: "test.com",
|
serverName: "test.com",
|
||||||
|
@ -106,7 +106,7 @@ func TestBackoff(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRelayServersListing(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 := ServerStatistics{statistics: &stats}
|
||||||
server.AddRelayServers([]spec.ServerName{"relay1", "relay1", "relay2"})
|
server.AddRelayServers([]spec.ServerName{"relay1", "relay1", "relay2"})
|
||||||
relayServers := server.KnownRelayServers()
|
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)
|
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)
|
fromDB, err := d.inner.FetchKeys(ctx, requests)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return results, err
|
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/DATA-DOG/go-sqlmock v1.5.0
|
||||||
github.com/MFAshby/stdemuxerhook v1.0.0
|
github.com/MFAshby/stdemuxerhook v1.0.0
|
||||||
github.com/Masterminds/semver/v3 v3.1.1
|
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/codeclysm/extract v2.2.0+incompatible
|
||||||
github.com/dgraph-io/ristretto v0.1.1
|
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/docker/go-connections v0.4.0
|
||||||
github.com/getsentry/sentry-go v0.14.0
|
github.com/getsentry/sentry-go v0.14.0
|
||||||
github.com/gologme/log v1.3.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/google/uuid v1.3.0
|
||||||
github.com/gorilla/mux v1.8.0
|
github.com/gorilla/mux v1.8.0
|
||||||
github.com/gorilla/websocket v1.5.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/dugong v0.0.0-20210921133753-66e6b1c67e2e
|
||||||
github.com/matrix-org/go-sqlite3-js v0.0.0-20220419092513-28aa791a1c91
|
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/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/pinecone v0.11.1-0.20230810010612-ea4c33717fd7
|
||||||
github.com/matrix-org/util v0.0.0-20221111132719-399730281e66
|
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-server/v2 v2.10.7
|
||||||
github.com/nats-io/nats.go v1.31.0
|
github.com/nats-io/nats.go v1.31.0
|
||||||
github.com/neilalexander/utp v0.1.1-0.20210727203401-54ae7b1cd5f9
|
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/opentracing/opentracing-go v1.2.0
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||||
github.com/pkg/errors v0.9.1
|
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/sirupsen/logrus v1.9.3
|
||||||
github.com/stretchr/testify v1.8.2
|
github.com/stretchr/testify v1.9.0
|
||||||
github.com/tidwall/gjson v1.17.0
|
github.com/tidwall/gjson v1.17.1
|
||||||
github.com/tidwall/sjson v1.2.5
|
github.com/tidwall/sjson v1.2.5
|
||||||
github.com/uber/jaeger-client-go v2.30.0+incompatible
|
github.com/uber/jaeger-client-go v2.30.0+incompatible
|
||||||
github.com/uber/jaeger-lib v2.4.1+incompatible
|
github.com/uber/jaeger-lib v2.4.1+incompatible
|
||||||
github.com/yggdrasil-network/yggdrasil-go v0.4.6
|
github.com/yggdrasil-network/yggdrasil-go v0.4.6
|
||||||
go.uber.org/atomic v1.10.0
|
go.uber.org/atomic v1.11.0
|
||||||
golang.org/x/crypto v0.17.0
|
golang.org/x/crypto v0.23.0
|
||||||
golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819
|
golang.org/x/exp v0.0.0-20231108232855-2478ac86f678
|
||||||
golang.org/x/image v0.10.0
|
golang.org/x/image v0.18.0
|
||||||
golang.org/x/mobile v0.0.0-20221020085226-b36e6246172e
|
golang.org/x/mobile v0.0.0-20221020085226-b36e6246172e
|
||||||
golang.org/x/sync v0.3.0
|
golang.org/x/sync v0.7.0
|
||||||
golang.org/x/term v0.15.0
|
golang.org/x/term v0.20.0
|
||||||
gopkg.in/h2non/bimg.v1 v1.1.9
|
gopkg.in/h2non/bimg.v1 v1.1.9
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
gotest.tools/v3 v3.4.0
|
gotest.tools/v3 v3.4.0
|
||||||
maunium.net/go/mautrix v0.15.1
|
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
|
nhooyr.io/websocket v1.8.7
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -62,21 +62,23 @@ require (
|
||||||
github.com/RoaringBitmap/roaring v1.2.3 // indirect
|
github.com/RoaringBitmap/roaring v1.2.3 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/bits-and-blooms/bitset v1.5.0 // indirect
|
github.com/bits-and-blooms/bitset v1.5.0 // indirect
|
||||||
github.com/blevesearch/bleve_index_api v1.0.5 // indirect
|
github.com/blevesearch/bleve_index_api v1.1.6 // indirect
|
||||||
github.com/blevesearch/geo v0.1.17 // 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/go-porterstemmer v1.0.3 // indirect
|
||||||
github.com/blevesearch/gtreap v0.1.1 // indirect
|
github.com/blevesearch/gtreap v0.1.1 // indirect
|
||||||
github.com/blevesearch/mmap-go v1.0.4 // 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/segment v0.9.1 // indirect
|
||||||
github.com/blevesearch/snowballstem v0.9.0 // indirect
|
github.com/blevesearch/snowballstem v0.9.0 // indirect
|
||||||
github.com/blevesearch/upsidedown_store_api v1.0.2 // indirect
|
github.com/blevesearch/upsidedown_store_api v1.0.2 // indirect
|
||||||
github.com/blevesearch/vellum v1.0.9 // indirect
|
github.com/blevesearch/vellum v1.0.10 // indirect
|
||||||
github.com/blevesearch/zapx/v11 v11.3.7 // indirect
|
github.com/blevesearch/zapx/v11 v11.3.10 // indirect
|
||||||
github.com/blevesearch/zapx/v12 v12.3.7 // indirect
|
github.com/blevesearch/zapx/v12 v12.3.10 // indirect
|
||||||
github.com/blevesearch/zapx/v13 v13.3.7 // indirect
|
github.com/blevesearch/zapx/v13 v13.3.10 // indirect
|
||||||
github.com/blevesearch/zapx/v14 v14.3.7 // indirect
|
github.com/blevesearch/zapx/v14 v14.3.10 // indirect
|
||||||
github.com/blevesearch/zapx/v15 v15.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/cespare/xxhash/v2 v2.2.0 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/docker/distribution v2.8.2+incompatible // 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/golang/snappy v0.0.4 // indirect
|
||||||
github.com/google/pprof v0.0.0-20230808223545-4887780b67fb // indirect
|
github.com/google/pprof v0.0.0-20230808223545-4887780b67fb // indirect
|
||||||
github.com/h2non/filetype v1.1.3 // 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/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/juju/errors v1.0.0 // 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.7 // indirect
|
||||||
github.com/klauspost/compress v1.17.4 // indirect
|
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.17 // 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/minio/highwayhash v1.0.2 // indirect
|
||||||
github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae // 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/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/morikuni/aec v1.0.0 // indirect
|
github.com/morikuni/aec v1.0.0 // indirect
|
||||||
github.com/mschoch/smat v0.2.0 // indirect
|
github.com/mschoch/smat v0.2.0 // indirect
|
||||||
github.com/nats-io/jwt/v2 v2.5.3 // indirect
|
github.com/nats-io/jwt/v2 v2.5.5 // indirect
|
||||||
github.com/nats-io/nkeys v0.4.6 // indirect
|
github.com/nats-io/nkeys v0.4.7 // indirect
|
||||||
github.com/nats-io/nuid v1.0.1 // 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/onsi/ginkgo/v2 v2.11.0 // indirect
|
||||||
github.com/opencontainers/go-digest v1.0.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/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/prometheus/client_model v0.3.0 // indirect
|
github.com/prometheus/client_model v0.5.0 // indirect
|
||||||
github.com/prometheus/common v0.42.0 // indirect
|
github.com/prometheus/common v0.48.0 // indirect
|
||||||
github.com/prometheus/procfs v0.10.1 // indirect
|
github.com/prometheus/procfs v0.12.0 // indirect
|
||||||
github.com/quic-go/qtls-go1-20 v0.3.2 // indirect
|
github.com/quic-go/qtls-go1-20 v0.3.2 // indirect
|
||||||
github.com/quic-go/quic-go v0.37.7 // indirect
|
github.com/quic-go/quic-go v0.37.7 // indirect
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // 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/rs/zerolog v1.29.1 // indirect
|
||||||
github.com/tidwall/match v1.1.1 // indirect
|
github.com/tidwall/match v1.1.1 // indirect
|
||||||
github.com/tidwall/pretty v1.2.1 // indirect
|
github.com/tidwall/pretty v1.2.1 // indirect
|
||||||
go.etcd.io/bbolt v1.3.6 // indirect
|
go.etcd.io/bbolt v1.3.7 // indirect
|
||||||
golang.org/x/mod v0.12.0 // indirect
|
golang.org/x/mod v0.17.0 // indirect
|
||||||
golang.org/x/net v0.17.0 // indirect
|
golang.org/x/net v0.25.0 // indirect
|
||||||
golang.org/x/sys v0.15.0 // indirect
|
golang.org/x/sys v0.20.0 // indirect
|
||||||
golang.org/x/text v0.14.0 // indirect
|
golang.org/x/text v0.16.0 // indirect
|
||||||
golang.org/x/time v0.5.0 // indirect
|
golang.org/x/time v0.5.0 // indirect
|
||||||
golang.org/x/tools v0.12.0 // indirect
|
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
|
||||||
google.golang.org/protobuf v1.30.0 // indirect
|
google.golang.org/protobuf v1.33.0 // indirect
|
||||||
gopkg.in/macaroon.v2 v2.1.0 // indirect
|
gopkg.in/macaroon.v2 v2.1.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // 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
|
maunium.net/go/maulogger/v2 v2.4.1 // indirect
|
||||||
modernc.org/cc/v3 v3.40.0 // indirect
|
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect
|
||||||
modernc.org/ccgo/v3 v3.16.13 // indirect
|
modernc.org/libc v1.41.0 // indirect
|
||||||
modernc.org/libc v1.22.5 // indirect
|
modernc.org/mathutil v1.6.0 // indirect
|
||||||
modernc.org/mathutil v1.5.0 // indirect
|
modernc.org/memory v1.7.2 // indirect
|
||||||
modernc.org/memory v1.5.0 // indirect
|
modernc.org/strutil v1.2.0 // indirect
|
||||||
modernc.org/opt v0.1.3 // indirect
|
modernc.org/token v1.1.0 // indirect
|
||||||
modernc.org/strutil v1.1.3 // indirect
|
|
||||||
modernc.org/token v1.0.1 // indirect
|
|
||||||
)
|
)
|
||||||
|
|
||||||
go 1.20
|
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.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 h1:NpE8frKRLGHIcEzkR+gZhiioW1+WbYV6fKwD6ZIpQT8=
|
||||||
github.com/bits-and-blooms/bitset v1.5.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
|
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.4.0 h1:2xyg+Wv60CFHYccXc+moGxbL+8QKT/dZK09AewHgKsg=
|
||||||
github.com/blevesearch/bleve/v2 v2.3.8/go.mod h1:Lh9aZEHrLKxwPnW4z4lsBEGnflZQ1V/aWP/t+htsiDw=
|
github.com/blevesearch/bleve/v2 v2.4.0/go.mod h1:IhQHoFAbHgWKYavb9rQgQEJJVMuY99cKdQ0wPpst2aY=
|
||||||
github.com/blevesearch/bleve_index_api v1.0.5 h1:Lc986kpC4Z0/n1g3gg8ul7H+lxgOQPcXb9SxvQGu+tw=
|
github.com/blevesearch/bleve_index_api v1.1.6 h1:orkqDFCBuNU2oHW9hN2YEJmet+TE9orml3FCGbl1cKk=
|
||||||
github.com/blevesearch/bleve_index_api v1.0.5/go.mod h1:YXMDwaXFFXwncRS8UobWs7nvo0DmusriM1nztTlj1ms=
|
github.com/blevesearch/bleve_index_api v1.1.6/go.mod h1:PbcwjIcRmjhGbkS/lJCpfgVSMROV6TRubGGAODaK1W8=
|
||||||
github.com/blevesearch/geo v0.1.17 h1:AguzI6/5mHXapzB0gE9IKWo+wWPHZmXZoscHcjFgAFA=
|
github.com/blevesearch/geo v0.1.20 h1:paaSpu2Ewh/tn5DKn/FB5SzvH0EWupxHEIwbCk/QPqM=
|
||||||
github.com/blevesearch/geo v0.1.17/go.mod h1:uRMGWG0HJYfWfFJpK3zTdnnr1K+ksZTuWKhXeSokfnM=
|
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 h1:GtmsqID0aZdCSNiY8SkuPJ12pD4jI+DdXTAn4YRcHCo=
|
||||||
github.com/blevesearch/go-porterstemmer v1.0.3/go.mod h1:angGc5Ht+k2xhJdZi511LtmxuEf0OVpvUUNrwmM1P7M=
|
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 h1:2JWigFrzDMR+42WGIN/V2p0cUvn4UP3C4Q5nmaZGW8Y=
|
||||||
github.com/blevesearch/gtreap v0.1.1/go.mod h1:QaQyDRAT51sotthUWAH4Sj08awFSSWzgYICSZ3w0tYk=
|
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 h1:OVhDhT5B/M1HNPpYPBKIEJaD0F3Si+CrEKULGCDPWmc=
|
||||||
github.com/blevesearch/mmap-go v1.0.4/go.mod h1:EWmEAOmdAS9z/pi/+Toxu99DnsbhG1TIxUoRmJw/pSs=
|
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.2.9 h1:3nBaSBRFokjE4FtPW3eUDgcAu3KphBg1GP07zy/6Uyk=
|
||||||
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/go.mod h1:ckbeb7knyOOvAdZinn/ASbB7EA3HoagnJkmEV3J7+sg=
|
||||||
github.com/blevesearch/segment v0.9.1 h1:+dThDy+Lvgj5JMxhmOVlgFfkUtZV2kw49xax4+jTfSU=
|
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/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 h1:lMQ189YspGP6sXvZQ4WZ+MLawfV8wOmPoD/iWeNXm8s=
|
||||||
github.com/blevesearch/snowballstem v0.9.0/go.mod h1:PivSj3JMc8WuaFkTSRDW2SlrulNWPl4ABg1tC/hlgLs=
|
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 h1:U53Q6YoWEARVLd1OYNc9kvhBMGZzVrdmaozG2MfoB+A=
|
||||||
github.com/blevesearch/upsidedown_store_api v1.0.2/go.mod h1:M01mh3Gpfy56Ps/UXHjEO/knbqyQ1Oamg8If49gRwrQ=
|
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.10 h1:HGPJDT2bTva12hrHepVT3rOyIKFFF4t7Gf6yMxyMIPI=
|
||||||
github.com/blevesearch/vellum v1.0.9/go.mod h1:ul1oT0FhSMDIExNjIxHqJoGpVrBpKCdgDQNxfqgJt7k=
|
github.com/blevesearch/vellum v1.0.10/go.mod h1:ul1oT0FhSMDIExNjIxHqJoGpVrBpKCdgDQNxfqgJt7k=
|
||||||
github.com/blevesearch/zapx/v11 v11.3.7 h1:Y6yIAF/DVPiqZUA/jNgSLXmqewfzwHzuwfKyfdG+Xaw=
|
github.com/blevesearch/zapx/v11 v11.3.10 h1:hvjgj9tZ9DeIqBCxKhi70TtSZYMdcFn7gDb71Xo/fvk=
|
||||||
github.com/blevesearch/zapx/v11 v11.3.7/go.mod h1:Xk9Z69AoAWIOvWudNDMlxJDqSYGf90LS0EfnaAIvXCA=
|
github.com/blevesearch/zapx/v11 v11.3.10/go.mod h1:0+gW+FaE48fNxoVtMY5ugtNHHof/PxCqh7CnhYdnMzQ=
|
||||||
github.com/blevesearch/zapx/v12 v12.3.7 h1:DfQ6rsmZfEK4PzzJJRXjiM6AObG02+HWvprlXQ1Y7eI=
|
github.com/blevesearch/zapx/v12 v12.3.10 h1:yHfj3vXLSYmmsBleJFROXuO08mS3L1qDCdDK81jDl8s=
|
||||||
github.com/blevesearch/zapx/v12 v12.3.7/go.mod h1:SgEtYIBGvM0mgIBn2/tQE/5SdrPXaJUaT/kVqpAPxm0=
|
github.com/blevesearch/zapx/v12 v12.3.10/go.mod h1:0yeZg6JhaGxITlsS5co73aqPtM04+ycnI6D1v0mhbCs=
|
||||||
github.com/blevesearch/zapx/v13 v13.3.7 h1:igIQg5eKmjw168I7av0Vtwedf7kHnQro/M+ubM4d2l8=
|
github.com/blevesearch/zapx/v13 v13.3.10 h1:0KY9tuxg06rXxOZHg3DwPJBjniSlqEgVpxIqMGahDE8=
|
||||||
github.com/blevesearch/zapx/v13 v13.3.7/go.mod h1:yyrB4kJ0OT75UPZwT/zS+Ru0/jYKorCOOSY5dBzAy+s=
|
github.com/blevesearch/zapx/v13 v13.3.10/go.mod h1:w2wjSDQ/WBVeEIvP0fvMJZAzDwqwIEzVPnCPrz93yAk=
|
||||||
github.com/blevesearch/zapx/v14 v14.3.7 h1:gfe+fbWslDWP/evHLtp/GOvmNM3sw1BbqD7LhycBX20=
|
github.com/blevesearch/zapx/v14 v14.3.10 h1:SG6xlsL+W6YjhX5N3aEiL/2tcWh3DO75Bnz77pSwwKU=
|
||||||
github.com/blevesearch/zapx/v14 v14.3.7/go.mod h1:9J/RbOkqZ1KSjmkOes03AkETX7hrXT0sFMpWH4ewC4w=
|
github.com/blevesearch/zapx/v14 v14.3.10/go.mod h1:qqyuR0u230jN1yMmE4FIAuCxmahRQEOehF78m6oTgns=
|
||||||
github.com/blevesearch/zapx/v15 v15.3.10 h1:bQ9ZxJCj6rKp873EuVJu2JPxQ+EWQZI1cjJGeroovaQ=
|
github.com/blevesearch/zapx/v15 v15.3.13 h1:6EkfaZiPlAxqXz0neniq35my6S48QI94W/wyhnpDHHQ=
|
||||||
github.com/blevesearch/zapx/v15 v15.3.10/go.mod h1:m7Y6m8soYUvS7MjN9eKlz1xrLCcmqfFadmu7GhWIrLY=
|
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-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-20190303215204-33e6a9893b0c/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo=
|
||||||
github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8 h1:GKTyiRCL6zVf5wWaqKnf+7Qs6GbEPfd4iMOitWzXJx8=
|
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/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 h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
|
||||||
github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
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.9+incompatible h1:HPGzNmwfLZWdxHqK9/II92pyi1EpYKsAqcl4G0Of9v0=
|
||||||
github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
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 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
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/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 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
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.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.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
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.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.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.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
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/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 h1:oqpb3Cwpc7EOml5PVGMYbSGmwNui2R7i8IW83gs4W0c=
|
||||||
github.com/google/pprof v0.0.0-20230808223545-4887780b67fb/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA=
|
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 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg=
|
||||||
github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
|
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/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 h1:pO2K/gKgKaat5LdpAhxhluX2GPQMaI3W5FUz/I/UnWk=
|
||||||
github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo=
|
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=
|
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/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 h1:JmZKFJQrmTGa/WiW+vkJXKmfzdjabuEW4Tirj5lLdR0=
|
||||||
github.com/kardianos/minwinsvc v1.0.2/go.mod h1:LUZNYhNmxujx2tR7FbdxqYJ9XDDoCd3MQcl1o//FWl4=
|
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/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
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.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||||
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
|
github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg=
|
||||||
github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
|
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.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
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/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 h1:kHKxCOLcHH8r4Fzarl4+Y3K5hjothkVW5z7T1dUM11U=
|
||||||
github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530/go.mod h1:/gBX06Kw0exX1HrwmoBibFA98yBk/jxKpGVeyQbff+s=
|
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-20240328203753-c2391f7113a5 h1:GuxmpyjZQoqb6UFQgKq8Td3wIITlXln/sItqp1jbTTA=
|
||||||
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/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 h1:6t8kJr8i1/1I5nNttw6nn1ryQJgzVlBmSGgPiiaTdw4=
|
||||||
github.com/matrix-org/pinecone v0.11.1-0.20230810010612-ea4c33717fd7/go.mod h1:ReWMS/LoVnOiRAdq9sNUC2NZnd1mZkMNB52QhpTRWjg=
|
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=
|
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.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 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
||||||
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
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.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||||
github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||||
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/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
|
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 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g=
|
||||||
github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY=
|
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.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 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM=
|
||||||
github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw=
|
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.5 h1:ROfXb50elFq5c9+1ztaUbdlrArNFl2+fQWP6B8HGEq4=
|
||||||
github.com/nats-io/jwt/v2 v2.5.3/go.mod h1:iysuPemFcc7p4IoYots3IuELSI4EDe9Y0bQMe+I3Bf4=
|
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 h1:f5VDy+GMu7JyuFA0Fef+6TfulfCs5nBTgq7MMkFJx5Y=
|
||||||
github.com/nats-io/nats-server/v2 v2.10.7/go.mod h1:V2JHOvPiPdtfDXTuEUsthUnCvSDeFrK4Xn9hRo6du7c=
|
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 h1:/WFBHEc/dOKBF6qf1TZhrdEfTmOZ5JzdJ+Y3m6Y/p7E=
|
||||||
github.com/nats-io/nats.go v1.31.0/go.mod h1:di3Bm5MLsoB4Bx61CBTsxuarI36WbhAwOm8QrW39+i8=
|
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.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI=
|
||||||
github.com/nats-io/nkeys v0.4.6/go.mod h1:4DxZNzenSVd1cYQoAa8948QY3QDjrHfcfVADymtkpts=
|
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 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
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 h1:lrVQzBtkeQEGGYUHwSX1XPe1E5GL6U3KYCNe2G4bncQ=
|
||||||
github.com/neilalexander/utp v0.1.1-0.20210727203401-54ae7b1cd5f9/go.mod h1:NPHGhPc0/wudcaCqL/H5AOddkRf8GPRhzOujuUKGQu8=
|
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=
|
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/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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
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.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
|
||||||
github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc=
|
github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
|
||||||
github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
|
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
|
||||||
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
|
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
|
||||||
github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM=
|
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
|
||||||
github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=
|
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
|
||||||
github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg=
|
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
|
||||||
github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM=
|
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 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/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 h1:AgKsQLZ1+YCwZd2GYhBUsJDYZwEkA5gENtAjb+MxONU=
|
||||||
github.com/quic-go/quic-go v0.37.7/go.mod h1:YsbH1r4mSHPJcLF4k4zruUkLBqctEMBDR6VPvcYjIsU=
|
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 h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
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.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
|
||||||
github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
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 h1:cO+d60CHkknCbvzEWxP0S9K6KqyTjrCNUy1LdQLCGPc=
|
||||||
github.com/rs/zerolog v1.29.1/go.mod h1:Le6ESbR7hc+DP6Lt1THiV8CQSdkkNrd3R0XbEgp3ZBU=
|
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/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/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.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.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||||
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/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
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.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
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.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.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.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.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
|
||||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
|
||||||
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
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.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U=
|
||||||
github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
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 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
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.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.2.1/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.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.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ=
|
||||||
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
|
go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
|
||||||
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
|
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||||
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||||
go.uber.org/atomic v1.10.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-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-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-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-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-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-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.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
|
||||||
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
|
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||||
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
|
||||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
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-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-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-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-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-20231108232855-2478ac86f678 h1:mchzmB1XO2pMaKFRqk/+MV3mgGG96aqaPXaMifQU47w=
|
||||||
golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
|
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-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-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.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
golang.org/x/image v0.10.0 h1:gXjUUtwtx5yOE0VKWq1CH4IJAClq4UGgUA3i+rpON9M=
|
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
|
||||||
golang.org/x/image v0.10.0/go.mod h1:jtrku+n79PfroUbvDdeUWMAI+heR786BofxrbiSF+J0=
|
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-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 h1:zSgtO19fpg781xknwqiQPmOHaASr6E7ZVlTseLd9Fx4=
|
||||||
golang.org/x/mobile v0.0.0-20221020085226-b36e6246172e/go.mod h1:aAjjkJNdrh3PMckS4B10TGS2nag27cbKR1y2BpUxsiY=
|
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.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.3.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.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.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
|
||||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
|
|
||||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
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-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
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-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-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-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.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
||||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||||
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/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
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-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-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-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.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
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/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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.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.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||||
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.15.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-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.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
|
||||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||||
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/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
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.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.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.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||||
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/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
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 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
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.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.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
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.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
|
||||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||||
golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss=
|
|
||||||
golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
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-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/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=
|
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-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.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
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 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-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/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.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
|
||||||
gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o=
|
gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o=
|
||||||
gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g=
|
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 h1:N7zSdd0mZkB2m2JtFUsiGTQQAdP0YeFWT7YMc80yAL8=
|
||||||
maunium.net/go/maulogger/v2 v2.4.1/go.mod h1:omPuYwYBILeVQobz8uO3XC8DIRuEb5rXYlQSuqrbCho=
|
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 h1:pmCtMjYRpd83+2UL+KTRFYQo5to0373yulimvLK+1k0=
|
||||||
maunium.net/go/mautrix v0.15.1/go.mod h1:icQIrvz2NldkRLTuzSGzmaeuMUmw+fzO7UVycPeauN8=
|
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/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
|
||||||
modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0=
|
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI=
|
||||||
modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw=
|
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4=
|
||||||
modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY=
|
modernc.org/libc v1.41.0 h1:g9YAc6BkKlgORsUWj+JwqoB1wU3o4DE3bM3yvA3k+Gk=
|
||||||
modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk=
|
modernc.org/libc v1.41.0/go.mod h1:w0eszPsiXoOnoMJgrXjglgLuDy/bt5RR4y3QzUUeodY=
|
||||||
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
|
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
|
||||||
modernc.org/libc v1.22.5 h1:91BNch/e5B0uPbJFgqbxXuOnxBQjlS//icfQEGmvyjE=
|
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
|
||||||
modernc.org/libc v1.22.5/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY=
|
modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E=
|
||||||
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
|
modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E=
|
||||||
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
modernc.org/sqlite v1.29.5 h1:8l/SQKAjDtZFo9lkJLdk8g9JEOeYRG4/ghStDCCTiTE=
|
||||||
modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds=
|
modernc.org/sqlite v1.29.5/go.mod h1:S02dvcmm7TnTRvGhv8IGYyLnIt7AS2KPaB1F/71p75U=
|
||||||
modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
|
||||||
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
|
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
|
||||||
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||||
modernc.org/sqlite v1.23.1 h1:nrSBg4aRQQwq59JpvGEQ15tNxoO5pX/kUjcRNwSAGQM=
|
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||||
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=
|
|
||||||
nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g=
|
nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g=
|
||||||
nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
|
nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
|
||||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
apiVersion: v2
|
apiVersion: v2
|
||||||
name: dendrite
|
name: dendrite
|
||||||
version: "0.13.6"
|
version: "0.14.1"
|
||||||
appVersion: "0.13.5"
|
appVersion: "0.13.7"
|
||||||
description: Dendrite Matrix Homeserver
|
description: Dendrite Matrix Homeserver
|
||||||
type: application
|
type: application
|
||||||
|
icon: https://avatars.githubusercontent.com/u/8418310?s=48&v=4
|
||||||
keywords:
|
keywords:
|
||||||
- matrix
|
- matrix
|
||||||
- chat
|
- chat
|
||||||
|
@ -13,7 +14,7 @@ home: https://github.com/matrix-org/dendrite
|
||||||
sources:
|
sources:
|
||||||
- https://github.com/matrix-org/dendrite
|
- https://github.com/matrix-org/dendrite
|
||||||
dependencies:
|
dependencies:
|
||||||
- name: postgresql
|
- name: postgresql
|
||||||
version: 12.1.7
|
version: 14.2.3
|
||||||
repository: https://charts.bitnami.com/bitnami
|
repository: https://charts.bitnami.com/bitnami
|
||||||
condition: postgresql.enabled
|
condition: postgresql.enabled
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
|
|
||||||
# dendrite
|
# 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
|
Dendrite Matrix Homeserver
|
||||||
|
|
||||||
Status: **NOT PRODUCTION READY**
|
Status: **NOT PRODUCTION READY**
|
||||||
|
@ -37,7 +37,7 @@ Create a folder `appservices` and place your configurations in there. The confi
|
||||||
|
|
||||||
| Repository | Name | Version |
|
| Repository | Name | Version |
|
||||||
|------------|------|---------|
|
|------------|------|---------|
|
||||||
| https://charts.bitnami.com/bitnami | postgresql | 12.1.7 |
|
| https://charts.bitnami.com/bitnami | postgresql | 14.2.3 |
|
||||||
## Values
|
## Values
|
||||||
|
|
||||||
| Key | Type | Default | Description |
|
| 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.create | bool | `true` | Create a new signing key, if not exists |
|
||||||
| signing_key.existingSecret | string | `""` | Use an existing secret |
|
| signing_key.existingSecret | string | `""` | Use an existing secret |
|
||||||
| resources | object | sets some sane default values | Default resource requests/limits. |
|
| 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.existingClaim | string | `""` | Use an existing volume claim for jetstream |
|
||||||
| persistence.jetstream.capacity | string | `"1Gi"` | PVC Storage Request for the jetstream volume |
|
| 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.existingClaim | string | `""` | Use an existing volume claim for media files |
|
||||||
| persistence.media.capacity | string | `"1Gi"` | PVC Storage Request for the media volume |
|
| 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.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.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 |
|
| extraVolumes | list | `[]` | Add additional volumes to the Dendrite Pod |
|
||||||
| extraVolumeMounts | list | `[]` | Configure additional mount points volumes in 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.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 |
|
| strategy.rollingUpdate.maxSurge | string | `"25%"` | Maximum number of pods that can be scheduled above the desired number of pods |
|
||||||
| dendrite_config.version | int | `2` | |
|
| 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 |
|
| 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.enabled | bool | See value.yaml | Enable and configure postgres as the database for dendrite. |
|
||||||
| postgresql.image.repository | string | `"bitnami/postgresql"` | |
|
| 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.username | string | `"dendrite"` | |
|
||||||
| postgresql.auth.password | string | `"changeme"` | |
|
| postgresql.auth.password | string | `"changeme"` | |
|
||||||
| postgresql.auth.database | string | `"dendrite"` | |
|
| 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`)
|
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) -}}
|
{{- $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 "/") -}}
|
{{- $wellKnownClientHost := default $serverNameHost (regexFind "//(\\[.+\\])?[^:/]*" .Values.dendrite_config.global.well_known_client_name | trimAll "/") -}}
|
||||||
{{- $allHosts := list $serverNameHost $wellKnownServerHost $wellKnownClientHost | uniq -}}
|
{{- $allHosts := list $serverNameHost $wellKnownServerHost $wellKnownClientHost | uniq -}}
|
||||||
|
|
||||||
{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}}
|
{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}}
|
||||||
apiVersion: networking.k8s.io/v1
|
apiVersion: networking.k8s.io/v1
|
||||||
{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
|
{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
|
||||||
|
@ -56,7 +57,7 @@ spec:
|
||||||
service:
|
service:
|
||||||
name: {{ $fullName }}
|
name: {{ $fullName }}
|
||||||
port:
|
port:
|
||||||
name: http
|
number: {{ $.Values.service.port }}
|
||||||
{{- else }}
|
{{- else }}
|
||||||
serviceName: {{ $fullName }}
|
serviceName: {{ $fullName }}
|
||||||
servicePort: http
|
servicePort: http
|
||||||
|
@ -72,7 +73,7 @@ spec:
|
||||||
service:
|
service:
|
||||||
name: {{ $fullName }}
|
name: {{ $fullName }}
|
||||||
port:
|
port:
|
||||||
name: http
|
number: {{ $.Values.service.port }}
|
||||||
{{- else }}
|
{{- else }}
|
||||||
serviceName: {{ $fullName }}
|
serviceName: {{ $fullName }}
|
||||||
servicePort: http
|
servicePort: http
|
||||||
|
@ -88,7 +89,7 @@ spec:
|
||||||
service:
|
service:
|
||||||
name: {{ $fullName }}
|
name: {{ $fullName }}
|
||||||
port:
|
port:
|
||||||
name: http
|
number: {{ $.Values.service.port }}
|
||||||
{{- else }}
|
{{- else }}
|
||||||
serviceName: {{ $fullName }}
|
serviceName: {{ $fullName }}
|
||||||
servicePort: http
|
servicePort: http
|
||||||
|
@ -105,7 +106,7 @@ spec:
|
||||||
service:
|
service:
|
||||||
name: {{ $fullName }}
|
name: {{ $fullName }}
|
||||||
port:
|
port:
|
||||||
name: http
|
number: {{ $.Values.service.port }}
|
||||||
{{- else }}
|
{{- else }}
|
||||||
serviceName: {{ $fullName }}
|
serviceName: {{ $fullName }}
|
||||||
servicePort: http
|
servicePort: http
|
||||||
|
|
|
@ -14,4 +14,4 @@ spec:
|
||||||
- name: http
|
- name: http
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
port: {{ .Values.service.port }}
|
port: {{ .Values.service.port }}
|
||||||
targetPort: 8008
|
targetPort: http
|
|
@ -26,13 +26,13 @@ persistence:
|
||||||
# -- The storage class to use for volume claims.
|
# -- The storage class to use for volume claims.
|
||||||
# Used unless specified at the specific component.
|
# Used unless specified at the specific component.
|
||||||
# Defaults to the cluster default storage class.
|
# Defaults to the cluster default storage class.
|
||||||
## If defined, storageClassName: <storageClass>
|
# If defined, storageClassName: <storageClass>
|
||||||
## If set to "-", storageClassName: "", which disables dynamic provisioning
|
# If set to "-", storageClassName: "", which disables dynamic provisioning
|
||||||
## If undefined (the default) or set to null, no storageClassName spec is
|
# If undefined (the default) or set to null, no storageClassName spec is
|
||||||
## set, choosing the default provisioner. (gp2 on AWS, standard on
|
# set, choosing the default provisioner. (gp2 on AWS, standard on
|
||||||
## GKE, AWS & OpenStack)
|
# GKE, AWS & OpenStack)
|
||||||
##
|
#
|
||||||
# storageClass: ""
|
storageClass:
|
||||||
jetstream:
|
jetstream:
|
||||||
# -- Use an existing volume claim for jetstream
|
# -- Use an existing volume claim for jetstream
|
||||||
existingClaim: ""
|
existingClaim: ""
|
||||||
|
@ -40,13 +40,12 @@ persistence:
|
||||||
capacity: "1Gi"
|
capacity: "1Gi"
|
||||||
# -- The storage class to use for volume claims.
|
# -- The storage class to use for volume claims.
|
||||||
# Defaults to persistence.storageClass
|
# Defaults to persistence.storageClass
|
||||||
## If defined, storageClassName: <storageClass>
|
# If defined, storageClassName: <storageClass>
|
||||||
## If set to "-", storageClassName: "", which disables dynamic provisioning
|
# If set to "-", storageClassName: "", which disables dynamic provisioning
|
||||||
## If undefined (the default) or set to null, no storageClassName spec is
|
# If undefined (the default) or set to null, no storageClassName spec is
|
||||||
## set, choosing the default provisioner. (gp2 on AWS, standard on
|
# set, choosing the default provisioner. (gp2 on AWS, standard on
|
||||||
## GKE, AWS & OpenStack)
|
# GKE, AWS & OpenStack)
|
||||||
##
|
storageClass:
|
||||||
# storageClass: ""
|
|
||||||
media:
|
media:
|
||||||
# -- Use an existing volume claim for media files
|
# -- Use an existing volume claim for media files
|
||||||
existingClaim: ""
|
existingClaim: ""
|
||||||
|
@ -54,13 +53,12 @@ persistence:
|
||||||
capacity: "1Gi"
|
capacity: "1Gi"
|
||||||
# -- The storage class to use for volume claims.
|
# -- The storage class to use for volume claims.
|
||||||
# Defaults to persistence.storageClass
|
# Defaults to persistence.storageClass
|
||||||
## If defined, storageClassName: <storageClass>
|
# If defined, storageClassName: <storageClass>
|
||||||
## If set to "-", storageClassName: "", which disables dynamic provisioning
|
# If set to "-", storageClassName: "", which disables dynamic provisioning
|
||||||
## If undefined (the default) or set to null, no storageClassName spec is
|
# If undefined (the default) or set to null, no storageClassName spec is
|
||||||
## set, choosing the default provisioner. (gp2 on AWS, standard on
|
# set, choosing the default provisioner. (gp2 on AWS, standard on
|
||||||
## GKE, AWS & OpenStack)
|
# GKE, AWS & OpenStack)
|
||||||
##
|
storageClass:
|
||||||
# storageClass: ""
|
|
||||||
search:
|
search:
|
||||||
# -- Use an existing volume claim for the fulltext search index
|
# -- Use an existing volume claim for the fulltext search index
|
||||||
existingClaim: ""
|
existingClaim: ""
|
||||||
|
@ -68,13 +66,12 @@ persistence:
|
||||||
capacity: "1Gi"
|
capacity: "1Gi"
|
||||||
# -- The storage class to use for volume claims.
|
# -- The storage class to use for volume claims.
|
||||||
# Defaults to persistence.storageClass
|
# Defaults to persistence.storageClass
|
||||||
## If defined, storageClassName: <storageClass>
|
# If defined, storageClassName: <storageClass>
|
||||||
## If set to "-", storageClassName: "", which disables dynamic provisioning
|
# If set to "-", storageClassName: "", which disables dynamic provisioning
|
||||||
## If undefined (the default) or set to null, no storageClassName spec is
|
# If undefined (the default) or set to null, no storageClassName spec is
|
||||||
## set, choosing the default provisioner. (gp2 on AWS, standard on
|
# set, choosing the default provisioner. (gp2 on AWS, standard on
|
||||||
## GKE, AWS & OpenStack)
|
# GKE, AWS & OpenStack)
|
||||||
##
|
storageClass:
|
||||||
# storageClass: ""
|
|
||||||
|
|
||||||
# -- Add additional volumes to the Dendrite Pod
|
# -- Add additional volumes to the Dendrite Pod
|
||||||
extraVolumes: []
|
extraVolumes: []
|
||||||
|
@ -92,7 +89,7 @@ extraVolumeMounts: []
|
||||||
strategy:
|
strategy:
|
||||||
# -- Strategy to use for rolling updates (e.g. Recreate, RollingUpdate)
|
# -- Strategy to use for rolling updates (e.g. Recreate, RollingUpdate)
|
||||||
# If you are using ReadWriteOnce volumes, you should probably use Recreate
|
# If you are using ReadWriteOnce volumes, you should probably use Recreate
|
||||||
type: RollingUpdate
|
type: Recreate
|
||||||
rollingUpdate:
|
rollingUpdate:
|
||||||
# -- Maximum number of pods that can be unavailable during the update process
|
# -- Maximum number of pods that can be unavailable during the update process
|
||||||
maxUnavailable: 25%
|
maxUnavailable: 25%
|
||||||
|
@ -378,7 +375,7 @@ postgresql:
|
||||||
enabled: false
|
enabled: false
|
||||||
image:
|
image:
|
||||||
repository: bitnami/postgresql
|
repository: bitnami/postgresql
|
||||||
tag: "15.1.0"
|
tag: "16.2.0"
|
||||||
auth:
|
auth:
|
||||||
username: dendrite
|
username: dendrite
|
||||||
password: changeme
|
password: changeme
|
||||||
|
|
|
@ -76,6 +76,8 @@ func MakeAuthAPI(
|
||||||
// add the user to Sentry, if enabled
|
// add the user to Sentry, if enabled
|
||||||
hub := sentry.GetHubFromContext(req.Context())
|
hub := sentry.GetHubFromContext(req.Context())
|
||||||
if hub != nil {
|
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{
|
hub.Scope().SetUser(sentry.User{
|
||||||
Username: device.UserID,
|
Username: device.UserID,
|
||||||
})
|
})
|
||||||
|
|
|
@ -66,15 +66,15 @@ func NewRouters() Routers {
|
||||||
}
|
}
|
||||||
|
|
||||||
var NotAllowedHandler = WrapHandlerInCORS(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
var NotAllowedHandler = WrapHandlerInCORS(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||||
unrecognizedErr, _ := json.Marshal(spec.Unrecognized("Unrecognized request")) // nolint:misspell
|
unrecognizedErr, _ := json.Marshal(spec.Unrecognized("Unrecognized request")) // nolint:misspell
|
||||||
_, _ = w.Write(unrecognizedErr) // nolint:misspell
|
_, _ = w.Write(unrecognizedErr) // nolint:misspell
|
||||||
}))
|
}))
|
||||||
|
|
||||||
var NotFoundCORSHandler = WrapHandlerInCORS(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
var NotFoundCORSHandler = WrapHandlerInCORS(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.WriteHeader(http.StatusNotFound)
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
unrecognizedErr, _ := json.Marshal(spec.Unrecognized("Unrecognized request")) // nolint:misspell
|
unrecognizedErr, _ := json.Marshal(spec.Unrecognized("Unrecognized request")) // nolint:misspell
|
||||||
_, _ = w.Write(unrecognizedErr) // nolint:misspell
|
_, _ = w.Write(unrecognizedErr) // nolint:misspell
|
||||||
}))
|
}))
|
||||||
|
|
|
@ -17,7 +17,7 @@ func TestRoutersError(t *testing.T) {
|
||||||
if rec.Code != http.StatusNotFound {
|
if rec.Code != http.StatusNotFound {
|
||||||
t.Fatalf("unexpected status code: %d - %s", rec.Code, rec.Body.String())
|
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)
|
t.Fatalf("unexpected content-type: %s", ct)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ func TestRoutersError(t *testing.T) {
|
||||||
if rec.Code != http.StatusMethodNotAllowed {
|
if rec.Code != http.StatusMethodNotAllowed {
|
||||||
t.Fatalf("unexpected status code: %d - %s", rec.Code, rec.Body.String())
|
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)
|
t.Fatalf("unexpected content-type: %s", ct)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,8 +3,7 @@ package sqlutil
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
|
"sync/atomic"
|
||||||
"go.uber.org/atomic"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ExclusiveWriter implements sqlutil.Writer.
|
// ExclusiveWriter implements sqlutil.Writer.
|
||||||
|
|
|
@ -19,6 +19,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"sync/atomic"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -26,7 +27,6 @@ import (
|
||||||
"github.com/matrix-org/gomatrixserverlib/spec"
|
"github.com/matrix-org/gomatrixserverlib/spec"
|
||||||
"github.com/nats-io/nats.go"
|
"github.com/nats-io/nats.go"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"go.uber.org/atomic"
|
|
||||||
"gotest.tools/v3/poll"
|
"gotest.tools/v3/poll"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/federationapi/producers"
|
"github.com/matrix-org/dendrite/federationapi/producers"
|
||||||
|
@ -228,7 +228,7 @@ func TestProcessTransactionRequestEDUTyping(t *testing.T) {
|
||||||
ctx := process.NewProcessContext()
|
ctx := process.NewProcessContext()
|
||||||
defer ctx.ShutdownDendrite()
|
defer ctx.ShutdownDendrite()
|
||||||
txn, js, cfg := createTransactionWithEDU(ctx, edus)
|
txn, js, cfg := createTransactionWithEDU(ctx, edus)
|
||||||
received := atomic.NewBool(false)
|
received := atomic.Bool{}
|
||||||
onMessage := func(ctx context.Context, msgs []*nats.Msg) bool {
|
onMessage := func(ctx context.Context, msgs []*nats.Msg) bool {
|
||||||
msg := msgs[0] // Guaranteed to exist if onMessage is called
|
msg := msgs[0] // Guaranteed to exist if onMessage is called
|
||||||
room := msg.Header.Get(jetstream.RoomID)
|
room := msg.Header.Get(jetstream.RoomID)
|
||||||
|
@ -294,7 +294,7 @@ func TestProcessTransactionRequestEDUToDevice(t *testing.T) {
|
||||||
ctx := process.NewProcessContext()
|
ctx := process.NewProcessContext()
|
||||||
defer ctx.ShutdownDendrite()
|
defer ctx.ShutdownDendrite()
|
||||||
txn, js, cfg := createTransactionWithEDU(ctx, edus)
|
txn, js, cfg := createTransactionWithEDU(ctx, edus)
|
||||||
received := atomic.NewBool(false)
|
received := atomic.Bool{}
|
||||||
onMessage := func(ctx context.Context, msgs []*nats.Msg) bool {
|
onMessage := func(ctx context.Context, msgs []*nats.Msg) bool {
|
||||||
msg := msgs[0] // Guaranteed to exist if onMessage is called
|
msg := msgs[0] // Guaranteed to exist if onMessage is called
|
||||||
|
|
||||||
|
@ -371,7 +371,7 @@ func TestProcessTransactionRequestEDUDeviceListUpdate(t *testing.T) {
|
||||||
ctx := process.NewProcessContext()
|
ctx := process.NewProcessContext()
|
||||||
defer ctx.ShutdownDendrite()
|
defer ctx.ShutdownDendrite()
|
||||||
txn, js, cfg := createTransactionWithEDU(ctx, edus)
|
txn, js, cfg := createTransactionWithEDU(ctx, edus)
|
||||||
received := atomic.NewBool(false)
|
received := atomic.Bool{}
|
||||||
onMessage := func(ctx context.Context, msgs []*nats.Msg) bool {
|
onMessage := func(ctx context.Context, msgs []*nats.Msg) bool {
|
||||||
msg := msgs[0] // Guaranteed to exist if onMessage is called
|
msg := msgs[0] // Guaranteed to exist if onMessage is called
|
||||||
|
|
||||||
|
@ -468,7 +468,7 @@ func TestProcessTransactionRequestEDUReceipt(t *testing.T) {
|
||||||
ctx := process.NewProcessContext()
|
ctx := process.NewProcessContext()
|
||||||
defer ctx.ShutdownDendrite()
|
defer ctx.ShutdownDendrite()
|
||||||
txn, js, cfg := createTransactionWithEDU(ctx, edus)
|
txn, js, cfg := createTransactionWithEDU(ctx, edus)
|
||||||
received := atomic.NewBool(false)
|
received := atomic.Bool{}
|
||||||
onMessage := func(ctx context.Context, msgs []*nats.Msg) bool {
|
onMessage := func(ctx context.Context, msgs []*nats.Msg) bool {
|
||||||
msg := msgs[0] // Guaranteed to exist if onMessage is called
|
msg := msgs[0] // Guaranteed to exist if onMessage is called
|
||||||
|
|
||||||
|
@ -512,7 +512,7 @@ func TestProcessTransactionRequestEDUSigningKeyUpdate(t *testing.T) {
|
||||||
ctx := process.NewProcessContext()
|
ctx := process.NewProcessContext()
|
||||||
defer ctx.ShutdownDendrite()
|
defer ctx.ShutdownDendrite()
|
||||||
txn, js, cfg := createTransactionWithEDU(ctx, edus)
|
txn, js, cfg := createTransactionWithEDU(ctx, edus)
|
||||||
received := atomic.NewBool(false)
|
received := atomic.Bool{}
|
||||||
onMessage := func(ctx context.Context, msgs []*nats.Msg) bool {
|
onMessage := func(ctx context.Context, msgs []*nats.Msg) bool {
|
||||||
msg := msgs[0] // Guaranteed to exist if onMessage is called
|
msg := msgs[0] // Guaranteed to exist if onMessage is called
|
||||||
|
|
||||||
|
@ -569,7 +569,7 @@ func TestProcessTransactionRequestEDUPresence(t *testing.T) {
|
||||||
ctx := process.NewProcessContext()
|
ctx := process.NewProcessContext()
|
||||||
defer ctx.ShutdownDendrite()
|
defer ctx.ShutdownDendrite()
|
||||||
txn, js, cfg := createTransactionWithEDU(ctx, edus)
|
txn, js, cfg := createTransactionWithEDU(ctx, edus)
|
||||||
received := atomic.NewBool(false)
|
received := atomic.Bool{}
|
||||||
onMessage := func(ctx context.Context, msgs []*nats.Msg) bool {
|
onMessage := func(ctx context.Context, msgs []*nats.Msg) bool {
|
||||||
msg := msgs[0] // Guaranteed to exist if onMessage is called
|
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)
|
ErrPasswordTooLong = fmt.Errorf("password too long: max %d characters", maxPasswordLength)
|
||||||
ErrPasswordWeak = fmt.Errorf("password too weak: min %d characters", minPasswordLength)
|
ErrPasswordWeak = fmt.Errorf("password too weak: min %d characters", minPasswordLength)
|
||||||
ErrUsernameTooLong = fmt.Errorf("username exceeds the maximum length of %d characters", maxUsernameLength)
|
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 '_'")
|
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
|
// ValidatePassword returns an error if the password is invalid
|
||||||
|
|
|
@ -129,6 +129,11 @@ func Test_validateUsername(t *testing.T) {
|
||||||
localpart: "i_am_allowed=1",
|
localpart: "i_am_allowed=1",
|
||||||
domain: "localhost",
|
domain: "localhost",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "special characters are allowed 3",
|
||||||
|
localpart: "+55555555555",
|
||||||
|
domain: "localhost",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "not all special characters are allowed",
|
name: "not all special characters are allowed",
|
||||||
localpart: "notallowed#", // contains #
|
localpart: "notallowed#", // contains #
|
||||||
|
@ -139,6 +144,16 @@ func Test_validateUsername(t *testing.T) {
|
||||||
JSON: spec.InvalidUsername(ErrUsernameInvalid.Error()),
|
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",
|
name: "username containing numbers",
|
||||||
localpart: "hello1337",
|
localpart: "hello1337",
|
||||||
|
|
|
@ -18,7 +18,7 @@ var build string
|
||||||
const (
|
const (
|
||||||
VersionMajor = 0
|
VersionMajor = 0
|
||||||
VersionMinor = 13
|
VersionMinor = 13
|
||||||
VersionPatch = 5
|
VersionPatch = 7
|
||||||
VersionTag = "" // example: "rc1"
|
VersionTag = "" // example: "rc1"
|
||||||
|
|
||||||
gitRevLen = 7 // 7 matches the displayed characters on github.com
|
gitRevLen = 7 // 7 matches the displayed characters on github.com
|
||||||
|
|
|
@ -17,6 +17,7 @@ package shared
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/internal/sqlutil"
|
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||||
"github.com/matrix-org/dendrite/mediaapi/storage/tables"
|
"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.
|
// 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.
|
// 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.Writer.Do(d.DB, nil, func(txn *sql.Tx) error {
|
||||||
return d.MediaRepository.InsertMedia(ctx, txn, mediaMetadata)
|
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.
|
// 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.
|
// 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.
|
// 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)
|
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 nil, nil
|
||||||
}
|
}
|
||||||
return mediaMetadata, err
|
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.
|
// 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.
|
// 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.
|
// 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)
|
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 nil, nil
|
||||||
}
|
}
|
||||||
return mediaMetadata, err
|
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.
|
// 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.
|
// 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.Writer.Do(d.DB, nil, func(txn *sql.Tx) error {
|
||||||
return d.Thumbnails.InsertThumbnail(ctx, txn, thumbnailMetadata)
|
return d.Thumbnails.InsertThumbnail(ctx, txn, thumbnailMetadata)
|
||||||
})
|
})
|
||||||
|
@ -72,27 +73,21 @@ func (d Database) StoreThumbnail(ctx context.Context, thumbnailMetadata *types.T
|
||||||
// GetThumbnail returns metadata about a specific thumbnail.
|
// GetThumbnail returns metadata about a specific thumbnail.
|
||||||
// The media could have been uploaded to this server or fetched from another server and cached here.
|
// 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.
|
// 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)
|
metadata, err := d.Thumbnails.SelectThumbnail(ctx, nil, mediaID, mediaOrigin, width, height, resizeMethod)
|
||||||
if err != nil {
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
if err == sql.ErrNoRows {
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return metadata, err
|
return metadata, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetThumbnails returns metadata about all thumbnails for a specific media stored on this server.
|
// 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.
|
// 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.
|
// 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)
|
metadatas, err := d.Thumbnails.SelectThumbnails(ctx, nil, mediaID, mediaOrigin)
|
||||||
if err != nil {
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
if err == sql.ErrNoRows {
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return metadatas, err
|
return metadatas, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,6 +108,8 @@ func MakeRelayAPI(
|
||||||
// add the user to Sentry, if enabled
|
// add the user to Sentry, if enabled
|
||||||
hub := sentry.GetHubFromContext(req.Context())
|
hub := sentry.GetHubFromContext(req.Context())
|
||||||
if hub != nil {
|
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("origin", string(fedReq.Origin()))
|
||||||
hub.Scope().SetTag("uri", fedReq.RequestURI())
|
hub.Scope().SetTag("uri", fedReq.RequestURI())
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"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"
|
||||||
"github.com/matrix-org/gomatrixserverlib/spec"
|
"github.com/matrix-org/gomatrixserverlib/spec"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
@ -32,42 +32,48 @@ import (
|
||||||
const MRoomServerACL = "m.room.server_acl"
|
const MRoomServerACL = "m.room.server_acl"
|
||||||
|
|
||||||
type ServerACLDatabase interface {
|
type ServerACLDatabase interface {
|
||||||
// GetKnownRooms returns a list of all rooms we know about.
|
// RoomsWithACLs returns all room IDs for rooms with ACLs
|
||||||
GetKnownRooms(ctx context.Context) ([]string, error)
|
RoomsWithACLs(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
|
// 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 there was an issue during the retrieval, returns an error
|
// If a tuple has the StateKey of '*' and allowWildcards=true then all state events with the EventType should be returned.
|
||||||
GetStateEvent(ctx context.Context, roomID, evType, stateKey string) (*types.HeaderedEvent, error)
|
GetBulkStateContent(ctx context.Context, roomIDs []string, tuples []gomatrixserverlib.StateKeyTuple, allowWildcards bool) ([]tables.StrippedEvent, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ServerACLs struct {
|
type ServerACLs struct {
|
||||||
acls map[string]*serverACL // room ID -> ACL
|
acls map[string]*serverACL // room ID -> ACL
|
||||||
aclsMutex sync.RWMutex // protects the above
|
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 {
|
func NewServerACLs(db ServerACLDatabase) *ServerACLs {
|
||||||
ctx := context.TODO()
|
ctx := context.TODO()
|
||||||
acls := &ServerACLs{
|
acls := &ServerACLs{
|
||||||
acls: make(map[string]*serverACL),
|
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.
|
// 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 {
|
if err != nil {
|
||||||
logrus.WithError(err).Fatalf("Failed to get known rooms")
|
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
|
// 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
|
// do then we'll process it into memory so that we have the regexes to
|
||||||
// hand.
|
// hand.
|
||||||
for _, room := range rooms {
|
|
||||||
state, err := db.GetStateEvent(ctx, room, MRoomServerACL, "")
|
events, err := db.GetBulkStateContent(ctx, rooms, []gomatrixserverlib.StateKeyTuple{{EventType: MRoomServerACL, StateKey: ""}}, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.WithError(err).Errorf("Failed to get server ACLs for room %q", room)
|
logrus.WithError(err).Errorf("Failed to get server ACLs for all rooms: %q", err)
|
||||||
continue
|
|
||||||
}
|
|
||||||
if state != nil {
|
|
||||||
acls.OnServerACLUpdate(state.PDU)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, event := range events {
|
||||||
|
acls.OnServerACLUpdate(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
return acls
|
return acls
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,8 +85,8 @@ type ServerACL struct {
|
||||||
|
|
||||||
type serverACL struct {
|
type serverACL struct {
|
||||||
ServerACL
|
ServerACL
|
||||||
allowedRegexes []*regexp.Regexp
|
allowedRegexes []**regexp.Regexp
|
||||||
deniedRegexes []*regexp.Regexp
|
deniedRegexes []**regexp.Regexp
|
||||||
}
|
}
|
||||||
|
|
||||||
func compileACLRegex(orig string) (*regexp.Regexp, error) {
|
func compileACLRegex(orig string) (*regexp.Regexp, error) {
|
||||||
|
@ -90,9 +96,28 @@ func compileACLRegex(orig string) (*regexp.Regexp, error) {
|
||||||
return regexp.Compile(escaped)
|
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{}
|
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")
|
logrus.WithError(err).Errorf("Failed to unmarshal state content for server ACLs")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -101,14 +126,14 @@ func (s *ServerACLs) OnServerACLUpdate(state gomatrixserverlib.PDU) {
|
||||||
// special characters and then replace * and ? with their regex counterparts.
|
// special characters and then replace * and ? with their regex counterparts.
|
||||||
// https://matrix.org/docs/spec/client_server/r0.6.1#m-room-server-acl
|
// https://matrix.org/docs/spec/client_server/r0.6.1#m-room-server-acl
|
||||||
for _, orig := range acls.Allowed {
|
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")
|
logrus.WithError(err).Errorf("Failed to compile allowed regex")
|
||||||
} else {
|
} else {
|
||||||
acls.allowedRegexes = append(acls.allowedRegexes, expr)
|
acls.allowedRegexes = append(acls.allowedRegexes, expr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, orig := range acls.Denied {
|
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")
|
logrus.WithError(err).Errorf("Failed to compile denied regex")
|
||||||
} else {
|
} else {
|
||||||
acls.deniedRegexes = append(acls.deniedRegexes, expr)
|
acls.deniedRegexes = append(acls.deniedRegexes, expr)
|
||||||
|
@ -118,10 +143,15 @@ func (s *ServerACLs) OnServerACLUpdate(state gomatrixserverlib.PDU) {
|
||||||
"allow_ip_literals": acls.AllowIPLiterals,
|
"allow_ip_literals": acls.AllowIPLiterals,
|
||||||
"num_allowed": len(acls.allowedRegexes),
|
"num_allowed": len(acls.allowedRegexes),
|
||||||
"num_denied": len(acls.deniedRegexes),
|
"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()
|
s.aclsMutex.Lock()
|
||||||
defer s.aclsMutex.Unlock()
|
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 {
|
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
|
// Check if the hostname matches one of the denied regexes. If it does then
|
||||||
// the server is banned from the room.
|
// the server is banned from the room.
|
||||||
for _, expr := range acls.deniedRegexes {
|
for _, expr := range acls.deniedRegexes {
|
||||||
if expr.MatchString(string(serverName)) {
|
if (*expr).MatchString(string(serverName)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Check if the hostname matches one of the allowed regexes. If it does then
|
// Check if the hostname matches one of the allowed regexes. If it does then
|
||||||
// the server is NOT banned from the room.
|
// the server is NOT banned from the room.
|
||||||
for _, expr := range acls.allowedRegexes {
|
for _, expr := range acls.allowedRegexes {
|
||||||
if expr.MatchString(string(serverName)) {
|
if (*expr).MatchString(string(serverName)) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,8 +15,14 @@
|
||||||
package acls
|
package acls
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"regexp"
|
"regexp"
|
||||||
"testing"
|
"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) {
|
func TestOpenACLsWithBlacklist(t *testing.T) {
|
||||||
|
@ -38,8 +44,8 @@ func TestOpenACLsWithBlacklist(t *testing.T) {
|
||||||
ServerACL: ServerACL{
|
ServerACL: ServerACL{
|
||||||
AllowIPLiterals: true,
|
AllowIPLiterals: true,
|
||||||
},
|
},
|
||||||
allowedRegexes: []*regexp.Regexp{allowRegex},
|
allowedRegexes: []**regexp.Regexp{&allowRegex},
|
||||||
deniedRegexes: []*regexp.Regexp{denyRegex},
|
deniedRegexes: []**regexp.Regexp{&denyRegex},
|
||||||
}
|
}
|
||||||
|
|
||||||
if acls.IsServerBannedFromRoom("1.2.3.4", roomID) {
|
if acls.IsServerBannedFromRoom("1.2.3.4", roomID) {
|
||||||
|
@ -77,8 +83,8 @@ func TestDefaultACLsWithWhitelist(t *testing.T) {
|
||||||
ServerACL: ServerACL{
|
ServerACL: ServerACL{
|
||||||
AllowIPLiterals: false,
|
AllowIPLiterals: false,
|
||||||
},
|
},
|
||||||
allowedRegexes: []*regexp.Regexp{allowRegex},
|
allowedRegexes: []**regexp.Regexp{&allowRegex},
|
||||||
deniedRegexes: []*regexp.Regexp{},
|
deniedRegexes: []**regexp.Regexp{},
|
||||||
}
|
}
|
||||||
|
|
||||||
if !acls.IsServerBannedFromRoom("1.2.3.4", roomID) {
|
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")
|
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,
|
req *QueryAuthChainRequest,
|
||||||
res *QueryAuthChainResponse,
|
res *QueryAuthChainResponse,
|
||||||
) error
|
) error
|
||||||
|
|
||||||
|
// RoomsWithACLs returns all room IDs for rooms with ACLs
|
||||||
|
RoomsWithACLs(ctx context.Context) ([]string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserRoomPrivateKeyCreator interface {
|
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
|
// 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.
|
// 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 {
|
type QueryMembershipAPI interface {
|
||||||
|
@ -220,6 +228,7 @@ type ClientRoomserverAPI interface {
|
||||||
UserRoomPrivateKeyCreator
|
UserRoomPrivateKeyCreator
|
||||||
QueryRoomHierarchyAPI
|
QueryRoomHierarchyAPI
|
||||||
DefaultRoomVersionAPI
|
DefaultRoomVersionAPI
|
||||||
|
|
||||||
QueryMembershipForUser(ctx context.Context, req *QueryMembershipForUserRequest, res *QueryMembershipForUserResponse) error
|
QueryMembershipForUser(ctx context.Context, req *QueryMembershipForUserRequest, res *QueryMembershipForUserResponse) error
|
||||||
QueryMembershipsForRoom(ctx context.Context, req *QueryMembershipsForRoomRequest, res *QueryMembershipsForRoomResponse) error
|
QueryMembershipsForRoom(ctx context.Context, req *QueryMembershipsForRoomRequest, res *QueryMembershipsForRoomResponse) error
|
||||||
QueryRoomsForUser(ctx context.Context, userID spec.UserID, desiredMembership string) ([]spec.RoomID, 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)
|
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)
|
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 {
|
type UserRoomserverAPI interface {
|
||||||
|
|
|
@ -53,8 +53,6 @@ type InviteInput struct {
|
||||||
RoomID spec.RoomID
|
RoomID spec.RoomID
|
||||||
Inviter spec.UserID
|
Inviter spec.UserID
|
||||||
Invitee spec.UserID
|
Invitee spec.UserID
|
||||||
DisplayName string
|
|
||||||
AvatarURL string
|
|
||||||
Reason string
|
Reason string
|
||||||
IsDirect bool
|
IsDirect bool
|
||||||
KeyID gomatrixserverlib.KeyID
|
KeyID gomatrixserverlib.KeyID
|
||||||
|
|
|
@ -346,6 +346,28 @@ type QueryServerBannedFromRoomResponse struct {
|
||||||
Banned bool `json:"banned"`
|
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.
|
// 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) {
|
func (r *QueryBulkStateContentResponse) MarshalJSON() ([]byte, error) {
|
||||||
se := make(map[string]string)
|
se := make(map[string]string)
|
||||||
|
|
|
@ -189,7 +189,7 @@ func PopulatePublicRooms(ctx context.Context, roomIDs []string, rsAPI QueryBulkS
|
||||||
RoomID: roomID,
|
RoomID: roomID,
|
||||||
}
|
}
|
||||||
joinCount := 0
|
joinCount := 0
|
||||||
var joinRule, guestAccess string
|
var guestAccess string
|
||||||
for tuple, contentVal := range data {
|
for tuple, contentVal := range data {
|
||||||
if tuple.EventType == spec.MRoomMember && contentVal == "join" {
|
if tuple.EventType == spec.MRoomMember && contentVal == "join" {
|
||||||
joinCount++
|
joinCount++
|
||||||
|
@ -210,12 +210,12 @@ func PopulatePublicRooms(ctx context.Context, roomIDs []string, rsAPI QueryBulkS
|
||||||
pub.WorldReadable = contentVal == "world_readable"
|
pub.WorldReadable = contentVal == "world_readable"
|
||||||
// need both of these to determine whether guests can join
|
// need both of these to determine whether guests can join
|
||||||
case joinRuleTuple:
|
case joinRuleTuple:
|
||||||
joinRule = contentVal
|
pub.JoinRule = contentVal
|
||||||
case guestTuple:
|
case guestTuple:
|
||||||
guestAccess = contentVal
|
guestAccess = contentVal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if joinRule == spec.Public && guestAccess == "can_join" {
|
if pub.JoinRule == spec.Public && guestAccess == "can_join" {
|
||||||
pub.GuestCanJoin = true
|
pub.GuestCanJoin = true
|
||||||
}
|
}
|
||||||
pub.JoinedMembersCount = joinCount
|
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) {
|
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)
|
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
|
r *Inputer
|
||||||
roomID string
|
roomID string
|
||||||
subscription *nats.Subscription
|
subscription *nats.Subscription
|
||||||
|
sentryHub *sentry.Hub
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Inputer) startWorkerForRoom(roomID string) {
|
func (r *Inputer) startWorkerForRoom(roomID string) {
|
||||||
v, loaded := r.workers.LoadOrStore(roomID, &worker{
|
v, loaded := r.workers.LoadOrStore(roomID, &worker{
|
||||||
r: r,
|
r: r,
|
||||||
roomID: roomID,
|
roomID: roomID,
|
||||||
|
sentryHub: sentry.CurrentHub().Clone(),
|
||||||
})
|
})
|
||||||
w := v.(*worker)
|
w := v.(*worker)
|
||||||
w.Lock()
|
w.Lock()
|
||||||
|
@ -265,9 +267,9 @@ func (w *worker) _next() {
|
||||||
// Look up what the next event is that's waiting to be processed.
|
// Look up what the next event is that's waiting to be processed.
|
||||||
ctx, cancel := context.WithTimeout(w.r.ProcessContext.Context(), time.Minute)
|
ctx, cancel := context.WithTimeout(w.r.ProcessContext.Context(), time.Minute)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
if scope := sentry.CurrentHub().Scope(); scope != nil {
|
w.sentryHub.ConfigureScope(func(scope *sentry.Scope) {
|
||||||
scope.SetTag("room_id", w.roomID)
|
scope.SetTag("room_id", w.roomID)
|
||||||
}
|
})
|
||||||
msgs, err := w.subscription.Fetch(1, nats.Context(ctx))
|
msgs, err := w.subscription.Fetch(1, nats.Context(ctx))
|
||||||
switch err {
|
switch err {
|
||||||
case nil:
|
case nil:
|
||||||
|
@ -323,9 +325,9 @@ func (w *worker) _next() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if scope := sentry.CurrentHub().Scope(); scope != nil {
|
w.sentryHub.ConfigureScope(func(scope *sentry.Scope) {
|
||||||
scope.SetTag("event_id", inputRoomEvent.Event.EventID())
|
scope.SetTag("event_id", inputRoomEvent.Event.EventID())
|
||||||
}
|
})
|
||||||
|
|
||||||
// Process the room event. If something goes wrong then we'll tell
|
// Process the room event. If something goes wrong then we'll tell
|
||||||
// NATS to terminate the message. We'll store the error result as
|
// NATS to terminate the message. We'll store the error result as
|
||||||
|
@ -347,7 +349,7 @@ func (w *worker) _next() {
|
||||||
}).Warn("Roomserver rejected event")
|
}).Warn("Roomserver rejected event")
|
||||||
default:
|
default:
|
||||||
if !errors.Is(err, context.DeadlineExceeded) && !errors.Is(err, context.Canceled) {
|
if !errors.Is(err, context.DeadlineExceeded) && !errors.Is(err, context.Canceled) {
|
||||||
sentry.CaptureException(err)
|
w.sentryHub.CaptureException(err)
|
||||||
}
|
}
|
||||||
logrus.WithError(err).WithFields(logrus.Fields{
|
logrus.WithError(err).WithFields(logrus.Fields{
|
||||||
"room_id": w.roomID,
|
"room_id": w.roomID,
|
||||||
|
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/roomserver/storage/tables"
|
||||||
"github.com/tidwall/gjson"
|
"github.com/tidwall/gjson"
|
||||||
|
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
|
@ -509,7 +510,13 @@ func (r *Inputer) processRoomEvent(
|
||||||
logrus.WithError(err).Error("failed to get server ACLs")
|
logrus.WithError(err).Error("failed to get server ACLs")
|
||||||
}
|
}
|
||||||
if aclEvent != nil {
|
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)
|
}).Warnf("State reset detected (removing %d events)", removed)
|
||||||
sentry.WithScope(func(scope *sentry.Scope) {
|
sentry.WithScope(func(scope *sentry.Scope) {
|
||||||
scope.SetLevel("warning")
|
scope.SetLevel("warning")
|
||||||
|
scope.SetTag("room_id", u.event.RoomID().String())
|
||||||
scope.SetContext("State reset", map[string]interface{}{
|
scope.SetContext("State reset", map[string]interface{}{
|
||||||
"Event ID": u.event.EventID(),
|
"Event ID": u.event.EventID(),
|
||||||
"Old state NID": fmt.Sprintf("%d", u.oldStateNID),
|
"Old state NID": fmt.Sprintf("%d", u.oldStateNID),
|
||||||
|
|
|
@ -354,3 +354,7 @@ func (r *Admin) PerformAdminDownloadState(
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Admin) PerformAdminDeleteEventReport(ctx context.Context, reportID uint64) error {
|
||||||
|
return r.DB.AdminDeleteEventReport(ctx, reportID)
|
||||||
|
}
|
||||||
|
|
|
@ -506,8 +506,6 @@ func (c *Creator) PerformCreateRoom(ctx context.Context, userID spec.UserID, roo
|
||||||
RoomID: roomID,
|
RoomID: roomID,
|
||||||
Inviter: userID,
|
Inviter: userID,
|
||||||
Invitee: *inviteeUserID,
|
Invitee: *inviteeUserID,
|
||||||
DisplayName: createRequest.UserDisplayName,
|
|
||||||
AvatarURL: createRequest.UserAvatarURL,
|
|
||||||
Reason: "",
|
Reason: "",
|
||||||
IsDirect: createRequest.IsDirect,
|
IsDirect: createRequest.IsDirect,
|
||||||
KeyID: createRequest.KeyID,
|
KeyID: createRequest.KeyID,
|
||||||
|
|
|
@ -145,8 +145,6 @@ func (r *Inviter) PerformInvite(
|
||||||
|
|
||||||
content := gomatrixserverlib.MemberContent{
|
content := gomatrixserverlib.MemberContent{
|
||||||
Membership: spec.Invite,
|
Membership: spec.Invite,
|
||||||
DisplayName: req.InviteInput.DisplayName,
|
|
||||||
AvatarURL: req.InviteInput.AvatarURL,
|
|
||||||
Reason: req.InviteInput.Reason,
|
Reason: req.InviteInput.Reason,
|
||||||
IsDirect: req.InviteInput.IsDirect,
|
IsDirect: req.InviteInput.IsDirect,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1099,3 +1099,18 @@ func (r *Queryer) QueryUserIDForSender(ctx context.Context, roomID spec.RoomID,
|
||||||
|
|
||||||
return nil, nil
|
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
|
// 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.
|
// can be cached.
|
||||||
func (querier *Queryer) QueryNextRoomHierarchyPage(ctx context.Context, walker roomserver.RoomHierarchyWalker, limit int) ([]fclient.RoomHierarchyRoom, *roomserver.RoomHierarchyWalker, error) {
|
func (querier *Queryer) QueryNextRoomHierarchyPage(ctx context.Context, walker roomserver.RoomHierarchyWalker, limit int) (
|
||||||
if authorised, _ := authorised(ctx, querier, walker.Caller, walker.RootRoomID, nil); !authorised {
|
[]fclient.RoomHierarchyRoom,
|
||||||
return nil, nil, roomserver.ErrRoomUnknownOrNotAllowed{Err: fmt.Errorf("room is unknown/forbidden")}
|
[]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{}
|
discoveredRooms := []fclient.RoomHierarchyRoom{}
|
||||||
|
@ -50,6 +55,7 @@ func (querier *Queryer) QueryNextRoomHierarchyPage(ctx context.Context, walker r
|
||||||
unvisited := make([]roomserver.RoomHierarchyWalkerQueuedRoom, len(walker.Unvisited))
|
unvisited := make([]roomserver.RoomHierarchyWalkerQueuedRoom, len(walker.Unvisited))
|
||||||
copy(unvisited, walker.Unvisited)
|
copy(unvisited, walker.Unvisited)
|
||||||
processed := walker.Processed.Copy()
|
processed := walker.Processed.Copy()
|
||||||
|
inaccessible := []string{}
|
||||||
|
|
||||||
// Depth first -> stack data structure
|
// Depth first -> stack data structure
|
||||||
for len(unvisited) > 0 {
|
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.
|
// as these children may be rooms we do know about.
|
||||||
roomType = spec.MSpace
|
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
|
// Get all `m.space.child` state events for this room
|
||||||
events, err := childReferences(ctx, querier, walker.SuggestedOnly, queuedRoom.RoomID)
|
events, err := childReferences(ctx, querier, walker.SuggestedOnly, queuedRoom.RoomID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -128,11 +134,15 @@ func (querier *Queryer) QueryNextRoomHierarchyPage(ctx context.Context, walker r
|
||||||
PublicRoom: *pubRoom,
|
PublicRoom: *pubRoom,
|
||||||
RoomType: roomType,
|
RoomType: roomType,
|
||||||
ChildrenState: events,
|
ChildrenState: events,
|
||||||
|
AllowedRoomIDs: allowedRoomIDs,
|
||||||
})
|
})
|
||||||
// don't walk children if the user is not joined/invited to the space
|
// don't walk children if the user is not joined/invited to the space
|
||||||
if !isJoinedOrInvited {
|
if !isJoinedOrInvited {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
} else if !authorised {
|
||||||
|
inaccessible = append(inaccessible, queuedRoom.RoomID.String())
|
||||||
|
continue
|
||||||
} else {
|
} else {
|
||||||
// room exists but user is not authorised
|
// room exists but user is not authorised
|
||||||
continue
|
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,
|
// 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
|
// 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.
|
// insert the highest timestamp last in a stack.
|
||||||
|
extendQueueLoop:
|
||||||
for i := len(discoveredChildEvents) - 1; i >= 0; i-- {
|
for i := len(discoveredChildEvents) - 1; i >= 0; i-- {
|
||||||
spaceContent := struct {
|
spaceContent := struct {
|
||||||
Via []string `json:"via"`
|
Via []string `json:"via"`
|
||||||
|
@ -161,6 +172,12 @@ func (querier *Queryer) QueryNextRoomHierarchyPage(ctx context.Context, walker r
|
||||||
if err != nil {
|
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")
|
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 {
|
} else {
|
||||||
|
// Make sure not to queue inaccessible rooms
|
||||||
|
for _, inaccessibleRoomID := range inaccessible {
|
||||||
|
if inaccessibleRoomID == childRoomID.String() {
|
||||||
|
continue extendQueueLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
unvisited = append(unvisited, roomserver.RoomHierarchyWalkerQueuedRoom{
|
unvisited = append(unvisited, roomserver.RoomHierarchyWalkerQueuedRoom{
|
||||||
RoomID: *childRoomID,
|
RoomID: *childRoomID,
|
||||||
ParentRoomID: &queuedRoom.RoomID,
|
ParentRoomID: &queuedRoom.RoomID,
|
||||||
|
@ -173,7 +190,7 @@ func (querier *Queryer) QueryNextRoomHierarchyPage(ctx context.Context, walker r
|
||||||
|
|
||||||
if len(unvisited) == 0 {
|
if len(unvisited) == 0 {
|
||||||
// If no more rooms to walk, then don't return a walker for future pages
|
// 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 {
|
} else {
|
||||||
// If there are more rooms to walk, then return a new walker to resume walking from (for querying more pages)
|
// If there are more rooms to walk, then return a new walker to resume walking from (for querying more pages)
|
||||||
newWalker := roomserver.RoomHierarchyWalker{
|
newWalker := roomserver.RoomHierarchyWalker{
|
||||||
|
@ -185,22 +202,25 @@ func (querier *Queryer) QueryNextRoomHierarchyPage(ctx context.Context, walker r
|
||||||
Processed: processed,
|
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
|
// 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 {
|
if clientCaller := caller.Device(); clientCaller != nil {
|
||||||
return authorisedUser(ctx, querier, clientCaller, roomID, parentRoomID)
|
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
|
// 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
|
// Check history visibility / join rules first
|
||||||
hisVisTuple := gomatrixserverlib.StateKeyTuple{
|
hisVisTuple := gomatrixserverlib.StateKeyTuple{
|
||||||
EventType: spec.MRoomHistoryVisibility,
|
EventType: spec.MRoomHistoryVisibility,
|
||||||
|
@ -219,13 +239,13 @@ func authorisedServer(ctx context.Context, querier *Queryer, roomID spec.RoomID,
|
||||||
}, &queryRoomRes)
|
}, &queryRoomRes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.GetLogger(ctx).WithError(err).Error("failed to QueryCurrentState")
|
util.GetLogger(ctx).WithError(err).Error("failed to QueryCurrentState")
|
||||||
return false
|
return false, []string{}
|
||||||
}
|
}
|
||||||
hisVisEv := queryRoomRes.StateEvents[hisVisTuple]
|
hisVisEv := queryRoomRes.StateEvents[hisVisTuple]
|
||||||
if hisVisEv != nil {
|
if hisVisEv != nil {
|
||||||
hisVis, _ := hisVisEv.HistoryVisibility()
|
hisVis, _ := hisVisEv.HistoryVisibility()
|
||||||
if hisVis == "world_readable" {
|
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()
|
rule, ruleErr := joinRuleEv.JoinRule()
|
||||||
if ruleErr != nil {
|
if ruleErr != nil {
|
||||||
util.GetLogger(ctx).WithError(ruleErr).WithField("parent_room_id", roomID).Warn("failed to get join rule")
|
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 {
|
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)...)
|
allowJoinedToRoomIDs = append(allowJoinedToRoomIDs, restrictedJoinRuleAllowedRooms(ctx, joinRuleEv)...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if server is joined to any allowed room
|
// 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 {
|
for _, allowedRoomID := range allowJoinedToRoomIDs {
|
||||||
var queryRes fs.QueryJoinedHostServerNamesInRoomResponse
|
var queryRes fs.QueryJoinedHostServerNamesInRoomResponse
|
||||||
err = querier.FSAPI.QueryJoinedHostServerNamesInRoom(ctx, &fs.QueryJoinedHostServerNamesInRoomRequest{
|
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 {
|
for _, srv := range queryRes.ServerNames {
|
||||||
if srv == callerServerName {
|
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
|
// 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.
|
// 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.
|
// 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{
|
hisVisTuple := gomatrixserverlib.StateKeyTuple{
|
||||||
EventType: spec.MRoomHistoryVisibility,
|
EventType: spec.MRoomHistoryVisibility,
|
||||||
StateKey: "",
|
StateKey: "",
|
||||||
|
@ -295,20 +319,20 @@ func authorisedUser(ctx context.Context, querier *Queryer, clientCaller *userapi
|
||||||
}, &queryRes)
|
}, &queryRes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.GetLogger(ctx).WithError(err).Error("failed to QueryCurrentState")
|
util.GetLogger(ctx).WithError(err).Error("failed to QueryCurrentState")
|
||||||
return false, false
|
return false, false, resultAllowedRoomIDs
|
||||||
}
|
}
|
||||||
memberEv := queryRes.StateEvents[roomMemberTuple]
|
memberEv := queryRes.StateEvents[roomMemberTuple]
|
||||||
if memberEv != nil {
|
if memberEv != nil {
|
||||||
membership, _ := memberEv.Membership()
|
membership, _ := memberEv.Membership()
|
||||||
if membership == spec.Join || membership == spec.Invite {
|
if membership == spec.Join || membership == spec.Invite {
|
||||||
return true, true
|
return true, true, resultAllowedRoomIDs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hisVisEv := queryRes.StateEvents[hisVisTuple]
|
hisVisEv := queryRes.StateEvents[hisVisTuple]
|
||||||
if hisVisEv != nil {
|
if hisVisEv != nil {
|
||||||
hisVis, _ := hisVisEv.HistoryVisibility()
|
hisVis, _ := hisVisEv.HistoryVisibility()
|
||||||
if hisVis == "world_readable" {
|
if hisVis == "world_readable" {
|
||||||
return true, false
|
return true, false, resultAllowedRoomIDs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
joinRuleEv := queryRes.StateEvents[joinRuleTuple]
|
joinRuleEv := queryRes.StateEvents[joinRuleTuple]
|
||||||
|
@ -323,6 +347,7 @@ func authorisedUser(ctx context.Context, querier *Queryer, clientCaller *userapi
|
||||||
allowedRoomIDs := restrictedJoinRuleAllowedRooms(ctx, joinRuleEv)
|
allowedRoomIDs := restrictedJoinRuleAllowedRooms(ctx, joinRuleEv)
|
||||||
// check parent is in the allowed set
|
// check parent is in the allowed set
|
||||||
for _, a := range allowedRoomIDs {
|
for _, a := range allowedRoomIDs {
|
||||||
|
resultAllowedRoomIDs = append(resultAllowedRoomIDs, a.String())
|
||||||
if *parentRoomID == a {
|
if *parentRoomID == a {
|
||||||
allowed = true
|
allowed = true
|
||||||
break
|
break
|
||||||
|
@ -345,13 +370,13 @@ func authorisedUser(ctx context.Context, querier *Queryer, clientCaller *userapi
|
||||||
if memberEv != nil {
|
if memberEv != nil {
|
||||||
membership, _ := memberEv.Membership()
|
membership, _ := memberEv.Membership()
|
||||||
if membership == spec.Join {
|
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
|
// helper function to fetch a state event
|
||||||
|
|
|
@ -17,6 +17,7 @@ package producers
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/roomserver/storage/tables"
|
||||||
"github.com/nats-io/nats.go"
|
"github.com/nats-io/nats.go"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/tidwall/gjson"
|
"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("") {
|
if eventType == acls.MRoomServerACL && update.NewRoomEvent.Event.StateKeyEquals("") {
|
||||||
ev := update.NewRoomEvent.Event.PDU
|
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)
|
logger.Tracef("Producing to topic '%s'", r.Topic)
|
||||||
|
|
|
@ -1284,3 +1284,38 @@ func TestRoomConsumerRecreation(t *testing.T) {
|
||||||
wantAckWait := input.MaximumMissingProcessingTime + (time.Second * 10)
|
wantAckWait := input.MaximumMissingProcessingTime + (time.Second * 10)
|
||||||
assert.Equal(t, wantAckWait, info.Config.AckWait)
|
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 {
|
type Database interface {
|
||||||
UserRoomKeys
|
UserRoomKeys
|
||||||
|
ReportedEvents
|
||||||
// Do we support processing input events for more than one room at a time?
|
// Do we support processing input events for more than one room at a time?
|
||||||
SupportsConcurrentRoomInputs() bool
|
SupportsConcurrentRoomInputs() bool
|
||||||
AssignRoomNID(ctx context.Context, roomID spec.RoomID, roomVersion gomatrixserverlib.RoomVersion) (roomNID types.RoomNID, err error)
|
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)
|
GetServerInRoom(ctx context.Context, roomNID types.RoomNID, serverName spec.ServerName) (bool, error)
|
||||||
// GetKnownUsers searches all users that userID knows about.
|
// GetKnownUsers searches all users that userID knows about.
|
||||||
GetKnownUsers(ctx context.Context, userID, searchString string, limit int) ([]string, error)
|
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 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
|
ForgetRoom(ctx context.Context, userID, roomID string, forget bool) error
|
||||||
|
|
||||||
|
@ -193,6 +192,12 @@ type Database interface {
|
||||||
MaybeRedactEvent(
|
MaybeRedactEvent(
|
||||||
ctx context.Context, roomInfo *types.RoomInfo, eventNID types.EventNID, event gomatrixserverlib.PDU, plResolver state.PowerLevelResolver, querier api.QuerySenderIDAPI,
|
ctx context.Context, roomInfo *types.RoomInfo, eventNID types.EventNID, event gomatrixserverlib.PDU, plResolver state.PowerLevelResolver, querier api.QuerySenderIDAPI,
|
||||||
) (gomatrixserverlib.PDU, gomatrixserverlib.PDU, error)
|
) (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 {
|
type UserRoomKeys interface {
|
||||||
|
@ -256,3 +261,11 @@ type EventDatabase interface {
|
||||||
) (gomatrixserverlib.PDU, gomatrixserverlib.PDU, error)
|
) (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)
|
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 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);
|
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 = "" +
|
const insertEventSQL = "" +
|
||||||
|
@ -147,6 +151,8 @@ const selectRoomNIDsForEventNIDsSQL = "" +
|
||||||
const selectEventRejectedSQL = "" +
|
const selectEventRejectedSQL = "" +
|
||||||
"SELECT is_rejected FROM roomserver_events WHERE room_nid = $1 AND event_id = $2"
|
"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 {
|
type eventStatements struct {
|
||||||
insertEventStmt *sql.Stmt
|
insertEventStmt *sql.Stmt
|
||||||
selectEventStmt *sql.Stmt
|
selectEventStmt *sql.Stmt
|
||||||
|
@ -166,6 +172,7 @@ type eventStatements struct {
|
||||||
selectMaxEventDepthStmt *sql.Stmt
|
selectMaxEventDepthStmt *sql.Stmt
|
||||||
selectRoomNIDsForEventNIDsStmt *sql.Stmt
|
selectRoomNIDsForEventNIDsStmt *sql.Stmt
|
||||||
selectEventRejectedStmt *sql.Stmt
|
selectEventRejectedStmt *sql.Stmt
|
||||||
|
selectRoomsWithEventTypeNIDStmt *sql.Stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateEventsTable(db *sql.DB) error {
|
func CreateEventsTable(db *sql.DB) error {
|
||||||
|
@ -206,6 +213,7 @@ func PrepareEventsTable(db *sql.DB) (tables.Events, error) {
|
||||||
{&s.selectMaxEventDepthStmt, selectMaxEventDepthSQL},
|
{&s.selectMaxEventDepthStmt, selectMaxEventDepthSQL},
|
||||||
{&s.selectRoomNIDsForEventNIDsStmt, selectRoomNIDsForEventNIDsSQL},
|
{&s.selectRoomNIDsForEventNIDsStmt, selectRoomNIDsForEventNIDsSQL},
|
||||||
{&s.selectEventRejectedStmt, selectEventRejectedSQL},
|
{&s.selectEventRejectedStmt, selectEventRejectedSQL},
|
||||||
|
{&s.selectRoomsWithEventTypeNIDStmt, selectRoomsWithEventTypeNIDSQL},
|
||||||
}.Prepare(db)
|
}.Prepare(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -582,3 +590,25 @@ func (s *eventStatements) SelectEventRejected(
|
||||||
err = stmt.QueryRowContext(ctx, roomNID, eventID).Scan(&rejected)
|
err = stmt.QueryRowContext(ctx, roomNID, eventID).Scan(&rejected)
|
||||||
return
|
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 = "" +
|
const selectRoomInfoSQL = "" +
|
||||||
"SELECT room_version, room_nid, state_snapshot_nid, latest_event_nids FROM roomserver_rooms WHERE room_id = $1"
|
"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 = "" +
|
const bulkSelectRoomIDsSQL = "" +
|
||||||
"SELECT room_id FROM roomserver_rooms WHERE room_nid = ANY($1)"
|
"SELECT room_id FROM roomserver_rooms WHERE room_nid = ANY($1)"
|
||||||
|
|
||||||
|
@ -94,7 +91,6 @@ type roomStatements struct {
|
||||||
updateLatestEventNIDsStmt *sql.Stmt
|
updateLatestEventNIDsStmt *sql.Stmt
|
||||||
selectRoomVersionsForRoomNIDsStmt *sql.Stmt
|
selectRoomVersionsForRoomNIDsStmt *sql.Stmt
|
||||||
selectRoomInfoStmt *sql.Stmt
|
selectRoomInfoStmt *sql.Stmt
|
||||||
selectRoomIDsStmt *sql.Stmt
|
|
||||||
bulkSelectRoomIDsStmt *sql.Stmt
|
bulkSelectRoomIDsStmt *sql.Stmt
|
||||||
bulkSelectRoomNIDsStmt *sql.Stmt
|
bulkSelectRoomNIDsStmt *sql.Stmt
|
||||||
}
|
}
|
||||||
|
@ -116,29 +112,11 @@ func PrepareRoomsTable(db *sql.DB) (tables.Rooms, error) {
|
||||||
{&s.updateLatestEventNIDsStmt, updateLatestEventNIDsSQL},
|
{&s.updateLatestEventNIDsStmt, updateLatestEventNIDsSQL},
|
||||||
{&s.selectRoomVersionsForRoomNIDsStmt, selectRoomVersionsForRoomNIDsSQL},
|
{&s.selectRoomVersionsForRoomNIDsStmt, selectRoomVersionsForRoomNIDsSQL},
|
||||||
{&s.selectRoomInfoStmt, selectRoomInfoSQL},
|
{&s.selectRoomInfoStmt, selectRoomInfoSQL},
|
||||||
{&s.selectRoomIDsStmt, selectRoomIDsSQL},
|
|
||||||
{&s.bulkSelectRoomIDsStmt, bulkSelectRoomIDsSQL},
|
{&s.bulkSelectRoomIDsStmt, bulkSelectRoomIDsSQL},
|
||||||
{&s.bulkSelectRoomNIDsStmt, bulkSelectRoomNIDsSQL},
|
{&s.bulkSelectRoomNIDsStmt, bulkSelectRoomNIDsSQL},
|
||||||
}.Prepare(db)
|
}.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(
|
func (s *roomStatements) InsertRoomNID(
|
||||||
ctx context.Context, txn *sql.Tx,
|
ctx context.Context, txn *sql.Tx,
|
||||||
roomID string, roomVersion gomatrixserverlib.RoomVersion,
|
roomID string, roomVersion gomatrixserverlib.RoomVersion,
|
||||||
|
|
|
@ -134,6 +134,9 @@ func (d *Database) create(db *sql.DB) error {
|
||||||
if err := CreateUserRoomKeysTable(db); err != nil {
|
if err := CreateUserRoomKeysTable(db); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := CreateReportedEventsTable(db); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -199,6 +202,10 @@ func (d *Database) prepare(db *sql.DB, writer sqlutil.Writer, cache caching.Room
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
reportedEvents, err := PrepareReportedEventsTable(db)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
d.Database = shared.Database{
|
d.Database = shared.Database{
|
||||||
DB: db,
|
DB: db,
|
||||||
|
@ -212,6 +219,7 @@ func (d *Database) prepare(db *sql.DB, writer sqlutil.Writer, cache caching.Room
|
||||||
EventStateKeysTable: eventStateKeys,
|
EventStateKeysTable: eventStateKeys,
|
||||||
PrevEventsTable: prevEvents,
|
PrevEventsTable: prevEvents,
|
||||||
RedactionsTable: redactions,
|
RedactionsTable: redactions,
|
||||||
|
ReportedEventsTable: reportedEvents,
|
||||||
},
|
},
|
||||||
Cache: cache,
|
Cache: cache,
|
||||||
Writer: writer,
|
Writer: writer,
|
||||||
|
|
|
@ -162,6 +162,9 @@ func (s *userRoomKeysStatements) SelectAllPublicKeysForUser(ctx context.Context,
|
||||||
if errors.Is(err, sql.ErrNoRows) {
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
defer internal.CloseAndLogIfError(ctx, rows, "SelectAllPublicKeysForUser: failed to close rows")
|
defer internal.CloseAndLogIfError(ctx, rows, "SelectAllPublicKeysForUser: failed to close rows")
|
||||||
|
|
||||||
resultMap := make(map[types.RoomNID]ed25519.PublicKey)
|
resultMap := make(map[types.RoomNID]ed25519.PublicKey)
|
||||||
|
|
|
@ -61,6 +61,7 @@ type EventDatabase struct {
|
||||||
EventStateKeysTable tables.EventStateKeys
|
EventStateKeysTable tables.EventStateKeys
|
||||||
PrevEventsTable tables.PreviousEvents
|
PrevEventsTable tables.PreviousEvents
|
||||||
RedactionsTable tables.Redactions
|
RedactionsTable tables.Redactions
|
||||||
|
ReportedEventsTable tables.ReportedEvents
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Database) SupportsConcurrentRoomInputs() bool {
|
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)
|
return d.MembershipTable.SelectKnownUsers(ctx, nil, stateKeyNID, searchString, limit)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetKnownRooms returns a list of all rooms we know about.
|
func (d *Database) RoomsWithACLs(ctx context.Context) ([]string, error) {
|
||||||
func (d *Database) GetKnownRooms(ctx context.Context) ([]string, error) {
|
|
||||||
return d.RoomsTable.SelectRoomIDsWithEvents(ctx, nil)
|
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
|
// ForgetRoom sets a users room to forgotten
|
||||||
|
@ -1867,6 +1883,252 @@ func (d *Database) SelectUserIDsForPublicKeys(ctx context.Context, publicKeys ma
|
||||||
return result, err
|
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
|
// 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!
|
// it should live in this package!
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,14 @@ const eventsSchema = `
|
||||||
auth_event_nids TEXT NOT NULL DEFAULT '[]',
|
auth_event_nids TEXT NOT NULL DEFAULT '[]',
|
||||||
is_rejected BOOLEAN NOT NULL DEFAULT FALSE
|
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 = `
|
const insertEventSQL = `
|
||||||
|
@ -120,6 +128,8 @@ const selectRoomNIDsForEventNIDsSQL = "" +
|
||||||
const selectEventRejectedSQL = "" +
|
const selectEventRejectedSQL = "" +
|
||||||
"SELECT is_rejected FROM roomserver_events WHERE room_nid = $1 AND event_id = $2"
|
"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 {
|
type eventStatements struct {
|
||||||
db *sql.DB
|
db *sql.DB
|
||||||
insertEventStmt *sql.Stmt
|
insertEventStmt *sql.Stmt
|
||||||
|
@ -135,6 +145,7 @@ type eventStatements struct {
|
||||||
bulkSelectStateAtEventAndReferenceStmt *sql.Stmt
|
bulkSelectStateAtEventAndReferenceStmt *sql.Stmt
|
||||||
bulkSelectEventIDStmt *sql.Stmt
|
bulkSelectEventIDStmt *sql.Stmt
|
||||||
selectEventRejectedStmt *sql.Stmt
|
selectEventRejectedStmt *sql.Stmt
|
||||||
|
selectRoomsWithEventTypeNIDStmt *sql.Stmt
|
||||||
//bulkSelectEventNIDStmt *sql.Stmt
|
//bulkSelectEventNIDStmt *sql.Stmt
|
||||||
//bulkSelectUnsentEventNIDStmt *sql.Stmt
|
//bulkSelectUnsentEventNIDStmt *sql.Stmt
|
||||||
//selectRoomNIDsForEventNIDsStmt *sql.Stmt
|
//selectRoomNIDsForEventNIDsStmt *sql.Stmt
|
||||||
|
@ -192,6 +203,7 @@ func PrepareEventsTable(db *sql.DB) (tables.Events, error) {
|
||||||
//{&s.bulkSelectUnsentEventNIDStmt, bulkSelectUnsentEventNIDSQL},
|
//{&s.bulkSelectUnsentEventNIDStmt, bulkSelectUnsentEventNIDSQL},
|
||||||
//{&s.selectRoomNIDForEventNIDStmt, selectRoomNIDForEventNIDSQL},
|
//{&s.selectRoomNIDForEventNIDStmt, selectRoomNIDForEventNIDSQL},
|
||||||
{&s.selectEventRejectedStmt, selectEventRejectedSQL},
|
{&s.selectEventRejectedStmt, selectEventRejectedSQL},
|
||||||
|
{&s.selectRoomsWithEventTypeNIDStmt, selectRoomsWithEventTypeNIDSQL},
|
||||||
}.Prepare(db)
|
}.Prepare(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -682,3 +694,25 @@ func (s *eventStatements) SelectEventRejected(
|
||||||
err = stmt.QueryRowContext(ctx, roomNID, eventID).Scan(&rejected)
|
err = stmt.QueryRowContext(ctx, roomNID, eventID).Scan(&rejected)
|
||||||
return
|
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 = "" +
|
const selectRoomInfoSQL = "" +
|
||||||
"SELECT room_version, room_nid, state_snapshot_nid, latest_event_nids FROM roomserver_rooms WHERE room_id = $1"
|
"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 = "" +
|
const bulkSelectRoomIDsSQL = "" +
|
||||||
"SELECT room_id FROM roomserver_rooms WHERE room_nid IN ($1)"
|
"SELECT room_id FROM roomserver_rooms WHERE room_nid IN ($1)"
|
||||||
|
|
||||||
|
@ -87,7 +84,6 @@ type roomStatements struct {
|
||||||
updateLatestEventNIDsStmt *sql.Stmt
|
updateLatestEventNIDsStmt *sql.Stmt
|
||||||
//selectRoomVersionForRoomNIDStmt *sql.Stmt
|
//selectRoomVersionForRoomNIDStmt *sql.Stmt
|
||||||
selectRoomInfoStmt *sql.Stmt
|
selectRoomInfoStmt *sql.Stmt
|
||||||
selectRoomIDsStmt *sql.Stmt
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateRoomsTable(db *sql.DB) error {
|
func CreateRoomsTable(db *sql.DB) error {
|
||||||
|
@ -108,29 +104,10 @@ func PrepareRoomsTable(db *sql.DB) (tables.Rooms, error) {
|
||||||
{&s.updateLatestEventNIDsStmt, updateLatestEventNIDsSQL},
|
{&s.updateLatestEventNIDsStmt, updateLatestEventNIDsSQL},
|
||||||
//{&s.selectRoomVersionForRoomNIDsStmt, selectRoomVersionForRoomNIDsSQL},
|
//{&s.selectRoomVersionForRoomNIDsStmt, selectRoomVersionForRoomNIDsSQL},
|
||||||
{&s.selectRoomInfoStmt, selectRoomInfoSQL},
|
{&s.selectRoomInfoStmt, selectRoomInfoSQL},
|
||||||
{&s.selectRoomIDsStmt, selectRoomIDsSQL},
|
|
||||||
{&s.selectRoomNIDForUpdateStmt, selectRoomNIDForUpdateSQL},
|
{&s.selectRoomNIDForUpdateStmt, selectRoomNIDForUpdateSQL},
|
||||||
}.Prepare(db)
|
}.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) {
|
func (s *roomStatements) SelectRoomInfo(ctx context.Context, txn *sql.Tx, roomID string) (*types.RoomInfo, error) {
|
||||||
var info types.RoomInfo
|
var info types.RoomInfo
|
||||||
var latestNIDsJSON string
|
var latestNIDsJSON string
|
||||||
|
|
|
@ -141,7 +141,9 @@ func (d *Database) create(db *sql.DB) error {
|
||||||
if err := CreateUserRoomKeysTable(db); err != nil {
|
if err := CreateUserRoomKeysTable(db); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := CreateReportedEventsTable(db); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,6 +208,10 @@ func (d *Database) prepare(db *sql.DB, writer sqlutil.Writer, cache caching.Room
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
reportedEvents, err := PrepareReportedEventsTable(db)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
d.Database = shared.Database{
|
d.Database = shared.Database{
|
||||||
DB: db,
|
DB: db,
|
||||||
|
@ -219,6 +225,7 @@ func (d *Database) prepare(db *sql.DB, writer sqlutil.Writer, cache caching.Room
|
||||||
EventJSONTable: eventJSON,
|
EventJSONTable: eventJSON,
|
||||||
PrevEventsTable: prevEvents,
|
PrevEventsTable: prevEvents,
|
||||||
RedactionsTable: redactions,
|
RedactionsTable: redactions,
|
||||||
|
ReportedEventsTable: reportedEvents,
|
||||||
},
|
},
|
||||||
Cache: cache,
|
Cache: cache,
|
||||||
Writer: writer,
|
Writer: writer,
|
||||||
|
|
|
@ -177,6 +177,9 @@ func (s *userRoomKeysStatements) SelectAllPublicKeysForUser(ctx context.Context,
|
||||||
if errors.Is(err, sql.ErrNoRows) {
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
defer internal.CloseAndLogIfError(ctx, rows, "SelectAllPublicKeysForUser: failed to close rows")
|
defer internal.CloseAndLogIfError(ctx, rows, "SelectAllPublicKeysForUser: failed to close rows")
|
||||||
|
|
||||||
resultMap := make(map[types.RoomNID]ed25519.PublicKey)
|
resultMap := make(map[types.RoomNID]ed25519.PublicKey)
|
||||||
|
|
|
@ -2,6 +2,7 @@ package tables_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/matrix-org/dendrite/internal/sqlutil"
|
"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)
|
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"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/roomserver/api"
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
"github.com/matrix-org/gomatrixserverlib/spec"
|
"github.com/matrix-org/gomatrixserverlib/spec"
|
||||||
"github.com/tidwall/gjson"
|
"github.com/tidwall/gjson"
|
||||||
|
@ -69,6 +70,8 @@ type Events interface {
|
||||||
SelectMaxEventDepth(ctx context.Context, txn *sql.Tx, eventNIDs []types.EventNID) (int64, error)
|
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)
|
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)
|
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 {
|
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
|
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)
|
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)
|
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)
|
BulkSelectRoomIDs(ctx context.Context, txn *sql.Tx, roomNIDs []types.RoomNID) ([]string, error)
|
||||||
BulkSelectRoomNIDs(ctx context.Context, txn *sql.Tx, roomIDs []string) ([]types.RoomNID, 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)
|
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
|
type MembershipState int64
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -235,6 +264,10 @@ func ExtractContentValue(ev *types.HeaderedEvent) string {
|
||||||
key = "topic"
|
key = "topic"
|
||||||
case "m.room.guest_access":
|
case "m.room.guest_access":
|
||||||
key = "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)
|
result := gjson.GetBytes(content, key)
|
||||||
if !result.Exists() {
|
if !result.Exists() {
|
||||||
|
|
|
@ -74,11 +74,6 @@ func TestRoomsTable(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Nil(t, roomInfo)
|
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})
|
roomVersions, err := tab.SelectRoomVersionsForRoomNIDs(ctx, nil, []types.RoomNID{wantRoomNID, 1337})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, roomVersions[wantRoomNID], room.Version)
|
assert.Equal(t, roomVersions[wantRoomNID], room.Version)
|
||||||
|
@ -86,7 +81,7 @@ func TestRoomsTable(t *testing.T) {
|
||||||
_, ok := roomVersions[1337]
|
_, ok := roomVersions[1337]
|
||||||
assert.False(t, ok)
|
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.NoError(t, err)
|
||||||
assert.Equal(t, []string{room.ID}, roomIDs)
|
assert.Equal(t, []string{room.ID}, roomIDs)
|
||||||
|
|
||||||
|
|
|
@ -28,13 +28,13 @@ import (
|
||||||
_ "net/http/pprof"
|
_ "net/http/pprof"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"sync/atomic"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
sentryhttp "github.com/getsentry/sentry-go/http"
|
sentryhttp "github.com/getsentry/sentry-go/http"
|
||||||
"github.com/matrix-org/gomatrixserverlib/fclient"
|
"github.com/matrix-org/gomatrixserverlib/fclient"
|
||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
"go.uber.org/atomic"
|
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/kardianos/minwinsvc"
|
"github.com/kardianos/minwinsvc"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
//go:build !linux && !darwin && !netbsd && !freebsd && !openbsd && !solaris && !dragonfly && !aix
|
//go:build !unix
|
||||||
// +build !linux,!darwin,!netbsd,!freebsd,!openbsd,!solaris,!dragonfly,!aix
|
// +build !unix
|
||||||
|
|
||||||
package base
|
package base
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
//go:build linux || darwin || netbsd || freebsd || openbsd || solaris || dragonfly || aix
|
//go:build unix
|
||||||
// +build linux darwin netbsd freebsd openbsd solaris dragonfly aix
|
// +build unix
|
||||||
|
|
||||||
package base
|
package base
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,9 @@ type AppServiceAPI struct {
|
||||||
// on appservice endpoints. This is not recommended in production!
|
// on appservice endpoints. This is not recommended in production!
|
||||||
DisableTLSValidation bool `yaml:"disable_tls_validation"`
|
DisableTLSValidation bool `yaml:"disable_tls_validation"`
|
||||||
|
|
||||||
|
LegacyAuth bool `yaml:"legacy_auth"`
|
||||||
|
LegacyPaths bool `yaml:"legacy_paths"`
|
||||||
|
|
||||||
ConfigFiles []string `yaml:"config_files"`
|
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.
|
// The default value is 16 if not specified, which is circa 18 hours.
|
||||||
FederationMaxRetries uint32 `yaml:"send_max_retries"`
|
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
|
// P2P Feature: How many consecutive failures that we should tolerate when
|
||||||
// sending federation requests to a specific server until we should assume they
|
// 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
|
// 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()
|
defer natsLock.Unlock()
|
||||||
// check if we need an in-process NATS Server
|
// check if we need an in-process NATS Server
|
||||||
if len(cfg.Addresses) != 0 {
|
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 {
|
if s.Server == nil {
|
||||||
var err error
|
var err error
|
||||||
|
|
|
@ -601,9 +601,11 @@ func (s *OutputRoomEventConsumer) writeFTS(ev *rstypes.HeaderedEvent, pduPositio
|
||||||
}
|
}
|
||||||
e.SetContentType(ev.Type())
|
e.SetContentType(ev.Type())
|
||||||
|
|
||||||
|
var relatesTo gjson.Result
|
||||||
switch ev.Type() {
|
switch ev.Type() {
|
||||||
case "m.room.message":
|
case "m.room.message":
|
||||||
e.Content = gjson.GetBytes(ev.Content(), "body").String()
|
e.Content = gjson.GetBytes(ev.Content(), "body").String()
|
||||||
|
relatesTo = gjson.GetBytes(ev.Content(), "m\\.relates_to")
|
||||||
case spec.MRoomName:
|
case spec.MRoomName:
|
||||||
e.Content = gjson.GetBytes(ev.Content(), "name").String()
|
e.Content = gjson.GetBytes(ev.Content(), "name").String()
|
||||||
case spec.MRoomTopic:
|
case spec.MRoomTopic:
|
||||||
|
@ -622,6 +624,22 @@ func (s *OutputRoomEventConsumer) writeFTS(ev *rstypes.HeaderedEvent, pduPositio
|
||||||
if err := s.fts.Index(e); err != nil {
|
if err := s.fts.Index(e); err != nil {
|
||||||
return err
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,6 +110,7 @@ func Context(
|
||||||
}
|
}
|
||||||
|
|
||||||
stateFilter := synctypes.StateFilter{
|
stateFilter := synctypes.StateFilter{
|
||||||
|
Limit: filter.Limit,
|
||||||
NotSenders: filter.NotSenders,
|
NotSenders: filter.NotSenders,
|
||||||
NotTypes: filter.NotTypes,
|
NotTypes: filter.NotTypes,
|
||||||
Senders: filter.Senders,
|
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)
|
eventsBefore, err := snapshot.SelectContextBeforeEvent(ctx, id, roomID, filter)
|
||||||
if err != nil && err != sql.ErrNoRows {
|
if err != nil && err != sql.ErrNoRows {
|
||||||
logrus.WithError(err).Error("unable to fetch before events")
|
logrus.WithError(err).Error("unable to fetch before events")
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
package routing
|
package routing
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"math"
|
"math"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
@ -33,31 +32,13 @@ type getMembershipResponse struct {
|
||||||
Chunk []synctypes.ClientEvent `json:"chunk"`
|
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
|
// GetMemberships implements
|
||||||
//
|
//
|
||||||
// GET /rooms/{roomId}/members
|
// GET /rooms/{roomId}/members
|
||||||
// GET /rooms/{roomId}/joined_members
|
|
||||||
func GetMemberships(
|
func GetMemberships(
|
||||||
req *http.Request, device *userapi.Device, roomID string,
|
req *http.Request, device *userapi.Device, roomID string,
|
||||||
syncDB storage.Database, rsAPI api.SyncRoomserverAPI,
|
syncDB storage.Database, rsAPI api.SyncRoomserverAPI,
|
||||||
joinedOnly bool, membership, notMembership *string, at string,
|
membership, notMembership *string, at string,
|
||||||
) util.JSONResponse {
|
) util.JSONResponse {
|
||||||
userID, err := spec.NewUserID(device.UserID, true)
|
userID, err := spec.NewUserID(device.UserID, true)
|
||||||
if err != nil {
|
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())
|
db, err := syncDB.NewDatabaseSnapshot(req.Context())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.JSONResponse{
|
return util.JSONResponse{
|
||||||
|
@ -139,40 +113,6 @@ func GetMemberships(
|
||||||
|
|
||||||
result := qryRes.Events
|
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{
|
return util.JSONResponse{
|
||||||
Code: http.StatusOK,
|
Code: http.StatusOK,
|
||||||
JSON: getMembershipResponse{synctypes.ToClientEvents(gomatrixserverlib.ToPDUs(result), synctypes.FormatAll, func(roomID spec.RoomID, senderID spec.SenderID) (*spec.UserID, error) {
|
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")
|
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()),
|
}, httputil.WithAllowGuests()),
|
||||||
).Methods(http.MethodGet, http.MethodOptions)
|
).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")
|
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
|
newPos = from
|
||||||
for _, delta := range stateDeltas {
|
for _, delta := range stateDeltas {
|
||||||
newRange := r
|
newRange := r
|
||||||
|
@ -218,7 +224,7 @@ func (p *PDUStreamProvider) IncrementalSync(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var pos types.StreamPosition
|
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")
|
req.Log.WithError(err).Error("d.addRoomDeltaToResponse failed")
|
||||||
if err == context.DeadlineExceeded || err == context.Canceled || err == sql.ErrTxDone {
|
if err == context.DeadlineExceeded || err == context.Canceled || err == sql.ErrTxDone {
|
||||||
return newPos
|
return newPos
|
||||||
|
@ -240,6 +246,66 @@ func (p *PDUStreamProvider) IncrementalSync(
|
||||||
return newPos
|
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
|
// Limit the recent events to X when going backwards
|
||||||
const recentEventBackwardsLimit = 100
|
const recentEventBackwardsLimit = 100
|
||||||
|
|
||||||
|
@ -253,29 +319,9 @@ func (p *PDUStreamProvider) addRoomDeltaToResponse(
|
||||||
eventFilter *synctypes.RoomEventFilter,
|
eventFilter *synctypes.RoomEventFilter,
|
||||||
stateFilter *synctypes.StateFilter,
|
stateFilter *synctypes.StateFilter,
|
||||||
req *types.SyncRequest,
|
req *types.SyncRequest,
|
||||||
|
dbEvents map[string]types.RecentEvents,
|
||||||
) (types.StreamPosition, error) {
|
) (types.StreamPosition, error) {
|
||||||
var err 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
|
recentStreamEvents := dbEvents[delta.RoomID].Events
|
||||||
limited := dbEvents[delta.RoomID].Limited
|
limited := dbEvents[delta.RoomID].Limited
|
||||||
|
|
||||||
|
@ -337,9 +383,9 @@ func (p *PDUStreamProvider) addRoomDeltaToResponse(
|
||||||
logrus.WithError(err).Error("unable to apply history visibility filter")
|
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
|
// 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
|
limited = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue