mirror of
https://forgejo.ellis.link/continuwuation/continuwuity.git
synced 2026-05-26 20:49:55 +00:00
Compare commits
119 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1c08c0cdac | |||
| 374a5d6fa4 | |||
| 5fff540775 | |||
| f030ba590d | |||
| 73c4042331 | |||
| 461da03ca5 | |||
| 671d0619ca | |||
| e8ddc9bd18 | |||
| 4217479455 | |||
| b01965b1ee | |||
| 90deb7ce1a | |||
| e02fa39688 | |||
| 84c3264c07 | |||
| 7a22442295 | |||
| 1103cf7290 | |||
| bf086158bd | |||
| 2ecbd75d64 | |||
| a682e9dbb8 | |||
| 46c193e74b | |||
| 93719018a8 | |||
| 70df8364b3 | |||
| bae8192fb3 | |||
| add5c7052c | |||
| 01200d9b54 | |||
| 0ba4a265be | |||
| 08fbcbba69 | |||
| b526935d45 | |||
| a737d845a4 | |||
| e508b1197f | |||
| d6fd30393c | |||
| 6e16a6ef8f | |||
| 0870c8d647 | |||
| d0f00e6f5c | |||
| 5d44653e3a | |||
| 44e60d0ea6 | |||
| d7514178ab | |||
| 1d45e0b68c | |||
| 3c44dccd65 | |||
| b57be072c7 | |||
| ea5dc8e09d | |||
| b9d60c64e5 | |||
| 94ae824149 | |||
| 640714922b | |||
| 2b268fdaf3 | |||
| e8d823a653 | |||
| 0ba77674c7 | |||
| 2ccbd7d60b | |||
| 60960c6e09 | |||
| ce40304667 | |||
| dcbc4b54c5 | |||
| fce024b30b | |||
| 3e4e696761 | |||
| f605913ea9 | |||
| 44302ce732 | |||
| bfb0a2b76a | |||
| fcd5669aa1 | |||
| 9b8b37f162 | |||
| 7a46563f23 | |||
| 1bf6537319 | |||
| 4ed04b343a | |||
| a4ad72e11d | |||
| 1f57508879 | |||
| a325dfa56a | |||
| b5d2ef9a4a | |||
| e200a7d991 | |||
| 034762c619 | |||
| e31d261e66 | |||
| c5db43ba9a | |||
| ec08e16b9f | |||
| f14725a51b | |||
| d03325c65a | |||
| 066794fe90 | |||
| beee996f72 | |||
| 7c58e40c96 | |||
| 5577ddca27 | |||
| c0f46269b5 | |||
| 01594a6243 | |||
| d78fc53577 | |||
| e3ae024ed3 | |||
| fb9d4c30f4 | |||
| cbcf4300df | |||
| a98da7d942 | |||
| e1655edd83 | |||
| 4158c1cf62 | |||
| edd5fc6c7e | |||
| 6b0288dd4c | |||
| 90f1a193e3 | |||
| a9a478f077 | |||
| b2620e6922 | |||
| 60caa448b0 | |||
| eb886b6760 | |||
| 73c991edd0 | |||
| c698d65a92 | |||
| 1a5ab33852 | |||
| 77c4f9ff2f | |||
| dcbacb5b78 | |||
| c203c1fead | |||
| cdf105a24e | |||
| 859ec56b4f | |||
| 45872ede7a | |||
| f83238df78 | |||
| 4c8dfc4c2c | |||
| bfd7ab5a22 | |||
| ee11afb460 | |||
| 1d840950b3 | |||
| f791dc6918 | |||
| 3eb4ee7af1 | |||
| 8f21403796 | |||
| 4fbecca2d3 | |||
| 0307238bf8 | |||
| 1d42b88f50 | |||
| 81f8151aca | |||
| 66e8cd8908 | |||
| 7beff25d3d | |||
| 0c302f3137 | |||
| c7ac2483a9 | |||
| c68378ffe3 | |||
| fbd404fa84 | |||
| ff93cfdc64 |
+2
-1
@@ -11,10 +11,11 @@ docker/
|
||||
*.iml
|
||||
|
||||
# Git folder
|
||||
.git
|
||||
# .git
|
||||
.gitea
|
||||
.gitlab
|
||||
.github
|
||||
.forgejo
|
||||
|
||||
# Dot files
|
||||
.env
|
||||
|
||||
@@ -22,3 +22,7 @@ indent_size = 2
|
||||
[*.rs]
|
||||
indent_style = tab
|
||||
max_line_length = 98
|
||||
|
||||
[*.yml]
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
name: prefligit
|
||||
description: |
|
||||
Runs prefligit, pre-commit reimplemented in Rust.
|
||||
inputs:
|
||||
extra_args:
|
||||
description: options to pass to pre-commit run
|
||||
required: false
|
||||
default: '--all-files'
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Install uv
|
||||
uses: https://github.com/astral-sh/setup-uv@v6
|
||||
with:
|
||||
enable-cache: true
|
||||
ignore-nothing-to-cache: true
|
||||
- name: Install Prefligit
|
||||
shell: bash
|
||||
run: |
|
||||
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/j178/prefligit/releases/download/v0.0.10/prefligit-installer.sh | sh
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.cache/prefligit
|
||||
key: prefligit-0|${{ hashFiles('.pre-commit-config.yaml') }}
|
||||
- run: prefligit run --show-diff-on-failure --color=always -v ${{ inputs.extra_args }}
|
||||
shell: bash
|
||||
@@ -0,0 +1,63 @@
|
||||
name: rust-toolchain
|
||||
description: |
|
||||
Install a Rust toolchain using rustup.
|
||||
See https://rust-lang.github.io/rustup/concepts/toolchains.html#toolchain-specification
|
||||
for more information about toolchains.
|
||||
inputs:
|
||||
toolchain:
|
||||
description: |
|
||||
Rust toolchain name.
|
||||
See https://rust-lang.github.io/rustup/concepts/toolchains.html#toolchain-specification
|
||||
required: false
|
||||
target:
|
||||
description: Target triple to install for this toolchain
|
||||
required: false
|
||||
components:
|
||||
description: Space-separated list of components to be additionally installed for a new toolchain
|
||||
required: false
|
||||
outputs:
|
||||
rustc_version:
|
||||
description: The rustc version installed
|
||||
value: ${{ steps.rustc-version.outputs.version }}
|
||||
rustup_version:
|
||||
description: The rustup version installed
|
||||
value: ${{ steps.rustup-version.outputs.version }}
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Check if rustup is already installed
|
||||
shell: bash
|
||||
id: rustup-version
|
||||
run: |
|
||||
echo "version=$(rustup --version)" >> $GITHUB_OUTPUT
|
||||
- name: Cache rustup toolchains
|
||||
if: steps.rustup-version.outputs.version == ''
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.rustup
|
||||
!~/.rustup/tmp
|
||||
!~/.rustup/downloads
|
||||
# Requires repo to be cloned if toolchain is not specified
|
||||
key: ${{ runner.os }}-rustup-${{ inputs.toolchain || hashFiles('**/rust-toolchain.toml') }}
|
||||
- name: Install Rust toolchain
|
||||
if: steps.rustup-version.outputs.version == ''
|
||||
shell: bash
|
||||
run: |
|
||||
if ! command -v rustup &> /dev/null ; then
|
||||
curl --proto '=https' --tlsv1.2 --retry 10 --retry-connrefused -fsSL "https://sh.rustup.rs" | sh -s -- --default-toolchain none -y
|
||||
echo "${CARGO_HOME:-$HOME/.cargo}/bin" >> $GITHUB_PATH
|
||||
fi
|
||||
- shell: bash
|
||||
run: |
|
||||
set -x
|
||||
${{ inputs.toolchain && format('rustup override set {0}', inputs.toolchain) }}
|
||||
${{ inputs.target && format('rustup target add {0}', inputs.target) }}
|
||||
${{ inputs.components && format('rustup component add {0}', inputs.components) }}
|
||||
cargo --version
|
||||
rustc --version
|
||||
- id: rustc-version
|
||||
shell: bash
|
||||
run: |
|
||||
echo "version=$(rustc --version)" >> $GITHUB_OUTPUT
|
||||
@@ -0,0 +1,29 @@
|
||||
name: sccache
|
||||
description: |
|
||||
Install sccache for caching builds in GitHub Actions.
|
||||
|
||||
inputs:
|
||||
token:
|
||||
description: 'A Github PAT'
|
||||
required: false
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Install sccache
|
||||
uses: https://github.com/mozilla-actions/sccache-action@v0.0.9
|
||||
with:
|
||||
token: ${{ inputs.token }}
|
||||
- name: Configure sccache
|
||||
uses: https://github.com/actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
core.exportVariable('ACTIONS_RESULTS_URL', process.env.ACTIONS_RESULTS_URL || '');
|
||||
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');
|
||||
- shell: bash
|
||||
run: |
|
||||
echo "SCCACHE_GHA_ENABLED=true" >> $GITHUB_ENV
|
||||
echo "RUSTC_WRAPPER=sccache" >> $GITHUB_ENV
|
||||
echo "CMAKE_C_COMPILER_LAUNCHER=sccache" >> $GITHUB_ENV
|
||||
echo "CMAKE_CXX_COMPILER_LAUNCHER=sccache" >> $GITHUB_ENV
|
||||
echo "CMAKE_CUDA_COMPILER_LAUNCHER=sccache" >> $GITHUB_ENV
|
||||
@@ -0,0 +1,46 @@
|
||||
name: timelord
|
||||
description: |
|
||||
Use timelord to set file timestamps
|
||||
inputs:
|
||||
key:
|
||||
description: |
|
||||
The key to use for caching the timelord data.
|
||||
This should be unique to the repository and the runner.
|
||||
required: true
|
||||
default: timelord-v0
|
||||
path:
|
||||
description: |
|
||||
The path to the directory to be timestamped.
|
||||
This should be the root of the repository.
|
||||
required: true
|
||||
default: .
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Cache timelord-cli installation
|
||||
id: cache-timelord-bin
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.cargo/bin/timelord
|
||||
key: timelord-cli-v3.0.1
|
||||
- name: Install timelord-cli
|
||||
uses: https://github.com/cargo-bins/cargo-binstall@main
|
||||
if: steps.cache-timelord-bin.outputs.cache-hit != 'true'
|
||||
- run: cargo binstall timelord-cli@3.0.1
|
||||
shell: bash
|
||||
if: steps.cache-timelord-bin.outputs.cache-hit != 'true'
|
||||
|
||||
- name: Load timelord files
|
||||
uses: actions/cache/restore@v3
|
||||
with:
|
||||
path: /timelord/
|
||||
key: ${{ inputs.key }}
|
||||
- name: Run timelord to set timestamps
|
||||
shell: bash
|
||||
run: timelord sync --source-dir ${{ inputs.path }} --cache-dir /timelord/
|
||||
- name: Save timelord
|
||||
uses: actions/cache/save@v3
|
||||
with:
|
||||
path: /timelord/
|
||||
key: ${{ inputs.key }}
|
||||
@@ -16,7 +16,7 @@ concurrency:
|
||||
jobs:
|
||||
docs:
|
||||
name: Build and Deploy Documentation
|
||||
runs-on: not-nexy
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Sync repository
|
||||
@@ -41,6 +41,7 @@ jobs:
|
||||
# Copy the Matrix .well-known files
|
||||
cp ./docs/static/server ./public/.well-known/matrix/server
|
||||
cp ./docs/static/client ./public/.well-known/matrix/client
|
||||
cp ./docs/static/client ./public/.well-known/matrix/support
|
||||
cp ./docs/static/announcements.json ./public/.well-known/continuwuity/announcements
|
||||
cp ./docs/static/announcements.schema.json ./public/schema/announcements.schema.json
|
||||
# Copy the custom headers file
|
||||
@@ -56,17 +57,17 @@ jobs:
|
||||
run: npm install --save-dev wrangler@latest
|
||||
|
||||
- name: Deploy to Cloudflare Pages (Production)
|
||||
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
|
||||
if: github.ref == 'refs/heads/main' && vars.CLOUDFLARE_PROJECT_NAME != ''
|
||||
uses: https://github.com/cloudflare/wrangler-action@v3
|
||||
with:
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
command: pages deploy ./public --branch=main --commit-dirty=true --project-name=${{ vars.CLOUDFLARE_PROJECT_NAME }}"
|
||||
command: pages deploy ./public --branch="main" --commit-dirty=true --project-name="${{ vars.CLOUDFLARE_PROJECT_NAME }}"
|
||||
|
||||
- name: Deploy to Cloudflare Pages (Preview)
|
||||
if: ${{ github.event_name != 'push' || github.ref != 'refs/heads/main' }}
|
||||
if: github.ref != 'refs/heads/main' && vars.CLOUDFLARE_PROJECT_NAME != ''
|
||||
uses: https://github.com/cloudflare/wrangler-action@v3
|
||||
with:
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
command: pages deploy ./public --branch=${{ github.head_ref }} --commit-dirty=true --project-name=${{ vars.CLOUDFLARE_PROJECT_NAME }}"
|
||||
command: pages deploy ./public --branch="${{ github.head_ref || github.ref_name }}" --commit-dirty=true --project-name="${{ vars.CLOUDFLARE_PROJECT_NAME }}"
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
name: Deploy Element Web
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: "element-${{ github.ref }}"
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build-and-deploy:
|
||||
name: Build and Deploy Element Web
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Setup Node.js
|
||||
uses: https://code.forgejo.org/actions/setup-node@v4
|
||||
with:
|
||||
node-version: "20"
|
||||
|
||||
- name: Clone, setup, and build Element Web
|
||||
run: |
|
||||
echo "Cloning Element Web..."
|
||||
git clone https://github.com/maunium/element-web
|
||||
cd element-web
|
||||
git checkout develop
|
||||
git pull
|
||||
|
||||
echo "Cloning matrix-js-sdk..."
|
||||
git clone https://github.com/matrix-org/matrix-js-sdk.git
|
||||
|
||||
echo "Installing Yarn..."
|
||||
npm install -g yarn
|
||||
|
||||
echo "Installing dependencies..."
|
||||
yarn install
|
||||
|
||||
echo "Preparing build environment..."
|
||||
mkdir -p .home
|
||||
|
||||
echo "Cleaning up specific node_modules paths..."
|
||||
rm -rf node_modules/@types/eslint-scope/ matrix-*-sdk/node_modules/@types/eslint-scope || echo "Cleanup paths not found, continuing."
|
||||
|
||||
echo "Getting matrix-js-sdk commit hash..."
|
||||
cd matrix-js-sdk
|
||||
jsver=$(git rev-parse HEAD)
|
||||
jsver=${jsver:0:12}
|
||||
cd ..
|
||||
echo "matrix-js-sdk version hash: $jsver"
|
||||
|
||||
echo "Getting element-web commit hash..."
|
||||
ver=$(git rev-parse HEAD)
|
||||
ver=${ver:0:12}
|
||||
echo "element-web version hash: $ver"
|
||||
|
||||
chmod +x ./build-sh
|
||||
|
||||
export VERSION="$ver-js-$jsver"
|
||||
echo "Building Element Web version: $VERSION"
|
||||
./build-sh
|
||||
|
||||
echo "Checking for build output..."
|
||||
ls -la webapp/
|
||||
|
||||
- name: Create config.json
|
||||
run: |
|
||||
cat <<EOF > ./element-web/webapp/config.json
|
||||
{
|
||||
"default_server_name": "continuwuity.org",
|
||||
"default_server_config": {
|
||||
"m.homeserver": {
|
||||
"base_url": "https://matrix.continuwuity.org"
|
||||
}
|
||||
},
|
||||
"default_country_code": "GB",
|
||||
"default_theme": "dark",
|
||||
"mobile_guide_toast": false,
|
||||
"show_labs_settings": true,
|
||||
"room_directory": [
|
||||
"continuwuity.org",
|
||||
"matrixrooms.info"
|
||||
],
|
||||
"settings_defaults": {
|
||||
"UIFeature.urlPreviews": true,
|
||||
"UIFeature.feedback": false,
|
||||
"UIFeature.voip": false,
|
||||
"UIFeature.shareQrCode": false,
|
||||
"UIFeature.shareSocial": false,
|
||||
"UIFeature.locationSharing": false,
|
||||
"enableSyntaxHighlightLanguageDetection": true
|
||||
},
|
||||
"features": {
|
||||
"feature_pinning": true,
|
||||
"feature_custom_themes": true
|
||||
}
|
||||
}
|
||||
EOF
|
||||
echo "Created ./element-web/webapp/config.json"
|
||||
cat ./element-web/webapp/config.json
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: https://code.forgejo.org/actions/upload-artifact@v3
|
||||
with:
|
||||
name: element-web
|
||||
path: ./element-web/webapp/
|
||||
retention-days: 14
|
||||
|
||||
- name: Install Wrangler
|
||||
run: npm install --save-dev wrangler@latest
|
||||
|
||||
- name: Deploy to Cloudflare Pages (Production)
|
||||
if: github.ref == 'refs/heads/main' && vars.CLOUDFLARE_PROJECT_NAME != ''
|
||||
uses: https://github.com/cloudflare/wrangler-action@v3
|
||||
with:
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
command: pages deploy ./element-web/webapp --branch="main" --commit-dirty=true --project-name="${{ vars.CLOUDFLARE_PROJECT_NAME }}-element"
|
||||
|
||||
- name: Deploy to Cloudflare Pages (Preview)
|
||||
if: github.ref != 'refs/heads/main' && vars.CLOUDFLARE_PROJECT_NAME != ''
|
||||
uses: https://github.com/cloudflare/wrangler-action@v3
|
||||
with:
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
command: pages deploy ./element-web/webapp --branch="${{ github.head_ref || github.ref_name }}" --commit-dirty=true --project-name="${{ vars.CLOUDFLARE_PROJECT_NAME }}-element"
|
||||
@@ -0,0 +1,22 @@
|
||||
name: Checks / Prefligit
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
prefligit:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
FROM_REF: ${{ github.event.pull_request.base.sha || (!github.event.forced && ( github.event.before != '0000000000000000000000000000000000000000' && github.event.before || github.sha )) || format('{0}~', github.sha) }}
|
||||
TO_REF: ${{ github.sha }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: ./.forgejo/actions/prefligit
|
||||
with:
|
||||
extra_args: --all-files --hook-stage manual
|
||||
@@ -1,23 +1,24 @@
|
||||
name: Release Docker Image
|
||||
concurrency:
|
||||
concurrency:
|
||||
group: "release-image-${{ github.ref }}"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
paths-ignore:
|
||||
- '.gitlab-ci.yml'
|
||||
- '.gitignore'
|
||||
- 'renovate.json'
|
||||
- 'debian/**'
|
||||
- 'docker/**'
|
||||
- "*.md"
|
||||
- "**/*.md"
|
||||
- ".gitlab-ci.yml"
|
||||
- ".gitignore"
|
||||
- "renovate.json"
|
||||
- "debian/**"
|
||||
- "docker/**"
|
||||
- "docs/**"
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
BUILTIN_REGISTRY: forgejo.ellis.link
|
||||
BUILTIN_REGISTRY_ENABLED: "${{ ((vars.BUILTIN_REGISTRY_USER && secrets.BUILTIN_REGISTRY_PASSWORD) || (github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false)) && 'true' || 'false' }}"
|
||||
|
||||
BUILTIN_REGISTRY: forgejo.ellis.link
|
||||
BUILTIN_REGISTRY_ENABLED: "${{ ((vars.BUILTIN_REGISTRY_USER && secrets.BUILTIN_REGISTRY_PASSWORD) || (github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false)) && 'true' || 'false' }}"
|
||||
|
||||
jobs:
|
||||
define-variables:
|
||||
@@ -36,7 +37,7 @@ jobs:
|
||||
script: |
|
||||
const githubRepo = '${{ github.repository }}'.toLowerCase()
|
||||
const repoId = githubRepo.split('/')[1]
|
||||
|
||||
|
||||
core.setOutput('github_repository', githubRepo)
|
||||
const builtinImage = '${{ env.BUILTIN_REGISTRY }}/' + githubRepo
|
||||
let images = []
|
||||
@@ -47,7 +48,8 @@ jobs:
|
||||
core.setOutput('images_list', images.join(","))
|
||||
const platforms = ['linux/amd64', 'linux/arm64']
|
||||
core.setOutput('build_matrix', JSON.stringify({
|
||||
platform: platforms,
|
||||
platform: platforms,
|
||||
target_cpu: ['base'],
|
||||
include: platforms.map(platform => { return {
|
||||
platform,
|
||||
slug: platform.replace('/', '-')
|
||||
@@ -56,7 +58,6 @@ jobs:
|
||||
|
||||
build-image:
|
||||
runs-on: dind
|
||||
container: ghcr.io/catthehacker/ubuntu:act-latest
|
||||
needs: define-variables
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -64,38 +65,32 @@ jobs:
|
||||
attestations: write
|
||||
id-token: write
|
||||
strategy:
|
||||
matrix: {
|
||||
"include": [
|
||||
{
|
||||
"platform": "linux/amd64",
|
||||
"slug": "linux-amd64"
|
||||
},
|
||||
{
|
||||
"platform": "linux/arm64",
|
||||
"slug": "linux-arm64"
|
||||
}
|
||||
],
|
||||
"platform": [
|
||||
"linux/amd64",
|
||||
"linux/arm64"
|
||||
]
|
||||
}
|
||||
matrix:
|
||||
{
|
||||
"target_cpu": ["base"],
|
||||
"profile": ["release"],
|
||||
"include":
|
||||
[
|
||||
{ "platform": "linux/amd64", "slug": "linux-amd64" },
|
||||
{ "platform": "linux/arm64", "slug": "linux-arm64" },
|
||||
],
|
||||
"platform": ["linux/amd64", "linux/arm64"],
|
||||
}
|
||||
|
||||
steps:
|
||||
- name: Echo strategy
|
||||
run: echo '${{ toJSON(fromJSON(needs.define-variables.outputs.build_matrix)) }}'
|
||||
- name: Echo matrix
|
||||
run: echo '${{ toJSON(matrix) }}'
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- run: |
|
||||
if ! command -v rustup &> /dev/null ; then
|
||||
curl --proto '=https' --tlsv1.2 --retry 10 --retry-connrefused -fsSL "https://sh.rustup.rs" | sh -s -- --default-toolchain none -y
|
||||
echo "${CARGO_HOME:-$HOME/.cargo}/bin" >> $GITHUB_PATH
|
||||
fi
|
||||
- uses: https://github.com/cargo-bins/cargo-binstall@main
|
||||
- run: cargo binstall timelord-cli@3.0.1
|
||||
- name: Install rust
|
||||
id: rust-toolchain
|
||||
uses: ./.forgejo/actions/rust-toolchain
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Set up QEMU
|
||||
@@ -104,9 +99,9 @@ jobs:
|
||||
- name: Login to builtin registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.BUILTIN_REGISTRY }}
|
||||
username: ${{ vars.BUILTIN_REGISTRY_USER || github.actor }}
|
||||
password: ${{ secrets.BUILTIN_REGISTRY_PASSWORD || secrets.GITHUB_TOKEN }}
|
||||
registry: ${{ env.BUILTIN_REGISTRY }}
|
||||
username: ${{ vars.BUILTIN_REGISTRY_USER || github.actor }}
|
||||
password: ${{ secrets.BUILTIN_REGISTRY_PASSWORD || secrets.GITHUB_TOKEN }}
|
||||
|
||||
# This step uses [docker/metadata-action](https://github.com/docker/metadata-action#about) to extract tags and labels that will be applied to the specified image. The `id` "meta" allows the output of this step to be referenced in a subsequent step. The `images` value provides the base name for the tags and labels.
|
||||
- name: Extract metadata (labels, annotations) for Docker
|
||||
@@ -129,18 +124,58 @@ jobs:
|
||||
echo "COMMIT_SHORT_SHA=$calculatedSha" >> $GITHUB_ENV
|
||||
- name: Get Git commit timestamps
|
||||
run: echo "TIMESTAMP=$(git log -1 --pretty=%ct)" >> $GITHUB_ENV
|
||||
- name: Set up timelord
|
||||
uses: actions/cache/restore@v3
|
||||
|
||||
- uses: ./.forgejo/actions/timelord
|
||||
with:
|
||||
path: /timelord/
|
||||
key: timelord-v0 # Cache is already split per runner
|
||||
- name: Run timelord to set timestamps
|
||||
run: timelord sync --source-dir . --cache-dir /timelord/
|
||||
- name: Save timelord
|
||||
uses: actions/cache/save@v3
|
||||
with:
|
||||
path: /timelord/
|
||||
key: timelord-v0
|
||||
path: .
|
||||
|
||||
- name: Cache Rust registry
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
.cargo/git
|
||||
.cargo/git/checkouts
|
||||
.cargo/registry
|
||||
.cargo/registry/src
|
||||
key: rust-registry-image-${{hashFiles('**/Cargo.lock') }}
|
||||
- name: Cache cargo target
|
||||
id: cache-cargo-target
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
cargo-target-${{ matrix.target_cpu }}-${{ matrix.slug }}-${{ matrix.profile }}
|
||||
key: cargo-target-${{ matrix.target_cpu }}-${{ matrix.slug }}-${{ matrix.profile }}-${{hashFiles('**/Cargo.lock') }}-${{steps.rust-toolchain.outputs.rustc_version}}
|
||||
- name: Cache apt cache
|
||||
id: cache-apt
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
var-cache-apt-${{ matrix.slug }}
|
||||
key: var-cache-apt-${{ matrix.slug }}
|
||||
- name: Cache apt lib
|
||||
id: cache-apt-lib
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
var-lib-apt-${{ matrix.slug }}
|
||||
key: var-lib-apt-${{ matrix.slug }}
|
||||
- name: inject cache into docker
|
||||
uses: https://github.com/reproducible-containers/buildkit-cache-dance@v3.1.0
|
||||
with:
|
||||
cache-map: |
|
||||
{
|
||||
".cargo/registry": "/usr/local/cargo/registry",
|
||||
".cargo/git/db": "/usr/local/cargo/git/db",
|
||||
"cargo-target-${{ matrix.target_cpu }}-${{ matrix.slug }}-${{ matrix.profile }}": {
|
||||
"target": "/app/target",
|
||||
"id": "cargo-target-${{ matrix.target_cpu }}-${{ matrix.slug }}-${{ matrix.profile }}"
|
||||
},
|
||||
"var-cache-apt-${{ matrix.slug }}": "/var/cache/apt",
|
||||
"var-lib-apt-${{ matrix.slug }}": "/var/lib/apt"
|
||||
}
|
||||
skip-extraction: ${{ steps.cache.outputs.cache-hit }}
|
||||
|
||||
- name: Build and push Docker image by digest
|
||||
id: build
|
||||
uses: docker/build-push-action@v6
|
||||
@@ -148,12 +183,15 @@ jobs:
|
||||
context: .
|
||||
file: "docker/Dockerfile"
|
||||
build-args: |
|
||||
CONDUWUIT_VERSION_EXTRA=${{ env.COMMIT_SHORT_SHA }}
|
||||
GIT_COMMIT_HASH=${{ github.sha }})
|
||||
GIT_COMMIT_HASH_SHORT=${{ env.COMMIT_SHORT_SHA }}
|
||||
GIT_REMOTE_URL=${{github.event.repository.html_url }}
|
||||
GIT_REMOTE_COMMIT_URL=${{github.event.head_commit.url }}
|
||||
platforms: ${{ matrix.platform }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
annotations: ${{ steps.meta.outputs.annotations }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
# cache-to: type=gha,mode=max
|
||||
sbom: true
|
||||
outputs: type=image,"name=${{ needs.define-variables.outputs.images_list }}",push-by-digest=true,name-canonical=true,push=true
|
||||
env:
|
||||
@@ -164,7 +202,7 @@ jobs:
|
||||
run: |
|
||||
mkdir -p /tmp/digests
|
||||
digest="${{ steps.build.outputs.digest }}"
|
||||
touch "/tmp/digests/${digest#sha256:}"
|
||||
touch "/tmp/digests/${digest#sha256:}"
|
||||
|
||||
- name: Upload digest
|
||||
uses: forgejo/upload-artifact@v4
|
||||
@@ -173,10 +211,9 @@ jobs:
|
||||
path: /tmp/digests/*
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
|
||||
merge:
|
||||
runs-on: dind
|
||||
container: ghcr.io/catthehacker/ubuntu:act-latest
|
||||
needs: [define-variables, build-image]
|
||||
steps:
|
||||
- name: Download digests
|
||||
@@ -189,9 +226,9 @@ jobs:
|
||||
- name: Login to builtin registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.BUILTIN_REGISTRY }}
|
||||
username: ${{ vars.BUILTIN_REGISTRY_USER || github.actor }}
|
||||
password: ${{ secrets.BUILTIN_REGISTRY_PASSWORD || secrets.GITHUB_TOKEN }}
|
||||
registry: ${{ env.BUILTIN_REGISTRY }}
|
||||
username: ${{ vars.BUILTIN_REGISTRY_USER || github.actor }}
|
||||
password: ${{ secrets.BUILTIN_REGISTRY_PASSWORD || secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
@@ -204,13 +241,13 @@ jobs:
|
||||
type=semver,pattern=v{{version}}
|
||||
type=semver,pattern=v{{major}}.{{minor}},enable=${{ !startsWith(github.ref, 'refs/tags/v0.0.') }}
|
||||
type=semver,pattern=v{{major}},enable=${{ !startsWith(github.ref, 'refs/tags/v0.') }}
|
||||
type=ref,event=branch,prefix=${{ format('refs/heads/{0}', github.event.repository.default_branch) 1= github.ref && 'branch-' || '' }}
|
||||
type=ref,event=branch,prefix=${{ format('refs/heads/{0}', github.event.repository.default_branch) != github.ref && 'branch-' || '' }}
|
||||
type=ref,event=pr
|
||||
type=sha,format=long
|
||||
images: ${{needs.define-variables.outputs.images}}
|
||||
# default labels & annotations: https://github.com/docker/metadata-action/blob/master/src/meta.ts#L509
|
||||
env:
|
||||
DOCKER_METADATA_ANNOTATIONS_LEVELS: index
|
||||
DOCKER_METADATA_ANNOTATIONS_LEVELS: index
|
||||
|
||||
- name: Create manifest list and push
|
||||
working-directory: /tmp/digests
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
name: Checks / Rust
|
||||
|
||||
on:
|
||||
push:
|
||||
|
||||
jobs:
|
||||
format:
|
||||
name: Format
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install rust
|
||||
uses: ./.forgejo/actions/rust-toolchain
|
||||
with:
|
||||
toolchain: "nightly"
|
||||
components: "rustfmt"
|
||||
|
||||
- name: Check formatting
|
||||
run: |
|
||||
cargo +nightly fmt --all -- --check
|
||||
|
||||
clippy:
|
||||
name: Clippy
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install rust
|
||||
uses: ./.forgejo/actions/rust-toolchain
|
||||
|
||||
- uses: https://github.com/actions/create-github-app-token@v2
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ vars.GH_APP_ID }}
|
||||
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
|
||||
github-api-url: https://api.github.com
|
||||
owner: ${{ vars.GH_APP_OWNER }}
|
||||
repositories: ""
|
||||
- name: Install sccache
|
||||
uses: ./.forgejo/actions/sccache
|
||||
with:
|
||||
token: ${{ steps.app-token.outputs.token }}
|
||||
- run: sudo apt-get update
|
||||
- name: Install system dependencies
|
||||
uses: https://github.com/awalsh128/cache-apt-pkgs-action@v1
|
||||
with:
|
||||
packages: clang liburing-dev
|
||||
version: 1
|
||||
- name: Cache Rust registry
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/git
|
||||
!~/.cargo/git/checkouts
|
||||
~/.cargo/registry
|
||||
!~/.cargo/registry/src
|
||||
key: rust-registry-${{hashFiles('**/Cargo.lock') }}
|
||||
- name: Timelord
|
||||
uses: ./.forgejo/actions/timelord
|
||||
with:
|
||||
key: sccache-v0
|
||||
path: .
|
||||
- name: Clippy
|
||||
run: |
|
||||
cargo clippy \
|
||||
--workspace \
|
||||
--locked \
|
||||
--no-deps \
|
||||
--profile test \
|
||||
-- \
|
||||
-D warnings
|
||||
|
||||
- name: Show sccache stats
|
||||
if: always()
|
||||
run: sccache --show-stats
|
||||
|
||||
cargo-test:
|
||||
name: Cargo Test
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install rust
|
||||
uses: ./.forgejo/actions/rust-toolchain
|
||||
|
||||
- uses: https://github.com/actions/create-github-app-token@v2
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ vars.GH_APP_ID }}
|
||||
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
|
||||
github-api-url: https://api.github.com
|
||||
owner: ${{ vars.GH_APP_OWNER }}
|
||||
repositories: ""
|
||||
- name: Install sccache
|
||||
uses: ./.forgejo/actions/sccache
|
||||
with:
|
||||
token: ${{ steps.app-token.outputs.token }}
|
||||
- run: sudo apt-get update
|
||||
- name: Install system dependencies
|
||||
uses: https://github.com/awalsh128/cache-apt-pkgs-action@v1
|
||||
with:
|
||||
packages: clang liburing-dev
|
||||
version: 1
|
||||
- name: Cache Rust registry
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/git
|
||||
!~/.cargo/git/checkouts
|
||||
~/.cargo/registry
|
||||
!~/.cargo/registry/src
|
||||
key: rust-registry-${{hashFiles('**/Cargo.lock') }}
|
||||
- name: Timelord
|
||||
uses: ./.forgejo/actions/timelord
|
||||
with:
|
||||
key: sccache-v0
|
||||
path: .
|
||||
- name: Cargo Test
|
||||
run: |
|
||||
cargo test \
|
||||
--workspace \
|
||||
--locked \
|
||||
--profile test \
|
||||
--all-targets \
|
||||
--no-fail-fast
|
||||
|
||||
- name: Show sccache stats
|
||||
if: always()
|
||||
run: sccache --show-stats
|
||||
@@ -5,3 +5,5 @@ f419c64aca300a338096b4e0db4c73ace54f23d0
|
||||
# use chain_width 60
|
||||
162948313c212193965dece50b816ef0903172ba
|
||||
5998a0d883d31b866f7c8c46433a8857eae51a89
|
||||
# trailing whitespace and newlines
|
||||
46c193e74b2ce86c48ce802333a0aabce37fd6e9
|
||||
|
||||
+1
-1
@@ -84,4 +84,4 @@ Cargo.lock text
|
||||
*.zst binary
|
||||
|
||||
# Text files where line endings should be preserved
|
||||
*.patch -text
|
||||
*.patch -text
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
default_install_hook_types:
|
||||
- pre-commit
|
||||
- commit-msg
|
||||
default_stages:
|
||||
- pre-commit
|
||||
- manual
|
||||
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v5.0.0
|
||||
hooks:
|
||||
- id: check-byte-order-marker
|
||||
- id: check-case-conflict
|
||||
- id: check-symlinks
|
||||
- id: destroyed-symlinks
|
||||
- id: check-yaml
|
||||
- id: check-json
|
||||
- id: check-toml
|
||||
- id: end-of-file-fixer
|
||||
- id: trailing-whitespace
|
||||
- id: mixed-line-ending
|
||||
- id: check-merge-conflict
|
||||
- id: check-added-large-files
|
||||
|
||||
- repo: https://github.com/crate-ci/typos
|
||||
rev: v1.26.0
|
||||
hooks:
|
||||
- id: typos
|
||||
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: cargo-fmt
|
||||
name: cargo fmt
|
||||
entry: cargo +nightly fmt --
|
||||
language: system
|
||||
types: [rust]
|
||||
pass_filenames: false
|
||||
stages:
|
||||
- pre-commit
|
||||
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: cargo-clippy
|
||||
name: cargo clippy
|
||||
language: system
|
||||
types: [rust]
|
||||
pass_filenames: false
|
||||
entry: cargo clippy --workspace --locked --no-deps --profile test -- -D warnings
|
||||
stages:
|
||||
- pre-commit
|
||||
@@ -0,0 +1,9 @@
|
||||
[files]
|
||||
extend-exclude = ["*.csr"]
|
||||
|
||||
[default.extend-words]
|
||||
"allocatedp" = "allocatedp"
|
||||
"conduwuit" = "conduwuit"
|
||||
"continuwuity" = "continuwuity"
|
||||
"continuwity" = "continuwuity"
|
||||
"execuse" = "execuse"
|
||||
+1
-3
@@ -1,4 +1,3 @@
|
||||
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
@@ -60,8 +59,7 @@ representative at an online or offline event.
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement over email at
|
||||
<strawberry@puppygock.gay> or over Matrix at @strawberry:puppygock.gay.
|
||||
reported to the community leaders responsible for enforcement over Matrix at [#continuwuity:continuwuity.org](https://matrix.to/#/#continuwuity:continuwuity.org) or email at <tom@tcpip.uk>, <jade@continuwuity.org> and <nex@continuwuity.org> respectively.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
|
||||
+14
-19
@@ -1,10 +1,10 @@
|
||||
# Contributing guide
|
||||
|
||||
This page is for about contributing to conduwuit. The
|
||||
This page is for about contributing to Continuwuity. The
|
||||
[development](./development.md) page may be of interest for you as well.
|
||||
|
||||
If you would like to work on an [issue][issues] that is not assigned, preferably
|
||||
ask in the Matrix room first at [#conduwuit:puppygock.gay][conduwuit-matrix],
|
||||
ask in the Matrix room first at [#continuwuity:continuwuity.org][continuwuity-matrix],
|
||||
and comment on it.
|
||||
|
||||
### Linting and Formatting
|
||||
@@ -23,9 +23,9 @@ suggestion, allow the lint and mention that in a comment.
|
||||
|
||||
### Running CI tests locally
|
||||
|
||||
conduwuit's CI for tests, linting, formatting, audit, etc use
|
||||
continuwuity's CI for tests, linting, formatting, audit, etc use
|
||||
[`engage`][engage]. engage can be installed from nixpkgs or `cargo install
|
||||
engage`. conduwuit's Nix flake devshell has the nixpkgs engage with `direnv`.
|
||||
engage`. continuwuity's Nix flake devshell has the nixpkgs engage with `direnv`.
|
||||
Use `engage --help` for more usage details.
|
||||
|
||||
To test, format, lint, etc that CI would do, install engage, allow the `.envrc`
|
||||
@@ -73,7 +73,7 @@ If you'd like to run Complement locally using Nix, see the
|
||||
|
||||
### Writing documentation
|
||||
|
||||
conduwuit's website uses [`mdbook`][mdbook] and deployed via CI using GitHub
|
||||
Continuwuity's website uses [`mdbook`][mdbook] and deployed via CI using GitHub
|
||||
Pages in the [`documentation.yml`][documentation.yml] workflow file with Nix's
|
||||
mdbook in the devshell. All documentation is in the `docs/` directory at the top
|
||||
level. The compiled mdbook website is also uploaded as an artifact.
|
||||
@@ -111,33 +111,28 @@ applies here.
|
||||
|
||||
### Creating pull requests
|
||||
|
||||
Please try to keep contributions to the GitHub. While the mirrors of conduwuit
|
||||
allow for pull/merge requests, there is no guarantee I will see them in a timely
|
||||
Please try to keep contributions to the Forgejo Instance. While the mirrors of continuwuity
|
||||
allow for pull/merge requests, there is no guarantee the maintainers will see them in a timely
|
||||
manner. Additionally, please mark WIP or unfinished or incomplete PRs as drafts.
|
||||
This prevents me from having to ping once in a while to double check the status
|
||||
This prevents us from having to ping once in a while to double check the status
|
||||
of it, especially when the CI completed successfully and everything so it
|
||||
*looks* done.
|
||||
|
||||
If you open a pull request on one of the mirrors, it is your responsibility to
|
||||
inform me about its existence. In the future I may try to solve this with more
|
||||
repo bots in the conduwuit Matrix room. There is no mailing list or email-patch
|
||||
support on the sr.ht mirror, but if you'd like to email me a git patch you can
|
||||
do so at `strawberry@puppygock.gay`.
|
||||
|
||||
Direct all PRs/MRs to the `main` branch.
|
||||
|
||||
By sending a pull request or patch, you are agreeing that your changes are
|
||||
allowed to be licenced under the Apache-2.0 licence and all of your conduct is
|
||||
in line with the Contributor's Covenant, and conduwuit's Code of Conduct.
|
||||
in line with the Contributor's Covenant, and continuwuity's Code of Conduct.
|
||||
|
||||
Contribution by users who violate either of these code of conducts will not have
|
||||
their contributions accepted. This includes users who have been banned from
|
||||
conduwuit Matrix rooms for Code of Conduct violations.
|
||||
continuwuityMatrix rooms for Code of Conduct violations.
|
||||
|
||||
[issues]: https://github.com/girlbossceo/conduwuit/issues
|
||||
[conduwuit-matrix]: https://matrix.to/#/#conduwuit:puppygock.gay
|
||||
[issues]: https://forgejo.ellis.link/continuwuation/continuwuity/issues
|
||||
[continuwuity-matrix]: https://matrix.to/#/#continuwuity:continuwuity.org
|
||||
[complement]: https://github.com/matrix-org/complement/
|
||||
[engage.toml]: https://github.com/girlbossceo/conduwuit/blob/main/engage.toml
|
||||
[engage.toml]: https://forgejo.ellis.link/continuwuation/continuwuity/src/branch/main/engage.toml
|
||||
[engage]: https://charles.page.computer.surgery/engage/
|
||||
[sytest]: https://github.com/matrix-org/sytest/
|
||||
[cargo-deb]: https://github.com/kornelski/cargo-deb
|
||||
@@ -146,4 +141,4 @@ conduwuit Matrix rooms for Code of Conduct violations.
|
||||
[cargo-audit]: https://github.com/RustSec/rustsec/tree/main/cargo-audit
|
||||
[direnv]: https://direnv.net/
|
||||
[mdbook]: https://rust-lang.github.io/mdBook/
|
||||
[documentation.yml]: https://github.com/girlbossceo/conduwuit/blob/main/.github/workflows/documentation.yml
|
||||
[documentation.yml]: https://forgejo.ellis.link/continuwuation/continuwuity/src/branch/main/.forgejo/workflows/documentation.yml
|
||||
|
||||
Generated
+634
-440
File diff suppressed because it is too large
Load Diff
+24
-15
@@ -21,7 +21,7 @@ license = "Apache-2.0"
|
||||
readme = "README.md"
|
||||
repository = "https://forgejo.ellis.link/continuwuation/continuwuity"
|
||||
rust-version = "1.86.0"
|
||||
version = "0.5.0-rc.5"
|
||||
version = "0.5.0-rc.6"
|
||||
|
||||
[workspace.metadata.crane]
|
||||
name = "conduwuit"
|
||||
@@ -298,7 +298,7 @@ version = "1.15.0"
|
||||
default-features = false
|
||||
features = ["serde"]
|
||||
|
||||
# Used for reading the configuration from conduwuit.toml & environment variables
|
||||
# Used for reading the configuration from continuwuity.toml & environment variables
|
||||
[workspace.dependencies.figment]
|
||||
version = "0.10.19"
|
||||
default-features = false
|
||||
@@ -350,7 +350,7 @@ version = "0.1.2"
|
||||
[workspace.dependencies.ruma]
|
||||
git = "https://forgejo.ellis.link/continuwuation/ruwuma"
|
||||
#branch = "conduwuit-changes"
|
||||
rev = "fa3c868e5a1c049dc9472310dc4955289a96bb35"
|
||||
rev = "d6870a7fb7f6cccff63f7fd0ff6c581bad80e983"
|
||||
features = [
|
||||
"compat",
|
||||
"rand",
|
||||
@@ -381,7 +381,7 @@ features = [
|
||||
"unstable-msc4121",
|
||||
"unstable-msc4125",
|
||||
"unstable-msc4186",
|
||||
"unstable-msc4203", # sending to-device events to appservices
|
||||
"unstable-msc4203", # sending to-device events to appservices
|
||||
"unstable-msc4210", # remove legacy mentions
|
||||
"unstable-extensible-events",
|
||||
"unstable-pdu",
|
||||
@@ -556,11 +556,11 @@ rev = "1e64095a8051a1adf0d1faa307f9f030889ec2aa"
|
||||
git = "https://forgejo.ellis.link/continuwuation/tracing"
|
||||
rev = "1e64095a8051a1adf0d1faa307f9f030889ec2aa"
|
||||
|
||||
# adds a tab completion callback: https://forgejo.ellis.link/continuwuation/rustyline-async/commit/de26100b0db03e419a3d8e1dd26895d170d1fe50
|
||||
# adds event for CTRL+\: https://forgejo.ellis.link/continuwuation/rustyline-async/commit/67d8c49aeac03a5ef4e818f663eaa94dd7bf339b
|
||||
# adds a tab completion callback: https://forgejo.ellis.link/continuwuation/rustyline-async/src/branch/main/.patchy/0002-add-tab-completion-callback.patch
|
||||
# adds event for CTRL+\: https://forgejo.ellis.link/continuwuation/rustyline-async/src/branch/main/.patchy/0001-add-event-for-ctrl.patch
|
||||
[patch.crates-io.rustyline-async]
|
||||
git = "https://forgejo.ellis.link/continuwuation/rustyline-async"
|
||||
rev = "deaeb0694e2083f53d363b648da06e10fc13900c"
|
||||
rev = "e9f01cf8c6605483cb80b3b0309b400940493d7f"
|
||||
|
||||
# adds LIFO queue scheduling; this should be updated with PR progress.
|
||||
[patch.crates-io.event-listener]
|
||||
@@ -580,12 +580,11 @@ rev = "9c8e51510c35077df888ee72a36b4b05637147da"
|
||||
git = "https://forgejo.ellis.link/continuwuation/hyper-util"
|
||||
rev = "e4ae7628fe4fcdacef9788c4c8415317a4489941"
|
||||
|
||||
# allows no-aaaa option in resolv.conf
|
||||
# bumps rust edition and toolchain to 1.86.0 and 2024
|
||||
# use sat_add on line number errors
|
||||
# Allows no-aaaa option in resolv.conf
|
||||
# Use 1-indexed line numbers when displaying parse error messages
|
||||
[patch.crates-io.resolv-conf]
|
||||
git = "https://forgejo.ellis.link/continuwuation/resolv-conf"
|
||||
rev = "200e958941d522a70c5877e3d846f55b5586c68d"
|
||||
rev = "56251316cc4127bcbf36e68ce5e2093f4d33e227"
|
||||
|
||||
#
|
||||
# Our crates
|
||||
@@ -626,6 +625,17 @@ package = "conduwuit_macros"
|
||||
path = "src/macros"
|
||||
default-features = false
|
||||
|
||||
[workspace.dependencies.conduwuit-web]
|
||||
package = "conduwuit_web"
|
||||
path = "src/web"
|
||||
default-features = false
|
||||
|
||||
|
||||
[workspace.dependencies.conduwuit-build-metadata]
|
||||
package = "conduwuit_build_metadata"
|
||||
path = "src/build_metadata"
|
||||
default-features = false
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
# Release profiles
|
||||
@@ -734,7 +744,6 @@ incremental = true
|
||||
|
||||
[profile.dev.package.conduwuit_core]
|
||||
inherits = "dev"
|
||||
incremental = false
|
||||
#rustflags = [
|
||||
# '--cfg', 'conduwuit_mods',
|
||||
# '-Ztime-passes',
|
||||
@@ -774,7 +783,6 @@ inherits = "dev"
|
||||
[profile.dev.package.'*']
|
||||
inherits = "dev"
|
||||
debug = 'limited'
|
||||
incremental = false
|
||||
codegen-units = 1
|
||||
opt-level = 'z'
|
||||
#rustflags = [
|
||||
@@ -796,7 +804,6 @@ inherits = "dev"
|
||||
strip = false
|
||||
opt-level = 0
|
||||
codegen-units = 16
|
||||
incremental = false
|
||||
|
||||
[profile.test.package.'*']
|
||||
inherits = "dev"
|
||||
@@ -804,7 +811,6 @@ debug = 0
|
||||
strip = false
|
||||
opt-level = 0
|
||||
codegen-units = 16
|
||||
incremental = false
|
||||
|
||||
###############################################################################
|
||||
#
|
||||
@@ -981,3 +987,6 @@ let_underscore_future = { level = "allow", priority = 1 }
|
||||
|
||||
# rust doesnt understand conduwuit's custom log macros
|
||||
literal_string_with_formatting_args = { level = "allow", priority = 1 }
|
||||
|
||||
|
||||
needless_raw_string_hashes = "allow"
|
||||
|
||||
@@ -7,16 +7,21 @@
|
||||
<!-- ANCHOR_END: catchphrase -->
|
||||
|
||||
[continuwuity] is a Matrix homeserver written in Rust.
|
||||
It's a community continuation of the [conduwuit](https://github.com/girlbossceo/conduwuit) homeserver.
|
||||
It's a community continuation of the [conduwuit](https://github.com/girlbossceo/conduwuit) homeserver.
|
||||
|
||||
<!-- ANCHOR: body -->
|
||||
|
||||
[](https://forgejo.ellis.link/continuwuation/continuwuity)  [](https://forgejo.ellis.link/continuwuation/continuwuity/issues?state=open) [](https://forgejo.ellis.link/continuwuation/continuwuity/pulls?state=open)
|
||||
|
||||
[](https://github.com/continuwuity/continuwuity) 
|
||||
|
||||
[](https://codeberg.org/nexy7574/continuwuity) 
|
||||
|
||||
### Why does this exist?
|
||||
|
||||
The original conduwuit project has been archived and is no longer maintained. Rather than letting this Rust-based Matrix homeserver disappear, a group of community contributors have forked the project to continue its development, fix outstanding issues, and add new features.
|
||||
|
||||
We aim to provide a stable, well-maintained alternative for current Conduit users and welcome newcomers seeking a lightweight, efficient Matrix homeserver.
|
||||
We aim to provide a stable, well-maintained alternative for current conduwuit users and welcome newcomers seeking a lightweight, efficient Matrix homeserver.
|
||||
|
||||
### Who are we?
|
||||
|
||||
@@ -46,8 +51,9 @@ Continuwuity aims to:
|
||||
|
||||
### Can I try it out?
|
||||
|
||||
Not right now. We've still got work to do!
|
||||
Check out the [documentation](introduction) for installation instructions.
|
||||
|
||||
There are currently no open registration Continuwuity instances available.
|
||||
|
||||
### What are we working on?
|
||||
|
||||
@@ -105,7 +111,7 @@ When incorporating code from other forks:
|
||||
|
||||
#### Contact
|
||||
|
||||
<!-- TODO: contact details -->
|
||||
Join our [Matrix room](https://matrix.to/#/#continuwuity:continuwuity.org) and [space](https://matrix.to/#/#space:continuwuity.org) to chat with us about the project!
|
||||
|
||||
<!-- ANCHOR_END: footer -->
|
||||
|
||||
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
# Security Policy for Continuwuity
|
||||
|
||||
This document outlines the security policy for Continuwuity. Our goal is to maintain a secure platform for all users, and we take security matters seriously.
|
||||
|
||||
## Supported Versions
|
||||
|
||||
We provide security updates for the following versions of Continuwuity:
|
||||
|
||||
| Version | Supported |
|
||||
| -------------- |:----------------:|
|
||||
| Latest release | ✅ |
|
||||
| Main branch | ✅ |
|
||||
| Older releases | ❌ |
|
||||
|
||||
We may backport fixes to the previous release at our discretion, but we don't guarantee this.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
### Responsible Disclosure
|
||||
|
||||
We appreciate the efforts of security researchers and the community in identifying and reporting vulnerabilities. To ensure that potential vulnerabilities are addressed properly, please follow these guidelines:
|
||||
|
||||
1. **Contact members of the team directly** over E2EE private message.
|
||||
- [@jade:ellis.link](https://matrix.to/#/@jade:ellis.link)
|
||||
- [@nex:nexy7574.co.uk](https://matrix.to/#/@nex:nexy7574.co.uk) <!-- ? -->
|
||||
2. **Email the security team** at [security@continuwuity.org](mailto:security@continuwuity.org). This is not E2EE, so don't include sensitive details.
|
||||
3. **Do not disclose the vulnerability publicly** until it has been addressed
|
||||
4. **Provide detailed information** about the vulnerability, including:
|
||||
- A clear description of the issue
|
||||
- Steps to reproduce
|
||||
- Potential impact
|
||||
- Any possible mitigations
|
||||
- Version(s) affected, including specific commits if possible
|
||||
|
||||
If you have any doubts about a potential security vulnerability, contact us via private channels first! We'd prefer that you bother us, instead of having a vulnerability disclosed without a fix.
|
||||
|
||||
### What to Expect
|
||||
|
||||
When you report a security vulnerability:
|
||||
|
||||
1. **Acknowledgment**: We will acknowledge receipt of your report.
|
||||
2. **Assessment**: We will assess the vulnerability and determine its impact on our users
|
||||
3. **Updates**: We will provide updates on our progress in addressing the vulnerability, and may request you help test mitigations
|
||||
4. **Resolution**: Once resolved, we will notify you and discuss coordinated disclosure
|
||||
5. **Credit**: We will recognize your contribution (unless you prefer to remain anonymous)
|
||||
|
||||
## Security Update Process
|
||||
|
||||
When security vulnerabilities are identified:
|
||||
|
||||
1. We will develop and test fixes in a private fork
|
||||
2. Security updates will be released as soon as possible
|
||||
3. Release notes will include information about the vulnerabilities, avoiding details that could facilitate exploitation where possible
|
||||
4. Critical security updates may be backported to the previous stable release
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- [Matrix Security Disclosure Policy](https://matrix.org/security-disclosure-policy/)
|
||||
- [Continuwuity Documentation](https://continuwuity.org/introduction)
|
||||
|
||||
---
|
||||
|
||||
This security policy was last updated on May 25, 2025.
|
||||
@@ -1,11 +1,11 @@
|
||||
[Unit]
|
||||
Description=conduwuit Matrix homeserver
|
||||
|
||||
Description=Continuwuity - Matrix homeserver
|
||||
Wants=network-online.target
|
||||
After=network-online.target
|
||||
Documentation=https://conduwuit.puppyirl.gay/
|
||||
Documentation=https://continuwuity.org/
|
||||
RequiresMountsFor=/var/lib/private/conduwuit
|
||||
Alias=matrix-conduwuit.service
|
||||
|
||||
[Service]
|
||||
DynamicUser=yes
|
||||
Type=notify-reload
|
||||
@@ -59,7 +59,7 @@ StateDirectory=conduwuit
|
||||
RuntimeDirectory=conduwuit
|
||||
RuntimeDirectoryMode=0750
|
||||
|
||||
Environment="CONDUWUIT_CONFIG=/etc/conduwuit/conduwuit.toml"
|
||||
Environment="CONTINUWUITY_CONFIG=/etc/conduwuit/conduwuit.toml"
|
||||
BindPaths=/var/lib/private/conduwuit:/var/lib/matrix-conduit
|
||||
BindPaths=/var/lib/private/conduwuit:/var/lib/private/matrix-conduit
|
||||
|
||||
|
||||
+137
-106
@@ -1,4 +1,4 @@
|
||||
### conduwuit Configuration
|
||||
### continuwuity Configuration
|
||||
###
|
||||
### THIS FILE IS GENERATED. CHANGES/CONTRIBUTIONS IN THE REPO WILL BE
|
||||
### OVERWRITTEN!
|
||||
@@ -13,7 +13,7 @@
|
||||
### that say "YOU NEED TO EDIT THIS".
|
||||
###
|
||||
### For more information, see:
|
||||
### https://conduwuit.puppyirl.gay/configuration.html
|
||||
### https://continuwuity.org/configuration.html
|
||||
|
||||
[global]
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
# suffix for user and room IDs/aliases.
|
||||
#
|
||||
# See the docs for reverse proxying and delegation:
|
||||
# https://conduwuit.puppyirl.gay/deploying/generic.html#setting-up-the-reverse-proxy
|
||||
# https://continuwuity.org/deploying/generic.html#setting-up-the-reverse-proxy
|
||||
#
|
||||
# Also see the `[global.well_known]` config section at the very bottom.
|
||||
#
|
||||
@@ -32,11 +32,11 @@
|
||||
# YOU NEED TO EDIT THIS. THIS CANNOT BE CHANGED AFTER WITHOUT A DATABASE
|
||||
# WIPE.
|
||||
#
|
||||
# example: "conduwuit.woof"
|
||||
# example: "continuwuity.org"
|
||||
#
|
||||
#server_name =
|
||||
|
||||
# The default address (IPv4 or IPv6) conduwuit will listen on.
|
||||
# The default address (IPv4 or IPv6) continuwuity will listen on.
|
||||
#
|
||||
# If you are using Docker or a container NAT networking setup, this must
|
||||
# be "0.0.0.0".
|
||||
@@ -46,10 +46,10 @@
|
||||
#
|
||||
#address = ["127.0.0.1", "::1"]
|
||||
|
||||
# The port(s) conduwuit will listen on.
|
||||
# The port(s) continuwuity will listen on.
|
||||
#
|
||||
# For reverse proxying, see:
|
||||
# https://conduwuit.puppyirl.gay/deploying/generic.html#setting-up-the-reverse-proxy
|
||||
# https://continuwuity.org/deploying/generic.html#setting-up-the-reverse-proxy
|
||||
#
|
||||
# If you are using Docker, don't change this, you'll need to map an
|
||||
# external port to this.
|
||||
@@ -58,16 +58,17 @@
|
||||
#
|
||||
#port = 8008
|
||||
|
||||
# The UNIX socket conduwuit will listen on.
|
||||
# The UNIX socket continuwuity will listen on.
|
||||
#
|
||||
# conduwuit cannot listen on both an IP address and a UNIX socket. If
|
||||
# continuwuity cannot listen on both an IP address and a UNIX socket. If
|
||||
# listening on a UNIX socket, you MUST remove/comment the `address` key.
|
||||
#
|
||||
# Remember to make sure that your reverse proxy has access to this socket
|
||||
# file, either by adding your reverse proxy to the 'conduwuit' group or
|
||||
# granting world R/W permissions with `unix_socket_perms` (666 minimum).
|
||||
# file, either by adding your reverse proxy to the appropriate user group
|
||||
# or granting world R/W permissions with `unix_socket_perms` (666
|
||||
# minimum).
|
||||
#
|
||||
# example: "/run/conduwuit/conduwuit.sock"
|
||||
# example: "/run/continuwuity/continuwuity.sock"
|
||||
#
|
||||
#unix_socket_path =
|
||||
|
||||
@@ -75,23 +76,23 @@
|
||||
#
|
||||
#unix_socket_perms = 660
|
||||
|
||||
# This is the only directory where conduwuit will save its data, including
|
||||
# media. Note: this was previously "/var/lib/matrix-conduit".
|
||||
# This is the only directory where continuwuity will save its data,
|
||||
# including media. Note: this was previously "/var/lib/matrix-conduit".
|
||||
#
|
||||
# YOU NEED TO EDIT THIS.
|
||||
#
|
||||
# example: "/var/lib/conduwuit"
|
||||
# example: "/var/lib/continuwuity"
|
||||
#
|
||||
#database_path =
|
||||
|
||||
# conduwuit supports online database backups using RocksDB's Backup engine
|
||||
# API. To use this, set a database backup path that conduwuit can write
|
||||
# to.
|
||||
# continuwuity supports online database backups using RocksDB's Backup
|
||||
# engine API. To use this, set a database backup path that continuwuity
|
||||
# can write to.
|
||||
#
|
||||
# For more information, see:
|
||||
# https://conduwuit.puppyirl.gay/maintenance.html#backups
|
||||
# https://continuwuity.org/maintenance.html#backups
|
||||
#
|
||||
# example: "/opt/conduwuit-db-backups"
|
||||
# example: "/opt/continuwuity-db-backups"
|
||||
#
|
||||
#database_backup_path =
|
||||
|
||||
@@ -112,14 +113,14 @@
|
||||
#
|
||||
#new_user_displayname_suffix = "🏳️⚧️"
|
||||
|
||||
# If enabled, conduwuit will send a simple GET request periodically to
|
||||
# If enabled, continuwuity will send a simple GET request periodically to
|
||||
# `https://continuwuity.org/.well-known/continuwuity/announcements` for any new
|
||||
# announcements or major updates. This is not an update check endpoint.
|
||||
#
|
||||
#allow_announcements_check =
|
||||
#allow_announcements_check = true
|
||||
|
||||
# Set this to any float value to multiply conduwuit's in-memory LRU caches
|
||||
# with such as "auth_chain_cache_capacity".
|
||||
# Set this to any float value to multiply continuwuity's in-memory LRU
|
||||
# caches with such as "auth_chain_cache_capacity".
|
||||
#
|
||||
# May be useful if you have significant memory to spare to increase
|
||||
# performance.
|
||||
@@ -131,7 +132,7 @@
|
||||
#
|
||||
#cache_capacity_modifier = 1.0
|
||||
|
||||
# Set this to any float value in megabytes for conduwuit to tell the
|
||||
# Set this to any float value in megabytes for continuwuity to tell the
|
||||
# database engine that this much memory is available for database read
|
||||
# caches.
|
||||
#
|
||||
@@ -145,7 +146,7 @@
|
||||
#
|
||||
#db_cache_capacity_mb = varies by system
|
||||
|
||||
# Set this to any float value in megabytes for conduwuit to tell the
|
||||
# Set this to any float value in megabytes for continuwuity to tell the
|
||||
# database engine that this much memory is available for database write
|
||||
# caches.
|
||||
#
|
||||
@@ -250,9 +251,9 @@
|
||||
# Enable using *only* TCP for querying your specified nameservers instead
|
||||
# of UDP.
|
||||
#
|
||||
# If you are running conduwuit in a container environment, this config
|
||||
# If you are running continuwuity in a container environment, this config
|
||||
# option may need to be enabled. For more details, see:
|
||||
# https://conduwuit.puppyirl.gay/troubleshooting.html#potential-dns-issues-when-using-docker
|
||||
# https://continuwuity.org/troubleshooting.html#potential-dns-issues-when-using-docker
|
||||
#
|
||||
#query_over_tcp_only = false
|
||||
|
||||
@@ -418,9 +419,9 @@
|
||||
# tokens. Multiple tokens can be added if you separate them with
|
||||
# whitespace
|
||||
#
|
||||
# conduwuit must be able to access the file, and it must not be empty
|
||||
# continuwuity must be able to access the file, and it must not be empty
|
||||
#
|
||||
# example: "/etc/conduwuit/.reg_token"
|
||||
# example: "/etc/continuwuity/.reg_token"
|
||||
#
|
||||
#registration_token_file =
|
||||
|
||||
@@ -512,16 +513,16 @@
|
||||
#allow_room_creation = true
|
||||
|
||||
# Set to false to disable users from joining or creating room versions
|
||||
# that aren't officially supported by conduwuit.
|
||||
# that aren't officially supported by continuwuity.
|
||||
#
|
||||
# conduwuit officially supports room versions 6 - 11.
|
||||
# continuwuity officially supports room versions 6 - 11.
|
||||
#
|
||||
# conduwuit has slightly experimental (though works fine in practice)
|
||||
# continuwuity has slightly experimental (though works fine in practice)
|
||||
# support for versions 3 - 5.
|
||||
#
|
||||
#allow_unstable_room_versions = true
|
||||
|
||||
# Default room version conduwuit will create rooms with.
|
||||
# Default room version continuwuity will create rooms with.
|
||||
#
|
||||
# Per spec, room version 11 is the default.
|
||||
#
|
||||
@@ -587,7 +588,7 @@
|
||||
# Servers listed here will be used to gather public keys of other servers
|
||||
# (notary trusted key servers).
|
||||
#
|
||||
# Currently, conduwuit doesn't support inbound batched key requests, so
|
||||
# Currently, continuwuity doesn't support inbound batched key requests, so
|
||||
# this list should only contain other Synapse servers.
|
||||
#
|
||||
# example: ["matrix.org", "tchncs.de"]
|
||||
@@ -628,7 +629,7 @@
|
||||
#
|
||||
#trusted_server_batch_size = 1024
|
||||
|
||||
# Max log level for conduwuit. Allows debug, info, warn, or error.
|
||||
# Max log level for continuwuity. Allows debug, info, warn, or error.
|
||||
#
|
||||
# See also:
|
||||
# https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives
|
||||
@@ -649,8 +650,9 @@
|
||||
#
|
||||
#log_span_events = "none"
|
||||
|
||||
# Configures whether CONDUWUIT_LOG EnvFilter matches values using regular
|
||||
# expressions. See the tracing_subscriber documentation on Directives.
|
||||
# Configures whether CONTINUWUITY_LOG EnvFilter matches values using
|
||||
# regular expressions. See the tracing_subscriber documentation on
|
||||
# Directives.
|
||||
#
|
||||
#log_filter_regex = true
|
||||
|
||||
@@ -718,7 +720,7 @@
|
||||
# This takes priority over "turn_secret" first, and falls back to
|
||||
# "turn_secret" if invalid or failed to open.
|
||||
#
|
||||
# example: "/etc/conduwuit/.turn_secret"
|
||||
# example: "/etc/continuwuity/.turn_secret"
|
||||
#
|
||||
#turn_secret_file =
|
||||
|
||||
@@ -726,12 +728,12 @@
|
||||
#
|
||||
#turn_ttl = 86400
|
||||
|
||||
# List/vector of room IDs or room aliases that conduwuit will make newly
|
||||
# registered users join. The rooms specified must be rooms that you have
|
||||
# joined at least once on the server, and must be public.
|
||||
# List/vector of room IDs or room aliases that continuwuity will make
|
||||
# newly registered users join. The rooms specified must be rooms that you
|
||||
# have joined at least once on the server, and must be public.
|
||||
#
|
||||
# example: ["#conduwuit:puppygock.gay",
|
||||
# "!eoIzvAvVwY23LPDay8:puppygock.gay"]
|
||||
# example: ["#continuwuity:continuwuity.org",
|
||||
# "!main-1:continuwuity.org"]
|
||||
#
|
||||
#auto_join_rooms = []
|
||||
|
||||
@@ -754,10 +756,10 @@
|
||||
#
|
||||
#auto_deactivate_banned_room_attempts = false
|
||||
|
||||
# RocksDB log level. This is not the same as conduwuit's log level. This
|
||||
# is the log level for the RocksDB engine/library which show up in your
|
||||
# database folder/path as `LOG` files. conduwuit will log RocksDB errors
|
||||
# as normal through tracing or panics if severe for safety.
|
||||
# RocksDB log level. This is not the same as continuwuity's log level.
|
||||
# This is the log level for the RocksDB engine/library which show up in
|
||||
# your database folder/path as `LOG` files. continuwuity will log RocksDB
|
||||
# errors as normal through tracing or panics if severe for safety.
|
||||
#
|
||||
#rocksdb_log_level = "error"
|
||||
|
||||
@@ -777,7 +779,7 @@
|
||||
# Set this to true to use RocksDB config options that are tailored to HDDs
|
||||
# (slower device storage).
|
||||
#
|
||||
# It is worth noting that by default, conduwuit will use RocksDB with
|
||||
# It is worth noting that by default, continuwuity will use RocksDB with
|
||||
# Direct IO enabled. *Generally* speaking this improves performance as it
|
||||
# bypasses buffered I/O (system page cache). However there is a potential
|
||||
# chance that Direct IO may cause issues with database operations if your
|
||||
@@ -785,7 +787,7 @@
|
||||
# possibly ZFS filesystem. RocksDB generally deals/corrects these issues
|
||||
# but it cannot account for all setups. If you experience any weird
|
||||
# RocksDB issues, try enabling this option as it turns off Direct IO and
|
||||
# feel free to report in the conduwuit Matrix room if this option fixes
|
||||
# feel free to report in the continuwuity Matrix room if this option fixes
|
||||
# your DB issues.
|
||||
#
|
||||
# For more information, see:
|
||||
@@ -840,7 +842,7 @@
|
||||
# as they all differ. See their `kDefaultCompressionLevel`.
|
||||
#
|
||||
# Note when using the default value we may override it with a setting
|
||||
# tailored specifically conduwuit.
|
||||
# tailored specifically for continuwuity.
|
||||
#
|
||||
#rocksdb_compression_level = 32767
|
||||
|
||||
@@ -856,7 +858,7 @@
|
||||
# algorithm.
|
||||
#
|
||||
# Note when using the default value we may override it with a setting
|
||||
# tailored specifically conduwuit.
|
||||
# tailored specifically for continuwuity.
|
||||
#
|
||||
#rocksdb_bottommost_compression_level = 32767
|
||||
|
||||
@@ -896,13 +898,13 @@
|
||||
# 0 = AbsoluteConsistency
|
||||
# 1 = TolerateCorruptedTailRecords (default)
|
||||
# 2 = PointInTime (use me if trying to recover)
|
||||
# 3 = SkipAnyCorruptedRecord (you now voided your Conduwuit warranty)
|
||||
# 3 = SkipAnyCorruptedRecord (you now voided your Continuwuity warranty)
|
||||
#
|
||||
# For more information on these modes, see:
|
||||
# https://github.com/facebook/rocksdb/wiki/WAL-Recovery-Modes
|
||||
#
|
||||
# For more details on recovering a corrupt database, see:
|
||||
# https://conduwuit.puppyirl.gay/troubleshooting.html#database-corruption
|
||||
# https://continuwuity.org/troubleshooting.html#database-corruption
|
||||
#
|
||||
#rocksdb_recovery_mode = 1
|
||||
|
||||
@@ -942,7 +944,7 @@
|
||||
# - Disabling repair mode and restarting the server is recommended after
|
||||
# running the repair.
|
||||
#
|
||||
# See https://conduwuit.puppyirl.gay/troubleshooting.html#database-corruption for more details on recovering a corrupt database.
|
||||
# See https://continuwuity.org/troubleshooting.html#database-corruption for more details on recovering a corrupt database.
|
||||
#
|
||||
#rocksdb_repair = false
|
||||
|
||||
@@ -967,9 +969,9 @@
|
||||
#rocksdb_compaction_ioprio_idle = true
|
||||
|
||||
# Enables RocksDB compaction. You should never ever have to set this
|
||||
# option to false. If you for some reason find yourself needing to use this
|
||||
# option as part of troubleshooting or a bug, please reach out to us in
|
||||
# the conduwuit Matrix room with information and details.
|
||||
# option to false. If you for some reason find yourself needing to use
|
||||
# this option as part of troubleshooting or a bug, please reach out to us
|
||||
# in the continuwuity Matrix room with information and details.
|
||||
#
|
||||
# Disabling compaction will lead to a significantly bloated and
|
||||
# explosively large database, gradually poor performance, unnecessarily
|
||||
@@ -995,7 +997,7 @@
|
||||
# purposes such as recovering/recreating your admin room, or inviting
|
||||
# yourself back.
|
||||
#
|
||||
# See https://conduwuit.puppyirl.gay/troubleshooting.html#lost-access-to-admin-room for other ways to get back into your admin room.
|
||||
# See https://continuwuity.org/troubleshooting.html#lost-access-to-admin-room for other ways to get back into your admin room.
|
||||
#
|
||||
# Once this password is unset, all sessions will be logged out for
|
||||
# security purposes.
|
||||
@@ -1010,8 +1012,8 @@
|
||||
|
||||
# Allow local (your server only) presence updates/requests.
|
||||
#
|
||||
# Note that presence on conduwuit is very fast unlike Synapse's. If using
|
||||
# outgoing presence, this MUST be enabled.
|
||||
# Note that presence on continuwuity is very fast unlike Synapse's. If
|
||||
# using outgoing presence, this MUST be enabled.
|
||||
#
|
||||
#allow_local_presence = true
|
||||
|
||||
@@ -1019,7 +1021,7 @@
|
||||
#
|
||||
# This option receives presence updates from other servers, but does not
|
||||
# send any unless `allow_outgoing_presence` is true. Note that presence on
|
||||
# conduwuit is very fast unlike Synapse's.
|
||||
# continuwuity is very fast unlike Synapse's.
|
||||
#
|
||||
#allow_incoming_presence = true
|
||||
|
||||
@@ -1027,8 +1029,8 @@
|
||||
#
|
||||
# This option sends presence updates to other servers, but does not
|
||||
# receive any unless `allow_incoming_presence` is true. Note that presence
|
||||
# on conduwuit is very fast unlike Synapse's. If using outgoing presence,
|
||||
# you MUST enable `allow_local_presence` as well.
|
||||
# on continuwuity is very fast unlike Synapse's. If using outgoing
|
||||
# presence, you MUST enable `allow_local_presence` as well.
|
||||
#
|
||||
#allow_outgoing_presence = true
|
||||
|
||||
@@ -1081,8 +1083,8 @@
|
||||
#
|
||||
#typing_client_timeout_max_s = 45
|
||||
|
||||
# Set this to true for conduwuit to compress HTTP response bodies using
|
||||
# zstd. This option does nothing if conduwuit was not built with
|
||||
# Set this to true for continuwuity to compress HTTP response bodies using
|
||||
# zstd. This option does nothing if continuwuity was not built with
|
||||
# `zstd_compression` feature. Please be aware that enabling HTTP
|
||||
# compression may weaken TLS. Most users should not need to enable this.
|
||||
# See https://breachattack.com/ and https://wikipedia.org/wiki/BREACH
|
||||
@@ -1090,8 +1092,8 @@
|
||||
#
|
||||
#zstd_compression = false
|
||||
|
||||
# Set this to true for conduwuit to compress HTTP response bodies using
|
||||
# gzip. This option does nothing if conduwuit was not built with
|
||||
# Set this to true for continuwuity to compress HTTP response bodies using
|
||||
# gzip. This option does nothing if continuwuity was not built with
|
||||
# `gzip_compression` feature. Please be aware that enabling HTTP
|
||||
# compression may weaken TLS. Most users should not need to enable this.
|
||||
# See https://breachattack.com/ and https://wikipedia.org/wiki/BREACH before
|
||||
@@ -1102,8 +1104,8 @@
|
||||
#
|
||||
#gzip_compression = false
|
||||
|
||||
# Set this to true for conduwuit to compress HTTP response bodies using
|
||||
# brotli. This option does nothing if conduwuit was not built with
|
||||
# Set this to true for continuwuity to compress HTTP response bodies using
|
||||
# brotli. This option does nothing if continuwuity was not built with
|
||||
# `brotli_compression` feature. Please be aware that enabling HTTP
|
||||
# compression may weaken TLS. Most users should not need to enable this.
|
||||
# See https://breachattack.com/ and https://wikipedia.org/wiki/BREACH
|
||||
@@ -1165,7 +1167,7 @@
|
||||
# Otherwise setting this to false reduces filesystem clutter and overhead
|
||||
# for managing these symlinks in the directory. This is now disabled by
|
||||
# default. You may still return to upstream Conduit but you have to run
|
||||
# conduwuit at least once with this set to true and allow the
|
||||
# continuwuity at least once with this set to true and allow the
|
||||
# media_startup_check to take place before shutting down to return to
|
||||
# Conduit.
|
||||
#
|
||||
@@ -1182,23 +1184,13 @@
|
||||
#
|
||||
#prune_missing_media = false
|
||||
|
||||
# Vector list of regex patterns of server names that conduwuit will refuse
|
||||
# to download remote media from.
|
||||
#
|
||||
# example: ["badserver\.tld$", "badphrase", "19dollarfortnitecards"]
|
||||
#
|
||||
#prevent_media_downloads_from = []
|
||||
|
||||
# List of forbidden server names via regex patterns that we will block
|
||||
# incoming AND outgoing federation with, and block client room joins /
|
||||
# remote user invites.
|
||||
#
|
||||
# Additionally, it will hide messages from these servers for all users
|
||||
# on this server.
|
||||
#
|
||||
# Note that your messages can still make it to forbidden servers through
|
||||
# backfilling. Events we receive from forbidden servers via backfill will
|
||||
# be stored in the database, but will not be sent to the client.
|
||||
# backfilling. Events we receive from forbidden servers via backfill
|
||||
# from servers we *do* federate with will be stored in the database.
|
||||
#
|
||||
# This check is applied on the room ID, room alias, sender server name,
|
||||
# sender user's server name, inbound federation X-Matrix origin, and
|
||||
@@ -1207,7 +1199,7 @@
|
||||
# You can set this to ["*"] to block all servers by default, and then
|
||||
# use `allowed_remote_server_names` to allow only specific servers.
|
||||
#
|
||||
# example: ["badserver\.tld$", "badphrase", "19dollarfortnitecards"]
|
||||
# example: ["badserver\\.tld$", "badphrase", "19dollarfortnitecards"]
|
||||
#
|
||||
#forbidden_remote_server_names = []
|
||||
|
||||
@@ -1216,10 +1208,17 @@
|
||||
#
|
||||
# This option has no effect if `forbidden_remote_server_names` is empty.
|
||||
#
|
||||
# example: ["goodserver\.tld$", "goodphrase"]
|
||||
# example: ["goodserver\\.tld$", "goodphrase"]
|
||||
#
|
||||
#allowed_remote_server_names = []
|
||||
|
||||
# Vector list of regex patterns of server names that continuwuity will
|
||||
# refuse to download remote media from.
|
||||
#
|
||||
# example: ["badserver\.tld$", "badphrase", "19dollarfortnitecards"]
|
||||
#
|
||||
#prevent_media_downloads_from = []
|
||||
|
||||
# List of forbidden server names via regex patterns that we will block all
|
||||
# outgoing federated room directory requests for. Useful for preventing
|
||||
# our users from wandering into bad servers or spaces.
|
||||
@@ -1228,8 +1227,31 @@
|
||||
#
|
||||
#forbidden_remote_room_directory_server_names = []
|
||||
|
||||
# Vector list of regex patterns of server names that continuwuity will not
|
||||
# send messages to the client from.
|
||||
#
|
||||
# Note that there is no way for clients to receive messages once a server
|
||||
# has become unignored without doing a full sync. This is a protocol
|
||||
# limitation with the current sync protocols. This means this is somewhat
|
||||
# of a nuclear option.
|
||||
#
|
||||
# example: ["reallybadserver\.tld$", "reallybadphrase",
|
||||
# "69dollarfortnitecards"]
|
||||
#
|
||||
#ignore_messages_from_server_names = []
|
||||
|
||||
# Send messages from users that the user has ignored to the client.
|
||||
#
|
||||
# There is no way for clients to receive messages sent while a user was
|
||||
# ignored without doing a full sync. This is a protocol limitation with
|
||||
# the current sync protocols. Disabling this option will move
|
||||
# responsibility of ignoring messages to the client, which can avoid this
|
||||
# limitation.
|
||||
#
|
||||
#send_messages_from_ignored_users_to_client = false
|
||||
|
||||
# Vector list of IPv4 and IPv6 CIDR ranges / subnets *in quotes* that you
|
||||
# do not want conduwuit to send outbound requests to. Defaults to
|
||||
# do not want continuwuity to send outbound requests to. Defaults to
|
||||
# RFC1918, unroutable, loopback, multicast, and testnet addresses for
|
||||
# security.
|
||||
#
|
||||
@@ -1379,26 +1401,26 @@
|
||||
|
||||
# Allow admins to enter commands in rooms other than "#admins" (admin
|
||||
# room) by prefixing your message with "\!admin" or "\\!admin" followed up
|
||||
# a normal conduwuit admin command. The reply will be publicly visible to
|
||||
# the room, originating from the sender.
|
||||
# a normal continuwuity admin command. The reply will be publicly visible
|
||||
# to the room, originating from the sender.
|
||||
#
|
||||
# example: \\!admin debug ping puppygock.gay
|
||||
#
|
||||
#admin_escape_commands = true
|
||||
|
||||
# Automatically activate the conduwuit admin room console / CLI on
|
||||
# startup. This option can also be enabled with `--console` conduwuit
|
||||
# Automatically activate the continuwuity admin room console / CLI on
|
||||
# startup. This option can also be enabled with `--console` continuwuity
|
||||
# argument.
|
||||
#
|
||||
#admin_console_automatic = false
|
||||
|
||||
# List of admin commands to execute on startup.
|
||||
#
|
||||
# This option can also be configured with the `--execute` conduwuit
|
||||
# This option can also be configured with the `--execute` continuwuity
|
||||
# argument and can take standard shell commands and environment variables
|
||||
#
|
||||
# For example: `./conduwuit --execute "server admin-notice conduwuit has
|
||||
# started up at $(date)"`
|
||||
# For example: `./continuwuity --execute "server admin-notice continuwuity
|
||||
# has started up at $(date)"`
|
||||
#
|
||||
# example: admin_execute = ["debug ping puppygock.gay", "debug echo hi"]`
|
||||
#
|
||||
@@ -1406,7 +1428,7 @@
|
||||
|
||||
# Ignore errors in startup commands.
|
||||
#
|
||||
# If false, conduwuit will error and fail to start if an admin execute
|
||||
# If false, continuwuity will error and fail to start if an admin execute
|
||||
# command (`--execute` / `admin_execute`) fails.
|
||||
#
|
||||
#admin_execute_errors_ignore = false
|
||||
@@ -1427,15 +1449,14 @@
|
||||
# The default room tag to apply on the admin room.
|
||||
#
|
||||
# On some clients like Element, the room tag "m.server_notice" is a
|
||||
# special pinned room at the very bottom of your room list. The conduwuit
|
||||
# admin room can be pinned here so you always have an easy-to-access
|
||||
# shortcut dedicated to your admin room.
|
||||
# special pinned room at the very bottom of your room list. The
|
||||
# continuwuity admin room can be pinned here so you always have an
|
||||
# easy-to-access shortcut dedicated to your admin room.
|
||||
#
|
||||
#admin_room_tag = "m.server_notice"
|
||||
|
||||
# Sentry.io crash/panic reporting, performance monitoring/metrics, etc.
|
||||
# This is NOT enabled by default. conduwuit's default Sentry reporting
|
||||
# endpoint domain is `o4506996327251968.ingest.us.sentry.io`.
|
||||
# This is NOT enabled by default.
|
||||
#
|
||||
#sentry = false
|
||||
|
||||
@@ -1443,7 +1464,7 @@
|
||||
#
|
||||
#sentry_endpoint = ""
|
||||
|
||||
# Report your conduwuit server_name in Sentry.io crash reports and
|
||||
# Report your continuwuity server_name in Sentry.io crash reports and
|
||||
# metrics.
|
||||
#
|
||||
#sentry_send_server_name = false
|
||||
@@ -1480,7 +1501,7 @@
|
||||
# Enable the tokio-console. This option is only relevant to developers.
|
||||
#
|
||||
# For more information, see:
|
||||
# https://conduwuit.puppyirl.gay/development.html#debugging-with-tokio-console
|
||||
# https://continuwuity.org/development.html#debugging-with-tokio-console
|
||||
#
|
||||
#tokio_console = false
|
||||
|
||||
@@ -1620,19 +1641,29 @@
|
||||
#
|
||||
#server =
|
||||
|
||||
# This item is undocumented. Please contribute documentation for it.
|
||||
# URL to a support page for the server, which will be served as part of
|
||||
# the MSC1929 server support endpoint at /.well-known/matrix/support.
|
||||
# Will be included alongside any contact information
|
||||
#
|
||||
#support_page =
|
||||
|
||||
# This item is undocumented. Please contribute documentation for it.
|
||||
# Role string for server support contacts, to be served as part of the
|
||||
# MSC1929 server support endpoint at /.well-known/matrix/support.
|
||||
#
|
||||
#support_role =
|
||||
#support_role = "m.role.admin"
|
||||
|
||||
# This item is undocumented. Please contribute documentation for it.
|
||||
# Email address for server support contacts, to be served as part of the
|
||||
# MSC1929 server support endpoint.
|
||||
# This will be used along with support_mxid if specified.
|
||||
#
|
||||
#support_email =
|
||||
|
||||
# This item is undocumented. Please contribute documentation for it.
|
||||
# Matrix ID for server support contacts, to be served as part of the
|
||||
# MSC1929 server support endpoint.
|
||||
# This will be used along with support_email if specified.
|
||||
#
|
||||
# If no email or mxid is specified, all of the server's admins will be
|
||||
# listed.
|
||||
#
|
||||
#support_mxid =
|
||||
|
||||
|
||||
Vendored
+2
-2
@@ -1,4 +1,4 @@
|
||||
# conduwuit for Debian
|
||||
# Continuwuity for Debian
|
||||
|
||||
Information about downloading and deploying the Debian package. This may also be
|
||||
referenced for other `apt`-based distros such as Ubuntu.
|
||||
@@ -22,7 +22,7 @@ options in `/etc/conduwuit/conduwuit.toml`.
|
||||
|
||||
### Running
|
||||
|
||||
The package uses the [`conduwuit.service`](../configuration/examples.md#example-systemd-unit-file) systemd unit file to start and stop conduwuit. The binary is installed at `/usr/sbin/conduwuit`.
|
||||
The package uses the [`conduwuit.service`](../configuration/examples.md#example-systemd-unit-file) systemd unit file to start and stop Continuwuity. The binary is installed at `/usr/sbin/conduwuit`.
|
||||
|
||||
This package assumes by default that conduwuit will be placed behind a reverse proxy. The default config options apply (listening on `localhost` and TCP port `6167`). Matrix federation requires a valid domain name and TLS, so you will need to set up TLS certificates and renewal for it to work properly if you intend to federate.
|
||||
|
||||
|
||||
Vendored
+4
-3
@@ -1,9 +1,10 @@
|
||||
[Unit]
|
||||
Description=conduwuit Matrix homeserver
|
||||
|
||||
Description=Continuwuity - Matrix homeserver
|
||||
Wants=network-online.target
|
||||
After=network-online.target
|
||||
Documentation=https://continuwuity.org/
|
||||
Alias=matrix-conduwuit.service
|
||||
Documentation=https://conduwuit.puppyirl.gay/
|
||||
|
||||
[Service]
|
||||
DynamicUser=yes
|
||||
@@ -11,7 +12,7 @@ User=conduwuit
|
||||
Group=conduwuit
|
||||
Type=notify
|
||||
|
||||
Environment="CONDUWUIT_CONFIG=/etc/conduwuit/conduwuit.toml"
|
||||
Environment="CONTINUWUITY_CONFIG=/etc/conduwuit/conduwuit.toml"
|
||||
|
||||
ExecStart=/usr/sbin/conduwuit
|
||||
|
||||
|
||||
+49
-25
@@ -1,15 +1,16 @@
|
||||
ARG RUST_VERSION=1
|
||||
ARG DEBIAN_VERSION=bookworm
|
||||
|
||||
FROM --platform=$BUILDPLATFORM docker.io/tonistiigi/xx AS xx
|
||||
FROM --platform=$BUILDPLATFORM rust:${RUST_VERSION}-slim-bookworm AS base
|
||||
FROM --platform=$BUILDPLATFORM rust:${RUST_VERSION}-slim-bookworm AS toolchain
|
||||
FROM --platform=$BUILDPLATFORM rust:${RUST_VERSION}-slim-${DEBIAN_VERSION} AS base
|
||||
FROM --platform=$BUILDPLATFORM rust:${RUST_VERSION}-slim-${DEBIAN_VERSION} AS toolchain
|
||||
|
||||
# Prevent deletion of apt cache
|
||||
RUN rm -f /etc/apt/apt.conf.d/docker-clean
|
||||
|
||||
# Match Rustc version as close as possible
|
||||
# rustc -vV
|
||||
ARG LLVM_VERSION=19
|
||||
ARG LLVM_VERSION=20
|
||||
# ENV RUSTUP_TOOLCHAIN=${RUST_VERSION}
|
||||
|
||||
# Install repo tools
|
||||
@@ -18,13 +19,22 @@ ARG LLVM_VERSION=19
|
||||
# Line three: for xx-verify
|
||||
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
|
||||
--mount=type=cache,target=/var/lib/apt,sharing=locked \
|
||||
apt-get update && apt-get install -y \
|
||||
clang-${LLVM_VERSION} lld-${LLVM_VERSION} pkg-config make jq \
|
||||
curl git \
|
||||
apt-get update && apt-get install -y \
|
||||
pkg-config make jq \
|
||||
curl git software-properties-common \
|
||||
file
|
||||
|
||||
# LLVM packages
|
||||
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
|
||||
--mount=type=cache,target=/var/lib/apt,sharing=locked \
|
||||
curl https://apt.llvm.org/llvm.sh > llvm.sh && \
|
||||
chmod +x llvm.sh && \
|
||||
./llvm.sh ${LLVM_VERSION} && \
|
||||
rm llvm.sh
|
||||
|
||||
# Create symlinks for LLVM tools
|
||||
RUN <<EOF
|
||||
set -o xtrace
|
||||
# clang
|
||||
ln -s /usr/bin/clang-${LLVM_VERSION} /usr/bin/clang
|
||||
ln -s "/usr/bin/clang++-${LLVM_VERSION}" "/usr/bin/clang++"
|
||||
@@ -38,7 +48,7 @@ EOF
|
||||
|
||||
# Developer tool versions
|
||||
# renovate: datasource=github-releases depName=cargo-bins/cargo-binstall
|
||||
ENV BINSTALL_VERSION=1.12.3
|
||||
ENV BINSTALL_VERSION=1.13.0
|
||||
# renovate: datasource=github-releases depName=psastras/sbom-rs
|
||||
ENV CARGO_SBOM_VERSION=0.9.1
|
||||
# renovate: datasource=crate depName=lddtree
|
||||
@@ -46,6 +56,7 @@ ENV LDDTREE_VERSION=0.3.7
|
||||
|
||||
# Install unpackaged tools
|
||||
RUN <<EOF
|
||||
set -o xtrace
|
||||
curl --retry 5 -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash
|
||||
cargo binstall --no-confirm cargo-sbom --version $CARGO_SBOM_VERSION
|
||||
cargo binstall --no-confirm lddtree --version $LDDTREE_VERSION
|
||||
@@ -59,7 +70,7 @@ ARG TARGETPLATFORM
|
||||
# xx-* are xx-specific meta-packages
|
||||
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
|
||||
--mount=type=cache,target=/var/lib/apt,sharing=locked \
|
||||
xx-apt-get install -y \
|
||||
xx-apt-get install -y \
|
||||
xx-c-essentials xx-cxx-essentials pkg-config \
|
||||
liburing-dev
|
||||
|
||||
@@ -75,6 +86,7 @@ RUN echo "CARGO_INCREMENTAL=0" >> /etc/environment
|
||||
|
||||
# Configure pkg-config
|
||||
RUN <<EOF
|
||||
set -o xtrace
|
||||
echo "PKG_CONFIG_LIBDIR=/usr/lib/$(xx-info)/pkgconfig" >> /etc/environment
|
||||
echo "PKG_CONFIG=/usr/bin/$(xx-info)-pkg-config" >> /etc/environment
|
||||
echo "PKG_CONFIG_ALLOW_CROSS=true" >> /etc/environment
|
||||
@@ -82,12 +94,14 @@ EOF
|
||||
|
||||
# Configure cc to use clang version
|
||||
RUN <<EOF
|
||||
set -o xtrace
|
||||
echo "CC=clang" >> /etc/environment
|
||||
echo "CXX=clang++" >> /etc/environment
|
||||
EOF
|
||||
|
||||
# Cross-language LTO
|
||||
RUN <<EOF
|
||||
set -o xtrace
|
||||
echo "CFLAGS=-flto" >> /etc/environment
|
||||
echo "CXXFLAGS=-flto" >> /etc/environment
|
||||
# Linker is set to target-compatible clang by xx
|
||||
@@ -98,6 +112,7 @@ EOF
|
||||
ARG TARGET_CPU=
|
||||
RUN <<EOF
|
||||
set -o allexport
|
||||
set -o xtrace
|
||||
. /etc/environment
|
||||
if [ -n "${TARGET_CPU}" ]; then
|
||||
echo "CFLAGS='${CFLAGS} -march=${TARGET_CPU}'" >> /etc/environment
|
||||
@@ -111,37 +126,44 @@ RUN mkdir /out
|
||||
|
||||
FROM toolchain AS builder
|
||||
|
||||
# Conduwuit version info
|
||||
ARG COMMIT_SHA=
|
||||
ARG CONDUWUIT_VERSION_EXTRA=
|
||||
ENV CONDUWUIT_VERSION_EXTRA=$CONDUWUIT_VERSION_EXTRA
|
||||
RUN <<EOF
|
||||
if [ -z "${CONDUWUIT_VERSION_EXTRA}" ]; then
|
||||
echo "CONDUWUIT_VERSION_EXTRA='$(set -e; git rev-parse --short ${COMMIT_SHA:-HEAD} || echo unknown revision)'" >> /etc/environment
|
||||
fi
|
||||
EOF
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
|
||||
# Verify environment configuration
|
||||
RUN cat /etc/environment
|
||||
RUN xx-cargo --print-target-triple
|
||||
|
||||
# Get source
|
||||
COPY . .
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
|
||||
# Verify environment configuration
|
||||
RUN xx-cargo --print-target-triple
|
||||
|
||||
# Conduwuit version info
|
||||
ARG GIT_COMMIT_HASH=
|
||||
ARG GIT_COMMIT_HASH_SHORT=
|
||||
ARG GIT_REMOTE_URL=
|
||||
ARG GIT_REMOTE_COMMIT_URL=
|
||||
ARG CONDUWUIT_VERSION_EXTRA=
|
||||
ARG CONTINUWUITY_VERSION_EXTRA=
|
||||
ENV GIT_COMMIT_HASH=$GIT_COMMIT_HASH
|
||||
ENV GIT_COMMIT_HASH_SHORT=$GIT_COMMIT_HASH_SHORT
|
||||
ENV GIT_REMOTE_URL=$GIT_REMOTE_URL
|
||||
ENV GIT_REMOTE_COMMIT_URL=$GIT_REMOTE_COMMIT_URL
|
||||
ENV CONDUWUIT_VERSION_EXTRA=$CONDUWUIT_VERSION_EXTRA
|
||||
ENV CONTINUWUITY_VERSION_EXTRA=$CONTINUWUITY_VERSION_EXTRA
|
||||
|
||||
ARG RUST_PROFILE=release
|
||||
|
||||
# Build the binary
|
||||
RUN --mount=type=cache,target=/usr/local/cargo/registry \
|
||||
--mount=type=cache,target=/usr/local/cargo/git/db \
|
||||
--mount=type=cache,target=/app/target \
|
||||
--mount=type=cache,target=/app/target,id=cargo-target-${TARGET_CPU}-${TARGETPLATFORM}-${RUST_PROFILE} \
|
||||
bash <<'EOF'
|
||||
set -o allexport
|
||||
set -o xtrace
|
||||
. /etc/environment
|
||||
TARGET_DIR=($(cargo metadata --no-deps --format-version 1 | \
|
||||
jq -r ".target_directory"))
|
||||
mkdir /out/sbin
|
||||
PACKAGE=conduwuit
|
||||
xx-cargo build --locked --release \
|
||||
xx-cargo build --locked --profile ${RUST_PROFILE} \
|
||||
-p $PACKAGE;
|
||||
BINARIES=($(cargo metadata --no-deps --format-version 1 | \
|
||||
jq -r ".packages[] | select(.name == \"$PACKAGE\") | .targets[] | select( .kind | map(. == \"bin\") | any ) | .name"))
|
||||
@@ -156,6 +178,7 @@ EOF
|
||||
RUN --mount=type=cache,target=/usr/local/cargo/registry \
|
||||
--mount=type=cache,target=/usr/local/cargo/git/db \
|
||||
bash <<'EOF'
|
||||
set -o xtrace
|
||||
mkdir /out/sbom
|
||||
typeset -A PACKAGES
|
||||
for BINARY in /out/sbin/*; do
|
||||
@@ -174,6 +197,7 @@ EOF
|
||||
|
||||
# Extract dynamically linked dependencies
|
||||
RUN <<EOF
|
||||
set -o xtrace
|
||||
mkdir /out/libs
|
||||
mkdir /out/libs-root
|
||||
for BINARY in /out/sbin/*; do
|
||||
|
||||
+2
-1
@@ -19,4 +19,5 @@
|
||||
- [Contributing](contributing.md)
|
||||
- [Testing](development/testing.md)
|
||||
- [Hot Reloading ("Live" Development)](development/hot_reload.md)
|
||||
- [conduwuit Community Code of Conduct](conduwuit_coc.md)
|
||||
- [Community (and Guidelines)](community.md)
|
||||
- [Security](security.md)
|
||||
|
||||
+5
-5
@@ -3,8 +3,8 @@
|
||||
## Getting help
|
||||
|
||||
If you run into any problems while setting up an Appservice: ask us in
|
||||
[#conduwuit:puppygock.gay](https://matrix.to/#/#conduwuit:puppygock.gay) or
|
||||
[open an issue on GitHub](https://github.com/girlbossceo/conduwuit/issues/new).
|
||||
[#continuwuity:continuwuity.org](https://matrix.to/#/#continuwuity:continuwuity.org) or
|
||||
[open an issue on Forgejo](https://forgejo.ellis.link/continuwuation/continuwuity/issues/new).
|
||||
|
||||
## Set up the appservice - general instructions
|
||||
|
||||
@@ -14,7 +14,7 @@ later starting it.
|
||||
|
||||
At some point the appservice guide should ask you to add a registration yaml
|
||||
file to the homeserver. In Synapse you would do this by adding the path to the
|
||||
homeserver.yaml, but in conduwuit you can do this from within Matrix:
|
||||
homeserver.yaml, but in Continuwuity you can do this from within Matrix:
|
||||
|
||||
First, go into the `#admins` room of your homeserver. The first person that
|
||||
registered on the homeserver automatically joins it. Then send a message into
|
||||
@@ -37,9 +37,9 @@ You can confirm it worked by sending a message like this:
|
||||
|
||||
The server bot should answer with `Appservices (1): your-bridge`
|
||||
|
||||
Then you are done. conduwuit will send messages to the appservices and the
|
||||
Then you are done. Continuwuity will send messages to the appservices and the
|
||||
appservice can send requests to the homeserver. You don't need to restart
|
||||
conduwuit, but if it doesn't work, restarting while the appservice is running
|
||||
Continuwuity, but if it doesn't work, restarting while the appservice is running
|
||||
could help.
|
||||
|
||||
## Appservice-specific instructions
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
# Continuwuity Community Guidelines
|
||||
|
||||
Welcome to the Continuwuity commuwunity! We're excited to have you here. Continuwuity is a
|
||||
continuation of the conduwuit homeserver, which in turn is a hard-fork of the Conduit homeserver,
|
||||
aimed at making Matrix more accessible and inclusive for everyone.
|
||||
|
||||
This space is dedicated to fostering a positive, supportive, and welcoming environment for everyone.
|
||||
These guidelines apply to all Continuwuity spaces, including our Matrix rooms and any other
|
||||
community channels that reference them. We've written these guidelines to help us all create an
|
||||
environment where everyone feels safe and respected.
|
||||
|
||||
For code and contribution guidelines, please refer to the
|
||||
[Contributor's Covenant](https://forgejo.ellis.link/continuwuation/continuwuity/src/branch/main/CODE_OF_CONDUCT.md).
|
||||
Below are additional guidelines specific to the Continuwuity community.
|
||||
|
||||
## Our Values and Expected Behaviors
|
||||
|
||||
We strive to create a community based on mutual respect, collaboration, and inclusivity. We expect
|
||||
all members to:
|
||||
|
||||
1. **Be Respectful and Inclusive**: Treat everyone with respect. We're committed to a community
|
||||
where everyone feels safe, regardless of background, identity, or experience. Discrimination,
|
||||
harassment, or hate speech won't be tolerated. Remember that each person experiences the world
|
||||
differently; share your own perspective and be open to learning about others'.
|
||||
|
||||
2. **Be Positive and Constructive**: Engage in discussions constructively and support each other.
|
||||
If you feel angry or frustrated, take a break before participating. Approach disagreements with
|
||||
the goal of understanding, not winning. Focus on the issue, not the person.
|
||||
|
||||
3. **Communicate Clearly and Kindly**: Our community includes neurodivergent individuals and those
|
||||
who may not appreciate sarcasm or subtlety. Communicate clearly and kindly. Avoid ambiguity and
|
||||
ensure your messages can be easily understood by all. Avoid placing the burden of education on
|
||||
marginalized groups; please make an effort to look into your questions before asking others for
|
||||
detailed explanations.
|
||||
|
||||
4. **Be Open to Improving Inclusivity**: Actively participate in making our community more inclusive.
|
||||
Report behaviour that contradicts these guidelines (see Reporting and Enforcement below) and be
|
||||
open to constructive feedback aimed at improving our community. Understand that discussing
|
||||
negative experiences can be emotionally taxing; focus on the message, not the tone.
|
||||
|
||||
5. **Commit to Our Values**: Building an inclusive community requires ongoing effort from everyone.
|
||||
Recognise that addressing bias and discrimination is a continuous process that needs commitment
|
||||
and action from all members.
|
||||
|
||||
## Unacceptable Behaviors
|
||||
|
||||
To ensure everyone feels safe and welcome, the following behaviors are considered unacceptable
|
||||
within the Continuwuity community:
|
||||
|
||||
* **Harassment and Discrimination**: Avoid offensive comments related to background, family status,
|
||||
gender, gender identity or expression, marital status, sex, sexual orientation, native language,
|
||||
age, ability, race and/or ethnicity, caste, national origin, socioeconomic status, religion,
|
||||
geographic location, or any other dimension of diversity. Don't deliberately misgender someone or
|
||||
question the legitimacy of their gender identity.
|
||||
|
||||
* **Violence and Threats**: Do not engage in any form of violence or threats, including inciting
|
||||
violence towards anyone or encouraging self-harm. Posting or threatening to post someone else's
|
||||
personally identifying information ("doxxing") is also forbidden.
|
||||
|
||||
* **Personal Attacks**: Disagreements happen, but they should never turn into personal attacks.
|
||||
Don't insult, demean, or belittle others.
|
||||
|
||||
* **Unwelcome Attention or Contact**: Avoid unwelcome sexual attention, inappropriate physical
|
||||
contact (or simulation thereof), sexualized comments, jokes, or imagery.
|
||||
|
||||
* **Disruption**: Do not engage in sustained disruption of discussions, events, or other
|
||||
community activities.
|
||||
|
||||
* **Bad Faith Actions**: Do not intentionally make false reports or otherwise abuse the reporting
|
||||
process.
|
||||
|
||||
This is not an exhaustive list. Any behaviour that makes others feel unsafe or unwelcome may be
|
||||
subject to enforcement action.
|
||||
|
||||
## Matrix Community
|
||||
|
||||
These Community Guidelines apply to the entire
|
||||
[Continuwuity Matrix Space](https://matrix.to/#/#space:continuwuity.org) and its rooms, including:
|
||||
|
||||
### [#continuwuity:continuwuity.org](https://matrix.to/#/#continuwuity:continuwuity.org)
|
||||
|
||||
This room is for support and discussions about Continuwuity. Ask questions, share insights, and help
|
||||
each other out while adhering to these guidelines.
|
||||
|
||||
We ask that this room remain focused on the Continuwuity software specifically: the team are
|
||||
typically happy to engage in conversations about related subjects in the off-topic room.
|
||||
|
||||
### [#offtopic:continuwuity.org](https://matrix.to/#/#offtopic:continuwuity.org)
|
||||
|
||||
For off-topic community conversations about any subject. While this room allows for a wide range of
|
||||
topics, the same guidelines apply. Please keep discussions respectful and inclusive, and avoid
|
||||
divisive or stressful subjects like specific country/world politics unless handled with exceptional
|
||||
care and respect for diverse viewpoints.
|
||||
|
||||
General topics, such as world events, are welcome as long as they follow the guidelines. If a member
|
||||
of the team asks for the conversation to end, please respect their decision.
|
||||
|
||||
### [#dev:continuwuity.org](https://matrix.to/#/#dev:continuwuity.org)
|
||||
|
||||
This room is dedicated to discussing active development of Continuwuity, including ongoing issues or
|
||||
code development. Collaboration here must follow these guidelines, and please consider raising
|
||||
[an issue](https://forgejo.ellis.link/continuwuation/continuwuity/issues) on the repository to help
|
||||
track progress.
|
||||
|
||||
## Reporting and Enforcement
|
||||
|
||||
We take these Community Guidelines seriously to protect our community members. If you witness or
|
||||
experience unacceptable behaviour, or have any other concerns, please report it.
|
||||
|
||||
**How to Report:**
|
||||
|
||||
* **Alert Moderators in the Room:** If you feel comfortable doing so, you can address the issue
|
||||
publicly in the relevant room by mentioning the moderation bot, `@rock:continuwuity.org`, which
|
||||
will immediately alert all available moderators.
|
||||
* **Direct Message:** If you're not comfortable raising the issue publicly, please send a direct
|
||||
message (DM) to one of the room moderators.
|
||||
|
||||
Reports will be handled with discretion. We will investigate promptly and thoroughly.
|
||||
|
||||
**Enforcement Actions:**
|
||||
|
||||
Anyone asked to stop unacceptable behaviour is expected to comply immediately. Failure to do so, or
|
||||
engaging in prohibited behaviour, may result in enforcement action. Moderators may take actions they
|
||||
deem appropriate, including but not limited to:
|
||||
|
||||
1. **Warning**: A direct message or public warning identifying the violation and requesting
|
||||
corrective action.
|
||||
2. **Temporary Mute**: Temporary restriction from participating in discussions for a specified
|
||||
period.
|
||||
3. **Kick or Ban**: Removal from a room (kick) or the entire community space (ban). Egregious or
|
||||
repeated violations may result in an immediate ban. Bans are typically permanent and reviewed
|
||||
only in exceptional circumstances.
|
||||
|
||||
Retaliation against those who report concerns in good faith will not be tolerated and will be
|
||||
subject to the same enforcement actions.
|
||||
|
||||
Together, let's build and maintain a community where everyone feels valued, safe, and respected.
|
||||
|
||||
— The Continuwuity Moderation Team
|
||||
@@ -1,93 +0,0 @@
|
||||
# conduwuit Community Code of Conduct
|
||||
|
||||
Welcome to the conduwuit community! We’re excited to have you here. conduwuit is
|
||||
a hard-fork of the Conduit homeserver, aimed at making Matrix more accessible
|
||||
and inclusive for everyone.
|
||||
|
||||
This space is dedicated to fostering a positive, supportive, and inclusive
|
||||
environment for everyone. This Code of Conduct applies to all conduwuit spaces,
|
||||
including any further community rooms that reference this CoC. Here are our
|
||||
guidelines to help maintain the welcoming atmosphere that sets conduwuit apart.
|
||||
|
||||
For the general foundational rules, please refer to the [Contributor's
|
||||
Covenant](https://github.com/girlbossceo/conduwuit/blob/main/CODE_OF_CONDUCT.md).
|
||||
Below are additional guidelines specific to the conduwuit community.
|
||||
|
||||
## Our Values and Guidelines
|
||||
|
||||
1. **Respect and Inclusivity**: We are committed to maintaining a community
|
||||
where everyone feels safe and respected. Discrimination, harassment, or hate
|
||||
speech of any kind will not be tolerated. Recognise that each community member
|
||||
experiences the world differently based on their past experiences, background,
|
||||
and identity. Share your own experiences and be open to learning about others'
|
||||
diverse perspectives.
|
||||
|
||||
2. **Positivity and Constructiveness**: Engage in constructive discussions and
|
||||
support each other. If you feel angry, negative, or aggressive, take a break
|
||||
until you can participate in a positive and constructive manner. Process intense
|
||||
feelings with a friend or in a private setting before engaging in community
|
||||
conversations to help maintain a supportive and focused environment.
|
||||
|
||||
3. **Clarity and Understanding**: Our community includes neurodivergent
|
||||
individuals and those who may not appreciate sarcasm or subtlety. Communicate
|
||||
clearly and kindly, avoiding sarcasm and ensuring your messages are easily
|
||||
understood by all. Additionally, avoid putting the burden of education on
|
||||
marginalized groups by doing your own research before asking for explanations.
|
||||
|
||||
4. **Be Open to Inclusivity**: Actively engage in conversations about making our
|
||||
community more inclusive. Report discriminatory behavior to the moderators
|
||||
and be open to constructive feedback that aims to improve our community.
|
||||
Understand that discussing discrimination and negative experiences can be
|
||||
emotionally taxing, so focus on the message rather than critiquing the tone
|
||||
used.
|
||||
|
||||
5. **Commit to Inclusivity**: Building an inclusive community requires time,
|
||||
energy, and resources. Recognise that addressing discrimination and bias is
|
||||
an ongoing process that necessitates commitment and action from all community
|
||||
members.
|
||||
|
||||
## Matrix Community
|
||||
|
||||
This Code of Conduct applies to the entire [conduwuit Matrix
|
||||
Space](https://matrix.to/#/#conduwuit-space:puppygock.gay) and its rooms,
|
||||
including:
|
||||
|
||||
### [#conduwuit:puppygock.gay](https://matrix.to/#/#conduwuit:puppygock.gay)
|
||||
|
||||
This room is for support and discussions about conduwuit. Ask questions, share
|
||||
insights, and help each other out.
|
||||
|
||||
### [#conduwuit-offtopic:girlboss.ceo](https://matrix.to/#/#conduwuit-offtopic:girlboss.ceo)
|
||||
|
||||
For off-topic community conversations about any subject. While this room allows
|
||||
for a wide range of topics, the same CoC applies. Keep discussions respectful
|
||||
and inclusive, and avoid divisive subjects like country/world politics. General
|
||||
topics, such as world events, are welcome as long as they follow the CoC.
|
||||
|
||||
### [#conduwuit-dev:puppygock.gay](https://matrix.to/#/#conduwuit-dev:puppygock.gay)
|
||||
|
||||
This room is dedicated to discussing active development of conduwuit. Posting
|
||||
requires an elevated power level, which can be requested in one of the other
|
||||
rooms. Use this space to collaborate and innovate.
|
||||
|
||||
## Enforcement
|
||||
|
||||
We have a zero-tolerance policy for violations of this Code of Conduct. If
|
||||
someone’s behavior makes you uncomfortable, please report it to the moderators.
|
||||
Actions we may take include:
|
||||
|
||||
1. **Warning**: A warning given directly in the room or via a private message
|
||||
from the moderators, identifying the violation and requesting corrective
|
||||
action.
|
||||
2. **Temporary Mute**: Temporary restriction from participating in discussions
|
||||
for a specified period to allow for reflection and cooling off.
|
||||
3. **Kick or Ban**: Egregious behavior may result in an immediate kick or ban to
|
||||
protect other community members. Bans are considered permanent and will only
|
||||
be reversed in exceptional circumstances after proven good behavior.
|
||||
|
||||
Please highlight issues directly in rooms when possible, but if you don't feel
|
||||
comfortable doing that, then please send a DM to one of the moderators directly.
|
||||
|
||||
Together, let’s build a community where everyone feels valued and respected.
|
||||
|
||||
— The conduwuit Moderation Team
|
||||
@@ -1,10 +1,10 @@
|
||||
# Configuration
|
||||
|
||||
This chapter describes various ways to configure conduwuit.
|
||||
This chapter describes various ways to configure Continuwuity.
|
||||
|
||||
## Basics
|
||||
|
||||
conduwuit uses a config file for the majority of the settings, but also supports
|
||||
Continuwuity uses a config file for the majority of the settings, but also supports
|
||||
setting individual config options via commandline.
|
||||
|
||||
Please refer to the [example config
|
||||
@@ -12,13 +12,13 @@ file](./configuration/examples.md#example-configuration) for all of those
|
||||
settings.
|
||||
|
||||
The config file to use can be specified on the commandline when running
|
||||
conduwuit by specifying the `-c`, `--config` flag. Alternatively, you can use
|
||||
Continuwuity by specifying the `-c`, `--config` flag. Alternatively, you can use
|
||||
the environment variable `CONDUWUIT_CONFIG` to specify the config file to used.
|
||||
Conduit's environment variables are supported for backwards compatibility.
|
||||
|
||||
## Option commandline flag
|
||||
|
||||
conduwuit supports setting individual config options in TOML format from the
|
||||
Continuwuity supports setting individual config options in TOML format from the
|
||||
`-O` / `--option` flag. For example, you can set your server name via `-O
|
||||
server_name=\"example.com\"`.
|
||||
|
||||
@@ -33,7 +33,7 @@ string. This does not apply to options that take booleans or numbers:
|
||||
|
||||
## Execute commandline flag
|
||||
|
||||
conduwuit supports running admin commands on startup using the commandline
|
||||
Continuwuity supports running admin commands on startup using the commandline
|
||||
argument `--execute`. The most notable use for this is to create an admin user
|
||||
on first startup.
|
||||
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
# Deploying
|
||||
|
||||
This chapter describes various ways to deploy conduwuit.
|
||||
This chapter describes various ways to deploy Continuwuity.
|
||||
|
||||
@@ -1,15 +1,5 @@
|
||||
# conduwuit for Arch Linux
|
||||
# Continuwuity for Arch Linux
|
||||
|
||||
Currently conduwuit is only on the Arch User Repository (AUR).
|
||||
Continuwuity is available on the `archlinuxcn` repository and AUR, with the same package name `continuwuity`, which includes latest taggged version. The development version is available on AUR as `continuwuity-git`
|
||||
|
||||
The conduwuit AUR packages are community maintained and are not maintained by
|
||||
conduwuit development team, but the AUR package maintainers are in the Matrix
|
||||
room. Please attempt to verify your AUR package's PKGBUILD file looks fine
|
||||
before asking for support.
|
||||
|
||||
- [conduwuit](https://aur.archlinux.org/packages/conduwuit) - latest tagged
|
||||
conduwuit
|
||||
- [conduwuit-git](https://aur.archlinux.org/packages/conduwuit-git) - latest git
|
||||
conduwuit from `main` branch
|
||||
- [conduwuit-bin](https://aur.archlinux.org/packages/conduwuit-bin) - latest
|
||||
tagged conduwuit static binary
|
||||
Simply install the `continuwuity` package. Configure the service in `/etc/conduwuit/conduwuit.toml`, then enable/start the continuwuity.service.
|
||||
|
||||
@@ -1,48 +1,49 @@
|
||||
# conduwuit - Behind Traefik Reverse Proxy
|
||||
# Continuwuity - Behind Traefik Reverse Proxy
|
||||
|
||||
services:
|
||||
homeserver:
|
||||
### If you already built the conduduwit image with 'docker build' or want to use the Docker Hub image,
|
||||
### then you are ready to go.
|
||||
image: girlbossceo/conduwuit:latest
|
||||
image: forgejo.ellis.link/continuwuation/continuwuity:latest
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- db:/var/lib/conduwuit
|
||||
#- ./conduwuit.toml:/etc/conduwuit.toml
|
||||
- db:/var/lib/continuwuity
|
||||
- /etc/resolv.conf:/etc/resolv.conf:ro # Use the host's DNS resolver rather than Docker's.
|
||||
#- ./continuwuity.toml:/etc/continuwuity.toml
|
||||
networks:
|
||||
- proxy
|
||||
environment:
|
||||
CONDUWUIT_SERVER_NAME: your.server.name.example # EDIT THIS
|
||||
CONDUWUIT_DATABASE_PATH: /var/lib/conduwuit
|
||||
CONDUWUIT_PORT: 6167 # should match the loadbalancer traefik label
|
||||
CONDUWUIT_MAX_REQUEST_SIZE: 20000000 # in bytes, ~20 MB
|
||||
CONDUWUIT_ALLOW_REGISTRATION: 'true'
|
||||
CONDUWUIT_REGISTRATION_TOKEN: 'YOUR_TOKEN' # A registration token is required when registration is allowed.
|
||||
#CONDUWUIT_YES_I_AM_VERY_VERY_SURE_I_WANT_AN_OPEN_REGISTRATION_SERVER_PRONE_TO_ABUSE: 'true'
|
||||
CONDUWUIT_ALLOW_FEDERATION: 'true'
|
||||
CONDUWUIT_ALLOW_CHECK_FOR_UPDATES: 'true'
|
||||
CONDUWUIT_TRUSTED_SERVERS: '["matrix.org"]'
|
||||
#CONDUWUIT_LOG: warn,state_res=warn
|
||||
CONDUWUIT_ADDRESS: 0.0.0.0
|
||||
#CONDUWUIT_CONFIG: '/etc/conduwuit.toml' # Uncomment if you mapped config toml above
|
||||
CONTINUWUITY_SERVER_NAME: your.server.name.example # EDIT THIS
|
||||
CONTINUWUITY_DATABASE_PATH: /var/lib/continuwuity
|
||||
CONTINUWUITY_PORT: 6167 # should match the loadbalancer traefik label
|
||||
CONTINUWUITY_MAX_REQUEST_SIZE: 20000000 # in bytes, ~20 MB
|
||||
CONTINUWUITY_ALLOW_REGISTRATION: 'true'
|
||||
CONTINUWUITY_REGISTRATION_TOKEN: 'YOUR_TOKEN' # A registration token is required when registration is allowed.
|
||||
#CONTINUWUITY_YES_I_AM_VERY_VERY_SURE_I_WANT_AN_OPEN_REGISTRATION_SERVER_PRONE_TO_ABUSE: 'true'
|
||||
CONTINUWUITY_ALLOW_FEDERATION: 'true'
|
||||
CONTINUWUITY_ALLOW_CHECK_FOR_UPDATES: 'true'
|
||||
CONTINUWUITY_TRUSTED_SERVERS: '["matrix.org"]'
|
||||
#CONTINUWUITY_LOG: warn,state_res=warn
|
||||
CONTINUWUITY_ADDRESS: 0.0.0.0
|
||||
#CONTINUWUITY_CONFIG: '/etc/continuwuity.toml' # Uncomment if you mapped config toml above
|
||||
|
||||
# We need some way to serve the client and server .well-known json. The simplest way is via the CONDUWUIT_WELL_KNOWN
|
||||
# variable / config option, there are multiple ways to do this, e.g. in the conduwuit.toml file, and in a seperate
|
||||
# We need some way to serve the client and server .well-known json. The simplest way is via the CONTINUWUITY_WELL_KNOWN
|
||||
# variable / config option, there are multiple ways to do this, e.g. in the continuwuity.toml file, and in a separate
|
||||
# see the override file for more information about delegation
|
||||
CONDUWUIT_WELL_KNOWN: |
|
||||
CONTINUWUITY_WELL_KNOWN: |
|
||||
{
|
||||
client=https://your.server.name.example,
|
||||
server=your.server.name.example:443
|
||||
}
|
||||
#cpuset: "0-4" # Uncomment to limit to specific CPU cores
|
||||
ulimits: # conduwuit uses quite a few file descriptors, and on some systems it defaults to 1024, so you can tell docker to increase it
|
||||
ulimits: # Continuwuity uses quite a few file descriptors, and on some systems it defaults to 1024, so you can tell docker to increase it
|
||||
nofile:
|
||||
soft: 1048567
|
||||
hard: 1048567
|
||||
|
||||
### Uncomment if you want to use your own Element-Web App.
|
||||
### Note: You need to provide a config.json for Element and you also need a second
|
||||
### Domain or Subdomain for the communication between Element and conduwuit
|
||||
### Domain or Subdomain for the communication between Element and Continuwuity
|
||||
### Config-Docs: https://github.com/vector-im/element-web/blob/develop/docs/config.md
|
||||
# element-web:
|
||||
# image: vectorim/element-web:latest
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# conduwuit - Traefik Reverse Proxy Labels
|
||||
# Continuwuity - Traefik Reverse Proxy Labels
|
||||
|
||||
services:
|
||||
homeserver:
|
||||
@@ -6,17 +6,17 @@ services:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.docker.network=proxy" # Change this to the name of your Traefik docker proxy network
|
||||
|
||||
- "traefik.http.routers.to-conduwuit.rule=Host(`<SUBDOMAIN>.<DOMAIN>`)" # Change to the address on which conduwuit is hosted
|
||||
- "traefik.http.routers.to-conduwuit.tls=true"
|
||||
- "traefik.http.routers.to-conduwuit.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.routers.to-conduwuit.middlewares=cors-headers@docker"
|
||||
- "traefik.http.services.to_conduwuit.loadbalancer.server.port=6167"
|
||||
- "traefik.http.routers.to-continuwuity.rule=Host(`<SUBDOMAIN>.<DOMAIN>`)" # Change to the address on which Continuwuity is hosted
|
||||
- "traefik.http.routers.to-continuwuity.tls=true"
|
||||
- "traefik.http.routers.to-continuwuity.tls.certresolver=letsencrypt"
|
||||
- "traefik.http.routers.to-continuwuity.middlewares=cors-headers@docker"
|
||||
- "traefik.http.services.to_continuwuity.loadbalancer.server.port=6167"
|
||||
|
||||
- "traefik.http.middlewares.cors-headers.headers.accessControlAllowOriginList=*"
|
||||
- "traefik.http.middlewares.cors-headers.headers.accessControlAllowHeaders=Origin, X-Requested-With, Content-Type, Accept, Authorization"
|
||||
- "traefik.http.middlewares.cors-headers.headers.accessControlAllowMethods=GET, POST, PUT, DELETE, OPTIONS"
|
||||
|
||||
# If you want to have your account on <DOMAIN>, but host conduwuit on a subdomain,
|
||||
# If you want to have your account on <DOMAIN>, but host Continuwuity on a subdomain,
|
||||
# you can let it only handle the well known file on that domain instead
|
||||
#- "traefik.http.routers.to-matrix-wellknown.rule=Host(`<DOMAIN>`) && PathPrefix(`/.well-known/matrix`)"
|
||||
#- "traefik.http.routers.to-matrix-wellknown.tls=true"
|
||||
@@ -34,4 +34,3 @@ services:
|
||||
# - "traefik.http.routers.to-element-web.tls.certresolver=letsencrypt"
|
||||
|
||||
# vim: ts=2:sw=2:expandtab
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
caddy:
|
||||
# This compose file uses caddy-docker-proxy as the reverse proxy for conduwuit!
|
||||
# This compose file uses caddy-docker-proxy as the reverse proxy for Continuwuity!
|
||||
# For more info, visit https://github.com/lucaslorentz/caddy-docker-proxy
|
||||
image: lucaslorentz/caddy-docker-proxy:ci-alpine
|
||||
ports:
|
||||
@@ -20,27 +20,28 @@ services:
|
||||
caddy.1_respond: /.well-known/matrix/client {"m.server":{"base_url":"https://matrix.example.com"},"m.homeserver":{"base_url":"https://matrix.example.com"},"org.matrix.msc3575.proxy":{"url":"https://matrix.example.com"}}
|
||||
|
||||
homeserver:
|
||||
### If you already built the conduwuit image with 'docker build' or want to use a registry image,
|
||||
### If you already built the Continuwuity image with 'docker build' or want to use a registry image,
|
||||
### then you are ready to go.
|
||||
image: girlbossceo/conduwuit:latest
|
||||
image: forgejo.ellis.link/continuwuation/continuwuity:latest
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- db:/var/lib/conduwuit
|
||||
#- ./conduwuit.toml:/etc/conduwuit.toml
|
||||
- db:/var/lib/continuwuity
|
||||
- /etc/resolv.conf:/etc/resolv.conf:ro # Use the host's DNS resolver rather than Docker's.
|
||||
#- ./continuwuity.toml:/etc/continuwuity.toml
|
||||
environment:
|
||||
CONDUWUIT_SERVER_NAME: example.com # EDIT THIS
|
||||
CONDUWUIT_DATABASE_PATH: /var/lib/conduwuit
|
||||
CONDUWUIT_PORT: 6167
|
||||
CONDUWUIT_MAX_REQUEST_SIZE: 20000000 # in bytes, ~20 MB
|
||||
CONDUWUIT_ALLOW_REGISTRATION: 'true'
|
||||
CONDUWUIT_REGISTRATION_TOKEN: 'YOUR_TOKEN' # A registration token is required when registration is allowed.
|
||||
#CONDUWUIT_YES_I_AM_VERY_VERY_SURE_I_WANT_AN_OPEN_REGISTRATION_SERVER_PRONE_TO_ABUSE: 'true'
|
||||
CONDUWUIT_ALLOW_FEDERATION: 'true'
|
||||
CONDUWUIT_ALLOW_CHECK_FOR_UPDATES: 'true'
|
||||
CONDUWUIT_TRUSTED_SERVERS: '["matrix.org"]'
|
||||
#CONDUWUIT_LOG: warn,state_res=warn
|
||||
CONDUWUIT_ADDRESS: 0.0.0.0
|
||||
#CONDUWUIT_CONFIG: '/etc/conduwuit.toml' # Uncomment if you mapped config toml above
|
||||
CONTINUWUITY_SERVER_NAME: example.com # EDIT THIS
|
||||
CONTINUWUITY_DATABASE_PATH: /var/lib/continuwuity
|
||||
CONTINUWUITY_PORT: 6167
|
||||
CONTINUWUITY_MAX_REQUEST_SIZE: 20000000 # in bytes, ~20 MB
|
||||
CONTINUWUITY_ALLOW_REGISTRATION: 'true'
|
||||
CONTINUWUITY_REGISTRATION_TOKEN: 'YOUR_TOKEN' # A registration token is required when registration is allowed.
|
||||
#CONTINUWUITY_YES_I_AM_VERY_VERY_SURE_I_WANT_AN_OPEN_REGISTRATION_SERVER_PRONE_TO_ABUSE: 'true'
|
||||
CONTINUWUITY_ALLOW_FEDERATION: 'true'
|
||||
CONTINUWUITY_ALLOW_CHECK_FOR_UPDATES: 'true'
|
||||
CONTINUWUITY_TRUSTED_SERVERS: '["matrix.org"]'
|
||||
#CONTINUWUITY_LOG: warn,state_res=warn
|
||||
CONTINUWUITY_ADDRESS: 0.0.0.0
|
||||
#CONTINUWUITY_CONFIG: '/etc/continuwuity.toml' # Uncomment if you mapped config toml above
|
||||
networks:
|
||||
- caddy
|
||||
labels:
|
||||
|
||||
@@ -1,56 +1,57 @@
|
||||
# conduwuit - Behind Traefik Reverse Proxy
|
||||
# Continuwuity - Behind Traefik Reverse Proxy
|
||||
|
||||
services:
|
||||
homeserver:
|
||||
### If you already built the conduwuit image with 'docker build' or want to use the Docker Hub image,
|
||||
### If you already built the Continuwuity image with 'docker build' or want to use the Docker Hub image,
|
||||
### then you are ready to go.
|
||||
image: girlbossceo/conduwuit:latest
|
||||
image: forgejo.ellis.link/continuwuation/continuwuity:latest
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- db:/var/lib/conduwuit
|
||||
#- ./conduwuit.toml:/etc/conduwuit.toml
|
||||
- db:/var/lib/continuwuity
|
||||
- /etc/resolv.conf:/etc/resolv.conf:ro # Use the host's DNS resolver rather than Docker's.
|
||||
#- ./continuwuity.toml:/etc/continuwuity.toml
|
||||
networks:
|
||||
- proxy
|
||||
environment:
|
||||
CONDUWUIT_SERVER_NAME: your.server.name.example # EDIT THIS
|
||||
CONDUWUIT_TRUSTED_SERVERS: '["matrix.org"]'
|
||||
CONDUWUIT_ALLOW_REGISTRATION: 'false' # After setting a secure registration token, you can enable this
|
||||
CONDUWUIT_REGISTRATION_TOKEN: "" # This is a token you can use to register on the server
|
||||
#CONDUWUIT_REGISTRATION_TOKEN_FILE: "" # Alternatively you can configure a path to a token file to read
|
||||
CONDUWUIT_ADDRESS: 0.0.0.0
|
||||
CONDUWUIT_PORT: 6167 # you need to match this with the traefik load balancer label if you're want to change it
|
||||
CONDUWUIT_DATABASE_PATH: /var/lib/conduwuit
|
||||
#CONDUWUIT_CONFIG: '/etc/conduit.toml' # Uncomment if you mapped config toml above
|
||||
### Uncomment and change values as desired, note that conduwuit has plenty of config options, so you should check out the example example config too
|
||||
CONTINUWUITY_SERVER_NAME: your.server.name.example # EDIT THIS
|
||||
CONTINUWUITY_TRUSTED_SERVERS: '["matrix.org"]'
|
||||
CONTINUWUITY_ALLOW_REGISTRATION: 'false' # After setting a secure registration token, you can enable this
|
||||
CONTINUWUITY_REGISTRATION_TOKEN: "" # This is a token you can use to register on the server
|
||||
#CONTINUWUITY_REGISTRATION_TOKEN_FILE: "" # Alternatively you can configure a path to a token file to read
|
||||
CONTINUWUITY_ADDRESS: 0.0.0.0
|
||||
CONTINUWUITY_PORT: 6167 # you need to match this with the traefik load balancer label if you're want to change it
|
||||
CONTINUWUITY_DATABASE_PATH: /var/lib/continuwuity
|
||||
#CONTINUWUITY_CONFIG: '/etc/continuwuity.toml' # Uncomment if you mapped config toml above
|
||||
### Uncomment and change values as desired, note that Continuwuity has plenty of config options, so you should check out the example example config too
|
||||
# Available levels are: error, warn, info, debug, trace - more info at: https://docs.rs/env_logger/*/env_logger/#enabling-logging
|
||||
# CONDUWUIT_LOG: info # default is: "warn,state_res=warn"
|
||||
# CONDUWUIT_ALLOW_ENCRYPTION: 'true'
|
||||
# CONDUWUIT_ALLOW_FEDERATION: 'true'
|
||||
# CONDUWUIT_ALLOW_CHECK_FOR_UPDATES: 'true'
|
||||
# CONDUWUIT_ALLOW_INCOMING_PRESENCE: true
|
||||
# CONDUWUIT_ALLOW_OUTGOING_PRESENCE: true
|
||||
# CONDUWUIT_ALLOW_LOCAL_PRESENCE: true
|
||||
# CONDUWUIT_WORKERS: 10
|
||||
# CONDUWUIT_MAX_REQUEST_SIZE: 20000000 # in bytes, ~20 MB
|
||||
# CONDUWUIT_NEW_USER_DISPLAYNAME_SUFFIX = "🏳<200d>⚧"
|
||||
# CONTINUWUITY_LOG: info # default is: "warn,state_res=warn"
|
||||
# CONTINUWUITY_ALLOW_ENCRYPTION: 'true'
|
||||
# CONTINUWUITY_ALLOW_FEDERATION: 'true'
|
||||
# CONTINUWUITY_ALLOW_CHECK_FOR_UPDATES: 'true'
|
||||
# CONTINUWUITY_ALLOW_INCOMING_PRESENCE: true
|
||||
# CONTINUWUITY_ALLOW_OUTGOING_PRESENCE: true
|
||||
# CONTINUWUITY_ALLOW_LOCAL_PRESENCE: true
|
||||
# CONTINUWUITY_WORKERS: 10
|
||||
# CONTINUWUITY_MAX_REQUEST_SIZE: 20000000 # in bytes, ~20 MB
|
||||
# CONTINUWUITY_NEW_USER_DISPLAYNAME_SUFFIX = "🏳<200d>⚧"
|
||||
|
||||
# We need some way to serve the client and server .well-known json. The simplest way is via the CONDUWUIT_WELL_KNOWN
|
||||
# variable / config option, there are multiple ways to do this, e.g. in the conduwuit.toml file, and in a seperate
|
||||
# We need some way to serve the client and server .well-known json. The simplest way is via the CONTINUWUITY_WELL_KNOWN
|
||||
# variable / config option, there are multiple ways to do this, e.g. in the continuwuity.toml file, and in a separate
|
||||
# reverse proxy, but since you do not have a reverse proxy and following this guide, this example is included
|
||||
CONDUWUIT_WELL_KNOWN: |
|
||||
CONTINUWUITY_WELL_KNOWN: |
|
||||
{
|
||||
client=https://your.server.name.example,
|
||||
server=your.server.name.example:443
|
||||
}
|
||||
#cpuset: "0-4" # Uncomment to limit to specific CPU cores
|
||||
ulimits: # conduwuit uses quite a few file descriptors, and on some systems it defaults to 1024, so you can tell docker to increase it
|
||||
ulimits: # Continuwuity uses quite a few file descriptors, and on some systems it defaults to 1024, so you can tell docker to increase it
|
||||
nofile:
|
||||
soft: 1048567
|
||||
hard: 1048567
|
||||
|
||||
### Uncomment if you want to use your own Element-Web App.
|
||||
### Note: You need to provide a config.json for Element and you also need a second
|
||||
### Domain or Subdomain for the communication between Element and conduwuit
|
||||
### Domain or Subdomain for the communication between Element and Continuwuity
|
||||
### Config-Docs: https://github.com/vector-im/element-web/blob/develop/docs/config.md
|
||||
# element-web:
|
||||
# image: vectorim/element-web:latest
|
||||
|
||||
@@ -1,34 +1,34 @@
|
||||
# conduwuit
|
||||
# Continuwuity
|
||||
|
||||
services:
|
||||
homeserver:
|
||||
### If you already built the conduwuit image with 'docker build' or want to use a registry image,
|
||||
### If you already built the Continuwuity image with 'docker build' or want to use a registry image,
|
||||
### then you are ready to go.
|
||||
image: girlbossceo/conduwuit:latest
|
||||
image: forgejo.ellis.link/continuwuation/continuwuity:latest
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 8448:6167
|
||||
volumes:
|
||||
- db:/var/lib/conduwuit
|
||||
#- ./conduwuit.toml:/etc/conduwuit.toml
|
||||
- db:/var/lib/continuwuity
|
||||
#- ./continuwuity.toml:/etc/continuwuity.toml
|
||||
environment:
|
||||
CONDUWUIT_SERVER_NAME: your.server.name # EDIT THIS
|
||||
CONDUWUIT_DATABASE_PATH: /var/lib/conduwuit
|
||||
CONDUWUIT_PORT: 6167
|
||||
CONDUWUIT_MAX_REQUEST_SIZE: 20000000 # in bytes, ~20 MB
|
||||
CONDUWUIT_ALLOW_REGISTRATION: 'true'
|
||||
CONDUWUIT_REGISTRATION_TOKEN: 'YOUR_TOKEN' # A registration token is required when registration is allowed.
|
||||
#CONDUWUIT_YES_I_AM_VERY_VERY_SURE_I_WANT_AN_OPEN_REGISTRATION_SERVER_PRONE_TO_ABUSE: 'true'
|
||||
CONDUWUIT_ALLOW_FEDERATION: 'true'
|
||||
CONDUWUIT_ALLOW_CHECK_FOR_UPDATES: 'true'
|
||||
CONDUWUIT_TRUSTED_SERVERS: '["matrix.org"]'
|
||||
#CONDUWUIT_LOG: warn,state_res=warn
|
||||
CONDUWUIT_ADDRESS: 0.0.0.0
|
||||
#CONDUWUIT_CONFIG: '/etc/conduwuit.toml' # Uncomment if you mapped config toml above
|
||||
CONTINUWUITY_SERVER_NAME: your.server.name # EDIT THIS
|
||||
CONTINUWUITY_DATABASE_PATH: /var/lib/continuwuity
|
||||
CONTINUWUITY_PORT: 6167
|
||||
CONTINUWUITY_MAX_REQUEST_SIZE: 20000000 # in bytes, ~20 MB
|
||||
CONTINUWUITY_ALLOW_REGISTRATION: 'true'
|
||||
CONTINUWUITY_REGISTRATION_TOKEN: 'YOUR_TOKEN' # A registration token is required when registration is allowed.
|
||||
#CONTINUWUITY_YES_I_AM_VERY_VERY_SURE_I_WANT_AN_OPEN_REGISTRATION_SERVER_PRONE_TO_ABUSE: 'true'
|
||||
CONTINUWUITY_ALLOW_FEDERATION: 'true'
|
||||
CONTINUWUITY_ALLOW_CHECK_FOR_UPDATES: 'true'
|
||||
CONTINUWUITY_TRUSTED_SERVERS: '["matrix.org"]'
|
||||
#CONTINUWUITY_LOG: warn,state_res=warn
|
||||
CONTINUWUITY_ADDRESS: 0.0.0.0
|
||||
#CONTINUWUITY_CONFIG: '/etc/continuwuity.toml' # Uncomment if you mapped config toml above
|
||||
#
|
||||
### Uncomment if you want to use your own Element-Web App.
|
||||
### Note: You need to provide a config.json for Element and you also need a second
|
||||
### Domain or Subdomain for the communication between Element and conduwuit
|
||||
### Domain or Subdomain for the communication between Element and Continuwuity
|
||||
### Config-Docs: https://github.com/vector-im/element-web/blob/develop/docs/config.md
|
||||
# element-web:
|
||||
# image: vectorim/element-web:latest
|
||||
|
||||
+36
-48
@@ -1,31 +1,20 @@
|
||||
# conduwuit for Docker
|
||||
# Continuwuity for Docker
|
||||
|
||||
## Docker
|
||||
|
||||
To run conduwuit with Docker you can either build the image yourself or pull it
|
||||
To run Continuwuity with Docker you can either build the image yourself or pull it
|
||||
from a registry.
|
||||
|
||||
### Use a registry
|
||||
|
||||
OCI images for conduwuit are available in the registries listed below.
|
||||
OCI images for Continuwuity are available in the registries listed below.
|
||||
|
||||
| Registry | Image | Size | Notes |
|
||||
| --------------- | --------------------------------------------------------------- | ----------------------------- | ---------------------- |
|
||||
| GitHub Registry | [ghcr.io/girlbossceo/conduwuit:latest][gh] | ![Image Size][shield-latest] | Stable latest tagged image. |
|
||||
| GitLab Registry | [registry.gitlab.com/conduwuit/conduwuit:latest][gl] | ![Image Size][shield-latest] | Stable latest tagged image. |
|
||||
| Docker Hub | [docker.io/girlbossceo/conduwuit:latest][dh] | ![Image Size][shield-latest] | Stable latest tagged image. |
|
||||
| GitHub Registry | [ghcr.io/girlbossceo/conduwuit:main][gh] | ![Image Size][shield-main] | Stable main branch. |
|
||||
| GitLab Registry | [registry.gitlab.com/conduwuit/conduwuit:main][gl] | ![Image Size][shield-main] | Stable main branch. |
|
||||
| Docker Hub | [docker.io/girlbossceo/conduwuit:main][dh] | ![Image Size][shield-main] | Stable main branch. |
|
||||
| Registry | Image | Notes |
|
||||
| --------------- | --------------------------------------------------------------- | -----------------------|
|
||||
| Forgejo Registry| [forgejo.ellis.link/continuwuation/continuwuity:latest][fj] | Latest tagged image. |
|
||||
| Forgejo Registry| [forgejo.ellis.link/continuwuation/continuwuity:main][fj] | Main branch image. |
|
||||
|
||||
[dh]: https://hub.docker.com/r/girlbossceo/conduwuit
|
||||
[gh]: https://github.com/girlbossceo/conduwuit/pkgs/container/conduwuit
|
||||
[gl]: https://gitlab.com/conduwuit/conduwuit/container_registry/6369729
|
||||
[shield-latest]: https://img.shields.io/docker/image-size/girlbossceo/conduwuit/latest
|
||||
[shield-main]: https://img.shields.io/docker/image-size/girlbossceo/conduwuit/main
|
||||
|
||||
OCI image `.tar.gz` files are also hosted directly at when uploaded by CI with a
|
||||
commit hash/revision or a tagged release: <https://pup.systems/~strawberry/conduwuit/>
|
||||
[fj]: https://forgejo.ellis.link/continuwuation/-/packages/container/continuwuity
|
||||
|
||||
Use
|
||||
|
||||
@@ -41,22 +30,22 @@ When you have the image you can simply run it with
|
||||
|
||||
```bash
|
||||
docker run -d -p 8448:6167 \
|
||||
-v db:/var/lib/conduwuit/ \
|
||||
-e CONDUWUIT_SERVER_NAME="your.server.name" \
|
||||
-e CONDUWUIT_ALLOW_REGISTRATION=false \
|
||||
--name conduwuit $LINK
|
||||
-v db:/var/lib/continuwuity/ \
|
||||
-e CONTINUWUITY_SERVER_NAME="your.server.name" \
|
||||
-e CONTINUWUITY_ALLOW_REGISTRATION=false \
|
||||
--name continuwuity $LINK
|
||||
```
|
||||
|
||||
or you can use [docker compose](#docker-compose).
|
||||
|
||||
The `-d` flag lets the container run in detached mode. You may supply an
|
||||
optional `conduwuit.toml` config file, the example config can be found
|
||||
optional `continuwuity.toml` config file, the example config can be found
|
||||
[here](../configuration/examples.md). You can pass in different env vars to
|
||||
change config values on the fly. You can even configure conduwuit completely by
|
||||
change config values on the fly. You can even configure Continuwuity completely by
|
||||
using env vars. For an overview of possible values, please take a look at the
|
||||
[`docker-compose.yml`](docker-compose.yml) file.
|
||||
|
||||
If you just want to test conduwuit for a short time, you can use the `--rm`
|
||||
If you just want to test Continuwuity for a short time, you can use the `--rm`
|
||||
flag, which will clean up everything related to your container after you stop
|
||||
it.
|
||||
|
||||
@@ -91,32 +80,32 @@ docker network create caddy
|
||||
After that, you can rename it so it matches `docker-compose.yml` and spin up the
|
||||
containers!
|
||||
|
||||
Additional info about deploying conduwuit can be found [here](generic.md).
|
||||
Additional info about deploying Continuwuity can be found [here](generic.md).
|
||||
|
||||
### Build
|
||||
|
||||
Official conduwuit images are built using Nix's
|
||||
[`buildLayeredImage`][nix-buildlayeredimage]. This ensures all OCI images are
|
||||
repeatable and reproducible by anyone, keeps the images lightweight, and can be
|
||||
built offline.
|
||||
Official Continuwuity images are built using **Docker Buildx** and the Dockerfile found at [`docker/Dockerfile`][dockerfile-path]. This approach uses common Docker tooling and enables multi-platform builds efficiently.
|
||||
|
||||
This also ensures portability of our images because `buildLayeredImage` builds
|
||||
OCI images, not Docker images, and works with other container software.
|
||||
The resulting images are broadly compatible with Docker and other container runtimes like Podman or containerd.
|
||||
|
||||
The OCI images are OS-less with only a very minimal environment of the `tini`
|
||||
init system, CA certificates, and the conduwuit binary. This does mean there is
|
||||
not a shell, but in theory you can get a shell by adding the necessary layers
|
||||
to the layered image. However it's very unlikely you will need a shell for any
|
||||
real troubleshooting.
|
||||
The images *do not contain a shell*. They contain only the Continuwuity binary, required libraries, TLS certificates and metadata. Please refer to the [`docker/Dockerfile`][dockerfile-path] for the specific details of the image composition.
|
||||
|
||||
The flake file for the OCI image definition is at [`nix/pkgs/oci-image/default.nix`][oci-image-def].
|
||||
To build an image locally using Docker Buildx, you can typically run a command like:
|
||||
|
||||
To build an OCI image using Nix, the following outputs can be built:
|
||||
- `nix build -L .#oci-image` (default features, x86_64 glibc)
|
||||
- `nix build -L .#oci-image-x86_64-linux-musl` (default features, x86_64 musl)
|
||||
- `nix build -L .#oci-image-aarch64-linux-musl` (default features, aarch64 musl)
|
||||
- `nix build -L .#oci-image-x86_64-linux-musl-all-features` (all features, x86_64 musl)
|
||||
- `nix build -L .#oci-image-aarch64-linux-musl-all-features` (all features, aarch64 musl)
|
||||
```bash
|
||||
# Build for the current platform and load into the local Docker daemon
|
||||
docker buildx build --load --tag continuwuity:latest -f docker/Dockerfile .
|
||||
|
||||
# Example: Build for specific platforms and push to a registry.
|
||||
# docker buildx build --platform linux/amd64,linux/arm64 --tag registry.io/org/continuwuity:latest -f docker/Dockerfile . --push
|
||||
|
||||
# Example: Build binary optimized for the current CPU
|
||||
# docker buildx build --load --tag continuwuity:latest --build-arg TARGET_CPU=native -f docker/Dockerfile .
|
||||
```
|
||||
|
||||
Refer to the Docker Buildx documentation for more advanced build options.
|
||||
|
||||
[dockerfile-path]: ../../docker/Dockerfile
|
||||
|
||||
### Run
|
||||
|
||||
@@ -138,10 +127,10 @@ web. With the two provided files,
|
||||
[`docker-compose.for-traefik.yml`](docker-compose.for-traefik.yml) (or
|
||||
[`docker-compose.with-traefik.yml`](docker-compose.with-traefik.yml)) and
|
||||
[`docker-compose.override.yml`](docker-compose.override.yml), it is equally easy
|
||||
to deploy and use conduwuit, with a little caveat. If you already took a look at
|
||||
to deploy and use Continuwuity, with a little caveat. If you already took a look at
|
||||
the files, then you should have seen the `well-known` service, and that is the
|
||||
little caveat. Traefik is simply a proxy and loadbalancer and is not able to
|
||||
serve any kind of content, but for conduwuit to federate, we need to either
|
||||
serve any kind of content, but for Continuwuity to federate, we need to either
|
||||
expose ports `443` and `8448` or serve two endpoints `.well-known/matrix/client`
|
||||
and `.well-known/matrix/server`.
|
||||
|
||||
@@ -153,4 +142,3 @@ those two files.
|
||||
See the [TURN](../turn.md) page.
|
||||
|
||||
[nix-buildlayeredimage]: https://ryantm.github.io/nixpkgs/builders/images/dockertools/#ssec-pkgs-dockerTools-buildLayeredImage
|
||||
[oci-image-def]: https://github.com/girlbossceo/conduwuit/blob/main/nix/pkgs/oci-image/default.nix
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# conduwuit for FreeBSD
|
||||
# Continuwuity for FreeBSD
|
||||
|
||||
conduwuit at the moment does not provide FreeBSD builds or have FreeBSD packaging, however conduwuit does build and work on FreeBSD using the system-provided RocksDB.
|
||||
Continuwuity at the moment does not provide FreeBSD builds or have FreeBSD packaging, however Continuwuity does build and work on FreeBSD using the system-provided RocksDB.
|
||||
|
||||
Contributions for getting conduwuit packaged are welcome.
|
||||
Contributions for getting Continuwuity packaged are welcome.
|
||||
|
||||
+26
-28
@@ -2,11 +2,11 @@
|
||||
|
||||
> ### Getting help
|
||||
>
|
||||
> If you run into any problems while setting up conduwuit, ask us in
|
||||
> `#conduwuit:puppygock.gay` or [open an issue on
|
||||
> GitHub](https://github.com/girlbossceo/conduwuit/issues/new).
|
||||
> If you run into any problems while setting up Continuwuity, ask us in
|
||||
> `#continuwuity:continuwuity.org` or [open an issue on
|
||||
> Forgejo](https://forgejo.ellis.link/continuwuation/continuwuity/issues/new).
|
||||
|
||||
## Installing conduwuit
|
||||
## Installing Continuwuity
|
||||
|
||||
### Static prebuilt binary
|
||||
|
||||
@@ -14,12 +14,10 @@ You may simply download the binary that fits your machine architecture (x86_64
|
||||
or aarch64). Run `uname -m` to see what you need.
|
||||
|
||||
Prebuilt fully static musl binaries can be downloaded from the latest tagged
|
||||
release [here](https://github.com/girlbossceo/conduwuit/releases/latest) or
|
||||
release [here](https://forgejo.ellis.link/continuwuation/continuwuity/releases/latest) or
|
||||
`main` CI branch workflow artifact output. These also include Debian/Ubuntu
|
||||
packages.
|
||||
|
||||
Binaries are also available on my website directly at: <https://pup.systems/~strawberry/conduwuit/>
|
||||
|
||||
These can be curl'd directly from. `ci-bins` are CI workflow binaries by commit
|
||||
hash/revision, and `releases` are tagged releases. Sort by descending last
|
||||
modified for the latest.
|
||||
@@ -37,7 +35,7 @@ for performance.
|
||||
### Compiling
|
||||
|
||||
Alternatively, you may compile the binary yourself. We recommend using
|
||||
Nix (or [Lix](https://lix.systems)) to build conduwuit as this has the most
|
||||
Nix (or [Lix](https://lix.systems)) to build Continuwuity as this has the most
|
||||
guaranteed reproducibiltiy and easiest to get a build environment and output
|
||||
going. This also allows easy cross-compilation.
|
||||
|
||||
@@ -51,35 +49,35 @@ If wanting to build using standard Rust toolchains, make sure you install:
|
||||
- `liburing-dev` on the compiling machine, and `liburing` on the target host
|
||||
- LLVM and libclang for RocksDB
|
||||
|
||||
You can build conduwuit using `cargo build --release --all-features`
|
||||
You can build Continuwuity using `cargo build --release --all-features`
|
||||
|
||||
## Adding a conduwuit user
|
||||
## Adding a Continuwuity user
|
||||
|
||||
While conduwuit can run as any user it is better to use dedicated users for
|
||||
While Continuwuity can run as any user it is better to use dedicated users for
|
||||
different services. This also allows you to make sure that the file permissions
|
||||
are correctly set up.
|
||||
|
||||
In Debian, you can use this command to create a conduwuit user:
|
||||
In Debian, you can use this command to create a Continuwuity user:
|
||||
|
||||
```bash
|
||||
sudo adduser --system conduwuit --group --disabled-login --no-create-home
|
||||
sudo adduser --system continuwuity --group --disabled-login --no-create-home
|
||||
```
|
||||
|
||||
For distros without `adduser` (or where it's a symlink to `useradd`):
|
||||
|
||||
```bash
|
||||
sudo useradd -r --shell /usr/bin/nologin --no-create-home conduwuit
|
||||
sudo useradd -r --shell /usr/bin/nologin --no-create-home continuwuity
|
||||
```
|
||||
|
||||
## Forwarding ports in the firewall or the router
|
||||
|
||||
Matrix's default federation port is port 8448, and clients must be using port 443.
|
||||
If you would like to use only port 443, or a different port, you will need to setup
|
||||
delegation. conduwuit has config options for doing delegation, or you can configure
|
||||
delegation. Continuwuity has config options for doing delegation, or you can configure
|
||||
your reverse proxy to manually serve the necessary JSON files to do delegation
|
||||
(see the `[global.well_known]` config section).
|
||||
|
||||
If conduwuit runs behind a router or in a container and has a different public
|
||||
If Continuwuity runs behind a router or in a container and has a different public
|
||||
IP address than the host system these public ports need to be forwarded directly
|
||||
or indirectly to the port mentioned in the config.
|
||||
|
||||
@@ -94,9 +92,9 @@ on the network level, consider something like NextDNS or Pi-Hole.
|
||||
|
||||
## Setting up a systemd service
|
||||
|
||||
Two example systemd units for conduwuit can be found
|
||||
Two example systemd units for Continuwuity can be found
|
||||
[on the configuration page](../configuration/examples.md#debian-systemd-unit-file).
|
||||
You may need to change the `ExecStart=` path to where you placed the conduwuit
|
||||
You may need to change the `ExecStart=` path to where you placed the Continuwuity
|
||||
binary if it is not `/usr/bin/conduwuit`.
|
||||
|
||||
On systems where rsyslog is used alongside journald (i.e. Red Hat-based distros
|
||||
@@ -114,10 +112,10 @@ and entering the following:
|
||||
ReadWritePaths=/path/to/custom/database/path
|
||||
```
|
||||
|
||||
## Creating the conduwuit configuration file
|
||||
## Creating the Continuwuity configuration file
|
||||
|
||||
Now we need to create the conduwuit's config file in
|
||||
`/etc/conduwuit/conduwuit.toml`. The example config can be found at
|
||||
Now we need to create the Continuwuity's config file in
|
||||
`/etc/continuwuity/continuwuity.toml`. The example config can be found at
|
||||
[conduwuit-example.toml](../configuration/examples.md).
|
||||
|
||||
**Please take a moment to read the config. You need to change at least the
|
||||
@@ -127,7 +125,7 @@ RocksDB is the only supported database backend.
|
||||
|
||||
## Setting the correct file permissions
|
||||
|
||||
If you are using a dedicated user for conduwuit, you will need to allow it to
|
||||
If you are using a dedicated user for Continuwuity, you will need to allow it to
|
||||
read the config. To do that you can run this:
|
||||
|
||||
```bash
|
||||
@@ -139,7 +137,7 @@ If you use the default database path you also need to run this:
|
||||
|
||||
```bash
|
||||
sudo mkdir -p /var/lib/conduwuit/
|
||||
sudo chown -R conduwuit:conduwuit /var/lib/conduwuit/
|
||||
sudo chown -R continuwuity:continuwuity /var/lib/conduwuit/
|
||||
sudo chmod 700 /var/lib/conduwuit/
|
||||
```
|
||||
|
||||
@@ -174,13 +172,13 @@ As we would prefer our users to use Caddy, we will not provide configuration fil
|
||||
|
||||
You will need to reverse proxy everything under following routes:
|
||||
- `/_matrix/` - core Matrix C-S and S-S APIs
|
||||
- `/_conduwuit/` - ad-hoc conduwuit routes such as `/local_user_count` and
|
||||
- `/_conduwuit/` - ad-hoc Continuwuity routes such as `/local_user_count` and
|
||||
`/server_version`
|
||||
|
||||
You can optionally reverse proxy the following individual routes:
|
||||
- `/.well-known/matrix/client` and `/.well-known/matrix/server` if using
|
||||
conduwuit to perform delegation (see the `[global.well_known]` config section)
|
||||
- `/.well-known/matrix/support` if using conduwuit to send the homeserver admin
|
||||
Continuwuity to perform delegation (see the `[global.well_known]` config section)
|
||||
- `/.well-known/matrix/support` if using Continuwuity to send the homeserver admin
|
||||
contact and support page (formerly known as MSC1929)
|
||||
- `/` if you would like to see `hewwo from conduwuit woof!` at the root
|
||||
|
||||
@@ -200,7 +198,7 @@ header, making federation non-functional. If a workaround is found, feel free to
|
||||
|
||||
If using Apache, you need to use `nocanon` in your `ProxyPass` directive to prevent httpd from messing with the `X-Matrix` header (note that Apache isn't very good as a general reverse proxy and we discourage the usage of it if you can).
|
||||
|
||||
If using Nginx, you need to give conduwuit the request URI using `$request_uri`, or like so:
|
||||
If using Nginx, you need to give Continuwuity the request URI using `$request_uri`, or like so:
|
||||
- `proxy_pass http://127.0.0.1:6167$request_uri;`
|
||||
- `proxy_pass http://127.0.0.1:6167;`
|
||||
|
||||
@@ -209,7 +207,7 @@ Nginx users need to increase `client_max_body_size` (default is 1M) to match
|
||||
|
||||
## You're done
|
||||
|
||||
Now you can start conduwuit with:
|
||||
Now you can start Continuwuity with:
|
||||
|
||||
```bash
|
||||
sudo systemctl start conduwuit
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
# conduwuit for Kubernetes
|
||||
# Continuwuity for Kubernetes
|
||||
|
||||
conduwuit doesn't support horizontal scalability or distributed loading
|
||||
Continuwuity doesn't support horizontal scalability or distributed loading
|
||||
natively, however a community maintained Helm Chart is available here to run
|
||||
conduwuit on Kubernetes: <https://gitlab.cronce.io/charts/conduwuit>
|
||||
|
||||
Should changes need to be made, please reach out to the maintainer in our
|
||||
Matrix room as this is not maintained/controlled by the conduwuit maintainers.
|
||||
This should be compatible with continuwuity, but you will need to change the image reference.
|
||||
|
||||
Should changes need to be made, please reach out to the maintainer as this is not maintained/controlled by the Continuwuity maintainers.
|
||||
|
||||
+12
-45
@@ -1,68 +1,35 @@
|
||||
# conduwuit for NixOS
|
||||
# Continuwuity for NixOS
|
||||
|
||||
conduwuit can be acquired by Nix (or [Lix][lix]) from various places:
|
||||
Continuwuity can be acquired by Nix (or [Lix][lix]) from various places:
|
||||
|
||||
* The `flake.nix` at the root of the repo
|
||||
* The `default.nix` at the root of the repo
|
||||
* From conduwuit's binary cache
|
||||
|
||||
A community maintained NixOS package is available at [`conduwuit`](https://search.nixos.org/packages?channel=unstable&show=conduwuit&from=0&size=50&sort=relevance&type=packages&query=conduwuit)
|
||||
|
||||
### Binary cache
|
||||
|
||||
A binary cache for conduwuit that the CI/CD publishes to is available at the
|
||||
following places (both are the same just different names):
|
||||
|
||||
```
|
||||
https://attic.kennel.juneis.dog/conduit
|
||||
conduit:eEKoUwlQGDdYmAI/Q/0slVlegqh/QmAvQd7HBSm21Wk=
|
||||
|
||||
https://attic.kennel.juneis.dog/conduwuit
|
||||
conduwuit:BbycGUgTISsltcmH0qNjFR9dbrQNYgdIAcmViSGoVTE=
|
||||
```
|
||||
|
||||
The binary caches were recreated some months ago due to attic issues. The old public
|
||||
keys were:
|
||||
|
||||
```
|
||||
conduit:Isq8FGyEC6FOXH6nD+BOeAA+bKp6X6UIbupSlGEPuOg=
|
||||
conduwuit:lYPVh7o1hLu1idH4Xt2QHaRa49WRGSAqzcfFd94aOTw=
|
||||
```
|
||||
|
||||
If needed, we have a binary cache on Cachix but it is only limited to 5GB:
|
||||
|
||||
```
|
||||
https://conduwuit.cachix.org
|
||||
conduwuit.cachix.org-1:MFRm6jcnfTf0jSAbmvLfhO3KBMt4px+1xaereWXp8Xg=
|
||||
```
|
||||
|
||||
If specifying a Git remote URL in your flake, you can use any remotes that
|
||||
are specified on the README (the mirrors), such as the GitHub: `github:girlbossceo/conduwuit`
|
||||
* From Continuwuity's binary cache
|
||||
|
||||
### NixOS module
|
||||
|
||||
The `flake.nix` and `default.nix` do not currently provide a NixOS module (contributions
|
||||
welcome!), so [`services.matrix-conduit`][module] from Nixpkgs can be used to configure
|
||||
conduwuit.
|
||||
Continuwuity.
|
||||
|
||||
### Conduit NixOS Config Module and SQLite
|
||||
|
||||
Beware! The [`services.matrix-conduit`][module] module defaults to SQLite as a database backend.
|
||||
Conduwuit dropped SQLite support in favor of exclusively supporting the much faster RocksDB.
|
||||
Continuwuity dropped SQLite support in favor of exclusively supporting the much faster RocksDB.
|
||||
Make sure that you are using the RocksDB backend before migrating!
|
||||
|
||||
There is a [tool to migrate a Conduit SQLite database to
|
||||
RocksDB](https://github.com/ShadowJonathan/conduit_toolbox/).
|
||||
|
||||
If you want to run the latest code, you should get conduwuit from the `flake.nix`
|
||||
If you want to run the latest code, you should get Continuwuity from the `flake.nix`
|
||||
or `default.nix` and set [`services.matrix-conduit.package`][package]
|
||||
appropriately to use conduwuit instead of Conduit.
|
||||
appropriately to use Continuwuity instead of Conduit.
|
||||
|
||||
### UNIX sockets
|
||||
|
||||
Due to the lack of a conduwuit NixOS module, when using the `services.matrix-conduit` module
|
||||
Due to the lack of a Continuwuity NixOS module, when using the `services.matrix-conduit` module
|
||||
a workaround like the one below is necessary to use UNIX sockets. This is because the UNIX
|
||||
socket option does not exist in Conduit, and the module forcibly sets the `address` and
|
||||
socket option does not exist in Conduit, and the module forcibly sets the `address` and
|
||||
`port` config options.
|
||||
|
||||
```nix
|
||||
@@ -84,13 +51,13 @@ disallows the namespace from accessing or creating UNIX sockets and has to be en
|
||||
systemd.services.conduit.serviceConfig.RestrictAddressFamilies = [ "AF_UNIX" ];
|
||||
```
|
||||
|
||||
Even though those workarounds are feasible a conduwuit NixOS configuration module, developed and
|
||||
Even though those workarounds are feasible a Continuwuity NixOS configuration module, developed and
|
||||
published by the community, would be appreciated.
|
||||
|
||||
### jemalloc and hardened profile
|
||||
|
||||
conduwuit uses jemalloc by default. This may interfere with the [`hardened.nix` profile][hardened.nix]
|
||||
due to them using `scudo` by default. You must either disable/hide `scudo` from conduwuit, or
|
||||
Continuwuity uses jemalloc by default. This may interfere with the [`hardened.nix` profile][hardened.nix]
|
||||
due to them using `scudo` by default. You must either disable/hide `scudo` from Continuwuity, or
|
||||
disable jemalloc like so:
|
||||
|
||||
```nix
|
||||
|
||||
+10
-10
@@ -4,9 +4,9 @@ Information about developing the project. If you are only interested in using
|
||||
it, you can safely ignore this page. If you plan on contributing, see the
|
||||
[contributor's guide](./contributing.md).
|
||||
|
||||
## conduwuit project layout
|
||||
## Continuwuity project layout
|
||||
|
||||
conduwuit uses a collection of sub-crates, packages, or workspace members
|
||||
Continuwuity uses a collection of sub-crates, packages, or workspace members
|
||||
that indicate what each general area of code is for. All of the workspace
|
||||
members are under `src/`. The workspace definition is at the top level / root
|
||||
`Cargo.toml`.
|
||||
@@ -14,11 +14,11 @@ members are under `src/`. The workspace definition is at the top level / root
|
||||
The crate names are generally self-explanatory:
|
||||
- `admin` is the admin room
|
||||
- `api` is the HTTP API, Matrix C-S and S-S endpoints, etc
|
||||
- `core` is core conduwuit functionality like config loading, error definitions,
|
||||
- `core` is core Continuwuity functionality like config loading, error definitions,
|
||||
global utilities, logging infrastructure, etc
|
||||
- `database` is RocksDB methods, helpers, RocksDB config, and general database definitions,
|
||||
utilities, or functions
|
||||
- `macros` are conduwuit Rust [macros][macros] like general helper macros, logging
|
||||
- `macros` are Continuwuity Rust [macros][macros] like general helper macros, logging
|
||||
and error handling macros, and [syn][syn] and [procedural macros][proc-macro]
|
||||
used for admin room commands and others
|
||||
- `main` is the "primary" sub-crate. This is where the `main()` function lives,
|
||||
@@ -35,7 +35,7 @@ if you truly find yourself needing to, we recommend reaching out to us in
|
||||
the Matrix room for discussions about it beforehand.
|
||||
|
||||
The primary inspiration for this design was apart of hot reloadable development,
|
||||
to support "conduwuit as a library" where specific parts can simply be swapped out.
|
||||
to support "Continuwuity as a library" where specific parts can simply be swapped out.
|
||||
There is evidence Conduit wanted to go this route too as `axum` is technically an
|
||||
optional feature in Conduit, and can be compiled without the binary or axum library
|
||||
for handling inbound web requests; but it was never completed or worked.
|
||||
@@ -68,10 +68,10 @@ do this if Rust supported workspace-level features to begin with.
|
||||
|
||||
## List of forked dependencies
|
||||
|
||||
During conduwuit development, we have had to fork
|
||||
During Continuwuity development, we have had to fork
|
||||
some dependencies to support our use-cases in some areas. This ranges from
|
||||
things said upstream project won't accept for any reason, faster-paced
|
||||
development (unresponsive or slow upstream), conduwuit-specific usecases, or
|
||||
development (unresponsive or slow upstream), Continuwuity-specific usecases, or
|
||||
lack of time to upstream some things.
|
||||
|
||||
- [ruma/ruma][1]: <https://github.com/girlbossceo/ruwuma> - various performance
|
||||
@@ -84,7 +84,7 @@ builds seem to be broken on upstream, fixes some broken/suspicious code in
|
||||
places, additional safety measures, and support redzones for Valgrind
|
||||
- [zyansheep/rustyline-async][4]:
|
||||
<https://github.com/girlbossceo/rustyline-async> - tab completion callback and
|
||||
`CTRL+\` signal quit event for conduwuit console CLI
|
||||
`CTRL+\` signal quit event for Continuwuity console CLI
|
||||
- [rust-rocksdb/rust-rocksdb][5]:
|
||||
<https://github.com/girlbossceo/rust-rocksdb-zaidoon1> - [`@zaidoon1`][8]'s fork
|
||||
has quicker updates, more up to date dependencies, etc. Our fork fixes musl build
|
||||
@@ -97,7 +97,7 @@ alongside other logging/metrics things
|
||||
## Debugging with `tokio-console`
|
||||
|
||||
[`tokio-console`][7] can be a useful tool for debugging and profiling. To make a
|
||||
`tokio-console`-enabled build of conduwuit, enable the `tokio_console` feature,
|
||||
`tokio-console`-enabled build of Continuwuity, enable the `tokio_console` feature,
|
||||
disable the default `release_max_log_level` feature, and set the `--cfg
|
||||
tokio_unstable` flag to enable experimental tokio APIs. A build might look like
|
||||
this:
|
||||
@@ -109,7 +109,7 @@ RUSTFLAGS="--cfg tokio_unstable" cargo +nightly build \
|
||||
--features=systemd,element_hacks,gzip_compression,brotli_compression,zstd_compression,tokio_console
|
||||
```
|
||||
|
||||
You will also need to enable the `tokio_console` config option in conduwuit when
|
||||
You will also need to enable the `tokio_console` config option in Continuwuity when
|
||||
starting it. This was due to tokio-console causing gradual memory leak/usage
|
||||
if left enabled.
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ guaranteed to work at this time.
|
||||
|
||||
### Summary
|
||||
|
||||
When developing in debug-builds with the nightly toolchain, conduwuit is modular
|
||||
When developing in debug-builds with the nightly toolchain, Continuwuity is modular
|
||||
using dynamic libraries and various parts of the application are hot-reloadable
|
||||
while the server is running: http api handlers, admin commands, services,
|
||||
database, etc. These are all split up into individual workspace crates as seen
|
||||
@@ -42,7 +42,7 @@ library, macOS, and likely other host architectures are not supported (if other
|
||||
architectures work, feel free to let us know and/or make a PR updating this).
|
||||
This should work on GNU ld and lld (rust-lld) and gcc/clang, however if you
|
||||
happen to have linker issues it's recommended to try using `mold` or `gold`
|
||||
linkers, and please let us know in the [conduwuit Matrix room][7] the linker
|
||||
linkers, and please let us know in the [Continuwuity Matrix room][7] the linker
|
||||
error and what linker solved this issue so we can figure out a solution. Ideally
|
||||
there should be minimal friction to using this, and in the future a build script
|
||||
(`build.rs`) may be suitable to making this easier to use if the capabilities
|
||||
@@ -52,13 +52,13 @@ allow us.
|
||||
|
||||
As of 19 May 2024, the instructions for using this are:
|
||||
|
||||
0. Have patience. Don't hesitate to join the [conduwuit Matrix room][7] to
|
||||
0. Have patience. Don't hesitate to join the [Continuwuity Matrix room][7] to
|
||||
receive help using this. As indicated by the various rustflags used and some
|
||||
of the interesting issues linked at the bottom, this is definitely not something
|
||||
the Rust ecosystem or toolchain is used to doing.
|
||||
|
||||
1. Install the nightly toolchain using rustup. You may need to use `rustup
|
||||
override set nightly` in your local conduwuit directory, or use `cargo
|
||||
override set nightly` in your local Continuwuity directory, or use `cargo
|
||||
+nightly` for all actions.
|
||||
|
||||
2. Uncomment `cargo-features` at the top level / root Cargo.toml
|
||||
@@ -85,14 +85,14 @@ LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$HOME/.rustup/toolchains/nightly-x86_64-unknown
|
||||
Cargo should only rebuild what was changed / what's necessary, so it should
|
||||
not be rebuilding all the crates.
|
||||
|
||||
9. In your conduwuit server terminal, hit/send `CTRL+C` signal. This will tell
|
||||
conduwuit to find which libraries need to be reloaded, and reloads them as
|
||||
9. In your Continuwuity server terminal, hit/send `CTRL+C` signal. This will tell
|
||||
Continuwuity to find which libraries need to be reloaded, and reloads them as
|
||||
necessary.
|
||||
|
||||
10. If there were no errors, it will tell you it successfully reloaded `#`
|
||||
modules, and your changes should now be visible. Repeat 7 - 9 as needed.
|
||||
|
||||
To shutdown conduwuit in this setup, hit/send `CTRL+\`. Normal builds still
|
||||
To shutdown Continuwuity in this setup, hit/send `CTRL+\`. Normal builds still
|
||||
shutdown with `CTRL+C` as usual.
|
||||
|
||||
Steps 1 - 5 are the initial first-time steps for using this. To remove the hot
|
||||
@@ -101,7 +101,7 @@ reload setup, revert/comment all the Cargo.toml changes.
|
||||
As mentioned in the requirements section, if you happen to have some linker
|
||||
issues, try using the `-fuse-ld=` rustflag and specify mold or gold in all the
|
||||
`rustflags` definitions in the top level Cargo.toml, and please let us know in
|
||||
the [conduwuit Matrix room][7] the problem. mold can be installed typically
|
||||
the [Continuwuity Matrix room][7] the problem. mold can be installed typically
|
||||
through your distro, and gold is provided by the binutils package.
|
||||
|
||||
It's possible a helper script can be made to do all of this, or most preferably
|
||||
@@ -136,7 +136,7 @@ acyclic graph. The primary rule is simple and illustrated in the figure below:
|
||||
**no crate is allowed to call a function or use a variable from a crate below
|
||||
it.**
|
||||
|
||||

|
||||
|
||||
When a symbol is referenced between crates they become bound: **crates cannot be
|
||||
@@ -147,7 +147,7 @@ by using an `RTLD_LOCAL` binding for just one link between the main executable
|
||||
and the first crate, freeing the executable from all modules as no global
|
||||
binding ever occurs between them.
|
||||
|
||||

|
||||
|
||||
Proper resource management is essential for reliable reloading to occur. This is
|
||||
@@ -190,11 +190,11 @@ The initial implementation PR is available [here][1].
|
||||
- [Workspace-level metadata
|
||||
(cargo-deb)](https://github.com/kornelski/cargo-deb/issues/68)
|
||||
|
||||
[1]: https://github.com/girlbossceo/conduwuit/pull/387
|
||||
[1]: https://forgejo.ellis.link/continuwuation/continuwuity/pulls/387
|
||||
[2]: https://wiki.musl-libc.org/functional-differences-from-glibc.html#Unloading-libraries
|
||||
[3]: https://github.com/rust-lang/rust/issues/28794
|
||||
[4]: https://github.com/rust-lang/rust/issues/28794#issuecomment-368693049
|
||||
[5]: https://github.com/rust-lang/cargo/issues/12746
|
||||
[6]: https://crates.io/crates/hot-lib-reloader/
|
||||
[7]: https://matrix.to/#/#conduwuit:puppygock.gay
|
||||
[7]: https://matrix.to/#/#continuwuity:continuwuity.org
|
||||
[8]: https://crates.io/crates/libloading
|
||||
|
||||
@@ -24,8 +24,9 @@ and run the script.
|
||||
If you're on macOS and need to build an image, run `nix build .#linux-complement`.
|
||||
|
||||
We have a Complement fork as some tests have needed to be fixed. This can be found
|
||||
at: <https://github.com/girlbossceo/complement>
|
||||
at: <https://forgejo.ellis.link/continuwuation/complement>
|
||||
|
||||
[ci-workflows]: https://github.com/girlbossceo/conduwuit/actions/workflows/ci.yml?query=event%3Apush+is%3Asuccess+actor%3Agirlbossceo
|
||||
[ci-workflows]:
|
||||
https://forgejo.ellis.link/continuwuation/continuwuity/actions/?workflow=ci.yml&actor=0&status=1
|
||||
[complement]: https://github.com/matrix-org/complement
|
||||
[direnv]: https://direnv.net/docs/hook.html
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# conduwuit
|
||||
# Continuwuity
|
||||
|
||||
{{#include ../README.md:catchphrase}}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
- [Deployment options](deploying.md)
|
||||
|
||||
If you want to connect an appservice to conduwuit, take a look at the
|
||||
If you want to connect an appservice to Continuwuity, take a look at the
|
||||
[appservices documentation](appservices.md).
|
||||
|
||||
#### How can I contribute?
|
||||
|
||||
+14
-14
@@ -1,14 +1,14 @@
|
||||
# Maintaining your conduwuit setup
|
||||
# Maintaining your Continuwuity setup
|
||||
|
||||
## Moderation
|
||||
|
||||
conduwuit has moderation through admin room commands. "binary commands" (medium
|
||||
Continuwuity has moderation through admin room commands. "binary commands" (medium
|
||||
priority) and an admin API (low priority) is planned. Some moderation-related
|
||||
config options are available in the example config such as "global ACLs" and
|
||||
blocking media requests to certain servers. See the example config for the
|
||||
moderation config options under the "Moderation / Privacy / Security" section.
|
||||
|
||||
conduwuit has moderation admin commands for:
|
||||
Continuwuity has moderation admin commands for:
|
||||
|
||||
- managing room aliases (`!admin rooms alias`)
|
||||
- managing room directory (`!admin rooms directory`)
|
||||
@@ -36,7 +36,7 @@ each object being newline delimited. An example of doing this is:
|
||||
## Database (RocksDB)
|
||||
|
||||
Generally there is very little you need to do. [Compaction][rocksdb-compaction]
|
||||
is ran automatically based on various defined thresholds tuned for conduwuit to
|
||||
is ran automatically based on various defined thresholds tuned for Continuwuity to
|
||||
be high performance with the least I/O amplifcation or overhead. Manually
|
||||
running compaction is not recommended, or compaction via a timer, due to
|
||||
creating unnecessary I/O amplification. RocksDB is built with io_uring support
|
||||
@@ -50,7 +50,7 @@ Some RocksDB settings can be adjusted such as the compression method chosen. See
|
||||
the RocksDB section in the [example config](configuration/examples.md).
|
||||
|
||||
btrfs users have reported that database compression does not need to be disabled
|
||||
on conduwuit as the filesystem already does not attempt to compress. This can be
|
||||
on Continuwuity as the filesystem already does not attempt to compress. This can be
|
||||
validated by using `filefrag -v` on a `.SST` file in your database, and ensure
|
||||
the `physical_offset` matches (no filesystem compression). It is very important
|
||||
to ensure no additional filesystem compression takes place as this can render
|
||||
@@ -70,8 +70,8 @@ they're server logs or database logs, however they are critical RocksDB files
|
||||
related to WAL tracking.
|
||||
|
||||
The only safe files that can be deleted are the `LOG` files (all caps). These
|
||||
are the real RocksDB telemetry/log files, however conduwuit has already
|
||||
configured to only store up to 3 RocksDB `LOG` files due to generall being
|
||||
are the real RocksDB telemetry/log files, however Continuwuity has already
|
||||
configured to only store up to 3 RocksDB `LOG` files due to generally being
|
||||
useless for average users unless troubleshooting something low-level. If you
|
||||
would like to store nearly none at all, see the `rocksdb_max_log_files`
|
||||
config option.
|
||||
@@ -88,7 +88,7 @@ still be joined together.
|
||||
|
||||
To restore a backup from an online RocksDB backup:
|
||||
|
||||
- shutdown conduwuit
|
||||
- shutdown Continuwuity
|
||||
- create a new directory for merging together the data
|
||||
- in the online backup created, copy all `.sst` files in
|
||||
`$DATABASE_BACKUP_PATH/shared_checksum` to your new directory
|
||||
@@ -99,9 +99,9 @@ To restore a backup from an online RocksDB backup:
|
||||
if you have multiple) to your new directory
|
||||
- set your `database_path` config option to your new directory, or replace your
|
||||
old one with the new one you crafted
|
||||
- start up conduwuit again and it should open as normal
|
||||
- start up Continuwuity again and it should open as normal
|
||||
|
||||
If you'd like to do an offline backup, shutdown conduwuit and copy your
|
||||
If you'd like to do an offline backup, shutdown Continuwuity and copy your
|
||||
`database_path` directory elsewhere. This can be restored with no modifications
|
||||
needed.
|
||||
|
||||
@@ -110,7 +110,7 @@ directory.
|
||||
|
||||
## Media
|
||||
|
||||
Media still needs various work, however conduwuit implements media deletion via:
|
||||
Media still needs various work, however Continuwuity implements media deletion via:
|
||||
|
||||
- MXC URI or Event ID (unencrypted and attempts to find the MXC URI in the
|
||||
event)
|
||||
@@ -118,17 +118,17 @@ event)
|
||||
- Delete remote media in the past `N` seconds/minutes via filesystem metadata on
|
||||
the file created time (`btime`) or file modified time (`mtime`)
|
||||
|
||||
See the `!admin media` command for further information. All media in conduwuit
|
||||
See the `!admin media` command for further information. All media in Continuwuity
|
||||
is stored at `$DATABASE_DIR/media`. This will be configurable soon.
|
||||
|
||||
If you are finding yourself needing extensive granular control over media, we
|
||||
recommend looking into [Matrix Media
|
||||
Repo](https://github.com/t2bot/matrix-media-repo). conduwuit intends to
|
||||
Repo](https://github.com/t2bot/matrix-media-repo). Continuwuity intends to
|
||||
implement various utilities for media, but MMR is dedicated to extensive media
|
||||
management.
|
||||
|
||||
Built-in S3 support is also planned, but for now using a "S3 filesystem" on
|
||||
`media/` works. conduwuit also sends a `Cache-Control` header of 1 year and
|
||||
`media/` works. Continuwuity also sends a `Cache-Control` header of 1 year and
|
||||
immutable for all media requests (download and thumbnail) to reduce unnecessary
|
||||
media requests from browsers, reduce bandwidth usage, and reduce load.
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{{#include ../SECURITY.md}}
|
||||
Vendored
+1
-1
@@ -3,4 +3,4 @@
|
||||
Content-Type: application/json
|
||||
/.well-known/continuwuity/*
|
||||
Access-Control-Allow-Origin: *
|
||||
Content-Type: application/json
|
||||
Content-Type: application/json
|
||||
|
||||
Vendored
+5
-1
@@ -4,6 +4,10 @@
|
||||
{
|
||||
"id": 1,
|
||||
"message": "Welcome to Continuwuity! Important announcements about the project will appear here."
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"message": "🎉 Continuwuity v0.5.0-rc.6 is now available! This release includes improved knock-restricted room handling, automatic support contact configuration, and a new HTML landing page. Check [the release notes for full details](https://forgejo.ellis.link/continuwuation/continuwuity/releases/tag/v0.5.0-rc.6) and upgrade instructions."
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+7
-3
@@ -3,7 +3,7 @@
|
||||
"$id": "https://continwuity.org/schema/announcements.schema.json",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"updates": {
|
||||
"announcements": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
@@ -16,6 +16,10 @@
|
||||
},
|
||||
"date": {
|
||||
"type": "string"
|
||||
},
|
||||
"mention_room": {
|
||||
"type": "boolean",
|
||||
"description": "Whether to mention the room (@room) when posting this announcement"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -26,6 +30,6 @@
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"updates"
|
||||
"announcements"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+24
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"contacts": [
|
||||
{
|
||||
"email_address": "security@continuwuity.org",
|
||||
"role": "m.role.security"
|
||||
},
|
||||
{
|
||||
"matrix_id": "@tom:continuwuity.org",
|
||||
"email_address": "tom@tcpip.uk",
|
||||
"role": "m.role.admin"
|
||||
},
|
||||
{
|
||||
"matrix_id": "@jade:continuwuity.org",
|
||||
"email_address": "jade@continuwuity.org",
|
||||
"role": "m.role.admin"
|
||||
},
|
||||
{
|
||||
"matrix_id": "@nex:continuwuity.org",
|
||||
"email_address": "nex@continuwuity.org",
|
||||
"role": "m.role.admin"
|
||||
}
|
||||
],
|
||||
"support_page": "https://continuwuity.org/introduction#contact"
|
||||
}
|
||||
+46
-45
@@ -1,47 +1,48 @@
|
||||
# Troubleshooting conduwuit
|
||||
# Troubleshooting Continuwuity
|
||||
|
||||
> ## Docker users ⚠️
|
||||
> **Docker users ⚠️**
|
||||
>
|
||||
> Docker is extremely UX unfriendly. Because of this, a ton of issues or support
|
||||
> is actually Docker support, not conduwuit support. We also cannot document the
|
||||
> ever-growing list of Docker issues here.
|
||||
>
|
||||
> If you intend on asking for support and you are using Docker, **PLEASE**
|
||||
> triple validate your issues are **NOT** because you have a misconfiguration in
|
||||
> your Docker setup.
|
||||
>
|
||||
> If there are things like Compose file issues or Dockerhub image issues, those
|
||||
> can still be mentioned as long as they're something we can fix.
|
||||
> Docker can be difficult to use and debug. It's common for Docker
|
||||
> misconfigurations to cause issues, particularly with networking and permissions.
|
||||
> Please check that your issues are not due to problems with your Docker setup.
|
||||
|
||||
## conduwuit and Matrix issues
|
||||
## Continuwuity and Matrix issues
|
||||
|
||||
#### Lost access to admin room
|
||||
### Lost access to admin room
|
||||
|
||||
You can reinvite yourself to the admin room through the following methods:
|
||||
- Use the `--execute "users make_user_admin <username>"` conduwuit binary
|
||||
|
||||
- Use the `--execute "users make_user_admin <username>"` Continuwuity binary
|
||||
argument once to invite yourslf to the admin room on startup
|
||||
- Use the conduwuit console/CLI to run the `users make_user_admin` command
|
||||
- Use the Continuwuity console/CLI to run the `users make_user_admin` command
|
||||
- Or specify the `emergency_password` config option to allow you to temporarily
|
||||
log into the server account (`@conduit`) from a web client
|
||||
|
||||
## General potential issues
|
||||
|
||||
#### Potential DNS issues when using Docker
|
||||
### Potential DNS issues when using Docker
|
||||
|
||||
Docker has issues with its default DNS setup that may cause DNS to not be
|
||||
properly functional when running conduwuit, resulting in federation issues. The
|
||||
symptoms of this have shown in excessively long room joins (30+ minutes) from
|
||||
very long DNS timeouts, log entries of "mismatching responding nameservers",
|
||||
Docker's DNS setup for containers in a non-default network intercepts queries to
|
||||
enable resolving of container hostnames to IP addresses. However, due to
|
||||
performance issues with Docker's built-in resolver, this can cause DNS queries
|
||||
to take a long time to resolve, resulting in federation issues.
|
||||
|
||||
This is particularly common with Docker Compose, as custom networks are easily
|
||||
created and configured.
|
||||
|
||||
Symptoms of this include excessively long room joins (30+ minutes) from very
|
||||
long DNS timeouts, log entries of "mismatching responding nameservers",
|
||||
and/or partial or non-functional inbound/outbound federation.
|
||||
|
||||
This is **not** a conduwuit issue, and is purely a Docker issue. It is not
|
||||
sustainable for heavy DNS activity which is normal for Matrix federation. The
|
||||
workarounds for this are:
|
||||
- Use DNS over TCP via the config option `query_over_tcp_only = true`
|
||||
- Don't use Docker's default DNS setup and instead allow the container to use
|
||||
and communicate with your host's DNS servers (host's `/etc/resolv.conf`)
|
||||
This is not a bug in continuwuity. Docker's default DNS resolver is not suitable
|
||||
for heavy DNS activity, which is normal for federated protocols like Matrix.
|
||||
|
||||
#### DNS No connections available error message
|
||||
Workarounds:
|
||||
|
||||
- Use DNS over TCP via the config option `query_over_tcp_only = true`
|
||||
- Bypass Docker's default DNS setup and instead allow the container to use and communicate with your host's DNS servers. Typically, this can be done by mounting the host's `/etc/resolv.conf`.
|
||||
|
||||
### DNS No connections available error message
|
||||
|
||||
If you receive spurious amounts of error logs saying "DNS No connections
|
||||
available", this is due to your DNS server (servers from `/etc/resolv.conf`)
|
||||
@@ -64,7 +65,7 @@ very computationally expensive, and is extremely susceptible to denial of
|
||||
service, especially on Matrix. Many servers also strangely have broken DNSSEC
|
||||
setups and will result in non-functional federation.
|
||||
|
||||
conduwuit cannot provide a "works-for-everyone" Unbound DNS setup guide, but
|
||||
Continuwuity cannot provide a "works-for-everyone" Unbound DNS setup guide, but
|
||||
the [official Unbound tuning guide][unbound-tuning] and the [Unbound Arch Linux wiki page][unbound-arch]
|
||||
may be of interest. Disabling DNSSEC on Unbound is commenting out trust-anchors
|
||||
config options and removing the `validator` module.
|
||||
@@ -75,9 +76,9 @@ high load, and we have identified its DNS caching to not be very effective.
|
||||
dnsmasq can possibly work, but it does **not** support TCP fallback which can be
|
||||
problematic when receiving large DNS responses such as from large SRV records.
|
||||
If you still want to use dnsmasq, make sure you **disable** `dns_tcp_fallback`
|
||||
in conduwuit config.
|
||||
in Continuwuity config.
|
||||
|
||||
Raising `dns_cache_entries` in conduwuit config from the default can also assist
|
||||
Raising `dns_cache_entries` in Continuwuity config from the default can also assist
|
||||
in DNS caching, but a full-fledged external caching resolver is better and more
|
||||
reliable.
|
||||
|
||||
@@ -91,13 +92,13 @@ reliability at a slight performance cost due to TCP overhead.
|
||||
|
||||
## RocksDB / database issues
|
||||
|
||||
#### Database corruption
|
||||
### Database corruption
|
||||
|
||||
If your database is corrupted *and* is failing to start (e.g. checksum
|
||||
mismatch), it may be recoverable but careful steps must be taken, and there is
|
||||
no guarantee it may be recoverable.
|
||||
|
||||
The first thing that can be done is launching conduwuit with the
|
||||
The first thing that can be done is launching Continuwuity with the
|
||||
`rocksdb_repair` config option set to true. This will tell RocksDB to attempt to
|
||||
repair itself at launch. If this does not work, disable the option and continue
|
||||
reading.
|
||||
@@ -109,7 +110,7 @@ RocksDB has the following recovery modes:
|
||||
- `PointInTime`
|
||||
- `SkipAnyCorruptedRecord`
|
||||
|
||||
By default, conduwuit uses `TolerateCorruptedTailRecords` as generally these may
|
||||
By default, Continuwuity uses `TolerateCorruptedTailRecords` as generally these may
|
||||
be due to bad federation and we can re-fetch the correct data over federation.
|
||||
The RocksDB default is `PointInTime` which will attempt to restore a "snapshot"
|
||||
of the data when it was last known to be good. This data can be either a few
|
||||
@@ -126,12 +127,12 @@ if `PointInTime` does not work as a last ditch effort.
|
||||
|
||||
With this in mind:
|
||||
|
||||
- First start conduwuit with the `PointInTime` recovery method. See the [example
|
||||
- First start Continuwuity with the `PointInTime` recovery method. See the [example
|
||||
config](configuration/examples.md) for how to do this using
|
||||
`rocksdb_recovery_mode`
|
||||
- If your database successfully opens, clients are recommended to clear their
|
||||
client cache to account for the rollback
|
||||
- Leave your conduwuit running in `PointInTime` for at least 30-60 minutes so as
|
||||
- Leave your Continuwuity running in `PointInTime` for at least 30-60 minutes so as
|
||||
much possible corruption is restored
|
||||
- If all goes will, you should be able to restore back to using
|
||||
`TolerateCorruptedTailRecords` and you have successfully recovered your database
|
||||
@@ -142,16 +143,16 @@ Note that users should not really be debugging things. If you find yourself
|
||||
debugging and find the issue, please let us know and/or how we can fix it.
|
||||
Various debug commands can be found in `!admin debug`.
|
||||
|
||||
#### Debug/Trace log level
|
||||
### Debug/Trace log level
|
||||
|
||||
conduwuit builds without debug or trace log levels at compile time by default
|
||||
Continuwuity builds without debug or trace log levels at compile time by default
|
||||
for substantial performance gains in CPU usage and improved compile times. If
|
||||
you need to access debug/trace log levels, you will need to build without the
|
||||
`release_max_log_level` feature or use our provided static debug binaries.
|
||||
|
||||
#### Changing log level dynamically
|
||||
### Changing log level dynamically
|
||||
|
||||
conduwuit supports changing the tracing log environment filter on-the-fly using
|
||||
Continuwuity supports changing the tracing log environment filter on-the-fly using
|
||||
the admin command `!admin debug change-log-level <log env filter>`. This accepts
|
||||
a string **without quotes** the same format as the `log` config option.
|
||||
|
||||
@@ -166,9 +167,9 @@ load, simply pass the `--reset` flag.
|
||||
|
||||
`!admin debug change-log-level --reset`
|
||||
|
||||
#### Pinging servers
|
||||
### Pinging servers
|
||||
|
||||
conduwuit can ping other servers using `!admin debug ping <server>`. This takes
|
||||
Continuwuity can ping other servers using `!admin debug ping <server>`. This takes
|
||||
a server name and goes through the server discovery process and queries
|
||||
`/_matrix/federation/v1/version`. Errors are outputted.
|
||||
|
||||
@@ -177,15 +178,15 @@ server performance on either side as that endpoint is completely unauthenticated
|
||||
and simply fetches a string on a static JSON endpoint. It is very low cost both
|
||||
bandwidth and computationally.
|
||||
|
||||
#### Allocator memory stats
|
||||
### Allocator memory stats
|
||||
|
||||
When using jemalloc with jemallocator's `stats` feature (`--enable-stats`), you
|
||||
can see conduwuit's high-level allocator stats by using
|
||||
can see Continuwuity's high-level allocator stats by using
|
||||
`!admin server memory-usage` at the bottom.
|
||||
|
||||
If you are a developer, you can also view the raw jemalloc statistics with
|
||||
`!admin debug memory-stats`. Please note that this output is extremely large
|
||||
which may only be visible in the conduwuit console CLI due to PDU size limits,
|
||||
which may only be visible in the Continuwuity console CLI due to PDU size limits,
|
||||
and is not easy for non-developers to understand.
|
||||
|
||||
[unbound-tuning]: https://unbound.docs.nlnetlabs.nl/en/latest/topics/core/performance.html
|
||||
|
||||
+4
-4
@@ -1,6 +1,6 @@
|
||||
# Setting up TURN/STURN
|
||||
|
||||
In order to make or receive calls, a TURN server is required. conduwuit suggests
|
||||
In order to make or receive calls, a TURN server is required. Continuwuity suggests
|
||||
using [Coturn](https://github.com/coturn/coturn) for this purpose, which is also
|
||||
available as a Docker image.
|
||||
|
||||
@@ -17,9 +17,9 @@ realm=<your server domain>
|
||||
A common way to generate a suitable alphanumeric secret key is by using `pwgen
|
||||
-s 64 1`.
|
||||
|
||||
These same values need to be set in conduwuit. See the [example
|
||||
These same values need to be set in Continuwuity. See the [example
|
||||
config](configuration/examples.md) in the TURN section for configuring these and
|
||||
restart conduwuit after.
|
||||
restart Continuwuity after.
|
||||
|
||||
`turn_secret` or a path to `turn_secret_file` must have a value of your
|
||||
coturn `static-auth-secret`, or use `turn_username` and `turn_password`
|
||||
@@ -34,7 +34,7 @@ If you are using TURN over TLS, you can replace `turn:` with `turns:` in the
|
||||
TURN over TLS. This is highly recommended.
|
||||
|
||||
If you need unauthenticated access to the TURN URIs, or some clients may be
|
||||
having trouble, you can enable `turn_guest_access` in conduwuit which disables
|
||||
having trouble, you can enable `turn_guest_access` in Continuwuity which disables
|
||||
authentication for the TURN URI endpoint `/_matrix/client/v3/voip/turnServer`
|
||||
|
||||
### Run
|
||||
|
||||
@@ -75,9 +75,9 @@ dockerTools.buildImage {
|
||||
else [];
|
||||
|
||||
Env = [
|
||||
"CONDUWUIT_TLS__KEY=${./private_key.key}"
|
||||
"CONDUWUIT_TLS__CERTS=${./certificate.crt}"
|
||||
"CONDUWUIT_CONFIG=${./config.toml}"
|
||||
"CONTINUWUITY_TLS__KEY=${./private_key.key}"
|
||||
"CONTINUWUITY_TLS__CERTS=${./certificate.crt}"
|
||||
"CONTINUWUITY_CONFIG=${./config.toml}"
|
||||
"RUST_BACKTRACE=full"
|
||||
];
|
||||
|
||||
|
||||
@@ -130,7 +130,8 @@ buildDepsOnlyEnv =
|
||||
});
|
||||
|
||||
buildPackageEnv = {
|
||||
CONDUWUIT_VERSION_EXTRA = inputs.self.shortRev or inputs.self.dirtyShortRev or "";
|
||||
GIT_COMMIT_HASH = inputs.self.rev or inputs.self.dirtyRev or "";
|
||||
GIT_COMMIT_HASH_SHORT = inputs.self.shortRev or inputs.self.dirtyShortRev or "";
|
||||
} // buildDepsOnlyEnv // {
|
||||
# Only needed in static stdenv because these are transitive dependencies of rocksdb
|
||||
CARGO_BUILD_RUSTFLAGS = buildDepsOnlyEnv.CARGO_BUILD_RUSTFLAGS
|
||||
|
||||
@@ -33,13 +33,13 @@ dockerTools.buildLayeredImage {
|
||||
<jason@zemos.net>";
|
||||
"org.opencontainers.image.created" ="@${toString inputs.self.lastModified}";
|
||||
"org.opencontainers.image.description" = "a very cool Matrix chat homeserver written in Rust";
|
||||
"org.opencontainers.image.documentation" = "https://conduwuit.puppyirl.gay/";
|
||||
"org.opencontainers.image.documentation" = "https://continuwuity.org/";
|
||||
"org.opencontainers.image.licenses" = "Apache-2.0";
|
||||
"org.opencontainers.image.revision" = inputs.self.rev or inputs.self.dirtyRev or "";
|
||||
"org.opencontainers.image.source" = "https://github.com/girlbossceo/conduwuit";
|
||||
"org.opencontainers.image.source" = "https://forgejo.ellis.link/continuwuation/continuwuity";
|
||||
"org.opencontainers.image.title" = main.pname;
|
||||
"org.opencontainers.image.url" = "https://conduwuit.puppyirl.gay/";
|
||||
"org.opencontainers.image.vendor" = "girlbossceo";
|
||||
"org.opencontainers.image.url" = "https://continuwuity.org/";
|
||||
"org.opencontainers.image.vendor" = "continuwuation";
|
||||
"org.opencontainers.image.version" = main.version;
|
||||
};
|
||||
};
|
||||
|
||||
+1
-9
@@ -9,7 +9,7 @@
|
||||
# If you're having trouble making the relevant changes, bug a maintainer.
|
||||
|
||||
[toolchain]
|
||||
channel = "1.86.0"
|
||||
channel = "1.87.0"
|
||||
profile = "minimal"
|
||||
components = [
|
||||
# For rust-analyzer
|
||||
@@ -19,11 +19,3 @@ components = [
|
||||
"rustfmt",
|
||||
"clippy",
|
||||
]
|
||||
targets = [
|
||||
#"x86_64-apple-darwin",
|
||||
"x86_64-unknown-linux-gnu",
|
||||
"x86_64-unknown-linux-musl",
|
||||
"aarch64-unknown-linux-musl",
|
||||
"aarch64-unknown-linux-gnu",
|
||||
#"aarch64-apple-darwin",
|
||||
]
|
||||
|
||||
+33
-19
@@ -7,7 +7,10 @@ use std::{
|
||||
|
||||
use conduwuit::{
|
||||
Err, Result, debug_error, err, info,
|
||||
matrix::pdu::{PduEvent, PduId, RawPduId},
|
||||
matrix::{
|
||||
Event,
|
||||
pdu::{PduEvent, PduId, RawPduId},
|
||||
},
|
||||
trace, utils,
|
||||
utils::{
|
||||
stream::{IterStream, ReadyExt},
|
||||
@@ -19,7 +22,7 @@ use futures::{FutureExt, StreamExt, TryStreamExt};
|
||||
use ruma::{
|
||||
CanonicalJsonObject, CanonicalJsonValue, EventId, OwnedEventId, OwnedRoomId,
|
||||
OwnedRoomOrAliasId, OwnedServerName, RoomId, RoomVersionId,
|
||||
api::federation::event::get_room_state,
|
||||
api::federation::event::get_room_state, events::AnyStateEvent, serde::Raw,
|
||||
};
|
||||
use service::rooms::{
|
||||
short::{ShortEventId, ShortRoomId},
|
||||
@@ -239,10 +242,11 @@ pub(super) async fn get_remote_pdu(
|
||||
})
|
||||
.await
|
||||
{
|
||||
| Err(e) =>
|
||||
| Err(e) => {
|
||||
return Err!(
|
||||
"Remote server did not have PDU or failed sending request to remote server: {e}"
|
||||
),
|
||||
);
|
||||
},
|
||||
| Ok(response) => {
|
||||
let json: CanonicalJsonObject =
|
||||
serde_json::from_str(response.pdu.get()).map_err(|e| {
|
||||
@@ -295,12 +299,12 @@ pub(super) async fn get_remote_pdu(
|
||||
#[admin_command]
|
||||
pub(super) async fn get_room_state(&self, room: OwnedRoomOrAliasId) -> Result {
|
||||
let room_id = self.services.rooms.alias.resolve(&room).await?;
|
||||
let room_state: Vec<_> = self
|
||||
let room_state: Vec<Raw<AnyStateEvent>> = self
|
||||
.services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_full_pdus(&room_id)
|
||||
.map_ok(PduEvent::into_state_event)
|
||||
.map_ok(Event::into_format)
|
||||
.try_collect()
|
||||
.await?;
|
||||
|
||||
@@ -384,8 +388,9 @@ pub(super) async fn change_log_level(&self, filter: Option<String>, reset: bool)
|
||||
.reload
|
||||
.reload(&old_filter_layer, Some(handles))
|
||||
{
|
||||
| Err(e) =>
|
||||
return Err!("Failed to modify and reload the global tracing log level: {e}"),
|
||||
| Err(e) => {
|
||||
return Err!("Failed to modify and reload the global tracing log level: {e}");
|
||||
},
|
||||
| Ok(()) => {
|
||||
let value = &self.services.server.config.log;
|
||||
let out = format!("Successfully changed log level back to config value {value}");
|
||||
@@ -407,9 +412,12 @@ pub(super) async fn change_log_level(&self, filter: Option<String>, reset: bool)
|
||||
.reload
|
||||
.reload(&new_filter_layer, Some(handles))
|
||||
{
|
||||
| Ok(()) => return self.write_str("Successfully changed log level").await,
|
||||
| Err(e) =>
|
||||
return Err!("Failed to modify and reload the global tracing log level: {e}"),
|
||||
| Ok(()) => {
|
||||
return self.write_str("Successfully changed log level").await;
|
||||
},
|
||||
| Err(e) => {
|
||||
return Err!("Failed to modify and reload the global tracing log level: {e}");
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -529,6 +537,7 @@ pub(super) async fn force_set_room_state_from_server(
|
||||
&self,
|
||||
room_id: OwnedRoomId,
|
||||
server_name: OwnedServerName,
|
||||
at_event: Option<OwnedEventId>,
|
||||
) -> Result {
|
||||
if !self
|
||||
.services
|
||||
@@ -540,13 +549,18 @@ pub(super) async fn force_set_room_state_from_server(
|
||||
return Err!("We are not participating in the room / we don't know about the room ID.");
|
||||
}
|
||||
|
||||
let first_pdu = self
|
||||
.services
|
||||
.rooms
|
||||
.timeline
|
||||
.latest_pdu_in_room(&room_id)
|
||||
.await
|
||||
.map_err(|_| err!(Database("Failed to find the latest PDU in database")))?;
|
||||
let at_event_id = match at_event {
|
||||
| Some(event_id) => event_id,
|
||||
| None => self
|
||||
.services
|
||||
.rooms
|
||||
.timeline
|
||||
.latest_pdu_in_room(&room_id)
|
||||
.await
|
||||
.map_err(|_| err!(Database("Failed to find the latest PDU in database")))?
|
||||
.event_id()
|
||||
.to_owned(),
|
||||
};
|
||||
|
||||
let room_version = self.services.rooms.state.get_room_version(&room_id).await?;
|
||||
|
||||
@@ -557,7 +571,7 @@ pub(super) async fn force_set_room_state_from_server(
|
||||
.sending
|
||||
.send_federation_request(&server_name, get_room_state::v1::Request {
|
||||
room_id: room_id.clone(),
|
||||
event_id: first_pdu.event_id.clone(),
|
||||
event_id: at_event_id,
|
||||
})
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -125,13 +125,13 @@ pub(super) enum DebugCommand {
|
||||
reset: bool,
|
||||
},
|
||||
|
||||
/// - Verify json signatures
|
||||
/// - Sign JSON blob
|
||||
///
|
||||
/// This command needs a JSON blob provided in a Markdown code block below
|
||||
/// the command.
|
||||
SignJson,
|
||||
|
||||
/// - Verify json signatures
|
||||
/// - Verify JSON signatures
|
||||
///
|
||||
/// This command needs a JSON blob provided in a Markdown code block below
|
||||
/// the command.
|
||||
@@ -177,6 +177,9 @@ pub(super) enum DebugCommand {
|
||||
room_id: OwnedRoomId,
|
||||
/// The server we will use to query the room state for
|
||||
server_name: OwnedServerName,
|
||||
/// The event ID of the latest known PDU in the room. Will be found
|
||||
/// automatically if not provided.
|
||||
event_id: Option<OwnedEventId>,
|
||||
},
|
||||
|
||||
/// - Runs a server name through conduwuit's true destination resolution
|
||||
|
||||
@@ -93,8 +93,7 @@ async fn process_command(services: Arc<Services>, input: &CommandInput) -> Proce
|
||||
|
||||
#[allow(clippy::result_large_err)]
|
||||
fn handle_panic(error: &Error, command: &CommandInput) -> ProcessorResult {
|
||||
let link =
|
||||
"Please submit a [bug report](https://github.com/girlbossceo/conduwuit/issues/new). 🥺";
|
||||
let link = "Please submit a [bug report](https://forgejo.ellis.link/continuwuation/continuwuity/issues/new). 🥺";
|
||||
let msg = format!("Panic occurred while processing command:\n```\n{error:#?}\n```\n{link}");
|
||||
let content = RoomMessageEventContent::notice_markdown(msg);
|
||||
error!("Panic while processing command: {error:?}");
|
||||
|
||||
@@ -5,7 +5,7 @@ use conduwuit::{
|
||||
utils::{IterStream, ReadyExt},
|
||||
warn,
|
||||
};
|
||||
use futures::StreamExt;
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use ruma::{OwnedRoomId, OwnedRoomOrAliasId, RoomAliasId, RoomId, RoomOrAliasId};
|
||||
|
||||
use crate::{admin_command, admin_command_dispatch, get_room_info};
|
||||
@@ -155,7 +155,10 @@ async fn ban_room(&self, room: OwnedRoomOrAliasId) -> Result {
|
||||
evicting admins too)",
|
||||
);
|
||||
|
||||
if let Err(e) = leave_room(self.services, user_id, &room_id, None).await {
|
||||
if let Err(e) = leave_room(self.services, user_id, &room_id, None)
|
||||
.boxed()
|
||||
.await
|
||||
{
|
||||
warn!("Failed to leave room: {e}");
|
||||
}
|
||||
|
||||
@@ -323,7 +326,10 @@ async fn ban_list_of_rooms(&self) -> Result {
|
||||
evicting admins too)",
|
||||
);
|
||||
|
||||
if let Err(e) = leave_room(self.services, user_id, &room_id, None).await {
|
||||
if let Err(e) = leave_room(self.services, user_id, &room_id, None)
|
||||
.boxed()
|
||||
.await
|
||||
{
|
||||
warn!("Failed to leave room: {e}");
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ pub(super) enum ServerCommand {
|
||||
/// - Print database memory usage statistics
|
||||
MemoryUsage,
|
||||
|
||||
/// - Clears all of Conduwuit's caches
|
||||
/// - Clears all of Continuwuity's caches
|
||||
ClearCaches,
|
||||
|
||||
/// - Performs an online backup of the database (only available for RocksDB
|
||||
|
||||
+19
-17
@@ -1,14 +1,16 @@
|
||||
use std::{collections::BTreeMap, fmt::Write as _};
|
||||
|
||||
use api::client::{full_user_deactivate, join_room_by_id_helper, leave_room};
|
||||
use api::client::{
|
||||
full_user_deactivate, join_room_by_id_helper, leave_all_rooms, leave_room, update_avatar_url,
|
||||
update_displayname,
|
||||
};
|
||||
use conduwuit::{
|
||||
Err, Result, debug, debug_warn, error, info, is_equal_to,
|
||||
matrix::pdu::PduBuilder,
|
||||
matrix::{Event, pdu::PduBuilder},
|
||||
utils::{self, ReadyExt},
|
||||
warn,
|
||||
};
|
||||
use conduwuit_api::client::{leave_all_rooms, update_avatar_url, update_displayname};
|
||||
use futures::StreamExt;
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use ruma::{
|
||||
OwnedEventId, OwnedRoomId, OwnedRoomOrAliasId, OwnedUserId, UserId,
|
||||
events::{
|
||||
@@ -243,8 +245,9 @@ pub(super) async fn reset_password(&self, username: String, password: Option<Str
|
||||
.set_password(&user_id, Some(new_password.as_str()))
|
||||
{
|
||||
| Err(e) => return Err!("Couldn't reset the password for user {user_id}: {e}"),
|
||||
| Ok(()) =>
|
||||
write!(self, "Successfully reset the password for user {user_id}: `{new_password}`"),
|
||||
| Ok(()) => {
|
||||
write!(self, "Successfully reset the password for user {user_id}: `{new_password}`")
|
||||
},
|
||||
}
|
||||
.await
|
||||
}
|
||||
@@ -655,7 +658,9 @@ pub(super) async fn force_leave_room(
|
||||
return Err!("{user_id} is not joined in the room");
|
||||
}
|
||||
|
||||
leave_room(self.services, &user_id, &room_id, None).await?;
|
||||
leave_room(self.services, &user_id, &room_id, None)
|
||||
.boxed()
|
||||
.await?;
|
||||
|
||||
self.write_str(&format!("{user_id} has left {room_id}.",))
|
||||
.await
|
||||
@@ -692,7 +697,7 @@ pub(super) async fn force_demote(&self, user_id: String, room_id: OwnedRoomOrAli
|
||||
.state_accessor
|
||||
.room_state_get(&room_id, &StateEventType::RoomCreate, "")
|
||||
.await
|
||||
.is_ok_and(|event| event.sender == user_id);
|
||||
.is_ok_and(|event| event.sender() == user_id);
|
||||
|
||||
if !user_can_demote_self {
|
||||
return Err!("User is not allowed to modify their own power levels in the room.",);
|
||||
@@ -843,10 +848,7 @@ pub(super) async fn redact_event(&self, event_id: OwnedEventId) -> Result {
|
||||
return Err!("Event is already redacted.");
|
||||
}
|
||||
|
||||
let room_id = event.room_id;
|
||||
let sender_user = event.sender;
|
||||
|
||||
if !self.services.globals.user_is_local(&sender_user) {
|
||||
if !self.services.globals.user_is_local(event.sender()) {
|
||||
return Err!("This command only works on local users.");
|
||||
}
|
||||
|
||||
@@ -856,21 +858,21 @@ pub(super) async fn redact_event(&self, event_id: OwnedEventId) -> Result {
|
||||
);
|
||||
|
||||
let redaction_event_id = {
|
||||
let state_lock = self.services.rooms.state.mutex.lock(&room_id).await;
|
||||
let state_lock = self.services.rooms.state.mutex.lock(event.room_id()).await;
|
||||
|
||||
self.services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder {
|
||||
redacts: Some(event.event_id.clone()),
|
||||
redacts: Some(event.event_id().to_owned()),
|
||||
..PduBuilder::timeline(&RoomRedactionEventContent {
|
||||
redacts: Some(event.event_id.clone()),
|
||||
redacts: Some(event.event_id().to_owned()),
|
||||
reason: Some(reason),
|
||||
})
|
||||
},
|
||||
&sender_user,
|
||||
&room_id,
|
||||
event.sender(),
|
||||
event.room_id(),
|
||||
&state_lock,
|
||||
)
|
||||
.await?
|
||||
|
||||
+60
-71
@@ -3,10 +3,9 @@ use std::fmt::Write;
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::InsecureClientIp;
|
||||
use conduwuit::{
|
||||
Err, Error, Result, debug_info, err, error, info, is_equal_to,
|
||||
Err, Error, Event, Result, debug_info, err, error, info, is_equal_to,
|
||||
matrix::pdu::PduBuilder,
|
||||
utils,
|
||||
utils::{ReadyExt, stream::BroadbandExt},
|
||||
utils::{self, ReadyExt, stream::BroadbandExt},
|
||||
warn,
|
||||
};
|
||||
use conduwuit_service::Services;
|
||||
@@ -26,10 +25,7 @@ use ruma::{
|
||||
},
|
||||
events::{
|
||||
GlobalAccountDataEventType, StateEventType,
|
||||
room::{
|
||||
message::RoomMessageEventContent,
|
||||
power_levels::{RoomPowerLevels, RoomPowerLevelsEventContent},
|
||||
},
|
||||
room::power_levels::{RoomPowerLevels, RoomPowerLevelsEventContent},
|
||||
},
|
||||
push,
|
||||
};
|
||||
@@ -151,16 +147,32 @@ pub(crate) async fn register_route(
|
||||
if !services.config.allow_registration && body.appservice_info.is_none() {
|
||||
match (body.username.as_ref(), body.initial_device_display_name.as_ref()) {
|
||||
| (Some(username), Some(device_display_name)) => {
|
||||
info!(%is_guest, user = %username, device_name = %device_display_name, "Rejecting registration attempt as registration is disabled");
|
||||
info!(
|
||||
%is_guest,
|
||||
user = %username,
|
||||
device_name = %device_display_name,
|
||||
"Rejecting registration attempt as registration is disabled"
|
||||
);
|
||||
},
|
||||
| (Some(username), _) => {
|
||||
info!(%is_guest, user = %username, "Rejecting registration attempt as registration is disabled");
|
||||
info!(
|
||||
%is_guest,
|
||||
user = %username,
|
||||
"Rejecting registration attempt as registration is disabled"
|
||||
);
|
||||
},
|
||||
| (_, Some(device_display_name)) => {
|
||||
info!(%is_guest, device_name = %device_display_name, "Rejecting registration attempt as registration is disabled");
|
||||
info!(
|
||||
%is_guest,
|
||||
device_name = %device_display_name,
|
||||
"Rejecting registration attempt as registration is disabled"
|
||||
);
|
||||
},
|
||||
| (None, _) => {
|
||||
info!(%is_guest, "Rejecting registration attempt as registration is disabled");
|
||||
info!(
|
||||
%is_guest,
|
||||
"Rejecting registration attempt as registration is disabled"
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
@@ -351,8 +363,7 @@ pub(crate) async fn register_route(
|
||||
if !services.globals.new_user_displayname_suffix().is_empty()
|
||||
&& body.appservice_info.is_none()
|
||||
{
|
||||
write!(displayname, " {}", services.server.config.new_user_displayname_suffix)
|
||||
.expect("should be able to write to string buffer");
|
||||
write!(displayname, " {}", services.server.config.new_user_displayname_suffix)?;
|
||||
}
|
||||
|
||||
services
|
||||
@@ -370,8 +381,7 @@ pub(crate) async fn register_route(
|
||||
content: ruma::events::push_rules::PushRulesEventContent {
|
||||
global: push::Ruleset::server_default(&user_id),
|
||||
},
|
||||
})
|
||||
.expect("to json always works"),
|
||||
})?,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -416,32 +426,21 @@ pub(crate) async fn register_route(
|
||||
// log in conduit admin channel if a non-guest user registered
|
||||
if body.appservice_info.is_none() && !is_guest {
|
||||
if !device_display_name.is_empty() {
|
||||
info!(
|
||||
"New user \"{user_id}\" registered on this server with device display name: \
|
||||
\"{device_display_name}\""
|
||||
let notice = format!(
|
||||
"New user \"{user_id}\" registered on this server from IP {client} and device \
|
||||
display name \"{device_display_name}\""
|
||||
);
|
||||
|
||||
info!("{notice}");
|
||||
if services.server.config.admin_room_notices {
|
||||
services
|
||||
.admin
|
||||
.send_message(RoomMessageEventContent::notice_plain(format!(
|
||||
"New user \"{user_id}\" registered on this server from IP {client} and \
|
||||
device display name \"{device_display_name}\""
|
||||
)))
|
||||
.await
|
||||
.ok();
|
||||
services.admin.notice(¬ice).await;
|
||||
}
|
||||
} else {
|
||||
info!("New user \"{user_id}\" registered on this server.");
|
||||
let notice = format!("New user \"{user_id}\" registered on this server.");
|
||||
|
||||
info!("{notice}");
|
||||
if services.server.config.admin_room_notices {
|
||||
services
|
||||
.admin
|
||||
.send_message(RoomMessageEventContent::notice_plain(format!(
|
||||
"New user \"{user_id}\" registered on this server from IP {client}"
|
||||
)))
|
||||
.await
|
||||
.ok();
|
||||
services.admin.notice(¬ice).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -454,24 +453,22 @@ pub(crate) async fn register_route(
|
||||
if services.server.config.admin_room_notices {
|
||||
services
|
||||
.admin
|
||||
.send_message(RoomMessageEventContent::notice_plain(format!(
|
||||
.notice(&format!(
|
||||
"Guest user \"{user_id}\" with device display name \
|
||||
\"{device_display_name}\" registered on this server from IP {client}"
|
||||
)))
|
||||
.await
|
||||
.ok();
|
||||
))
|
||||
.await;
|
||||
}
|
||||
} else {
|
||||
#[allow(clippy::collapsible_else_if)]
|
||||
if services.server.config.admin_room_notices {
|
||||
services
|
||||
.admin
|
||||
.send_message(RoomMessageEventContent::notice_plain(format!(
|
||||
.notice(&format!(
|
||||
"Guest user \"{user_id}\" with no device display name registered on \
|
||||
this server from IP {client}",
|
||||
)))
|
||||
.await
|
||||
.ok();
|
||||
))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -584,7 +581,6 @@ pub(crate) async fn change_password_route(
|
||||
.sender_user
|
||||
.as_ref()
|
||||
.ok_or_else(|| err!(Request(MissingToken("Missing access token."))))?;
|
||||
let sender_device = body.sender_device();
|
||||
|
||||
let mut uiaainfo = UiaaInfo {
|
||||
flows: vec![AuthFlow { stages: vec![AuthType::Password] }],
|
||||
@@ -598,7 +594,7 @@ pub(crate) async fn change_password_route(
|
||||
| Some(auth) => {
|
||||
let (worked, uiaainfo) = services
|
||||
.uiaa
|
||||
.try_auth(sender_user, sender_device, auth, &uiaainfo)
|
||||
.try_auth(sender_user, body.sender_device(), auth, &uiaainfo)
|
||||
.await?;
|
||||
|
||||
if !worked {
|
||||
@@ -612,7 +608,7 @@ pub(crate) async fn change_password_route(
|
||||
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
||||
services
|
||||
.uiaa
|
||||
.create(sender_user, sender_device, &uiaainfo, json);
|
||||
.create(sender_user, body.sender_device(), &uiaainfo, json);
|
||||
|
||||
return Err(Error::Uiaa(uiaainfo));
|
||||
},
|
||||
@@ -631,7 +627,7 @@ pub(crate) async fn change_password_route(
|
||||
services
|
||||
.users
|
||||
.all_device_ids(sender_user)
|
||||
.ready_filter(|id| *id != sender_device)
|
||||
.ready_filter(|id| *id != body.sender_device())
|
||||
.for_each(|id| services.users.remove_device(sender_user, id))
|
||||
.await;
|
||||
|
||||
@@ -640,17 +636,17 @@ pub(crate) async fn change_password_route(
|
||||
.pusher
|
||||
.get_pushkeys(sender_user)
|
||||
.map(ToOwned::to_owned)
|
||||
.broad_filter_map(|pushkey| async move {
|
||||
.broad_filter_map(async |pushkey| {
|
||||
services
|
||||
.pusher
|
||||
.get_pusher_device(&pushkey)
|
||||
.await
|
||||
.ok()
|
||||
.filter(|pusher_device| pusher_device != sender_device)
|
||||
.filter(|pusher_device| pusher_device != body.sender_device())
|
||||
.is_some()
|
||||
.then_some(pushkey)
|
||||
})
|
||||
.for_each(|pushkey| async move {
|
||||
.for_each(async |pushkey| {
|
||||
services.pusher.delete_pusher(sender_user, &pushkey).await;
|
||||
})
|
||||
.await;
|
||||
@@ -661,11 +657,8 @@ pub(crate) async fn change_password_route(
|
||||
if services.server.config.admin_room_notices {
|
||||
services
|
||||
.admin
|
||||
.send_message(RoomMessageEventContent::notice_plain(format!(
|
||||
"User {sender_user} changed their password."
|
||||
)))
|
||||
.await
|
||||
.ok();
|
||||
.notice(&format!("User {sender_user} changed their password."))
|
||||
.await;
|
||||
}
|
||||
|
||||
Ok(change_password::v3::Response {})
|
||||
@@ -680,13 +673,10 @@ pub(crate) async fn whoami_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<whoami::v3::Request>,
|
||||
) -> Result<whoami::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let device_id = body.sender_device.clone();
|
||||
|
||||
Ok(whoami::v3::Response {
|
||||
user_id: sender_user.clone(),
|
||||
device_id,
|
||||
is_guest: services.users.is_deactivated(sender_user).await?
|
||||
user_id: body.sender_user().to_owned(),
|
||||
device_id: body.sender_device.clone(),
|
||||
is_guest: services.users.is_deactivated(body.sender_user()).await?
|
||||
&& body.appservice_info.is_none(),
|
||||
})
|
||||
}
|
||||
@@ -714,7 +704,6 @@ pub(crate) async fn deactivate_route(
|
||||
.sender_user
|
||||
.as_ref()
|
||||
.ok_or_else(|| err!(Request(MissingToken("Missing access token."))))?;
|
||||
let sender_device = body.sender_device();
|
||||
|
||||
let mut uiaainfo = UiaaInfo {
|
||||
flows: vec![AuthFlow { stages: vec![AuthType::Password] }],
|
||||
@@ -728,7 +717,7 @@ pub(crate) async fn deactivate_route(
|
||||
| Some(auth) => {
|
||||
let (worked, uiaainfo) = services
|
||||
.uiaa
|
||||
.try_auth(sender_user, sender_device, auth, &uiaainfo)
|
||||
.try_auth(sender_user, body.sender_device(), auth, &uiaainfo)
|
||||
.await?;
|
||||
|
||||
if !worked {
|
||||
@@ -741,7 +730,7 @@ pub(crate) async fn deactivate_route(
|
||||
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
||||
services
|
||||
.uiaa
|
||||
.create(sender_user, sender_device, &uiaainfo, json);
|
||||
.create(sender_user, body.sender_device(), &uiaainfo, json);
|
||||
|
||||
return Err(Error::Uiaa(uiaainfo));
|
||||
},
|
||||
@@ -763,18 +752,17 @@ pub(crate) async fn deactivate_route(
|
||||
super::update_displayname(&services, sender_user, None, &all_joined_rooms).await;
|
||||
super::update_avatar_url(&services, sender_user, None, None, &all_joined_rooms).await;
|
||||
|
||||
full_user_deactivate(&services, sender_user, &all_joined_rooms).await?;
|
||||
full_user_deactivate(&services, sender_user, &all_joined_rooms)
|
||||
.boxed()
|
||||
.await?;
|
||||
|
||||
info!("User {sender_user} deactivated their account.");
|
||||
|
||||
if services.server.config.admin_room_notices {
|
||||
services
|
||||
.admin
|
||||
.send_message(RoomMessageEventContent::notice_plain(format!(
|
||||
"User {sender_user} deactivated their account."
|
||||
)))
|
||||
.await
|
||||
.ok();
|
||||
.notice(&format!("User {sender_user} deactivated their account."))
|
||||
.await;
|
||||
}
|
||||
|
||||
Ok(deactivate::v3::Response {
|
||||
@@ -851,6 +839,7 @@ pub async fn full_user_deactivate(
|
||||
all_joined_rooms: &[OwnedRoomId],
|
||||
) -> Result<()> {
|
||||
services.users.deactivate_account(user_id).await.ok();
|
||||
|
||||
super::update_displayname(services, user_id, None, all_joined_rooms).await;
|
||||
super::update_avatar_url(services, user_id, None, None, all_joined_rooms).await;
|
||||
|
||||
@@ -887,7 +876,7 @@ pub async fn full_user_deactivate(
|
||||
.state_accessor
|
||||
.room_state_get(room_id, &StateEventType::RoomCreate, "")
|
||||
.await
|
||||
.is_ok_and(|event| event.sender == user_id);
|
||||
.is_ok_and(|event| event.sender() == user_id);
|
||||
|
||||
if user_can_demote_self {
|
||||
let mut power_levels_content = room_power_levels.unwrap_or_default();
|
||||
@@ -915,7 +904,7 @@ pub async fn full_user_deactivate(
|
||||
}
|
||||
}
|
||||
|
||||
super::leave_all_rooms(services, user_id).await;
|
||||
super::leave_all_rooms(services, user_id).boxed().await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -17,8 +17,7 @@ pub(crate) async fn create_alias_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<create_alias::v3::Request>,
|
||||
) -> Result<create_alias::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
let sender_user = body.sender_user();
|
||||
services
|
||||
.rooms
|
||||
.alias
|
||||
@@ -62,8 +61,7 @@ pub(crate) async fn delete_alias_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<delete_alias::v3::Request>,
|
||||
) -> Result<delete_alias::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
let sender_user = body.sender_user();
|
||||
services
|
||||
.rooms
|
||||
.alias
|
||||
|
||||
+43
-91
@@ -2,8 +2,10 @@ use std::cmp::Ordering;
|
||||
|
||||
use axum::extract::State;
|
||||
use conduwuit::{Err, Result, err};
|
||||
use conduwuit_service::Services;
|
||||
use futures::{FutureExt, future::try_join};
|
||||
use ruma::{
|
||||
UInt,
|
||||
UInt, UserId,
|
||||
api::client::backup::{
|
||||
add_backup_keys, add_backup_keys_for_room, add_backup_keys_for_session,
|
||||
create_backup_version, delete_backup_keys, delete_backup_keys_for_room,
|
||||
@@ -58,21 +60,9 @@ pub(crate) async fn get_latest_backup_info_route(
|
||||
.await
|
||||
.map_err(|_| err!(Request(NotFound("Key backup does not exist."))))?;
|
||||
|
||||
Ok(get_latest_backup_info::v3::Response {
|
||||
algorithm,
|
||||
count: (UInt::try_from(
|
||||
services
|
||||
.key_backups
|
||||
.count_keys(body.sender_user(), &version)
|
||||
.await,
|
||||
)
|
||||
.expect("user backup keys count should not be that high")),
|
||||
etag: services
|
||||
.key_backups
|
||||
.get_etag(body.sender_user(), &version)
|
||||
.await,
|
||||
version,
|
||||
})
|
||||
let (count, etag) = get_count_etag(&services, body.sender_user(), &version).await?;
|
||||
|
||||
Ok(get_latest_backup_info::v3::Response { algorithm, count, etag, version })
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/client/v3/room_keys/version/{version}`
|
||||
@@ -90,17 +80,12 @@ pub(crate) async fn get_backup_info_route(
|
||||
err!(Request(NotFound("Key backup does not exist at version {:?}", body.version)))
|
||||
})?;
|
||||
|
||||
let (count, etag) = get_count_etag(&services, body.sender_user(), &body.version).await?;
|
||||
|
||||
Ok(get_backup_info::v3::Response {
|
||||
algorithm,
|
||||
count: services
|
||||
.key_backups
|
||||
.count_keys(body.sender_user(), &body.version)
|
||||
.await
|
||||
.try_into()?,
|
||||
etag: services
|
||||
.key_backups
|
||||
.get_etag(body.sender_user(), &body.version)
|
||||
.await,
|
||||
count,
|
||||
etag,
|
||||
version: body.version.clone(),
|
||||
})
|
||||
}
|
||||
@@ -155,17 +140,9 @@ pub(crate) async fn add_backup_keys_route(
|
||||
}
|
||||
}
|
||||
|
||||
Ok(add_backup_keys::v3::Response {
|
||||
count: services
|
||||
.key_backups
|
||||
.count_keys(body.sender_user(), &body.version)
|
||||
.await
|
||||
.try_into()?,
|
||||
etag: services
|
||||
.key_backups
|
||||
.get_etag(body.sender_user(), &body.version)
|
||||
.await,
|
||||
})
|
||||
let (count, etag) = get_count_etag(&services, body.sender_user(), &body.version).await?;
|
||||
|
||||
Ok(add_backup_keys::v3::Response { count, etag })
|
||||
}
|
||||
|
||||
/// # `PUT /_matrix/client/r0/room_keys/keys/{roomId}`
|
||||
@@ -198,17 +175,9 @@ pub(crate) async fn add_backup_keys_for_room_route(
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(add_backup_keys_for_room::v3::Response {
|
||||
count: services
|
||||
.key_backups
|
||||
.count_keys(body.sender_user(), &body.version)
|
||||
.await
|
||||
.try_into()?,
|
||||
etag: services
|
||||
.key_backups
|
||||
.get_etag(body.sender_user(), &body.version)
|
||||
.await,
|
||||
})
|
||||
let (count, etag) = get_count_etag(&services, body.sender_user(), &body.version).await?;
|
||||
|
||||
Ok(add_backup_keys_for_room::v3::Response { count, etag })
|
||||
}
|
||||
|
||||
/// # `PUT /_matrix/client/r0/room_keys/keys/{roomId}/{sessionId}`
|
||||
@@ -306,17 +275,9 @@ pub(crate) async fn add_backup_keys_for_session_route(
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(add_backup_keys_for_session::v3::Response {
|
||||
count: services
|
||||
.key_backups
|
||||
.count_keys(body.sender_user(), &body.version)
|
||||
.await
|
||||
.try_into()?,
|
||||
etag: services
|
||||
.key_backups
|
||||
.get_etag(body.sender_user(), &body.version)
|
||||
.await,
|
||||
})
|
||||
let (count, etag) = get_count_etag(&services, body.sender_user(), &body.version).await?;
|
||||
|
||||
Ok(add_backup_keys_for_session::v3::Response { count, etag })
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/client/r0/room_keys/keys`
|
||||
@@ -379,17 +340,9 @@ pub(crate) async fn delete_backup_keys_route(
|
||||
.delete_all_keys(body.sender_user(), &body.version)
|
||||
.await;
|
||||
|
||||
Ok(delete_backup_keys::v3::Response {
|
||||
count: services
|
||||
.key_backups
|
||||
.count_keys(body.sender_user(), &body.version)
|
||||
.await
|
||||
.try_into()?,
|
||||
etag: services
|
||||
.key_backups
|
||||
.get_etag(body.sender_user(), &body.version)
|
||||
.await,
|
||||
})
|
||||
let (count, etag) = get_count_etag(&services, body.sender_user(), &body.version).await?;
|
||||
|
||||
Ok(delete_backup_keys::v3::Response { count, etag })
|
||||
}
|
||||
|
||||
/// # `DELETE /_matrix/client/r0/room_keys/keys/{roomId}`
|
||||
@@ -404,17 +357,9 @@ pub(crate) async fn delete_backup_keys_for_room_route(
|
||||
.delete_room_keys(body.sender_user(), &body.version, &body.room_id)
|
||||
.await;
|
||||
|
||||
Ok(delete_backup_keys_for_room::v3::Response {
|
||||
count: services
|
||||
.key_backups
|
||||
.count_keys(body.sender_user(), &body.version)
|
||||
.await
|
||||
.try_into()?,
|
||||
etag: services
|
||||
.key_backups
|
||||
.get_etag(body.sender_user(), &body.version)
|
||||
.await,
|
||||
})
|
||||
let (count, etag) = get_count_etag(&services, body.sender_user(), &body.version).await?;
|
||||
|
||||
Ok(delete_backup_keys_for_room::v3::Response { count, etag })
|
||||
}
|
||||
|
||||
/// # `DELETE /_matrix/client/r0/room_keys/keys/{roomId}/{sessionId}`
|
||||
@@ -429,15 +374,22 @@ pub(crate) async fn delete_backup_keys_for_session_route(
|
||||
.delete_room_key(body.sender_user(), &body.version, &body.room_id, &body.session_id)
|
||||
.await;
|
||||
|
||||
Ok(delete_backup_keys_for_session::v3::Response {
|
||||
count: services
|
||||
.key_backups
|
||||
.count_keys(body.sender_user(), &body.version)
|
||||
.await
|
||||
.try_into()?,
|
||||
etag: services
|
||||
.key_backups
|
||||
.get_etag(body.sender_user(), &body.version)
|
||||
.await,
|
||||
})
|
||||
let (count, etag) = get_count_etag(&services, body.sender_user(), &body.version).await?;
|
||||
|
||||
Ok(delete_backup_keys_for_session::v3::Response { count, etag })
|
||||
}
|
||||
|
||||
async fn get_count_etag(
|
||||
services: &Services,
|
||||
sender_user: &UserId,
|
||||
version: &str,
|
||||
) -> Result<(UInt, String)> {
|
||||
let count = services
|
||||
.key_backups
|
||||
.count_keys(sender_user, version)
|
||||
.map(TryInto::try_into);
|
||||
|
||||
let etag = services.key_backups.get_etag(sender_user, version).map(Ok);
|
||||
|
||||
Ok(try_join(count, etag).await?)
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ use crate::Ruma;
|
||||
|
||||
/// # `GET /_matrix/client/v3/capabilities`
|
||||
///
|
||||
/// Get information on the supported feature set and other relevent capabilities
|
||||
/// Get information on the supported feature set and other relevant capabilities
|
||||
/// of this server.
|
||||
pub(crate) async fn get_capabilities_route(
|
||||
State(services): State<crate::State>,
|
||||
@@ -26,8 +26,8 @@ pub(crate) async fn get_capabilities_route(
|
||||
|
||||
let mut capabilities = Capabilities::default();
|
||||
capabilities.room_versions = RoomVersionsCapability {
|
||||
default: services.server.config.default_room_version.clone(),
|
||||
available,
|
||||
default: services.server.config.default_room_version.clone(),
|
||||
};
|
||||
|
||||
// we do not implement 3PID stuff
|
||||
@@ -38,16 +38,12 @@ pub(crate) async fn get_capabilities_route(
|
||||
};
|
||||
|
||||
// MSC4133 capability
|
||||
capabilities
|
||||
.set("uk.tcpip.msc4133.profile_fields", json!({"enabled": true}))
|
||||
.expect("this is valid JSON we created");
|
||||
capabilities.set("uk.tcpip.msc4133.profile_fields", json!({"enabled": true}))?;
|
||||
|
||||
capabilities
|
||||
.set(
|
||||
"org.matrix.msc4267.forget_forced_upon_leave",
|
||||
json!({"enabled": services.config.forget_forced_upon_leave}),
|
||||
)
|
||||
.expect("valid JSON we created");
|
||||
capabilities.set(
|
||||
"org.matrix.msc4267.forget_forced_upon_leave",
|
||||
json!({"enabled": services.config.forget_forced_upon_leave}),
|
||||
)?;
|
||||
|
||||
Ok(get_capabilities::v3::Response { capabilities })
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
use axum::extract::State;
|
||||
use conduwuit::{
|
||||
Err, Result, at, debug_warn, err,
|
||||
matrix::pdu::PduEvent,
|
||||
ref_at,
|
||||
Err, Event, Result, at, debug_warn, err, ref_at,
|
||||
utils::{
|
||||
IterStream,
|
||||
future::TryExtExt,
|
||||
@@ -111,7 +109,7 @@ pub(crate) async fn get_context_route(
|
||||
|
||||
let lazy_loading_context = lazy_loading::Context {
|
||||
user_id: sender_user,
|
||||
device_id: sender_device,
|
||||
device_id: Some(sender_device),
|
||||
room_id,
|
||||
token: Some(base_count.into_unsigned()),
|
||||
options: Some(&filter.lazy_load_options),
|
||||
@@ -179,12 +177,12 @@ pub(crate) async fn get_context_route(
|
||||
.broad_filter_map(|event_id: &OwnedEventId| {
|
||||
services.rooms.timeline.get_pdu(event_id.as_ref()).ok()
|
||||
})
|
||||
.map(PduEvent::into_state_event)
|
||||
.map(Event::into_format)
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
Ok(get_context::v3::Response {
|
||||
event: base_event.map(at!(1)).map(PduEvent::into_room_event),
|
||||
event: base_event.map(at!(1)).map(Event::into_format),
|
||||
|
||||
start: events_before
|
||||
.last()
|
||||
@@ -203,13 +201,13 @@ pub(crate) async fn get_context_route(
|
||||
events_before: events_before
|
||||
.into_iter()
|
||||
.map(at!(1))
|
||||
.map(PduEvent::into_room_event)
|
||||
.map(Event::into_format)
|
||||
.collect(),
|
||||
|
||||
events_after: events_after
|
||||
.into_iter()
|
||||
.map(at!(1))
|
||||
.map(PduEvent::into_room_event)
|
||||
.map(Event::into_format)
|
||||
.collect(),
|
||||
|
||||
state,
|
||||
|
||||
@@ -21,11 +21,9 @@ pub(crate) async fn get_devices_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<get_devices::v3::Request>,
|
||||
) -> Result<get_devices::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
let devices: Vec<device::Device> = services
|
||||
.users
|
||||
.all_devices_metadata(sender_user)
|
||||
.all_devices_metadata(body.sender_user())
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
@@ -39,11 +37,9 @@ pub(crate) async fn get_device_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<get_device::v3::Request>,
|
||||
) -> Result<get_device::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
let device = services
|
||||
.users
|
||||
.get_device_metadata(sender_user, &body.body.device_id)
|
||||
.get_device_metadata(body.sender_user(), &body.body.device_id)
|
||||
.await
|
||||
.map_err(|_| err!(Request(NotFound("Device not found."))))?;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::InsecureClientIp;
|
||||
use conduwuit::{
|
||||
Err, Result, err, info,
|
||||
Err, Event, Result, err, info,
|
||||
utils::{
|
||||
TryFutureExtExt,
|
||||
math::Expected,
|
||||
@@ -349,7 +349,7 @@ async fn user_can_publish_room(
|
||||
.room_state_get(room_id, &StateEventType::RoomPowerLevels, "")
|
||||
.await
|
||||
{
|
||||
| Ok(event) => serde_json::from_str(event.content.get())
|
||||
| Ok(event) => serde_json::from_str(event.content().get())
|
||||
.map_err(|_| err!(Database("Invalid event content for m.room.power_levels")))
|
||||
.map(|content: RoomPowerLevelsEventContent| {
|
||||
RoomPowerLevels::from(content)
|
||||
@@ -362,7 +362,7 @@ async fn user_can_publish_room(
|
||||
.room_state_get(room_id, &StateEventType::RoomCreate, "")
|
||||
.await
|
||||
{
|
||||
| Ok(event) => Ok(event.sender == user_id),
|
||||
| Ok(event) => Ok(event.sender() == user_id),
|
||||
| _ => Err!(Request(Forbidden("User is not allowed to publish this room"))),
|
||||
}
|
||||
},
|
||||
|
||||
@@ -13,11 +13,9 @@ pub(crate) async fn get_filter_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<get_filter::v3::Request>,
|
||||
) -> Result<get_filter::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
services
|
||||
.users
|
||||
.get_filter(sender_user, &body.filter_id)
|
||||
.get_filter(body.sender_user(), &body.filter_id)
|
||||
.await
|
||||
.map(get_filter::v3::Response::new)
|
||||
.map_err(|_| err!(Request(NotFound("Filter not found."))))
|
||||
@@ -30,9 +28,9 @@ pub(crate) async fn create_filter_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<create_filter::v3::Request>,
|
||||
) -> Result<create_filter::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
let filter_id = services.users.create_filter(sender_user, &body.filter);
|
||||
let filter_id = services
|
||||
.users
|
||||
.create_filter(body.sender_user(), &body.filter);
|
||||
|
||||
Ok(create_filter::v3::Response::new(filter_id))
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ pub(crate) async fn get_keys_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<get_keys::v3::Request>,
|
||||
) -> Result<get_keys::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let sender_user = body.sender_user();
|
||||
|
||||
get_keys_helper(
|
||||
&services,
|
||||
@@ -157,8 +157,7 @@ pub(crate) async fn upload_signing_keys_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<upload_signing_keys::v3::Request>,
|
||||
) -> Result<upload_signing_keys::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
|
||||
let (sender_user, sender_device) = body.sender();
|
||||
|
||||
// UIAA
|
||||
let mut uiaainfo = UiaaInfo {
|
||||
@@ -203,12 +202,12 @@ pub(crate) async fn upload_signing_keys_route(
|
||||
}
|
||||
// Success!
|
||||
},
|
||||
| _ => match body.json_body {
|
||||
| _ => match body.json_body.as_ref() {
|
||||
| Some(json) => {
|
||||
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
||||
services
|
||||
.uiaa
|
||||
.create(sender_user, sender_device, &uiaainfo, &json);
|
||||
.create(sender_user, sender_device, &uiaainfo, json);
|
||||
|
||||
return Err(Error::Uiaa(uiaainfo));
|
||||
},
|
||||
@@ -373,7 +372,7 @@ pub(crate) async fn get_key_changes_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<get_key_changes::v3::Request>,
|
||||
) -> Result<get_key_changes::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let sender_user = body.sender_user();
|
||||
|
||||
let mut device_list_updates = HashSet::new();
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ pub(crate) async fn create_content_route(
|
||||
InsecureClientIp(client): InsecureClientIp,
|
||||
body: Ruma<create_content::v3::Request>,
|
||||
) -> Result<create_content::v3::Response> {
|
||||
let user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let user = body.sender_user();
|
||||
|
||||
let filename = body.filename.as_deref();
|
||||
let content_type = body.content_type.as_deref();
|
||||
@@ -94,7 +94,7 @@ pub(crate) async fn get_content_thumbnail_route(
|
||||
InsecureClientIp(client): InsecureClientIp,
|
||||
body: Ruma<get_content_thumbnail::v1::Request>,
|
||||
) -> Result<get_content_thumbnail::v1::Response> {
|
||||
let user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let user = body.sender_user();
|
||||
|
||||
let dim = Dim::from_ruma(body.width, body.height, body.method.clone())?;
|
||||
let mxc = Mxc {
|
||||
@@ -131,7 +131,7 @@ pub(crate) async fn get_content_route(
|
||||
InsecureClientIp(client): InsecureClientIp,
|
||||
body: Ruma<get_content::v1::Request>,
|
||||
) -> Result<get_content::v1::Response> {
|
||||
let user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let user = body.sender_user();
|
||||
|
||||
let mxc = Mxc {
|
||||
server_name: &body.server_name,
|
||||
@@ -167,7 +167,7 @@ pub(crate) async fn get_content_as_filename_route(
|
||||
InsecureClientIp(client): InsecureClientIp,
|
||||
body: Ruma<get_content_as_filename::v1::Request>,
|
||||
) -> Result<get_content_as_filename::v1::Response> {
|
||||
let user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let user = body.sender_user();
|
||||
|
||||
let mxc = Mxc {
|
||||
server_name: &body.server_name,
|
||||
@@ -203,7 +203,7 @@ pub(crate) async fn get_media_preview_route(
|
||||
InsecureClientIp(client): InsecureClientIp,
|
||||
body: Ruma<get_media_preview::v1::Request>,
|
||||
) -> Result<get_media_preview::v1::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let sender_user = body.sender_user();
|
||||
|
||||
let url = &body.url;
|
||||
let url = Url::parse(&body.url).map_err(|e| {
|
||||
|
||||
@@ -55,7 +55,7 @@ pub(crate) async fn get_media_preview_legacy_route(
|
||||
InsecureClientIp(client): InsecureClientIp,
|
||||
body: Ruma<get_media_preview::v3::Request>,
|
||||
) -> Result<get_media_preview::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let sender_user = body.sender_user();
|
||||
|
||||
let url = &body.url;
|
||||
let url = Url::parse(&body.url).map_err(|e| {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,55 @@
|
||||
use axum::extract::State;
|
||||
use conduwuit::{Err, Result, matrix::pdu::PduBuilder};
|
||||
use ruma::{
|
||||
api::client::membership::ban_user,
|
||||
events::room::member::{MembershipState, RoomMemberEventContent},
|
||||
};
|
||||
|
||||
use crate::Ruma;
|
||||
|
||||
/// # `POST /_matrix/client/r0/rooms/{roomId}/ban`
|
||||
///
|
||||
/// Tries to send a ban event into the room.
|
||||
pub(crate) async fn ban_user_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<ban_user::v3::Request>,
|
||||
) -> Result<ban_user::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
|
||||
if sender_user == body.user_id {
|
||||
return Err!(Request(Forbidden("You cannot ban yourself.")));
|
||||
}
|
||||
|
||||
let state_lock = services.rooms.state.mutex.lock(&body.room_id).await;
|
||||
|
||||
let current_member_content = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.get_member(&body.room_id, &body.user_id)
|
||||
.await
|
||||
.unwrap_or_else(|_| RoomMemberEventContent::new(MembershipState::Ban));
|
||||
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(body.user_id.to_string(), &RoomMemberEventContent {
|
||||
membership: MembershipState::Ban,
|
||||
reason: body.reason.clone(),
|
||||
displayname: None, // display name may be offensive
|
||||
avatar_url: None, // avatar may be offensive
|
||||
is_direct: None,
|
||||
join_authorized_via_users_server: None,
|
||||
third_party_invite: None,
|
||||
..current_member_content
|
||||
}),
|
||||
sender_user,
|
||||
&body.room_id,
|
||||
&state_lock,
|
||||
)
|
||||
.await?;
|
||||
|
||||
drop(state_lock);
|
||||
|
||||
Ok(ban_user::v3::Response::new())
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
use axum::extract::State;
|
||||
use conduwuit::{Err, Result, is_matching, result::NotFound, utils::FutureBoolExt};
|
||||
use futures::pin_mut;
|
||||
use ruma::{api::client::membership::forget_room, events::room::member::MembershipState};
|
||||
|
||||
use crate::Ruma;
|
||||
|
||||
/// # `POST /_matrix/client/v3/rooms/{roomId}/forget`
|
||||
///
|
||||
/// Forgets about a room.
|
||||
///
|
||||
/// - If the sender user currently left the room: Stops sender user from
|
||||
/// receiving information about the room
|
||||
///
|
||||
/// Note: Other devices of the user have no way of knowing the room was
|
||||
/// forgotten, so this has to be called from every device
|
||||
pub(crate) async fn forget_room_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<forget_room::v3::Request>,
|
||||
) -> Result<forget_room::v3::Response> {
|
||||
let user_id = body.sender_user();
|
||||
let room_id = &body.room_id;
|
||||
|
||||
let joined = services.rooms.state_cache.is_joined(user_id, room_id);
|
||||
let knocked = services.rooms.state_cache.is_knocked(user_id, room_id);
|
||||
let invited = services.rooms.state_cache.is_invited(user_id, room_id);
|
||||
|
||||
pin_mut!(joined, knocked, invited);
|
||||
if joined.or(knocked).or(invited).await {
|
||||
return Err!(Request(Unknown("You must leave the room before forgetting it")));
|
||||
}
|
||||
|
||||
let membership = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.get_member(room_id, user_id)
|
||||
.await;
|
||||
|
||||
if membership.is_not_found() {
|
||||
return Err!(Request(Unknown("No membership event was found, room was never joined")));
|
||||
}
|
||||
|
||||
let non_membership = membership
|
||||
.map(|member| member.membership)
|
||||
.is_ok_and(is_matching!(MembershipState::Leave | MembershipState::Ban));
|
||||
|
||||
if non_membership || services.rooms.state_cache.is_left(user_id, room_id).await {
|
||||
services.rooms.state_cache.forget(room_id, user_id);
|
||||
}
|
||||
|
||||
Ok(forget_room::v3::Response::new())
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::InsecureClientIp;
|
||||
use conduwuit::{
|
||||
Err, Result, debug_error, err, info,
|
||||
matrix::{event::gen_event_id_canonical_json, pdu::PduBuilder},
|
||||
};
|
||||
use futures::{FutureExt, join};
|
||||
use ruma::{
|
||||
OwnedServerName, RoomId, UserId,
|
||||
api::{client::membership::invite_user, federation::membership::create_invite},
|
||||
events::room::member::{MembershipState, RoomMemberEventContent},
|
||||
};
|
||||
use service::Services;
|
||||
|
||||
use super::banned_room_check;
|
||||
use crate::Ruma;
|
||||
|
||||
/// # `POST /_matrix/client/r0/rooms/{roomId}/invite`
|
||||
///
|
||||
/// Tries to send an invite event into the room.
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "invite")]
|
||||
pub(crate) async fn invite_user_route(
|
||||
State(services): State<crate::State>,
|
||||
InsecureClientIp(client): InsecureClientIp,
|
||||
body: Ruma<invite_user::v3::Request>,
|
||||
) -> Result<invite_user::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
|
||||
if !services.users.is_admin(sender_user).await && services.config.block_non_admin_invites {
|
||||
debug_error!(
|
||||
"User {sender_user} is not an admin and attempted to send an invite to room {}",
|
||||
&body.room_id
|
||||
);
|
||||
return Err!(Request(Forbidden("Invites are not allowed on this server.")));
|
||||
}
|
||||
|
||||
banned_room_check(
|
||||
&services,
|
||||
sender_user,
|
||||
Some(&body.room_id),
|
||||
body.room_id.server_name(),
|
||||
client,
|
||||
)
|
||||
.await?;
|
||||
|
||||
match &body.recipient {
|
||||
| invite_user::v3::InvitationRecipient::UserId { user_id } => {
|
||||
let sender_ignored_recipient = services.users.user_is_ignored(sender_user, user_id);
|
||||
let recipient_ignored_by_sender =
|
||||
services.users.user_is_ignored(user_id, sender_user);
|
||||
|
||||
let (sender_ignored_recipient, recipient_ignored_by_sender) =
|
||||
join!(sender_ignored_recipient, recipient_ignored_by_sender);
|
||||
|
||||
if sender_ignored_recipient {
|
||||
return Ok(invite_user::v3::Response {});
|
||||
}
|
||||
|
||||
if let Ok(target_user_membership) = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.get_member(&body.room_id, user_id)
|
||||
.await
|
||||
{
|
||||
if target_user_membership.membership == MembershipState::Ban {
|
||||
return Err!(Request(Forbidden("User is banned from this room.")));
|
||||
}
|
||||
}
|
||||
|
||||
if recipient_ignored_by_sender {
|
||||
// silently drop the invite to the recipient if they've been ignored by the
|
||||
// sender, pretend it worked
|
||||
return Ok(invite_user::v3::Response {});
|
||||
}
|
||||
|
||||
invite_helper(
|
||||
&services,
|
||||
sender_user,
|
||||
user_id,
|
||||
&body.room_id,
|
||||
body.reason.clone(),
|
||||
false,
|
||||
)
|
||||
.boxed()
|
||||
.await?;
|
||||
|
||||
Ok(invite_user::v3::Response {})
|
||||
},
|
||||
| _ => {
|
||||
Err!(Request(NotFound("User not found.")))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn invite_helper(
|
||||
services: &Services,
|
||||
sender_user: &UserId,
|
||||
user_id: &UserId,
|
||||
room_id: &RoomId,
|
||||
reason: Option<String>,
|
||||
is_direct: bool,
|
||||
) -> Result {
|
||||
if !services.users.is_admin(sender_user).await && services.config.block_non_admin_invites {
|
||||
info!(
|
||||
"User {sender_user} is not an admin and attempted to send an invite to room \
|
||||
{room_id}"
|
||||
);
|
||||
return Err!(Request(Forbidden("Invites are not allowed on this server.")));
|
||||
}
|
||||
|
||||
if !services.globals.user_is_local(user_id) {
|
||||
let (pdu, pdu_json, invite_room_state) = {
|
||||
let state_lock = services.rooms.state.mutex.lock(room_id).await;
|
||||
|
||||
let content = RoomMemberEventContent {
|
||||
avatar_url: services.users.avatar_url(user_id).await.ok(),
|
||||
is_direct: Some(is_direct),
|
||||
reason,
|
||||
..RoomMemberEventContent::new(MembershipState::Invite)
|
||||
};
|
||||
|
||||
let (pdu, pdu_json) = services
|
||||
.rooms
|
||||
.timeline
|
||||
.create_hash_and_sign_event(
|
||||
PduBuilder::state(user_id.to_string(), &content),
|
||||
sender_user,
|
||||
room_id,
|
||||
&state_lock,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let invite_room_state = services.rooms.state.summary_stripped(&pdu).await;
|
||||
|
||||
drop(state_lock);
|
||||
|
||||
(pdu, pdu_json, invite_room_state)
|
||||
};
|
||||
|
||||
let room_version_id = services.rooms.state.get_room_version(room_id).await?;
|
||||
|
||||
let response = services
|
||||
.sending
|
||||
.send_federation_request(user_id.server_name(), create_invite::v2::Request {
|
||||
room_id: room_id.to_owned(),
|
||||
event_id: (*pdu.event_id).to_owned(),
|
||||
room_version: room_version_id.clone(),
|
||||
event: services
|
||||
.sending
|
||||
.convert_to_outgoing_federation_event(pdu_json.clone())
|
||||
.await,
|
||||
invite_room_state,
|
||||
via: services
|
||||
.rooms
|
||||
.state_cache
|
||||
.servers_route_via(room_id)
|
||||
.await
|
||||
.ok(),
|
||||
})
|
||||
.await?;
|
||||
|
||||
// We do not add the event_id field to the pdu here because of signature and
|
||||
// hashes checks
|
||||
let (event_id, value) = gen_event_id_canonical_json(&response.event, &room_version_id)
|
||||
.map_err(|e| {
|
||||
err!(Request(BadJson(warn!("Could not convert event to canonical JSON: {e}"))))
|
||||
})?;
|
||||
|
||||
if pdu.event_id != event_id {
|
||||
return Err!(Request(BadJson(warn!(
|
||||
%pdu.event_id, %event_id,
|
||||
"Server {} sent event with wrong event ID",
|
||||
user_id.server_name()
|
||||
))));
|
||||
}
|
||||
|
||||
let origin: OwnedServerName = serde_json::from_value(serde_json::to_value(
|
||||
value
|
||||
.get("origin")
|
||||
.ok_or_else(|| err!(Request(BadJson("Event missing origin field."))))?,
|
||||
)?)
|
||||
.map_err(|e| {
|
||||
err!(Request(BadJson(warn!("Origin field in event is not a valid server name: {e}"))))
|
||||
})?;
|
||||
|
||||
let pdu_id = services
|
||||
.rooms
|
||||
.event_handler
|
||||
.handle_incoming_pdu(&origin, room_id, &event_id, value, true)
|
||||
.boxed()
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
err!(Request(InvalidParam("Could not accept incoming PDU as timeline event.")))
|
||||
})?;
|
||||
|
||||
return services.sending.send_pdu_room(room_id, &pdu_id).await;
|
||||
}
|
||||
|
||||
if !services
|
||||
.rooms
|
||||
.state_cache
|
||||
.is_joined(sender_user, room_id)
|
||||
.await
|
||||
{
|
||||
return Err!(Request(Forbidden(
|
||||
"You must be joined in the room you are trying to invite from."
|
||||
)));
|
||||
}
|
||||
|
||||
let state_lock = services.rooms.state.mutex.lock(room_id).await;
|
||||
|
||||
let content = RoomMemberEventContent {
|
||||
displayname: services.users.displayname(user_id).await.ok(),
|
||||
avatar_url: services.users.avatar_url(user_id).await.ok(),
|
||||
blurhash: services.users.blurhash(user_id).await.ok(),
|
||||
is_direct: Some(is_direct),
|
||||
reason,
|
||||
..RoomMemberEventContent::new(MembershipState::Invite)
|
||||
};
|
||||
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(user_id.to_string(), &content),
|
||||
sender_user,
|
||||
room_id,
|
||||
&state_lock,
|
||||
)
|
||||
.await?;
|
||||
|
||||
drop(state_lock);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,975 @@
|
||||
use std::{borrow::Borrow, collections::HashMap, iter::once, sync::Arc};
|
||||
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::InsecureClientIp;
|
||||
use conduwuit::{
|
||||
Err, Result, debug, debug_info, debug_warn, err, error, info,
|
||||
matrix::{
|
||||
StateKey,
|
||||
event::{gen_event_id, gen_event_id_canonical_json},
|
||||
pdu::{PduBuilder, PduEvent},
|
||||
state_res,
|
||||
},
|
||||
result::FlatOk,
|
||||
trace,
|
||||
utils::{
|
||||
self, shuffle,
|
||||
stream::{IterStream, ReadyExt},
|
||||
},
|
||||
warn,
|
||||
};
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use ruma::{
|
||||
CanonicalJsonObject, CanonicalJsonValue, OwnedRoomId, OwnedServerName, OwnedUserId, RoomId,
|
||||
RoomVersionId, UserId,
|
||||
api::{
|
||||
client::{
|
||||
error::ErrorKind,
|
||||
membership::{ThirdPartySigned, join_room_by_id, join_room_by_id_or_alias},
|
||||
},
|
||||
federation::{self},
|
||||
},
|
||||
canonical_json::to_canonical_value,
|
||||
events::{
|
||||
StateEventType,
|
||||
room::{
|
||||
join_rules::{AllowRule, JoinRule, RoomJoinRulesEventContent},
|
||||
member::{MembershipState, RoomMemberEventContent},
|
||||
},
|
||||
},
|
||||
};
|
||||
use service::{
|
||||
Services,
|
||||
appservice::RegistrationInfo,
|
||||
rooms::{
|
||||
state::RoomMutexGuard,
|
||||
state_compressor::{CompressedState, HashSetCompressStateEvent},
|
||||
},
|
||||
};
|
||||
|
||||
use super::banned_room_check;
|
||||
use crate::Ruma;
|
||||
|
||||
/// # `POST /_matrix/client/r0/rooms/{roomId}/join`
|
||||
///
|
||||
/// Tries to join the sender user into a room.
|
||||
///
|
||||
/// - If the server knowns about this room: creates the join event and does auth
|
||||
/// rules locally
|
||||
/// - If the server does not know about the room: asks other servers over
|
||||
/// federation
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "join")]
|
||||
pub(crate) async fn join_room_by_id_route(
|
||||
State(services): State<crate::State>,
|
||||
InsecureClientIp(client): InsecureClientIp,
|
||||
body: Ruma<join_room_by_id::v3::Request>,
|
||||
) -> Result<join_room_by_id::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
|
||||
banned_room_check(
|
||||
&services,
|
||||
sender_user,
|
||||
Some(&body.room_id),
|
||||
body.room_id.server_name(),
|
||||
client,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// There is no body.server_name for /roomId/join
|
||||
let mut servers: Vec<_> = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.servers_invite_via(&body.room_id)
|
||||
.map(ToOwned::to_owned)
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
servers.extend(
|
||||
services
|
||||
.rooms
|
||||
.state_cache
|
||||
.invite_state(sender_user, &body.room_id)
|
||||
.await
|
||||
.unwrap_or_default()
|
||||
.iter()
|
||||
.filter_map(|event| event.get_field("sender").ok().flatten())
|
||||
.filter_map(|sender: &str| UserId::parse(sender).ok())
|
||||
.map(|user| user.server_name().to_owned()),
|
||||
);
|
||||
|
||||
if let Some(server) = body.room_id.server_name() {
|
||||
servers.push(server.into());
|
||||
}
|
||||
|
||||
servers.sort_unstable();
|
||||
servers.dedup();
|
||||
shuffle(&mut servers);
|
||||
|
||||
join_room_by_id_helper(
|
||||
&services,
|
||||
sender_user,
|
||||
&body.room_id,
|
||||
body.reason.clone(),
|
||||
&servers,
|
||||
body.third_party_signed.as_ref(),
|
||||
&body.appservice_info,
|
||||
)
|
||||
.boxed()
|
||||
.await
|
||||
}
|
||||
|
||||
/// # `POST /_matrix/client/r0/join/{roomIdOrAlias}`
|
||||
///
|
||||
/// Tries to join the sender user into a room.
|
||||
///
|
||||
/// - If the server knowns about this room: creates the join event and does auth
|
||||
/// rules locally
|
||||
/// - If the server does not know about the room: use the server name query
|
||||
/// param if specified. if not specified, asks other servers over federation
|
||||
/// via room alias server name and room ID server name
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "join")]
|
||||
pub(crate) async fn join_room_by_id_or_alias_route(
|
||||
State(services): State<crate::State>,
|
||||
InsecureClientIp(client): InsecureClientIp,
|
||||
body: Ruma<join_room_by_id_or_alias::v3::Request>,
|
||||
) -> Result<join_room_by_id_or_alias::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let appservice_info = &body.appservice_info;
|
||||
let body = &body.body;
|
||||
|
||||
let (servers, room_id) = match OwnedRoomId::try_from(body.room_id_or_alias.clone()) {
|
||||
| Ok(room_id) => {
|
||||
banned_room_check(
|
||||
&services,
|
||||
sender_user,
|
||||
Some(&room_id),
|
||||
room_id.server_name(),
|
||||
client,
|
||||
)
|
||||
.boxed()
|
||||
.await?;
|
||||
|
||||
let mut servers = body.via.clone();
|
||||
servers.extend(
|
||||
services
|
||||
.rooms
|
||||
.state_cache
|
||||
.servers_invite_via(&room_id)
|
||||
.map(ToOwned::to_owned)
|
||||
.collect::<Vec<_>>()
|
||||
.await,
|
||||
);
|
||||
|
||||
servers.extend(
|
||||
services
|
||||
.rooms
|
||||
.state_cache
|
||||
.invite_state(sender_user, &room_id)
|
||||
.await
|
||||
.unwrap_or_default()
|
||||
.iter()
|
||||
.filter_map(|event| event.get_field("sender").ok().flatten())
|
||||
.filter_map(|sender: &str| UserId::parse(sender).ok())
|
||||
.map(|user| user.server_name().to_owned()),
|
||||
);
|
||||
|
||||
if let Some(server) = room_id.server_name() {
|
||||
servers.push(server.to_owned());
|
||||
}
|
||||
|
||||
servers.sort_unstable();
|
||||
servers.dedup();
|
||||
shuffle(&mut servers);
|
||||
|
||||
(servers, room_id)
|
||||
},
|
||||
| Err(room_alias) => {
|
||||
let (room_id, mut servers) = services
|
||||
.rooms
|
||||
.alias
|
||||
.resolve_alias(&room_alias, Some(body.via.clone()))
|
||||
.await?;
|
||||
|
||||
banned_room_check(
|
||||
&services,
|
||||
sender_user,
|
||||
Some(&room_id),
|
||||
Some(room_alias.server_name()),
|
||||
client,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let addl_via_servers = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.servers_invite_via(&room_id)
|
||||
.map(ToOwned::to_owned);
|
||||
|
||||
let addl_state_servers = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.invite_state(sender_user, &room_id)
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
|
||||
let mut addl_servers: Vec<_> = addl_state_servers
|
||||
.iter()
|
||||
.map(|event| event.get_field("sender"))
|
||||
.filter_map(FlatOk::flat_ok)
|
||||
.map(|user: &UserId| user.server_name().to_owned())
|
||||
.stream()
|
||||
.chain(addl_via_servers)
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
addl_servers.sort_unstable();
|
||||
addl_servers.dedup();
|
||||
shuffle(&mut addl_servers);
|
||||
servers.append(&mut addl_servers);
|
||||
|
||||
(servers, room_id)
|
||||
},
|
||||
};
|
||||
|
||||
let join_room_response = join_room_by_id_helper(
|
||||
&services,
|
||||
sender_user,
|
||||
&room_id,
|
||||
body.reason.clone(),
|
||||
&servers,
|
||||
body.third_party_signed.as_ref(),
|
||||
appservice_info,
|
||||
)
|
||||
.boxed()
|
||||
.await?;
|
||||
|
||||
Ok(join_room_by_id_or_alias::v3::Response { room_id: join_room_response.room_id })
|
||||
}
|
||||
|
||||
pub async fn join_room_by_id_helper(
|
||||
services: &Services,
|
||||
sender_user: &UserId,
|
||||
room_id: &RoomId,
|
||||
reason: Option<String>,
|
||||
servers: &[OwnedServerName],
|
||||
third_party_signed: Option<&ThirdPartySigned>,
|
||||
appservice_info: &Option<RegistrationInfo>,
|
||||
) -> Result<join_room_by_id::v3::Response> {
|
||||
let state_lock = services.rooms.state.mutex.lock(room_id).await;
|
||||
|
||||
let user_is_guest = services
|
||||
.users
|
||||
.is_deactivated(sender_user)
|
||||
.await
|
||||
.unwrap_or(false)
|
||||
&& appservice_info.is_none();
|
||||
|
||||
if user_is_guest && !services.rooms.state_accessor.guest_can_join(room_id).await {
|
||||
return Err!(Request(Forbidden("Guests are not allowed to join this room")));
|
||||
}
|
||||
|
||||
if services
|
||||
.rooms
|
||||
.state_cache
|
||||
.is_joined(sender_user, room_id)
|
||||
.await
|
||||
{
|
||||
debug_warn!("{sender_user} is already joined in {room_id}");
|
||||
return Ok(join_room_by_id::v3::Response { room_id: room_id.into() });
|
||||
}
|
||||
|
||||
if let Ok(membership) = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.get_member(room_id, sender_user)
|
||||
.await
|
||||
{
|
||||
if membership.membership == MembershipState::Ban {
|
||||
debug_warn!("{sender_user} is banned from {room_id} but attempted to join");
|
||||
return Err!(Request(Forbidden("You are banned from the room.")));
|
||||
}
|
||||
}
|
||||
|
||||
let server_in_room = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.server_in_room(services.globals.server_name(), room_id)
|
||||
.await;
|
||||
|
||||
let local_join = server_in_room
|
||||
|| servers.is_empty()
|
||||
|| (servers.len() == 1 && services.globals.server_is_ours(&servers[0]));
|
||||
|
||||
if local_join {
|
||||
join_room_by_id_helper_local(
|
||||
services,
|
||||
sender_user,
|
||||
room_id,
|
||||
reason,
|
||||
servers,
|
||||
third_party_signed,
|
||||
state_lock,
|
||||
)
|
||||
.boxed()
|
||||
.await?;
|
||||
} else {
|
||||
// Ask a remote server if we are not participating in this room
|
||||
join_room_by_id_helper_remote(
|
||||
services,
|
||||
sender_user,
|
||||
room_id,
|
||||
reason,
|
||||
servers,
|
||||
third_party_signed,
|
||||
state_lock,
|
||||
)
|
||||
.boxed()
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(join_room_by_id::v3::Response::new(room_id.to_owned()))
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all, fields(%sender_user, %room_id), name = "join_remote")]
|
||||
async fn join_room_by_id_helper_remote(
|
||||
services: &Services,
|
||||
sender_user: &UserId,
|
||||
room_id: &RoomId,
|
||||
reason: Option<String>,
|
||||
servers: &[OwnedServerName],
|
||||
_third_party_signed: Option<&ThirdPartySigned>,
|
||||
state_lock: RoomMutexGuard,
|
||||
) -> Result {
|
||||
info!("Joining {room_id} over federation.");
|
||||
|
||||
let (make_join_response, remote_server) =
|
||||
make_join_request(services, sender_user, room_id, servers).await?;
|
||||
|
||||
info!("make_join finished");
|
||||
|
||||
let Some(room_version_id) = make_join_response.room_version else {
|
||||
return Err!(BadServerResponse("Remote room version is not supported by conduwuit"));
|
||||
};
|
||||
|
||||
if !services.server.supported_room_version(&room_version_id) {
|
||||
return Err!(BadServerResponse(
|
||||
"Remote room version {room_version_id} is not supported by conduwuit"
|
||||
));
|
||||
}
|
||||
|
||||
let mut join_event_stub: CanonicalJsonObject =
|
||||
serde_json::from_str(make_join_response.event.get()).map_err(|e| {
|
||||
err!(BadServerResponse(warn!(
|
||||
"Invalid make_join event json received from server: {e:?}"
|
||||
)))
|
||||
})?;
|
||||
|
||||
let join_authorized_via_users_server = {
|
||||
use RoomVersionId::*;
|
||||
if !matches!(room_version_id, V1 | V2 | V3 | V4 | V5 | V6 | V7) {
|
||||
join_event_stub
|
||||
.get("content")
|
||||
.map(|s| {
|
||||
s.as_object()?
|
||||
.get("join_authorised_via_users_server")?
|
||||
.as_str()
|
||||
})
|
||||
.and_then(|s| OwnedUserId::try_from(s.unwrap_or_default()).ok())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
join_event_stub.insert(
|
||||
"origin".to_owned(),
|
||||
CanonicalJsonValue::String(services.globals.server_name().as_str().to_owned()),
|
||||
);
|
||||
join_event_stub.insert(
|
||||
"origin_server_ts".to_owned(),
|
||||
CanonicalJsonValue::Integer(
|
||||
utils::millis_since_unix_epoch()
|
||||
.try_into()
|
||||
.expect("Timestamp is valid js_int value"),
|
||||
),
|
||||
);
|
||||
join_event_stub.insert(
|
||||
"content".to_owned(),
|
||||
to_canonical_value(RoomMemberEventContent {
|
||||
displayname: services.users.displayname(sender_user).await.ok(),
|
||||
avatar_url: services.users.avatar_url(sender_user).await.ok(),
|
||||
blurhash: services.users.blurhash(sender_user).await.ok(),
|
||||
reason,
|
||||
join_authorized_via_users_server: join_authorized_via_users_server.clone(),
|
||||
..RoomMemberEventContent::new(MembershipState::Join)
|
||||
})
|
||||
.expect("event is valid, we just created it"),
|
||||
);
|
||||
|
||||
// We keep the "event_id" in the pdu only in v1 or
|
||||
// v2 rooms
|
||||
match room_version_id {
|
||||
| RoomVersionId::V1 | RoomVersionId::V2 => {},
|
||||
| _ => {
|
||||
join_event_stub.remove("event_id");
|
||||
},
|
||||
}
|
||||
|
||||
// In order to create a compatible ref hash (EventID) the `hashes` field needs
|
||||
// to be present
|
||||
services
|
||||
.server_keys
|
||||
.hash_and_sign_event(&mut join_event_stub, &room_version_id)?;
|
||||
|
||||
// Generate event id
|
||||
let event_id = gen_event_id(&join_event_stub, &room_version_id)?;
|
||||
|
||||
// Add event_id back
|
||||
join_event_stub
|
||||
.insert("event_id".to_owned(), CanonicalJsonValue::String(event_id.clone().into()));
|
||||
|
||||
// It has enough fields to be called a proper event now
|
||||
let mut join_event = join_event_stub;
|
||||
|
||||
info!("Asking {remote_server} for send_join in room {room_id}");
|
||||
let send_join_request = federation::membership::create_join_event::v2::Request {
|
||||
room_id: room_id.to_owned(),
|
||||
event_id: event_id.clone(),
|
||||
omit_members: false,
|
||||
pdu: services
|
||||
.sending
|
||||
.convert_to_outgoing_federation_event(join_event.clone())
|
||||
.await,
|
||||
};
|
||||
|
||||
let send_join_response = match services
|
||||
.sending
|
||||
.send_synapse_request(&remote_server, send_join_request)
|
||||
.await
|
||||
{
|
||||
| Ok(response) => response,
|
||||
| Err(e) => {
|
||||
error!("send_join failed: {e}");
|
||||
return Err(e);
|
||||
},
|
||||
};
|
||||
|
||||
info!("send_join finished");
|
||||
|
||||
if join_authorized_via_users_server.is_some() {
|
||||
if let Some(signed_raw) = &send_join_response.room_state.event {
|
||||
debug_info!(
|
||||
"There is a signed event with join_authorized_via_users_server. This room is \
|
||||
probably using restricted joins. Adding signature to our event"
|
||||
);
|
||||
|
||||
let (signed_event_id, signed_value) =
|
||||
gen_event_id_canonical_json(signed_raw, &room_version_id).map_err(|e| {
|
||||
err!(Request(BadJson(warn!(
|
||||
"Could not convert event to canonical JSON: {e}"
|
||||
))))
|
||||
})?;
|
||||
|
||||
if signed_event_id != event_id {
|
||||
return Err!(Request(BadJson(warn!(
|
||||
%signed_event_id, %event_id,
|
||||
"Server {remote_server} sent event with wrong event ID"
|
||||
))));
|
||||
}
|
||||
|
||||
match signed_value["signatures"]
|
||||
.as_object()
|
||||
.ok_or_else(|| {
|
||||
err!(BadServerResponse(warn!(
|
||||
"Server {remote_server} sent invalid signatures type"
|
||||
)))
|
||||
})
|
||||
.and_then(|e| {
|
||||
e.get(remote_server.as_str()).ok_or_else(|| {
|
||||
err!(BadServerResponse(warn!(
|
||||
"Server {remote_server} did not send its signature for a restricted \
|
||||
room"
|
||||
)))
|
||||
})
|
||||
}) {
|
||||
| Ok(signature) => {
|
||||
join_event
|
||||
.get_mut("signatures")
|
||||
.expect("we created a valid pdu")
|
||||
.as_object_mut()
|
||||
.expect("we created a valid pdu")
|
||||
.insert(remote_server.to_string(), signature.clone());
|
||||
},
|
||||
| Err(e) => {
|
||||
warn!(
|
||||
"Server {remote_server} sent invalid signature in send_join signatures \
|
||||
for event {signed_value:?}: {e:?}",
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
services
|
||||
.rooms
|
||||
.short
|
||||
.get_or_create_shortroomid(room_id)
|
||||
.await;
|
||||
|
||||
info!("Parsing join event");
|
||||
let parsed_join_pdu = PduEvent::from_id_val(&event_id, join_event.clone())
|
||||
.map_err(|e| err!(BadServerResponse("Invalid join event PDU: {e:?}")))?;
|
||||
|
||||
info!("Acquiring server signing keys for response events");
|
||||
let resp_events = &send_join_response.room_state;
|
||||
let resp_state = &resp_events.state;
|
||||
let resp_auth = &resp_events.auth_chain;
|
||||
services
|
||||
.server_keys
|
||||
.acquire_events_pubkeys(resp_auth.iter().chain(resp_state.iter()))
|
||||
.await;
|
||||
|
||||
info!("Going through send_join response room_state");
|
||||
let cork = services.db.cork_and_flush();
|
||||
let state = send_join_response
|
||||
.room_state
|
||||
.state
|
||||
.iter()
|
||||
.stream()
|
||||
.then(|pdu| {
|
||||
services
|
||||
.server_keys
|
||||
.validate_and_add_event_id_no_fetch(pdu, &room_version_id)
|
||||
})
|
||||
.ready_filter_map(Result::ok)
|
||||
.fold(HashMap::new(), |mut state, (event_id, value)| async move {
|
||||
let pdu = match PduEvent::from_id_val(&event_id, value.clone()) {
|
||||
| Ok(pdu) => pdu,
|
||||
| Err(e) => {
|
||||
debug_warn!("Invalid PDU in send_join response: {e:?}: {value:#?}");
|
||||
return state;
|
||||
},
|
||||
};
|
||||
|
||||
services.rooms.outlier.add_pdu_outlier(&event_id, &value);
|
||||
if let Some(state_key) = &pdu.state_key {
|
||||
let shortstatekey = services
|
||||
.rooms
|
||||
.short
|
||||
.get_or_create_shortstatekey(&pdu.kind.to_string().into(), state_key)
|
||||
.await;
|
||||
|
||||
state.insert(shortstatekey, pdu.event_id.clone());
|
||||
}
|
||||
|
||||
state
|
||||
})
|
||||
.await;
|
||||
|
||||
drop(cork);
|
||||
|
||||
info!("Going through send_join response auth_chain");
|
||||
let cork = services.db.cork_and_flush();
|
||||
send_join_response
|
||||
.room_state
|
||||
.auth_chain
|
||||
.iter()
|
||||
.stream()
|
||||
.then(|pdu| {
|
||||
services
|
||||
.server_keys
|
||||
.validate_and_add_event_id_no_fetch(pdu, &room_version_id)
|
||||
})
|
||||
.ready_filter_map(Result::ok)
|
||||
.ready_for_each(|(event_id, value)| {
|
||||
services.rooms.outlier.add_pdu_outlier(&event_id, &value);
|
||||
})
|
||||
.await;
|
||||
|
||||
drop(cork);
|
||||
|
||||
debug!("Running send_join auth check");
|
||||
let fetch_state = &state;
|
||||
let state_fetch = |k: StateEventType, s: StateKey| async move {
|
||||
let shortstatekey = services.rooms.short.get_shortstatekey(&k, &s).await.ok()?;
|
||||
|
||||
let event_id = fetch_state.get(&shortstatekey)?;
|
||||
services.rooms.timeline.get_pdu(event_id).await.ok()
|
||||
};
|
||||
|
||||
let auth_check = state_res::event_auth::auth_check(
|
||||
&state_res::RoomVersion::new(&room_version_id)?,
|
||||
&parsed_join_pdu,
|
||||
None, // TODO: third party invite
|
||||
|k, s| state_fetch(k.clone(), s.into()),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| err!(Request(Forbidden(warn!("Auth check failed: {e:?}")))))?;
|
||||
|
||||
if !auth_check {
|
||||
return Err!(Request(Forbidden("Auth check failed")));
|
||||
}
|
||||
|
||||
info!("Compressing state from send_join");
|
||||
let compressed: CompressedState = services
|
||||
.rooms
|
||||
.state_compressor
|
||||
.compress_state_events(state.iter().map(|(ssk, eid)| (ssk, eid.borrow())))
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
debug!("Saving compressed state");
|
||||
let HashSetCompressStateEvent {
|
||||
shortstatehash: statehash_before_join,
|
||||
added,
|
||||
removed,
|
||||
} = services
|
||||
.rooms
|
||||
.state_compressor
|
||||
.save_state(room_id, Arc::new(compressed))
|
||||
.await?;
|
||||
|
||||
debug!("Forcing state for new room");
|
||||
services
|
||||
.rooms
|
||||
.state
|
||||
.force_state(room_id, statehash_before_join, added, removed, &state_lock)
|
||||
.await?;
|
||||
|
||||
info!("Updating joined counts for new room");
|
||||
services
|
||||
.rooms
|
||||
.state_cache
|
||||
.update_joined_count(room_id)
|
||||
.await;
|
||||
|
||||
// We append to state before appending the pdu, so we don't have a moment in
|
||||
// time with the pdu without it's state. This is okay because append_pdu can't
|
||||
// fail.
|
||||
let statehash_after_join = services
|
||||
.rooms
|
||||
.state
|
||||
.append_to_state(&parsed_join_pdu)
|
||||
.await?;
|
||||
|
||||
info!("Appending new room join event");
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.append_pdu(
|
||||
&parsed_join_pdu,
|
||||
join_event,
|
||||
once(parsed_join_pdu.event_id.borrow()),
|
||||
&state_lock,
|
||||
)
|
||||
.await?;
|
||||
|
||||
info!("Setting final room state for new room");
|
||||
// We set the room state after inserting the pdu, so that we never have a moment
|
||||
// in time where events in the current room state do not exist
|
||||
services
|
||||
.rooms
|
||||
.state
|
||||
.set_room_state(room_id, statehash_after_join, &state_lock);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all, fields(%sender_user, %room_id), name = "join_local")]
|
||||
async fn join_room_by_id_helper_local(
|
||||
services: &Services,
|
||||
sender_user: &UserId,
|
||||
room_id: &RoomId,
|
||||
reason: Option<String>,
|
||||
servers: &[OwnedServerName],
|
||||
_third_party_signed: Option<&ThirdPartySigned>,
|
||||
state_lock: RoomMutexGuard,
|
||||
) -> Result {
|
||||
debug_info!("We can join locally");
|
||||
|
||||
let join_rules_event_content = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get_content::<RoomJoinRulesEventContent>(
|
||||
room_id,
|
||||
&StateEventType::RoomJoinRules,
|
||||
"",
|
||||
)
|
||||
.await;
|
||||
|
||||
let restriction_rooms = match join_rules_event_content {
|
||||
| Ok(RoomJoinRulesEventContent {
|
||||
join_rule: JoinRule::Restricted(restricted) | JoinRule::KnockRestricted(restricted),
|
||||
}) => restricted
|
||||
.allow
|
||||
.into_iter()
|
||||
.filter_map(|a| match a {
|
||||
| AllowRule::RoomMembership(r) => Some(r.room_id),
|
||||
| _ => None,
|
||||
})
|
||||
.collect(),
|
||||
| _ => Vec::new(),
|
||||
};
|
||||
|
||||
let join_authorized_via_users_server: Option<OwnedUserId> = {
|
||||
if restriction_rooms
|
||||
.iter()
|
||||
.stream()
|
||||
.any(|restriction_room_id| {
|
||||
services
|
||||
.rooms
|
||||
.state_cache
|
||||
.is_joined(sender_user, restriction_room_id)
|
||||
})
|
||||
.await
|
||||
{
|
||||
services
|
||||
.rooms
|
||||
.state_cache
|
||||
.local_users_in_room(room_id)
|
||||
.filter(|user| {
|
||||
services.rooms.state_accessor.user_can_invite(
|
||||
room_id,
|
||||
user,
|
||||
sender_user,
|
||||
&state_lock,
|
||||
)
|
||||
})
|
||||
.boxed()
|
||||
.next()
|
||||
.await
|
||||
.map(ToOwned::to_owned)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
let content = RoomMemberEventContent {
|
||||
displayname: services.users.displayname(sender_user).await.ok(),
|
||||
avatar_url: services.users.avatar_url(sender_user).await.ok(),
|
||||
blurhash: services.users.blurhash(sender_user).await.ok(),
|
||||
reason: reason.clone(),
|
||||
join_authorized_via_users_server,
|
||||
..RoomMemberEventContent::new(MembershipState::Join)
|
||||
};
|
||||
|
||||
// Try normal join first
|
||||
let Err(error) = services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(sender_user.to_string(), &content),
|
||||
sender_user,
|
||||
room_id,
|
||||
&state_lock,
|
||||
)
|
||||
.await
|
||||
else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
if restriction_rooms.is_empty()
|
||||
&& (servers.is_empty()
|
||||
|| servers.len() == 1 && services.globals.server_is_ours(&servers[0]))
|
||||
{
|
||||
return Err(error);
|
||||
}
|
||||
|
||||
warn!(
|
||||
"We couldn't do the join locally, maybe federation can help to satisfy the restricted \
|
||||
join requirements"
|
||||
);
|
||||
let Ok((make_join_response, remote_server)) =
|
||||
make_join_request(services, sender_user, room_id, servers).await
|
||||
else {
|
||||
return Err(error);
|
||||
};
|
||||
|
||||
let Some(room_version_id) = make_join_response.room_version else {
|
||||
return Err!(BadServerResponse("Remote room version is not supported by conduwuit"));
|
||||
};
|
||||
|
||||
if !services.server.supported_room_version(&room_version_id) {
|
||||
return Err!(BadServerResponse(
|
||||
"Remote room version {room_version_id} is not supported by conduwuit"
|
||||
));
|
||||
}
|
||||
|
||||
let mut join_event_stub: CanonicalJsonObject =
|
||||
serde_json::from_str(make_join_response.event.get()).map_err(|e| {
|
||||
err!(BadServerResponse("Invalid make_join event json received from server: {e:?}"))
|
||||
})?;
|
||||
|
||||
let join_authorized_via_users_server = join_event_stub
|
||||
.get("content")
|
||||
.map(|s| {
|
||||
s.as_object()?
|
||||
.get("join_authorised_via_users_server")?
|
||||
.as_str()
|
||||
})
|
||||
.and_then(|s| OwnedUserId::try_from(s.unwrap_or_default()).ok());
|
||||
|
||||
join_event_stub.insert(
|
||||
"origin".to_owned(),
|
||||
CanonicalJsonValue::String(services.globals.server_name().as_str().to_owned()),
|
||||
);
|
||||
join_event_stub.insert(
|
||||
"origin_server_ts".to_owned(),
|
||||
CanonicalJsonValue::Integer(
|
||||
utils::millis_since_unix_epoch()
|
||||
.try_into()
|
||||
.expect("Timestamp is valid js_int value"),
|
||||
),
|
||||
);
|
||||
join_event_stub.insert(
|
||||
"content".to_owned(),
|
||||
to_canonical_value(RoomMemberEventContent {
|
||||
displayname: services.users.displayname(sender_user).await.ok(),
|
||||
avatar_url: services.users.avatar_url(sender_user).await.ok(),
|
||||
blurhash: services.users.blurhash(sender_user).await.ok(),
|
||||
reason,
|
||||
join_authorized_via_users_server,
|
||||
..RoomMemberEventContent::new(MembershipState::Join)
|
||||
})
|
||||
.expect("event is valid, we just created it"),
|
||||
);
|
||||
|
||||
// We keep the "event_id" in the pdu only in v1 or
|
||||
// v2 rooms
|
||||
match room_version_id {
|
||||
| RoomVersionId::V1 | RoomVersionId::V2 => {},
|
||||
| _ => {
|
||||
join_event_stub.remove("event_id");
|
||||
},
|
||||
}
|
||||
|
||||
// In order to create a compatible ref hash (EventID) the `hashes` field needs
|
||||
// to be present
|
||||
services
|
||||
.server_keys
|
||||
.hash_and_sign_event(&mut join_event_stub, &room_version_id)?;
|
||||
|
||||
// Generate event id
|
||||
let event_id = gen_event_id(&join_event_stub, &room_version_id)?;
|
||||
|
||||
// Add event_id back
|
||||
join_event_stub
|
||||
.insert("event_id".to_owned(), CanonicalJsonValue::String(event_id.clone().into()));
|
||||
|
||||
// It has enough fields to be called a proper event now
|
||||
let join_event = join_event_stub;
|
||||
|
||||
let send_join_response = services
|
||||
.sending
|
||||
.send_synapse_request(
|
||||
&remote_server,
|
||||
federation::membership::create_join_event::v2::Request {
|
||||
room_id: room_id.to_owned(),
|
||||
event_id: event_id.clone(),
|
||||
omit_members: false,
|
||||
pdu: services
|
||||
.sending
|
||||
.convert_to_outgoing_federation_event(join_event.clone())
|
||||
.await,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
if let Some(signed_raw) = send_join_response.room_state.event {
|
||||
let (signed_event_id, signed_value) =
|
||||
gen_event_id_canonical_json(&signed_raw, &room_version_id).map_err(|e| {
|
||||
err!(Request(BadJson(warn!("Could not convert event to canonical JSON: {e}"))))
|
||||
})?;
|
||||
|
||||
if signed_event_id != event_id {
|
||||
return Err!(Request(BadJson(
|
||||
warn!(%signed_event_id, %event_id, "Server {remote_server} sent event with wrong event ID")
|
||||
)));
|
||||
}
|
||||
|
||||
drop(state_lock);
|
||||
services
|
||||
.rooms
|
||||
.event_handler
|
||||
.handle_incoming_pdu(&remote_server, room_id, &signed_event_id, signed_value, true)
|
||||
.boxed()
|
||||
.await?;
|
||||
} else {
|
||||
return Err(error);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn make_join_request(
|
||||
services: &Services,
|
||||
sender_user: &UserId,
|
||||
room_id: &RoomId,
|
||||
servers: &[OwnedServerName],
|
||||
) -> Result<(federation::membership::prepare_join_event::v1::Response, OwnedServerName)> {
|
||||
let mut make_join_response_and_server =
|
||||
Err!(BadServerResponse("No server available to assist in joining."));
|
||||
|
||||
let mut make_join_counter: usize = 0;
|
||||
let mut incompatible_room_version_count: usize = 0;
|
||||
|
||||
for remote_server in servers {
|
||||
if services.globals.server_is_ours(remote_server) {
|
||||
continue;
|
||||
}
|
||||
info!("Asking {remote_server} for make_join ({make_join_counter})");
|
||||
let make_join_response = services
|
||||
.sending
|
||||
.send_federation_request(
|
||||
remote_server,
|
||||
federation::membership::prepare_join_event::v1::Request {
|
||||
room_id: room_id.to_owned(),
|
||||
user_id: sender_user.to_owned(),
|
||||
ver: services.server.supported_room_versions().collect(),
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
trace!("make_join response: {:?}", make_join_response);
|
||||
make_join_counter = make_join_counter.saturating_add(1);
|
||||
|
||||
if let Err(ref e) = make_join_response {
|
||||
if matches!(
|
||||
e.kind(),
|
||||
ErrorKind::IncompatibleRoomVersion { .. } | ErrorKind::UnsupportedRoomVersion
|
||||
) {
|
||||
incompatible_room_version_count =
|
||||
incompatible_room_version_count.saturating_add(1);
|
||||
}
|
||||
|
||||
if incompatible_room_version_count > 15 {
|
||||
info!(
|
||||
"15 servers have responded with M_INCOMPATIBLE_ROOM_VERSION or \
|
||||
M_UNSUPPORTED_ROOM_VERSION, assuming that conduwuit does not support the \
|
||||
room version {room_id}: {e}"
|
||||
);
|
||||
make_join_response_and_server =
|
||||
Err!(BadServerResponse("Room version is not supported by Conduwuit"));
|
||||
return make_join_response_and_server;
|
||||
}
|
||||
|
||||
if make_join_counter > 40 {
|
||||
warn!(
|
||||
"40 servers failed to provide valid make_join response, assuming no server \
|
||||
can assist in joining."
|
||||
);
|
||||
make_join_response_and_server =
|
||||
Err!(BadServerResponse("No server available to assist in joining."));
|
||||
|
||||
return make_join_response_and_server;
|
||||
}
|
||||
}
|
||||
|
||||
make_join_response_and_server = make_join_response.map(|r| (r, remote_server.clone()));
|
||||
|
||||
if make_join_response_and_server.is_ok() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
make_join_response_and_server
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
use axum::extract::State;
|
||||
use conduwuit::{Err, Result, matrix::pdu::PduBuilder};
|
||||
use ruma::{
|
||||
api::client::membership::kick_user,
|
||||
events::room::member::{MembershipState, RoomMemberEventContent},
|
||||
};
|
||||
|
||||
use crate::Ruma;
|
||||
|
||||
/// # `POST /_matrix/client/r0/rooms/{roomId}/kick`
|
||||
///
|
||||
/// Tries to send a kick event into the room.
|
||||
pub(crate) async fn kick_user_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<kick_user::v3::Request>,
|
||||
) -> Result<kick_user::v3::Response> {
|
||||
let state_lock = services.rooms.state.mutex.lock(&body.room_id).await;
|
||||
|
||||
let Ok(event) = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.get_member(&body.room_id, &body.user_id)
|
||||
.await
|
||||
else {
|
||||
// copy synapse's behaviour of returning 200 without any change to the state
|
||||
// instead of erroring on left users
|
||||
return Ok(kick_user::v3::Response::new());
|
||||
};
|
||||
|
||||
if !matches!(
|
||||
event.membership,
|
||||
MembershipState::Invite | MembershipState::Knock | MembershipState::Join,
|
||||
) {
|
||||
return Err!(Request(Forbidden(
|
||||
"Cannot kick a user who is not apart of the room (current membership: {})",
|
||||
event.membership
|
||||
)));
|
||||
}
|
||||
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(body.user_id.to_string(), &RoomMemberEventContent {
|
||||
membership: MembershipState::Leave,
|
||||
reason: body.reason.clone(),
|
||||
is_direct: None,
|
||||
join_authorized_via_users_server: None,
|
||||
third_party_invite: None,
|
||||
..event
|
||||
}),
|
||||
body.sender_user(),
|
||||
&body.room_id,
|
||||
&state_lock,
|
||||
)
|
||||
.await?;
|
||||
|
||||
drop(state_lock);
|
||||
|
||||
Ok(kick_user::v3::Response::new())
|
||||
}
|
||||
@@ -0,0 +1,767 @@
|
||||
use std::{borrow::Borrow, collections::HashMap, iter::once, sync::Arc};
|
||||
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::InsecureClientIp;
|
||||
use conduwuit::{
|
||||
Err, Result, debug, debug_info, debug_warn, err, info,
|
||||
matrix::{
|
||||
event::{Event, gen_event_id},
|
||||
pdu::{PduBuilder, PduEvent},
|
||||
},
|
||||
result::FlatOk,
|
||||
trace,
|
||||
utils::{self, shuffle, stream::IterStream},
|
||||
warn,
|
||||
};
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use ruma::{
|
||||
CanonicalJsonObject, CanonicalJsonValue, OwnedEventId, OwnedRoomId, OwnedServerName, RoomId,
|
||||
RoomVersionId, UserId,
|
||||
api::{
|
||||
client::knock::knock_room,
|
||||
federation::{self},
|
||||
},
|
||||
canonical_json::to_canonical_value,
|
||||
events::{
|
||||
StateEventType,
|
||||
room::{
|
||||
join_rules::{AllowRule, JoinRule},
|
||||
member::{MembershipState, RoomMemberEventContent},
|
||||
},
|
||||
},
|
||||
};
|
||||
use service::{
|
||||
Services,
|
||||
rooms::{
|
||||
state::RoomMutexGuard,
|
||||
state_compressor::{CompressedState, HashSetCompressStateEvent},
|
||||
},
|
||||
};
|
||||
|
||||
use super::{banned_room_check, join::join_room_by_id_helper};
|
||||
use crate::Ruma;
|
||||
|
||||
/// # `POST /_matrix/client/*/knock/{roomIdOrAlias}`
|
||||
///
|
||||
/// Tries to knock the room to ask permission to join for the sender user.
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "knock")]
|
||||
pub(crate) async fn knock_room_route(
|
||||
State(services): State<crate::State>,
|
||||
InsecureClientIp(client): InsecureClientIp,
|
||||
body: Ruma<knock_room::v3::Request>,
|
||||
) -> Result<knock_room::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let body = &body.body;
|
||||
|
||||
let (servers, room_id) = match OwnedRoomId::try_from(body.room_id_or_alias.clone()) {
|
||||
| Ok(room_id) => {
|
||||
banned_room_check(
|
||||
&services,
|
||||
sender_user,
|
||||
Some(&room_id),
|
||||
room_id.server_name(),
|
||||
client,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut servers = body.via.clone();
|
||||
servers.extend(
|
||||
services
|
||||
.rooms
|
||||
.state_cache
|
||||
.servers_invite_via(&room_id)
|
||||
.map(ToOwned::to_owned)
|
||||
.collect::<Vec<_>>()
|
||||
.await,
|
||||
);
|
||||
|
||||
servers.extend(
|
||||
services
|
||||
.rooms
|
||||
.state_cache
|
||||
.invite_state(sender_user, &room_id)
|
||||
.await
|
||||
.unwrap_or_default()
|
||||
.iter()
|
||||
.filter_map(|event| event.get_field("sender").ok().flatten())
|
||||
.filter_map(|sender: &str| UserId::parse(sender).ok())
|
||||
.map(|user| user.server_name().to_owned()),
|
||||
);
|
||||
|
||||
if let Some(server) = room_id.server_name() {
|
||||
servers.push(server.to_owned());
|
||||
}
|
||||
|
||||
servers.sort_unstable();
|
||||
servers.dedup();
|
||||
shuffle(&mut servers);
|
||||
|
||||
(servers, room_id)
|
||||
},
|
||||
| Err(room_alias) => {
|
||||
let (room_id, mut servers) = services
|
||||
.rooms
|
||||
.alias
|
||||
.resolve_alias(&room_alias, Some(body.via.clone()))
|
||||
.await?;
|
||||
|
||||
banned_room_check(
|
||||
&services,
|
||||
sender_user,
|
||||
Some(&room_id),
|
||||
Some(room_alias.server_name()),
|
||||
client,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let addl_via_servers = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.servers_invite_via(&room_id)
|
||||
.map(ToOwned::to_owned);
|
||||
|
||||
let addl_state_servers = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.invite_state(sender_user, &room_id)
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
|
||||
let mut addl_servers: Vec<_> = addl_state_servers
|
||||
.iter()
|
||||
.map(|event| event.get_field("sender"))
|
||||
.filter_map(FlatOk::flat_ok)
|
||||
.map(|user: &UserId| user.server_name().to_owned())
|
||||
.stream()
|
||||
.chain(addl_via_servers)
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
addl_servers.sort_unstable();
|
||||
addl_servers.dedup();
|
||||
shuffle(&mut addl_servers);
|
||||
servers.append(&mut addl_servers);
|
||||
|
||||
(servers, room_id)
|
||||
},
|
||||
};
|
||||
|
||||
knock_room_by_id_helper(&services, sender_user, &room_id, body.reason.clone(), &servers)
|
||||
.boxed()
|
||||
.await
|
||||
}
|
||||
|
||||
async fn knock_room_by_id_helper(
|
||||
services: &Services,
|
||||
sender_user: &UserId,
|
||||
room_id: &RoomId,
|
||||
reason: Option<String>,
|
||||
servers: &[OwnedServerName],
|
||||
) -> Result<knock_room::v3::Response> {
|
||||
let state_lock = services.rooms.state.mutex.lock(room_id).await;
|
||||
|
||||
if services
|
||||
.rooms
|
||||
.state_cache
|
||||
.is_invited(sender_user, room_id)
|
||||
.await
|
||||
{
|
||||
debug_warn!("{sender_user} is already invited in {room_id} but attempted to knock");
|
||||
return Err!(Request(Forbidden(
|
||||
"You cannot knock on a room you are already invited/accepted to."
|
||||
)));
|
||||
}
|
||||
|
||||
if services
|
||||
.rooms
|
||||
.state_cache
|
||||
.is_joined(sender_user, room_id)
|
||||
.await
|
||||
{
|
||||
debug_warn!("{sender_user} is already joined in {room_id} but attempted to knock");
|
||||
return Err!(Request(Forbidden("You cannot knock on a room you are already joined in.")));
|
||||
}
|
||||
|
||||
if services
|
||||
.rooms
|
||||
.state_cache
|
||||
.is_knocked(sender_user, room_id)
|
||||
.await
|
||||
{
|
||||
debug_warn!("{sender_user} is already knocked in {room_id}");
|
||||
return Ok(knock_room::v3::Response { room_id: room_id.into() });
|
||||
}
|
||||
|
||||
if let Ok(membership) = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.get_member(room_id, sender_user)
|
||||
.await
|
||||
{
|
||||
if membership.membership == MembershipState::Ban {
|
||||
debug_warn!("{sender_user} is banned from {room_id} but attempted to knock");
|
||||
return Err!(Request(Forbidden("You cannot knock on a room you are banned from.")));
|
||||
}
|
||||
}
|
||||
|
||||
// For knock_restricted rooms, check if the user meets the restricted conditions
|
||||
// If they do, attempt to join instead of knock
|
||||
// This is not mentioned in the spec, but should be allowable (we're allowed to
|
||||
// auto-join invites to knocked rooms)
|
||||
let join_rule = services.rooms.state_accessor.get_join_rules(room_id).await;
|
||||
|
||||
if let JoinRule::KnockRestricted(restricted) = &join_rule {
|
||||
let restriction_rooms: Vec<_> = restricted
|
||||
.allow
|
||||
.iter()
|
||||
.filter_map(|a| match a {
|
||||
| AllowRule::RoomMembership(r) => Some(&r.room_id),
|
||||
| _ => None,
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Check if the user is in any of the allowed rooms
|
||||
let mut user_meets_restrictions = false;
|
||||
for restriction_room_id in &restriction_rooms {
|
||||
if services
|
||||
.rooms
|
||||
.state_cache
|
||||
.is_joined(sender_user, restriction_room_id)
|
||||
.await
|
||||
{
|
||||
user_meets_restrictions = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If the user meets the restrictions, try joining instead
|
||||
if user_meets_restrictions {
|
||||
debug_info!(
|
||||
"{sender_user} meets the restricted criteria in knock_restricted room \
|
||||
{room_id}, attempting to join instead of knock"
|
||||
);
|
||||
// For this case, we need to drop the state lock and get a new one in
|
||||
// join_room_by_id_helper We need to release the lock here and let
|
||||
// join_room_by_id_helper acquire it again
|
||||
drop(state_lock);
|
||||
match join_room_by_id_helper(
|
||||
services,
|
||||
sender_user,
|
||||
room_id,
|
||||
reason.clone(),
|
||||
servers,
|
||||
None,
|
||||
&None,
|
||||
)
|
||||
.await
|
||||
{
|
||||
| Ok(_) => return Ok(knock_room::v3::Response::new(room_id.to_owned())),
|
||||
| Err(e) => {
|
||||
debug_warn!(
|
||||
"Failed to convert knock to join for {sender_user} in {room_id}: {e:?}"
|
||||
);
|
||||
// Get a new state lock for the remaining knock logic
|
||||
let new_state_lock = services.rooms.state.mutex.lock(room_id).await;
|
||||
|
||||
let server_in_room = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.server_in_room(services.globals.server_name(), room_id)
|
||||
.await;
|
||||
|
||||
let local_knock = server_in_room
|
||||
|| servers.is_empty()
|
||||
|| (servers.len() == 1 && services.globals.server_is_ours(&servers[0]));
|
||||
|
||||
if local_knock {
|
||||
knock_room_helper_local(
|
||||
services,
|
||||
sender_user,
|
||||
room_id,
|
||||
reason,
|
||||
servers,
|
||||
new_state_lock,
|
||||
)
|
||||
.boxed()
|
||||
.await?;
|
||||
} else {
|
||||
knock_room_helper_remote(
|
||||
services,
|
||||
sender_user,
|
||||
room_id,
|
||||
reason,
|
||||
servers,
|
||||
new_state_lock,
|
||||
)
|
||||
.boxed()
|
||||
.await?;
|
||||
}
|
||||
|
||||
return Ok(knock_room::v3::Response::new(room_id.to_owned()));
|
||||
},
|
||||
}
|
||||
}
|
||||
} else if !matches!(join_rule, JoinRule::Knock | JoinRule::KnockRestricted(_)) {
|
||||
debug_warn!(
|
||||
"{sender_user} attempted to knock on room {room_id} but its join rule is \
|
||||
{join_rule:?}, not knock or knock_restricted"
|
||||
);
|
||||
}
|
||||
|
||||
let server_in_room = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.server_in_room(services.globals.server_name(), room_id)
|
||||
.await;
|
||||
|
||||
let local_knock = server_in_room
|
||||
|| servers.is_empty()
|
||||
|| (servers.len() == 1 && services.globals.server_is_ours(&servers[0]));
|
||||
|
||||
if local_knock {
|
||||
knock_room_helper_local(services, sender_user, room_id, reason, servers, state_lock)
|
||||
.boxed()
|
||||
.await?;
|
||||
} else {
|
||||
knock_room_helper_remote(services, sender_user, room_id, reason, servers, state_lock)
|
||||
.boxed()
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(knock_room::v3::Response::new(room_id.to_owned()))
|
||||
}
|
||||
|
||||
async fn knock_room_helper_local(
|
||||
services: &Services,
|
||||
sender_user: &UserId,
|
||||
room_id: &RoomId,
|
||||
reason: Option<String>,
|
||||
servers: &[OwnedServerName],
|
||||
state_lock: RoomMutexGuard,
|
||||
) -> Result {
|
||||
debug_info!("We can knock locally");
|
||||
|
||||
let room_version_id = services.rooms.state.get_room_version(room_id).await?;
|
||||
|
||||
if matches!(
|
||||
room_version_id,
|
||||
RoomVersionId::V1
|
||||
| RoomVersionId::V2
|
||||
| RoomVersionId::V3
|
||||
| RoomVersionId::V4
|
||||
| RoomVersionId::V5
|
||||
| RoomVersionId::V6
|
||||
) {
|
||||
return Err!(Request(Forbidden("This room does not support knocking.")));
|
||||
}
|
||||
|
||||
let content = RoomMemberEventContent {
|
||||
displayname: services.users.displayname(sender_user).await.ok(),
|
||||
avatar_url: services.users.avatar_url(sender_user).await.ok(),
|
||||
blurhash: services.users.blurhash(sender_user).await.ok(),
|
||||
reason: reason.clone(),
|
||||
..RoomMemberEventContent::new(MembershipState::Knock)
|
||||
};
|
||||
|
||||
// Try normal knock first
|
||||
let Err(error) = services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(sender_user.to_string(), &content),
|
||||
sender_user,
|
||||
room_id,
|
||||
&state_lock,
|
||||
)
|
||||
.await
|
||||
else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
if servers.is_empty() || (servers.len() == 1 && services.globals.server_is_ours(&servers[0]))
|
||||
{
|
||||
return Err(error);
|
||||
}
|
||||
|
||||
warn!("We couldn't do the knock locally, maybe federation can help to satisfy the knock");
|
||||
|
||||
let (make_knock_response, remote_server) =
|
||||
make_knock_request(services, sender_user, room_id, servers).await?;
|
||||
|
||||
info!("make_knock finished");
|
||||
|
||||
let room_version_id = make_knock_response.room_version;
|
||||
|
||||
if !services.server.supported_room_version(&room_version_id) {
|
||||
return Err!(BadServerResponse(
|
||||
"Remote room version {room_version_id} is not supported by conduwuit"
|
||||
));
|
||||
}
|
||||
|
||||
let mut knock_event_stub = serde_json::from_str::<CanonicalJsonObject>(
|
||||
make_knock_response.event.get(),
|
||||
)
|
||||
.map_err(|e| {
|
||||
err!(BadServerResponse("Invalid make_knock event json received from server: {e:?}"))
|
||||
})?;
|
||||
|
||||
knock_event_stub.insert(
|
||||
"origin".to_owned(),
|
||||
CanonicalJsonValue::String(services.globals.server_name().as_str().to_owned()),
|
||||
);
|
||||
knock_event_stub.insert(
|
||||
"origin_server_ts".to_owned(),
|
||||
CanonicalJsonValue::Integer(
|
||||
utils::millis_since_unix_epoch()
|
||||
.try_into()
|
||||
.expect("Timestamp is valid js_int value"),
|
||||
),
|
||||
);
|
||||
knock_event_stub.insert(
|
||||
"content".to_owned(),
|
||||
to_canonical_value(RoomMemberEventContent {
|
||||
displayname: services.users.displayname(sender_user).await.ok(),
|
||||
avatar_url: services.users.avatar_url(sender_user).await.ok(),
|
||||
blurhash: services.users.blurhash(sender_user).await.ok(),
|
||||
reason,
|
||||
..RoomMemberEventContent::new(MembershipState::Knock)
|
||||
})
|
||||
.expect("event is valid, we just created it"),
|
||||
);
|
||||
|
||||
// In order to create a compatible ref hash (EventID) the `hashes` field needs
|
||||
// to be present
|
||||
services
|
||||
.server_keys
|
||||
.hash_and_sign_event(&mut knock_event_stub, &room_version_id)?;
|
||||
|
||||
// Generate event id
|
||||
let event_id = gen_event_id(&knock_event_stub, &room_version_id)?;
|
||||
|
||||
// Add event_id
|
||||
knock_event_stub
|
||||
.insert("event_id".to_owned(), CanonicalJsonValue::String(event_id.clone().into()));
|
||||
|
||||
// It has enough fields to be called a proper event now
|
||||
let knock_event = knock_event_stub;
|
||||
|
||||
info!("Asking {remote_server} for send_knock in room {room_id}");
|
||||
let send_knock_request = federation::knock::send_knock::v1::Request {
|
||||
room_id: room_id.to_owned(),
|
||||
event_id: event_id.clone(),
|
||||
pdu: services
|
||||
.sending
|
||||
.convert_to_outgoing_federation_event(knock_event.clone())
|
||||
.await,
|
||||
};
|
||||
|
||||
let send_knock_response = services
|
||||
.sending
|
||||
.send_federation_request(&remote_server, send_knock_request)
|
||||
.await?;
|
||||
|
||||
info!("send_knock finished");
|
||||
|
||||
services
|
||||
.rooms
|
||||
.short
|
||||
.get_or_create_shortroomid(room_id)
|
||||
.await;
|
||||
|
||||
info!("Parsing knock event");
|
||||
|
||||
let parsed_knock_pdu = PduEvent::from_id_val(&event_id, knock_event.clone())
|
||||
.map_err(|e| err!(BadServerResponse("Invalid knock event PDU: {e:?}")))?;
|
||||
|
||||
info!("Updating membership locally to knock state with provided stripped state events");
|
||||
services
|
||||
.rooms
|
||||
.state_cache
|
||||
.update_membership(
|
||||
room_id,
|
||||
sender_user,
|
||||
parsed_knock_pdu
|
||||
.get_content::<RoomMemberEventContent>()
|
||||
.expect("we just created this"),
|
||||
sender_user,
|
||||
Some(send_knock_response.knock_room_state),
|
||||
None,
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
info!("Appending room knock event locally");
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.append_pdu(
|
||||
&parsed_knock_pdu,
|
||||
knock_event,
|
||||
once(parsed_knock_pdu.event_id.borrow()),
|
||||
&state_lock,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn knock_room_helper_remote(
|
||||
services: &Services,
|
||||
sender_user: &UserId,
|
||||
room_id: &RoomId,
|
||||
reason: Option<String>,
|
||||
servers: &[OwnedServerName],
|
||||
state_lock: RoomMutexGuard,
|
||||
) -> Result {
|
||||
info!("Knocking {room_id} over federation.");
|
||||
|
||||
let (make_knock_response, remote_server) =
|
||||
make_knock_request(services, sender_user, room_id, servers).await?;
|
||||
|
||||
info!("make_knock finished");
|
||||
|
||||
let room_version_id = make_knock_response.room_version;
|
||||
|
||||
if !services.server.supported_room_version(&room_version_id) {
|
||||
return Err!(BadServerResponse(
|
||||
"Remote room version {room_version_id} is not supported by conduwuit"
|
||||
));
|
||||
}
|
||||
|
||||
let mut knock_event_stub: CanonicalJsonObject =
|
||||
serde_json::from_str(make_knock_response.event.get()).map_err(|e| {
|
||||
err!(BadServerResponse("Invalid make_knock event json received from server: {e:?}"))
|
||||
})?;
|
||||
|
||||
knock_event_stub.insert(
|
||||
"origin".to_owned(),
|
||||
CanonicalJsonValue::String(services.globals.server_name().as_str().to_owned()),
|
||||
);
|
||||
knock_event_stub.insert(
|
||||
"origin_server_ts".to_owned(),
|
||||
CanonicalJsonValue::Integer(
|
||||
utils::millis_since_unix_epoch()
|
||||
.try_into()
|
||||
.expect("Timestamp is valid js_int value"),
|
||||
),
|
||||
);
|
||||
knock_event_stub.insert(
|
||||
"content".to_owned(),
|
||||
to_canonical_value(RoomMemberEventContent {
|
||||
displayname: services.users.displayname(sender_user).await.ok(),
|
||||
avatar_url: services.users.avatar_url(sender_user).await.ok(),
|
||||
blurhash: services.users.blurhash(sender_user).await.ok(),
|
||||
reason,
|
||||
..RoomMemberEventContent::new(MembershipState::Knock)
|
||||
})
|
||||
.expect("event is valid, we just created it"),
|
||||
);
|
||||
|
||||
// In order to create a compatible ref hash (EventID) the `hashes` field needs
|
||||
// to be present
|
||||
services
|
||||
.server_keys
|
||||
.hash_and_sign_event(&mut knock_event_stub, &room_version_id)?;
|
||||
|
||||
// Generate event id
|
||||
let event_id = gen_event_id(&knock_event_stub, &room_version_id)?;
|
||||
|
||||
// Add event_id
|
||||
knock_event_stub
|
||||
.insert("event_id".to_owned(), CanonicalJsonValue::String(event_id.clone().into()));
|
||||
|
||||
// It has enough fields to be called a proper event now
|
||||
let knock_event = knock_event_stub;
|
||||
|
||||
info!("Asking {remote_server} for send_knock in room {room_id}");
|
||||
let send_knock_request = federation::knock::send_knock::v1::Request {
|
||||
room_id: room_id.to_owned(),
|
||||
event_id: event_id.clone(),
|
||||
pdu: services
|
||||
.sending
|
||||
.convert_to_outgoing_federation_event(knock_event.clone())
|
||||
.await,
|
||||
};
|
||||
|
||||
let send_knock_response = services
|
||||
.sending
|
||||
.send_federation_request(&remote_server, send_knock_request)
|
||||
.await?;
|
||||
|
||||
info!("send_knock finished");
|
||||
|
||||
services
|
||||
.rooms
|
||||
.short
|
||||
.get_or_create_shortroomid(room_id)
|
||||
.await;
|
||||
|
||||
info!("Parsing knock event");
|
||||
let parsed_knock_pdu = PduEvent::from_id_val(&event_id, knock_event.clone())
|
||||
.map_err(|e| err!(BadServerResponse("Invalid knock event PDU: {e:?}")))?;
|
||||
|
||||
info!("Going through send_knock response knock state events");
|
||||
let state = send_knock_response
|
||||
.knock_room_state
|
||||
.iter()
|
||||
.map(|event| serde_json::from_str::<CanonicalJsonObject>(event.clone().into_json().get()))
|
||||
.filter_map(Result::ok);
|
||||
|
||||
let mut state_map: HashMap<u64, OwnedEventId> = HashMap::new();
|
||||
|
||||
for event in state {
|
||||
let Some(state_key) = event.get("state_key") else {
|
||||
debug_warn!("send_knock stripped state event missing state_key: {event:?}");
|
||||
continue;
|
||||
};
|
||||
let Some(event_type) = event.get("type") else {
|
||||
debug_warn!("send_knock stripped state event missing event type: {event:?}");
|
||||
continue;
|
||||
};
|
||||
|
||||
let Ok(state_key) = serde_json::from_value::<String>(state_key.clone().into()) else {
|
||||
debug_warn!("send_knock stripped state event has invalid state_key: {event:?}");
|
||||
continue;
|
||||
};
|
||||
let Ok(event_type) = serde_json::from_value::<StateEventType>(event_type.clone().into())
|
||||
else {
|
||||
debug_warn!("send_knock stripped state event has invalid event type: {event:?}");
|
||||
continue;
|
||||
};
|
||||
|
||||
let event_id = gen_event_id(&event, &room_version_id)?;
|
||||
let shortstatekey = services
|
||||
.rooms
|
||||
.short
|
||||
.get_or_create_shortstatekey(&event_type, &state_key)
|
||||
.await;
|
||||
|
||||
services.rooms.outlier.add_pdu_outlier(&event_id, &event);
|
||||
state_map.insert(shortstatekey, event_id.clone());
|
||||
}
|
||||
|
||||
info!("Compressing state from send_knock");
|
||||
let compressed: CompressedState = services
|
||||
.rooms
|
||||
.state_compressor
|
||||
.compress_state_events(state_map.iter().map(|(ssk, eid)| (ssk, eid.borrow())))
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
debug!("Saving compressed state");
|
||||
let HashSetCompressStateEvent {
|
||||
shortstatehash: statehash_before_knock,
|
||||
added,
|
||||
removed,
|
||||
} = services
|
||||
.rooms
|
||||
.state_compressor
|
||||
.save_state(room_id, Arc::new(compressed))
|
||||
.await?;
|
||||
|
||||
debug!("Forcing state for new room");
|
||||
services
|
||||
.rooms
|
||||
.state
|
||||
.force_state(room_id, statehash_before_knock, added, removed, &state_lock)
|
||||
.await?;
|
||||
|
||||
let statehash_after_knock = services
|
||||
.rooms
|
||||
.state
|
||||
.append_to_state(&parsed_knock_pdu)
|
||||
.await?;
|
||||
|
||||
info!("Updating membership locally to knock state with provided stripped state events");
|
||||
services
|
||||
.rooms
|
||||
.state_cache
|
||||
.update_membership(
|
||||
room_id,
|
||||
sender_user,
|
||||
parsed_knock_pdu
|
||||
.get_content::<RoomMemberEventContent>()
|
||||
.expect("we just created this"),
|
||||
sender_user,
|
||||
Some(send_knock_response.knock_room_state),
|
||||
None,
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
info!("Appending room knock event locally");
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.append_pdu(
|
||||
&parsed_knock_pdu,
|
||||
knock_event,
|
||||
once(parsed_knock_pdu.event_id.borrow()),
|
||||
&state_lock,
|
||||
)
|
||||
.await?;
|
||||
|
||||
info!("Setting final room state for new room");
|
||||
// We set the room state after inserting the pdu, so that we never have a moment
|
||||
// in time where events in the current room state do not exist
|
||||
services
|
||||
.rooms
|
||||
.state
|
||||
.set_room_state(room_id, statehash_after_knock, &state_lock);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn make_knock_request(
|
||||
services: &Services,
|
||||
sender_user: &UserId,
|
||||
room_id: &RoomId,
|
||||
servers: &[OwnedServerName],
|
||||
) -> Result<(federation::knock::create_knock_event_template::v1::Response, OwnedServerName)> {
|
||||
let mut make_knock_response_and_server =
|
||||
Err!(BadServerResponse("No server available to assist in knocking."));
|
||||
|
||||
let mut make_knock_counter: usize = 0;
|
||||
|
||||
for remote_server in servers {
|
||||
if services.globals.server_is_ours(remote_server) {
|
||||
continue;
|
||||
}
|
||||
|
||||
info!("Asking {remote_server} for make_knock ({make_knock_counter})");
|
||||
|
||||
let make_knock_response = services
|
||||
.sending
|
||||
.send_federation_request(
|
||||
remote_server,
|
||||
federation::knock::create_knock_event_template::v1::Request {
|
||||
room_id: room_id.to_owned(),
|
||||
user_id: sender_user.to_owned(),
|
||||
ver: services.server.supported_room_versions().collect(),
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
trace!("make_knock response: {make_knock_response:?}");
|
||||
make_knock_counter = make_knock_counter.saturating_add(1);
|
||||
|
||||
make_knock_response_and_server = make_knock_response.map(|r| (r, remote_server.clone()));
|
||||
|
||||
if make_knock_response_and_server.is_ok() {
|
||||
break;
|
||||
}
|
||||
|
||||
if make_knock_counter > 40 {
|
||||
warn!(
|
||||
"50 servers failed to provide valid make_knock response, assuming no server can \
|
||||
assist in knocking."
|
||||
);
|
||||
make_knock_response_and_server =
|
||||
Err!(BadServerResponse("No server available to assist in knocking."));
|
||||
|
||||
return make_knock_response_and_server;
|
||||
}
|
||||
}
|
||||
|
||||
make_knock_response_and_server
|
||||
}
|
||||
@@ -0,0 +1,385 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use axum::extract::State;
|
||||
use conduwuit::{
|
||||
Err, Result, debug_info, debug_warn, err,
|
||||
matrix::{event::gen_event_id, pdu::PduBuilder},
|
||||
utils::{self, FutureBoolExt, future::ReadyEqExt},
|
||||
warn,
|
||||
};
|
||||
use futures::{FutureExt, StreamExt, TryFutureExt, pin_mut};
|
||||
use ruma::{
|
||||
CanonicalJsonObject, CanonicalJsonValue, OwnedServerName, RoomId, RoomVersionId, UserId,
|
||||
api::{
|
||||
client::membership::leave_room,
|
||||
federation::{self},
|
||||
},
|
||||
events::{
|
||||
StateEventType,
|
||||
room::member::{MembershipState, RoomMemberEventContent},
|
||||
},
|
||||
};
|
||||
use service::Services;
|
||||
|
||||
use crate::Ruma;
|
||||
|
||||
/// # `POST /_matrix/client/v3/rooms/{roomId}/leave`
|
||||
///
|
||||
/// Tries to leave the sender user from a room.
|
||||
///
|
||||
/// - This should always work if the user is currently joined.
|
||||
pub(crate) async fn leave_room_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<leave_room::v3::Request>,
|
||||
) -> Result<leave_room::v3::Response> {
|
||||
leave_room(&services, body.sender_user(), &body.room_id, body.reason.clone())
|
||||
.boxed()
|
||||
.await
|
||||
.map(|()| leave_room::v3::Response::new())
|
||||
}
|
||||
|
||||
// Make a user leave all their joined rooms, rescinds knocks, forgets all rooms,
|
||||
// and ignores errors
|
||||
pub async fn leave_all_rooms(services: &Services, user_id: &UserId) {
|
||||
let rooms_joined = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.rooms_joined(user_id)
|
||||
.map(ToOwned::to_owned);
|
||||
|
||||
let rooms_invited = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.rooms_invited(user_id)
|
||||
.map(|(r, _)| r);
|
||||
|
||||
let rooms_knocked = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.rooms_knocked(user_id)
|
||||
.map(|(r, _)| r);
|
||||
|
||||
let all_rooms: Vec<_> = rooms_joined
|
||||
.chain(rooms_invited)
|
||||
.chain(rooms_knocked)
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
for room_id in all_rooms {
|
||||
// ignore errors
|
||||
if let Err(e) = leave_room(services, user_id, &room_id, None).boxed().await {
|
||||
warn!(%user_id, "Failed to leave {room_id} remotely: {e}");
|
||||
}
|
||||
|
||||
services.rooms.state_cache.forget(&room_id, user_id);
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn leave_room(
|
||||
services: &Services,
|
||||
user_id: &UserId,
|
||||
room_id: &RoomId,
|
||||
reason: Option<String>,
|
||||
) -> Result {
|
||||
let default_member_content = RoomMemberEventContent {
|
||||
membership: MembershipState::Leave,
|
||||
reason: reason.clone(),
|
||||
join_authorized_via_users_server: None,
|
||||
is_direct: None,
|
||||
avatar_url: None,
|
||||
displayname: None,
|
||||
third_party_invite: None,
|
||||
blurhash: None,
|
||||
};
|
||||
|
||||
let is_banned = services.rooms.metadata.is_banned(room_id);
|
||||
let is_disabled = services.rooms.metadata.is_disabled(room_id);
|
||||
|
||||
pin_mut!(is_banned, is_disabled);
|
||||
if is_banned.or(is_disabled).await {
|
||||
// the room is banned/disabled, the room must be rejected locally since we
|
||||
// cant/dont want to federate with this server
|
||||
services
|
||||
.rooms
|
||||
.state_cache
|
||||
.update_membership(
|
||||
room_id,
|
||||
user_id,
|
||||
default_member_content,
|
||||
user_id,
|
||||
None,
|
||||
None,
|
||||
true,
|
||||
)
|
||||
.await?;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let dont_have_room = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.server_in_room(services.globals.server_name(), room_id)
|
||||
.eq(&false);
|
||||
|
||||
let not_knocked = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.is_knocked(user_id, room_id)
|
||||
.eq(&false);
|
||||
|
||||
// Ask a remote server if we don't have this room and are not knocking on it
|
||||
if dont_have_room.and(not_knocked).await {
|
||||
if let Err(e) = remote_leave_room(services, user_id, room_id, reason.clone())
|
||||
.boxed()
|
||||
.await
|
||||
{
|
||||
warn!(%user_id, "Failed to leave room {room_id} remotely: {e}");
|
||||
// Don't tell the client about this error
|
||||
}
|
||||
|
||||
let last_state = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.invite_state(user_id, room_id)
|
||||
.or_else(|_| services.rooms.state_cache.knock_state(user_id, room_id))
|
||||
.or_else(|_| services.rooms.state_cache.left_state(user_id, room_id))
|
||||
.await
|
||||
.ok();
|
||||
|
||||
// We always drop the invite, we can't rely on other servers
|
||||
services
|
||||
.rooms
|
||||
.state_cache
|
||||
.update_membership(
|
||||
room_id,
|
||||
user_id,
|
||||
default_member_content,
|
||||
user_id,
|
||||
last_state,
|
||||
None,
|
||||
true,
|
||||
)
|
||||
.await?;
|
||||
} else {
|
||||
let state_lock = services.rooms.state.mutex.lock(room_id).await;
|
||||
|
||||
let Ok(event) = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get_content::<RoomMemberEventContent>(
|
||||
room_id,
|
||||
&StateEventType::RoomMember,
|
||||
user_id.as_str(),
|
||||
)
|
||||
.await
|
||||
else {
|
||||
debug_warn!(
|
||||
"Trying to leave a room you are not a member of, marking room as left locally."
|
||||
);
|
||||
|
||||
return services
|
||||
.rooms
|
||||
.state_cache
|
||||
.update_membership(
|
||||
room_id,
|
||||
user_id,
|
||||
default_member_content,
|
||||
user_id,
|
||||
None,
|
||||
None,
|
||||
true,
|
||||
)
|
||||
.await;
|
||||
};
|
||||
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(user_id.to_string(), &RoomMemberEventContent {
|
||||
membership: MembershipState::Leave,
|
||||
reason,
|
||||
join_authorized_via_users_server: None,
|
||||
is_direct: None,
|
||||
..event
|
||||
}),
|
||||
user_id,
|
||||
room_id,
|
||||
&state_lock,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn remote_leave_room(
|
||||
services: &Services,
|
||||
user_id: &UserId,
|
||||
room_id: &RoomId,
|
||||
reason: Option<String>,
|
||||
) -> Result<()> {
|
||||
let mut make_leave_response_and_server =
|
||||
Err!(BadServerResponse("No remote server available to assist in leaving {room_id}."));
|
||||
|
||||
let mut servers: HashSet<OwnedServerName> = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.servers_invite_via(room_id)
|
||||
.map(ToOwned::to_owned)
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
match services
|
||||
.rooms
|
||||
.state_cache
|
||||
.invite_state(user_id, room_id)
|
||||
.await
|
||||
{
|
||||
| Ok(invite_state) => {
|
||||
servers.extend(
|
||||
invite_state
|
||||
.iter()
|
||||
.filter_map(|event| event.get_field("sender").ok().flatten())
|
||||
.filter_map(|sender: &str| UserId::parse(sender).ok())
|
||||
.map(|user| user.server_name().to_owned()),
|
||||
);
|
||||
},
|
||||
| _ => {
|
||||
match services
|
||||
.rooms
|
||||
.state_cache
|
||||
.knock_state(user_id, room_id)
|
||||
.await
|
||||
{
|
||||
| Ok(knock_state) => {
|
||||
servers.extend(
|
||||
knock_state
|
||||
.iter()
|
||||
.filter_map(|event| event.get_field("sender").ok().flatten())
|
||||
.filter_map(|sender: &str| UserId::parse(sender).ok())
|
||||
.filter_map(|sender| {
|
||||
if !services.globals.user_is_local(sender) {
|
||||
Some(sender.server_name().to_owned())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}),
|
||||
);
|
||||
},
|
||||
| _ => {},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
if let Some(room_id_server_name) = room_id.server_name() {
|
||||
servers.insert(room_id_server_name.to_owned());
|
||||
}
|
||||
|
||||
debug_info!("servers in remote_leave_room: {servers:?}");
|
||||
|
||||
for remote_server in servers {
|
||||
let make_leave_response = services
|
||||
.sending
|
||||
.send_federation_request(
|
||||
&remote_server,
|
||||
federation::membership::prepare_leave_event::v1::Request {
|
||||
room_id: room_id.to_owned(),
|
||||
user_id: user_id.to_owned(),
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
make_leave_response_and_server = make_leave_response.map(|r| (r, remote_server));
|
||||
|
||||
if make_leave_response_and_server.is_ok() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let (make_leave_response, remote_server) = make_leave_response_and_server?;
|
||||
|
||||
let Some(room_version_id) = make_leave_response.room_version else {
|
||||
return Err!(BadServerResponse(warn!(
|
||||
"No room version was returned by {remote_server} for {room_id}, room version is \
|
||||
likely not supported by conduwuit"
|
||||
)));
|
||||
};
|
||||
|
||||
if !services.server.supported_room_version(&room_version_id) {
|
||||
return Err!(BadServerResponse(warn!(
|
||||
"Remote room version {room_version_id} for {room_id} is not supported by conduwuit",
|
||||
)));
|
||||
}
|
||||
|
||||
let mut leave_event_stub = serde_json::from_str::<CanonicalJsonObject>(
|
||||
make_leave_response.event.get(),
|
||||
)
|
||||
.map_err(|e| {
|
||||
err!(BadServerResponse(warn!(
|
||||
"Invalid make_leave event json received from {remote_server} for {room_id}: {e:?}"
|
||||
)))
|
||||
})?;
|
||||
|
||||
// TODO: Is origin needed?
|
||||
leave_event_stub.insert(
|
||||
"origin".to_owned(),
|
||||
CanonicalJsonValue::String(services.globals.server_name().as_str().to_owned()),
|
||||
);
|
||||
leave_event_stub.insert(
|
||||
"origin_server_ts".to_owned(),
|
||||
CanonicalJsonValue::Integer(
|
||||
utils::millis_since_unix_epoch()
|
||||
.try_into()
|
||||
.expect("Timestamp is valid js_int value"),
|
||||
),
|
||||
);
|
||||
// Inject the reason key into the event content dict if it exists
|
||||
if let Some(reason) = reason {
|
||||
if let Some(CanonicalJsonValue::Object(content)) = leave_event_stub.get_mut("content") {
|
||||
content.insert("reason".to_owned(), CanonicalJsonValue::String(reason));
|
||||
}
|
||||
}
|
||||
|
||||
// room v3 and above removed the "event_id" field from remote PDU format
|
||||
match room_version_id {
|
||||
| RoomVersionId::V1 | RoomVersionId::V2 => {},
|
||||
| _ => {
|
||||
leave_event_stub.remove("event_id");
|
||||
},
|
||||
}
|
||||
|
||||
// In order to create a compatible ref hash (EventID) the `hashes` field needs
|
||||
// to be present
|
||||
services
|
||||
.server_keys
|
||||
.hash_and_sign_event(&mut leave_event_stub, &room_version_id)?;
|
||||
|
||||
// Generate event id
|
||||
let event_id = gen_event_id(&leave_event_stub, &room_version_id)?;
|
||||
|
||||
// Add event_id back
|
||||
leave_event_stub
|
||||
.insert("event_id".to_owned(), CanonicalJsonValue::String(event_id.clone().into()));
|
||||
|
||||
// It has enough fields to be called a proper event now
|
||||
let leave_event = leave_event_stub;
|
||||
|
||||
services
|
||||
.sending
|
||||
.send_federation_request(
|
||||
&remote_server,
|
||||
federation::membership::create_leave_event::v2::Request {
|
||||
room_id: room_id.to_owned(),
|
||||
event_id,
|
||||
pdu: services
|
||||
.sending
|
||||
.convert_to_outgoing_federation_event(leave_event.clone())
|
||||
.await,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
use axum::extract::State;
|
||||
use conduwuit::{
|
||||
Err, Event, Result, at,
|
||||
utils::{
|
||||
future::TryExtExt,
|
||||
stream::{BroadbandExt, ReadyExt},
|
||||
},
|
||||
};
|
||||
use futures::{FutureExt, StreamExt, future::join};
|
||||
use ruma::{
|
||||
api::client::membership::{
|
||||
get_member_events::{self, v3::MembershipEventFilter},
|
||||
joined_members::{self, v3::RoomMember},
|
||||
},
|
||||
events::{
|
||||
StateEventType,
|
||||
room::member::{MembershipState, RoomMemberEventContent},
|
||||
},
|
||||
};
|
||||
|
||||
use crate::Ruma;
|
||||
|
||||
/// # `POST /_matrix/client/r0/rooms/{roomId}/members`
|
||||
///
|
||||
/// Lists all joined users in a room (TODO: at a specific point in time, with a
|
||||
/// specific membership).
|
||||
///
|
||||
/// - Only works if the user is currently joined
|
||||
pub(crate) async fn get_member_events_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<get_member_events::v3::Request>,
|
||||
) -> Result<get_member_events::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let membership = body.membership.as_ref();
|
||||
let not_membership = body.not_membership.as_ref();
|
||||
|
||||
if !services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.user_can_see_state_events(sender_user, &body.room_id)
|
||||
.await
|
||||
{
|
||||
return Err!(Request(Forbidden("You don't have permission to view this room.")));
|
||||
}
|
||||
|
||||
Ok(get_member_events::v3::Response {
|
||||
chunk: services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_full(&body.room_id)
|
||||
.ready_filter_map(Result::ok)
|
||||
.ready_filter(|((ty, _), _)| *ty == StateEventType::RoomMember)
|
||||
.map(at!(1))
|
||||
.ready_filter_map(|pdu| membership_filter(pdu, membership, not_membership))
|
||||
.map(Event::into_format)
|
||||
.collect()
|
||||
.boxed()
|
||||
.await,
|
||||
})
|
||||
}
|
||||
|
||||
/// # `POST /_matrix/client/r0/rooms/{roomId}/joined_members`
|
||||
///
|
||||
/// Lists all members of a room.
|
||||
///
|
||||
/// - The sender user must be in the room
|
||||
/// - TODO: An appservice just needs a puppet joined
|
||||
pub(crate) async fn joined_members_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<joined_members::v3::Request>,
|
||||
) -> Result<joined_members::v3::Response> {
|
||||
if !services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.user_can_see_state_events(body.sender_user(), &body.room_id)
|
||||
.await
|
||||
{
|
||||
return Err!(Request(Forbidden("You don't have permission to view this room.")));
|
||||
}
|
||||
|
||||
Ok(joined_members::v3::Response {
|
||||
joined: services
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_members(&body.room_id)
|
||||
.map(ToOwned::to_owned)
|
||||
.broad_then(|user_id| async move {
|
||||
let (display_name, avatar_url) = join(
|
||||
services.users.displayname(&user_id).ok(),
|
||||
services.users.avatar_url(&user_id).ok(),
|
||||
)
|
||||
.await;
|
||||
|
||||
(user_id, RoomMember { display_name, avatar_url })
|
||||
})
|
||||
.collect()
|
||||
.await,
|
||||
})
|
||||
}
|
||||
|
||||
fn membership_filter<Pdu: Event>(
|
||||
pdu: Pdu,
|
||||
for_membership: Option<&MembershipEventFilter>,
|
||||
not_membership: Option<&MembershipEventFilter>,
|
||||
) -> Option<impl Event> {
|
||||
let membership_state_filter = match for_membership {
|
||||
| Some(MembershipEventFilter::Ban) => MembershipState::Ban,
|
||||
| Some(MembershipEventFilter::Invite) => MembershipState::Invite,
|
||||
| Some(MembershipEventFilter::Knock) => MembershipState::Knock,
|
||||
| Some(MembershipEventFilter::Leave) => MembershipState::Leave,
|
||||
| Some(_) | None => MembershipState::Join,
|
||||
};
|
||||
|
||||
let not_membership_state_filter = match not_membership {
|
||||
| Some(MembershipEventFilter::Ban) => MembershipState::Ban,
|
||||
| Some(MembershipEventFilter::Invite) => MembershipState::Invite,
|
||||
| Some(MembershipEventFilter::Join) => MembershipState::Join,
|
||||
| Some(MembershipEventFilter::Knock) => MembershipState::Knock,
|
||||
| Some(_) | None => MembershipState::Leave,
|
||||
};
|
||||
|
||||
let evt_membership = pdu.get_content::<RoomMemberEventContent>().ok()?.membership;
|
||||
|
||||
if for_membership.is_some() && not_membership.is_some() {
|
||||
if membership_state_filter != evt_membership
|
||||
|| not_membership_state_filter == evt_membership
|
||||
{
|
||||
None
|
||||
} else {
|
||||
Some(pdu)
|
||||
}
|
||||
} else if for_membership.is_some() && not_membership.is_none() {
|
||||
if membership_state_filter != evt_membership {
|
||||
None
|
||||
} else {
|
||||
Some(pdu)
|
||||
}
|
||||
} else if not_membership.is_some() && for_membership.is_none() {
|
||||
if not_membership_state_filter == evt_membership {
|
||||
None
|
||||
} else {
|
||||
Some(pdu)
|
||||
}
|
||||
} else {
|
||||
Some(pdu)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
mod ban;
|
||||
mod forget;
|
||||
mod invite;
|
||||
mod join;
|
||||
mod kick;
|
||||
mod knock;
|
||||
mod leave;
|
||||
mod members;
|
||||
mod unban;
|
||||
|
||||
use std::net::IpAddr;
|
||||
|
||||
use axum::extract::State;
|
||||
use conduwuit::{Err, Result, warn};
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use ruma::{OwnedRoomId, RoomId, ServerName, UserId, api::client::membership::joined_rooms};
|
||||
use service::Services;
|
||||
|
||||
pub(crate) use self::{
|
||||
ban::ban_user_route,
|
||||
forget::forget_room_route,
|
||||
invite::{invite_helper, invite_user_route},
|
||||
join::{join_room_by_id_or_alias_route, join_room_by_id_route},
|
||||
kick::kick_user_route,
|
||||
knock::knock_room_route,
|
||||
leave::leave_room_route,
|
||||
members::{get_member_events_route, joined_members_route},
|
||||
unban::unban_user_route,
|
||||
};
|
||||
pub use self::{
|
||||
join::join_room_by_id_helper,
|
||||
leave::{leave_all_rooms, leave_room},
|
||||
};
|
||||
use crate::{Ruma, client::full_user_deactivate};
|
||||
|
||||
/// # `POST /_matrix/client/r0/joined_rooms`
|
||||
///
|
||||
/// Lists all rooms the user has joined.
|
||||
pub(crate) async fn joined_rooms_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<joined_rooms::v3::Request>,
|
||||
) -> Result<joined_rooms::v3::Response> {
|
||||
Ok(joined_rooms::v3::Response {
|
||||
joined_rooms: services
|
||||
.rooms
|
||||
.state_cache
|
||||
.rooms_joined(body.sender_user())
|
||||
.map(ToOwned::to_owned)
|
||||
.collect()
|
||||
.await,
|
||||
})
|
||||
}
|
||||
|
||||
/// Checks if the room is banned in any way possible and the sender user is not
|
||||
/// an admin.
|
||||
///
|
||||
/// Performs automatic deactivation if `auto_deactivate_banned_room_attempts` is
|
||||
/// enabled
|
||||
#[tracing::instrument(skip(services))]
|
||||
pub(crate) async fn banned_room_check(
|
||||
services: &Services,
|
||||
user_id: &UserId,
|
||||
room_id: Option<&RoomId>,
|
||||
server_name: Option<&ServerName>,
|
||||
client_ip: IpAddr,
|
||||
) -> Result {
|
||||
if services.users.is_admin(user_id).await {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let Some(room_id) = room_id {
|
||||
if services.rooms.metadata.is_banned(room_id).await
|
||||
|| services
|
||||
.moderation
|
||||
.is_remote_server_forbidden(room_id.server_name().expect("legacy room mxid"))
|
||||
{
|
||||
warn!(
|
||||
"User {user_id} who is not an admin attempted to send an invite for or \
|
||||
attempted to join a banned room or banned room server name: {room_id}"
|
||||
);
|
||||
|
||||
if services.server.config.auto_deactivate_banned_room_attempts {
|
||||
warn!(
|
||||
"Automatically deactivating user {user_id} due to attempted banned room join"
|
||||
);
|
||||
|
||||
if services.server.config.admin_room_notices {
|
||||
services
|
||||
.admin
|
||||
.send_text(&format!(
|
||||
"Automatically deactivating user {user_id} due to attempted banned \
|
||||
room join from IP {client_ip}"
|
||||
))
|
||||
.await;
|
||||
}
|
||||
|
||||
let all_joined_rooms: Vec<OwnedRoomId> = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.rooms_joined(user_id)
|
||||
.map(Into::into)
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
full_user_deactivate(services, user_id, &all_joined_rooms)
|
||||
.boxed()
|
||||
.await?;
|
||||
}
|
||||
|
||||
return Err!(Request(Forbidden("This room is banned on this homeserver.")));
|
||||
}
|
||||
} else if let Some(server_name) = server_name {
|
||||
if services
|
||||
.config
|
||||
.forbidden_remote_server_names
|
||||
.is_match(server_name.host())
|
||||
{
|
||||
warn!(
|
||||
"User {user_id} who is not an admin tried joining a room which has the server \
|
||||
name {server_name} that is globally forbidden. Rejecting.",
|
||||
);
|
||||
|
||||
if services.server.config.auto_deactivate_banned_room_attempts {
|
||||
warn!(
|
||||
"Automatically deactivating user {user_id} due to attempted banned room join"
|
||||
);
|
||||
|
||||
if services.server.config.admin_room_notices {
|
||||
services
|
||||
.admin
|
||||
.send_text(&format!(
|
||||
"Automatically deactivating user {user_id} due to attempted banned \
|
||||
room join from IP {client_ip}"
|
||||
))
|
||||
.await;
|
||||
}
|
||||
|
||||
let all_joined_rooms: Vec<OwnedRoomId> = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.rooms_joined(user_id)
|
||||
.map(Into::into)
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
full_user_deactivate(services, user_id, &all_joined_rooms)
|
||||
.boxed()
|
||||
.await?;
|
||||
}
|
||||
|
||||
return Err!(Request(Forbidden("This remote server is banned on this homeserver.")));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
use axum::extract::State;
|
||||
use conduwuit::{Err, Result, matrix::pdu::PduBuilder};
|
||||
use ruma::{
|
||||
api::client::membership::unban_user,
|
||||
events::room::member::{MembershipState, RoomMemberEventContent},
|
||||
};
|
||||
|
||||
use crate::Ruma;
|
||||
|
||||
/// # `POST /_matrix/client/r0/rooms/{roomId}/unban`
|
||||
///
|
||||
/// Tries to send an unban event into the room.
|
||||
pub(crate) async fn unban_user_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<unban_user::v3::Request>,
|
||||
) -> Result<unban_user::v3::Response> {
|
||||
let state_lock = services.rooms.state.mutex.lock(&body.room_id).await;
|
||||
|
||||
let current_member_content = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.get_member(&body.room_id, &body.user_id)
|
||||
.await
|
||||
.unwrap_or_else(|_| RoomMemberEventContent::new(MembershipState::Leave));
|
||||
|
||||
if current_member_content.membership != MembershipState::Ban {
|
||||
return Err!(Request(Forbidden(
|
||||
"Cannot unban a user who is not banned (current membership: {})",
|
||||
current_member_content.membership
|
||||
)));
|
||||
}
|
||||
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(body.user_id.to_string(), &RoomMemberEventContent {
|
||||
membership: MembershipState::Leave,
|
||||
reason: body.reason.clone(),
|
||||
join_authorized_via_users_server: None,
|
||||
third_party_invite: None,
|
||||
is_direct: None,
|
||||
..current_member_content
|
||||
}),
|
||||
body.sender_user(),
|
||||
&body.room_id,
|
||||
&state_lock,
|
||||
)
|
||||
.await?;
|
||||
|
||||
drop(state_lock);
|
||||
|
||||
Ok(unban_user::v3::Response::new())
|
||||
}
|
||||
+38
-26
@@ -2,9 +2,10 @@ use axum::extract::State;
|
||||
use conduwuit::{
|
||||
Err, Result, at,
|
||||
matrix::{
|
||||
Event,
|
||||
pdu::{PduCount, PduEvent},
|
||||
event::{Event, Matches},
|
||||
pdu::PduCount,
|
||||
},
|
||||
ref_at,
|
||||
utils::{
|
||||
IterStream, ReadyExt,
|
||||
result::{FlatOk, LogErr},
|
||||
@@ -32,6 +33,7 @@ use ruma::{
|
||||
},
|
||||
serde::Raw,
|
||||
};
|
||||
use tracing::warn;
|
||||
|
||||
use crate::Ruma;
|
||||
|
||||
@@ -71,7 +73,7 @@ pub(crate) async fn get_message_events_route(
|
||||
) -> Result<get_message_events::v3::Response> {
|
||||
debug_assert!(IGNORED_MESSAGE_TYPES.is_sorted(), "IGNORED_MESSAGE_TYPES is not sorted");
|
||||
let sender_user = body.sender_user();
|
||||
let sender_device = body.sender_device.as_ref();
|
||||
let sender_device = body.sender_device.as_deref();
|
||||
let room_id = &body.room_id;
|
||||
let filter = &body.filter;
|
||||
|
||||
@@ -132,20 +134,20 @@ pub(crate) async fn get_message_events_route(
|
||||
.take(limit)
|
||||
.collect()
|
||||
.await;
|
||||
// let appservice_id = body.appservice_info.map(|appservice|
|
||||
// appservice.registration.id);
|
||||
|
||||
let lazy_loading_context = lazy_loading::Context {
|
||||
user_id: sender_user,
|
||||
device_id: match sender_device {
|
||||
| Some(device_id) => device_id,
|
||||
| None =>
|
||||
if let Some(registration) = body.appservice_info.as_ref() {
|
||||
<&DeviceId>::from(registration.registration.id.as_str())
|
||||
} else {
|
||||
<&DeviceId>::from("")
|
||||
},
|
||||
},
|
||||
device_id: sender_device.or_else(|| {
|
||||
if let Some(registration) = body.appservice_info.as_ref() {
|
||||
Some(<&DeviceId>::from(registration.registration.id.as_str()))
|
||||
} else {
|
||||
warn!(
|
||||
"No device_id provided and no appservice registration found, this should be \
|
||||
unreachable"
|
||||
);
|
||||
None
|
||||
}
|
||||
}),
|
||||
room_id,
|
||||
token: Some(from.into_unsigned()),
|
||||
options: Some(&filter.lazy_load_options),
|
||||
@@ -174,7 +176,7 @@ pub(crate) async fn get_message_events_route(
|
||||
let chunk = events
|
||||
.into_iter()
|
||||
.map(at!(1))
|
||||
.map(PduEvent::into_room_event)
|
||||
.map(Event::into_format)
|
||||
.collect();
|
||||
|
||||
Ok(get_message_events::v3::Response {
|
||||
@@ -215,7 +217,9 @@ where
|
||||
pin_mut!(receipts);
|
||||
let witness: Witness = events
|
||||
.stream()
|
||||
.map(|(_, pdu)| pdu.sender.clone())
|
||||
.map(ref_at!(1))
|
||||
.map(Event::sender)
|
||||
.map(ToOwned::to_owned)
|
||||
.chain(
|
||||
receipts
|
||||
.ready_take_while(|(_, c, _)| *c <= newest.into_unsigned())
|
||||
@@ -240,7 +244,7 @@ async fn get_member_event(
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get(room_id, &StateEventType::RoomMember, user_id.as_str())
|
||||
.map_ok(PduEvent::into_state_event)
|
||||
.map_ok(Event::into_format)
|
||||
.await
|
||||
.ok()
|
||||
}
|
||||
@@ -260,25 +264,33 @@ pub(crate) async fn ignored_filter(
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) async fn is_ignored_pdu(
|
||||
pub(crate) async fn is_ignored_pdu<Pdu>(
|
||||
services: &Services,
|
||||
pdu: &PduEvent,
|
||||
event: &Pdu,
|
||||
user_id: &UserId,
|
||||
) -> bool {
|
||||
) -> bool
|
||||
where
|
||||
Pdu: Event + Send + Sync,
|
||||
{
|
||||
// exclude Synapse's dummy events from bloating up response bodies. clients
|
||||
// don't need to see this.
|
||||
if pdu.kind.to_cow_str() == "org.matrix.dummy_event" {
|
||||
if event.kind().to_cow_str() == "org.matrix.dummy_event" {
|
||||
return true;
|
||||
}
|
||||
|
||||
let ignored_type = IGNORED_MESSAGE_TYPES.binary_search(&pdu.kind).is_ok();
|
||||
let ignored_type = IGNORED_MESSAGE_TYPES.binary_search(event.kind()).is_ok();
|
||||
|
||||
let ignored_server = services
|
||||
.moderation
|
||||
.is_remote_server_forbidden(pdu.sender().server_name());
|
||||
.is_remote_server_ignored(event.sender().server_name());
|
||||
|
||||
if ignored_type
|
||||
&& (ignored_server || services.users.user_is_ignored(&pdu.sender, user_id).await)
|
||||
&& (ignored_server
|
||||
|| (!services.config.send_messages_from_ignored_users_to_client
|
||||
&& services
|
||||
.users
|
||||
.user_is_ignored(event.sender(), user_id)
|
||||
.await))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -297,7 +309,7 @@ pub(crate) async fn visibility_filter(
|
||||
services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.user_can_see_event(user_id, &pdu.room_id, &pdu.event_id)
|
||||
.user_can_see_event(user_id, pdu.room_id(), pdu.event_id())
|
||||
.await
|
||||
.then_some(item)
|
||||
}
|
||||
@@ -305,7 +317,7 @@ pub(crate) async fn visibility_filter(
|
||||
#[inline]
|
||||
pub(crate) fn event_filter(item: PdusIterItem, filter: &RoomEventFilter) -> Option<PdusIterItem> {
|
||||
let (_, pdu) = &item;
|
||||
pdu.matches(filter).then_some(item)
|
||||
filter.matches(pdu).then_some(item)
|
||||
}
|
||||
|
||||
#[cfg_attr(debug_assertions, conduwuit::ctor)]
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use axum::extract::State;
|
||||
use conduwuit::{Error, Result, utils};
|
||||
use ruma::{
|
||||
api::client::{account, error::ErrorKind},
|
||||
authentication::TokenType,
|
||||
};
|
||||
use conduwuit::{Err, Result, utils};
|
||||
use ruma::{api::client::account, authentication::TokenType};
|
||||
|
||||
use super::TOKEN_LENGTH;
|
||||
use crate::Ruma;
|
||||
@@ -19,17 +16,15 @@ pub(crate) async fn create_openid_token_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<account::request_openid_token::v3::Request>,
|
||||
) -> Result<account::request_openid_token::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let sender_user = body.sender_user();
|
||||
|
||||
if sender_user != &body.user_id {
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::InvalidParam,
|
||||
if sender_user != body.user_id {
|
||||
return Err!(Request(InvalidParam(
|
||||
"Not allowed to request OpenID tokens on behalf of other users",
|
||||
));
|
||||
)));
|
||||
}
|
||||
|
||||
let access_token = utils::random_string(TOKEN_LENGTH);
|
||||
|
||||
let expires_in = services
|
||||
.users
|
||||
.create_openid_token(&body.user_id, &access_token)?;
|
||||
|
||||
+38
-40
@@ -2,21 +2,21 @@ use std::collections::BTreeMap;
|
||||
|
||||
use axum::extract::State;
|
||||
use conduwuit::{
|
||||
Err, Error, Result,
|
||||
Err, Result,
|
||||
matrix::pdu::PduBuilder,
|
||||
utils::{IterStream, stream::TryIgnore},
|
||||
utils::{IterStream, future::TryExtExt, stream::TryIgnore},
|
||||
warn,
|
||||
};
|
||||
use conduwuit_service::Services;
|
||||
use futures::{StreamExt, TryStreamExt, future::join3};
|
||||
use futures::{
|
||||
StreamExt, TryStreamExt,
|
||||
future::{join, join3, join4},
|
||||
};
|
||||
use ruma::{
|
||||
OwnedMxcUri, OwnedRoomId, UserId,
|
||||
api::{
|
||||
client::{
|
||||
error::ErrorKind,
|
||||
profile::{
|
||||
get_avatar_url, get_display_name, get_profile, set_avatar_url, set_display_name,
|
||||
},
|
||||
client::profile::{
|
||||
get_avatar_url, get_display_name, get_profile, set_avatar_url, set_display_name,
|
||||
},
|
||||
federation,
|
||||
},
|
||||
@@ -35,7 +35,7 @@ pub(crate) async fn set_displayname_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<set_display_name::v3::Request>,
|
||||
) -> Result<set_display_name::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let sender_user = body.sender_user();
|
||||
|
||||
if *sender_user != body.user_id && body.appservice_info.is_none() {
|
||||
return Err!(Request(Forbidden("You cannot update the profile of another user")));
|
||||
@@ -107,7 +107,7 @@ pub(crate) async fn get_displayname_route(
|
||||
if !services.users.exists(&body.user_id).await {
|
||||
// Return 404 if this user doesn't exist and we couldn't fetch it over
|
||||
// federation
|
||||
return Err(Error::BadRequest(ErrorKind::NotFound, "Profile was not found."));
|
||||
return Err!(Request(NotFound("Profile was not found.")));
|
||||
}
|
||||
|
||||
Ok(get_display_name::v3::Response {
|
||||
@@ -124,7 +124,7 @@ pub(crate) async fn set_avatar_url_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<set_avatar_url::v3::Request>,
|
||||
) -> Result<set_avatar_url::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let sender_user = body.sender_user();
|
||||
|
||||
if *sender_user != body.user_id && body.appservice_info.is_none() {
|
||||
return Err!(Request(Forbidden("You cannot update the profile of another user")));
|
||||
@@ -189,11 +189,9 @@ pub(crate) async fn get_avatar_url_route(
|
||||
services
|
||||
.users
|
||||
.set_displayname(&body.user_id, response.displayname.clone());
|
||||
|
||||
services
|
||||
.users
|
||||
.set_avatar_url(&body.user_id, response.avatar_url.clone());
|
||||
|
||||
services
|
||||
.users
|
||||
.set_blurhash(&body.user_id, response.blurhash.clone());
|
||||
@@ -208,13 +206,16 @@ pub(crate) async fn get_avatar_url_route(
|
||||
if !services.users.exists(&body.user_id).await {
|
||||
// Return 404 if this user doesn't exist and we couldn't fetch it over
|
||||
// federation
|
||||
return Err(Error::BadRequest(ErrorKind::NotFound, "Profile was not found."));
|
||||
return Err!(Request(NotFound("Profile was not found.")));
|
||||
}
|
||||
|
||||
Ok(get_avatar_url::v3::Response {
|
||||
avatar_url: services.users.avatar_url(&body.user_id).await.ok(),
|
||||
blurhash: services.users.blurhash(&body.user_id).await.ok(),
|
||||
})
|
||||
let (avatar_url, blurhash) = join(
|
||||
services.users.avatar_url(&body.user_id).ok(),
|
||||
services.users.blurhash(&body.user_id).ok(),
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(get_avatar_url::v3::Response { avatar_url, blurhash })
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/client/v3/profile/{userId}`
|
||||
@@ -247,15 +248,12 @@ pub(crate) async fn get_profile_route(
|
||||
services
|
||||
.users
|
||||
.set_displayname(&body.user_id, response.displayname.clone());
|
||||
|
||||
services
|
||||
.users
|
||||
.set_avatar_url(&body.user_id, response.avatar_url.clone());
|
||||
|
||||
services
|
||||
.users
|
||||
.set_blurhash(&body.user_id, response.blurhash.clone());
|
||||
|
||||
services
|
||||
.users
|
||||
.set_timezone(&body.user_id, response.tz.clone());
|
||||
@@ -281,7 +279,7 @@ pub(crate) async fn get_profile_route(
|
||||
if !services.users.exists(&body.user_id).await {
|
||||
// Return 404 if this user doesn't exist and we couldn't fetch it over
|
||||
// federation
|
||||
return Err(Error::BadRequest(ErrorKind::NotFound, "Profile was not found."));
|
||||
return Err!(Request(NotFound("Profile was not found.")));
|
||||
}
|
||||
|
||||
let mut custom_profile_fields: BTreeMap<String, serde_json::Value> = services
|
||||
@@ -294,11 +292,19 @@ pub(crate) async fn get_profile_route(
|
||||
custom_profile_fields.remove("us.cloke.msc4175.tz");
|
||||
custom_profile_fields.remove("m.tz");
|
||||
|
||||
let (avatar_url, blurhash, displayname, tz) = join4(
|
||||
services.users.avatar_url(&body.user_id).ok(),
|
||||
services.users.blurhash(&body.user_id).ok(),
|
||||
services.users.displayname(&body.user_id).ok(),
|
||||
services.users.timezone(&body.user_id).ok(),
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(get_profile::v3::Response {
|
||||
avatar_url: services.users.avatar_url(&body.user_id).await.ok(),
|
||||
blurhash: services.users.blurhash(&body.user_id).await.ok(),
|
||||
displayname: services.users.displayname(&body.user_id).await.ok(),
|
||||
tz: services.users.timezone(&body.user_id).await.ok(),
|
||||
avatar_url,
|
||||
blurhash,
|
||||
displayname,
|
||||
tz,
|
||||
custom_profile_fields,
|
||||
})
|
||||
}
|
||||
@@ -310,16 +316,12 @@ pub async fn update_displayname(
|
||||
all_joined_rooms: &[OwnedRoomId],
|
||||
) {
|
||||
let (current_avatar_url, current_blurhash, current_displayname) = join3(
|
||||
services.users.avatar_url(user_id),
|
||||
services.users.blurhash(user_id),
|
||||
services.users.displayname(user_id),
|
||||
services.users.avatar_url(user_id).ok(),
|
||||
services.users.blurhash(user_id).ok(),
|
||||
services.users.displayname(user_id).ok(),
|
||||
)
|
||||
.await;
|
||||
|
||||
let current_avatar_url = current_avatar_url.ok();
|
||||
let current_blurhash = current_blurhash.ok();
|
||||
let current_displayname = current_displayname.ok();
|
||||
|
||||
if displayname == current_displayname {
|
||||
return;
|
||||
}
|
||||
@@ -362,16 +364,12 @@ pub async fn update_avatar_url(
|
||||
all_joined_rooms: &[OwnedRoomId],
|
||||
) {
|
||||
let (current_avatar_url, current_blurhash, current_displayname) = join3(
|
||||
services.users.avatar_url(user_id),
|
||||
services.users.blurhash(user_id),
|
||||
services.users.displayname(user_id),
|
||||
services.users.avatar_url(user_id).ok(),
|
||||
services.users.blurhash(user_id).ok(),
|
||||
services.users.displayname(user_id).ok(),
|
||||
)
|
||||
.await;
|
||||
|
||||
let current_avatar_url = current_avatar_url.ok();
|
||||
let current_blurhash = current_blurhash.ok();
|
||||
let current_displayname = current_displayname.ok();
|
||||
|
||||
if current_avatar_url == avatar_url && current_blurhash == blurhash {
|
||||
return;
|
||||
}
|
||||
|
||||
+44
-69
@@ -79,17 +79,14 @@ pub(crate) async fn get_pushrules_all_route(
|
||||
|
||||
global_ruleset.update_with_server_default(Ruleset::server_default(sender_user));
|
||||
|
||||
let ty = GlobalAccountDataEventType::PushRules;
|
||||
let event = PushRulesEvent {
|
||||
content: PushRulesEventContent { global: global_ruleset.clone() },
|
||||
};
|
||||
|
||||
services
|
||||
.account_data
|
||||
.update(
|
||||
None,
|
||||
sender_user,
|
||||
GlobalAccountDataEventType::PushRules.to_string().into(),
|
||||
&serde_json::to_value(PushRulesEvent {
|
||||
content: PushRulesEventContent { global: global_ruleset.clone() },
|
||||
})
|
||||
.expect("to json always works"),
|
||||
)
|
||||
.update(None, sender_user, ty.to_string().into(), &serde_json::to_value(event)?)
|
||||
.await?;
|
||||
}
|
||||
};
|
||||
@@ -106,7 +103,7 @@ pub(crate) async fn get_pushrules_global_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<get_pushrules_global_scope::v3::Request>,
|
||||
) -> Result<get_pushrules_global_scope::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let sender_user = body.sender_user();
|
||||
|
||||
let Some(content_value) = services
|
||||
.account_data
|
||||
@@ -118,19 +115,17 @@ pub(crate) async fn get_pushrules_global_route(
|
||||
else {
|
||||
// user somehow has non-existent push rule event. recreate it and return server
|
||||
// default silently
|
||||
|
||||
let ty = GlobalAccountDataEventType::PushRules;
|
||||
let event = PushRulesEvent {
|
||||
content: PushRulesEventContent {
|
||||
global: Ruleset::server_default(sender_user),
|
||||
},
|
||||
};
|
||||
|
||||
services
|
||||
.account_data
|
||||
.update(
|
||||
None,
|
||||
sender_user,
|
||||
GlobalAccountDataEventType::PushRules.to_string().into(),
|
||||
&serde_json::to_value(PushRulesEvent {
|
||||
content: PushRulesEventContent {
|
||||
global: Ruleset::server_default(sender_user),
|
||||
},
|
||||
})
|
||||
.expect("to json always works"),
|
||||
)
|
||||
.update(None, sender_user, ty.to_string().into(), &serde_json::to_value(event)?)
|
||||
.await?;
|
||||
|
||||
return Ok(get_pushrules_global_scope::v3::Response {
|
||||
@@ -223,7 +218,7 @@ pub(crate) async fn get_pushrule_route(
|
||||
if let Some(rule) = rule {
|
||||
Ok(get_pushrule::v3::Response { rule })
|
||||
} else {
|
||||
Err(Error::BadRequest(ErrorKind::NotFound, "Push rule not found."))
|
||||
Err!(Request(NotFound("Push rule not found.")))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,9 +229,8 @@ pub(crate) async fn set_pushrule_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<set_pushrule::v3::Request>,
|
||||
) -> Result<set_pushrule::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let body = body.body;
|
||||
|
||||
let sender_user = body.sender_user();
|
||||
let body = &body.body;
|
||||
let mut account_data: PushRulesEvent = services
|
||||
.account_data
|
||||
.get_global(sender_user, GlobalAccountDataEventType::PushRules)
|
||||
@@ -275,14 +269,10 @@ pub(crate) async fn set_pushrule_route(
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
let ty = GlobalAccountDataEventType::PushRules;
|
||||
services
|
||||
.account_data
|
||||
.update(
|
||||
None,
|
||||
sender_user,
|
||||
GlobalAccountDataEventType::PushRules.to_string().into(),
|
||||
&serde_json::to_value(account_data).expect("to json value always works"),
|
||||
)
|
||||
.update(None, sender_user, ty.to_string().into(), &serde_json::to_value(account_data)?)
|
||||
.await?;
|
||||
|
||||
Ok(set_pushrule::v3::Response {})
|
||||
@@ -295,7 +285,7 @@ pub(crate) async fn get_pushrule_actions_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<get_pushrule_actions::v3::Request>,
|
||||
) -> Result<get_pushrule_actions::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let sender_user = body.sender_user();
|
||||
|
||||
// remove old deprecated mentions push rules as per MSC4210
|
||||
#[allow(deprecated)]
|
||||
@@ -329,7 +319,7 @@ pub(crate) async fn set_pushrule_actions_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<set_pushrule_actions::v3::Request>,
|
||||
) -> Result<set_pushrule_actions::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let sender_user = body.sender_user();
|
||||
|
||||
let mut account_data: PushRulesEvent = services
|
||||
.account_data
|
||||
@@ -343,17 +333,13 @@ pub(crate) async fn set_pushrule_actions_route(
|
||||
.set_actions(body.kind.clone(), &body.rule_id, body.actions.clone())
|
||||
.is_err()
|
||||
{
|
||||
return Err(Error::BadRequest(ErrorKind::NotFound, "Push rule not found."));
|
||||
return Err!(Request(NotFound("Push rule not found.")));
|
||||
}
|
||||
|
||||
let ty = GlobalAccountDataEventType::PushRules;
|
||||
services
|
||||
.account_data
|
||||
.update(
|
||||
None,
|
||||
sender_user,
|
||||
GlobalAccountDataEventType::PushRules.to_string().into(),
|
||||
&serde_json::to_value(account_data).expect("to json value always works"),
|
||||
)
|
||||
.update(None, sender_user, ty.to_string().into(), &serde_json::to_value(account_data)?)
|
||||
.await?;
|
||||
|
||||
Ok(set_pushrule_actions::v3::Response {})
|
||||
@@ -366,7 +352,7 @@ pub(crate) async fn get_pushrule_enabled_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<get_pushrule_enabled::v3::Request>,
|
||||
) -> Result<get_pushrule_enabled::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let sender_user = body.sender_user();
|
||||
|
||||
// remove old deprecated mentions push rules as per MSC4210
|
||||
#[allow(deprecated)]
|
||||
@@ -400,7 +386,7 @@ pub(crate) async fn set_pushrule_enabled_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<set_pushrule_enabled::v3::Request>,
|
||||
) -> Result<set_pushrule_enabled::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let sender_user = body.sender_user();
|
||||
|
||||
let mut account_data: PushRulesEvent = services
|
||||
.account_data
|
||||
@@ -414,17 +400,13 @@ pub(crate) async fn set_pushrule_enabled_route(
|
||||
.set_enabled(body.kind.clone(), &body.rule_id, body.enabled)
|
||||
.is_err()
|
||||
{
|
||||
return Err(Error::BadRequest(ErrorKind::NotFound, "Push rule not found."));
|
||||
return Err!(Request(NotFound("Push rule not found.")));
|
||||
}
|
||||
|
||||
let ty = GlobalAccountDataEventType::PushRules;
|
||||
services
|
||||
.account_data
|
||||
.update(
|
||||
None,
|
||||
sender_user,
|
||||
GlobalAccountDataEventType::PushRules.to_string().into(),
|
||||
&serde_json::to_value(account_data).expect("to json value always works"),
|
||||
)
|
||||
.update(None, sender_user, ty.to_string().into(), &serde_json::to_value(account_data)?)
|
||||
.await?;
|
||||
|
||||
Ok(set_pushrule_enabled::v3::Response {})
|
||||
@@ -437,7 +419,7 @@ pub(crate) async fn delete_pushrule_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<delete_pushrule::v3::Request>,
|
||||
) -> Result<delete_pushrule::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let sender_user = body.sender_user();
|
||||
|
||||
let mut account_data: PushRulesEvent = services
|
||||
.account_data
|
||||
@@ -463,14 +445,10 @@ pub(crate) async fn delete_pushrule_route(
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
let ty = GlobalAccountDataEventType::PushRules;
|
||||
services
|
||||
.account_data
|
||||
.update(
|
||||
None,
|
||||
sender_user,
|
||||
GlobalAccountDataEventType::PushRules.to_string().into(),
|
||||
&serde_json::to_value(account_data).expect("to json value always works"),
|
||||
)
|
||||
.update(None, sender_user, ty.to_string().into(), &serde_json::to_value(account_data)?)
|
||||
.await?;
|
||||
|
||||
Ok(delete_pushrule::v3::Response {})
|
||||
@@ -483,7 +461,7 @@ pub(crate) async fn get_pushers_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<get_pushers::v3::Request>,
|
||||
) -> Result<get_pushers::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let sender_user = body.sender_user();
|
||||
|
||||
Ok(get_pushers::v3::Response {
|
||||
pushers: services.pusher.get_pushers(sender_user).await,
|
||||
@@ -499,7 +477,7 @@ pub(crate) async fn set_pushers_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<set_pusher::v3::Request>,
|
||||
) -> Result<set_pusher::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let sender_user = body.sender_user();
|
||||
|
||||
services
|
||||
.pusher
|
||||
@@ -515,19 +493,16 @@ async fn recreate_push_rules_and_return(
|
||||
services: &Services,
|
||||
sender_user: &ruma::UserId,
|
||||
) -> Result<get_pushrules_all::v3::Response> {
|
||||
let ty = GlobalAccountDataEventType::PushRules;
|
||||
let event = PushRulesEvent {
|
||||
content: PushRulesEventContent {
|
||||
global: Ruleset::server_default(sender_user),
|
||||
},
|
||||
};
|
||||
|
||||
services
|
||||
.account_data
|
||||
.update(
|
||||
None,
|
||||
sender_user,
|
||||
GlobalAccountDataEventType::PushRules.to_string().into(),
|
||||
&serde_json::to_value(PushRulesEvent {
|
||||
content: PushRulesEventContent {
|
||||
global: Ruleset::server_default(sender_user),
|
||||
},
|
||||
})
|
||||
.expect("to json always works"),
|
||||
)
|
||||
.update(None, sender_user, ty.to_string().into(), &serde_json::to_value(event)?)
|
||||
.await?;
|
||||
|
||||
Ok(get_pushrules_all::v3::Response {
|
||||
|
||||
@@ -37,7 +37,7 @@ pub(crate) async fn set_read_marker_route(
|
||||
Some(&body.room_id),
|
||||
sender_user,
|
||||
RoomAccountDataEventType::FullyRead,
|
||||
&serde_json::to_value(fully_read_event).expect("to json value always works"),
|
||||
&serde_json::to_value(fully_read_event)?,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
@@ -146,7 +146,7 @@ pub(crate) async fn create_receipt_route(
|
||||
Some(&body.room_id),
|
||||
sender_user,
|
||||
RoomAccountDataEventType::FullyRead,
|
||||
&serde_json::to_value(fully_read_event).expect("to json value always works"),
|
||||
&serde_json::to_value(fully_read_event)?,
|
||||
)
|
||||
.await?;
|
||||
},
|
||||
|
||||
@@ -15,8 +15,8 @@ pub(crate) async fn redact_event_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<redact_event::v3::Request>,
|
||||
) -> Result<redact_event::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let body = body.body;
|
||||
let sender_user = body.sender_user();
|
||||
let body = &body.body;
|
||||
|
||||
let state_lock = services.rooms.state.mutex.lock(&body.room_id).await;
|
||||
|
||||
|
||||
+10
-10
@@ -1,10 +1,10 @@
|
||||
use axum::extract::State;
|
||||
use conduwuit::{
|
||||
Result, at,
|
||||
matrix::pdu::PduCount,
|
||||
matrix::{Event, event::RelationTypeEqual, pdu::PduCount},
|
||||
utils::{IterStream, ReadyExt, result::FlatOk, stream::WidebandExt},
|
||||
};
|
||||
use conduwuit_service::{Services, rooms::timeline::PdusIterItem};
|
||||
use conduwuit_service::Services;
|
||||
use futures::StreamExt;
|
||||
use ruma::{
|
||||
EventId, RoomId, UInt, UserId,
|
||||
@@ -129,7 +129,7 @@ async fn paginate_relations_with_filter(
|
||||
// Spec (v1.10) recommends depth of at least 3
|
||||
let depth: u8 = if recurse { 3 } else { 1 };
|
||||
|
||||
let events: Vec<PdusIterItem> = services
|
||||
let events: Vec<_> = services
|
||||
.rooms
|
||||
.pdu_metadata
|
||||
.get_relations(sender_user, room_id, target, start, limit, depth, dir)
|
||||
@@ -138,12 +138,12 @@ async fn paginate_relations_with_filter(
|
||||
.filter(|(_, pdu)| {
|
||||
filter_event_type
|
||||
.as_ref()
|
||||
.is_none_or(|kind| *kind == pdu.kind)
|
||||
.is_none_or(|kind| kind == pdu.kind())
|
||||
})
|
||||
.filter(|(_, pdu)| {
|
||||
filter_rel_type
|
||||
.as_ref()
|
||||
.is_none_or(|rel_type| pdu.relation_type_equal(rel_type))
|
||||
.is_none_or(|rel_type| rel_type.relation_type_equal(pdu))
|
||||
})
|
||||
.stream()
|
||||
.ready_take_while(|(count, _)| Some(*count) != to)
|
||||
@@ -167,22 +167,22 @@ async fn paginate_relations_with_filter(
|
||||
chunk: events
|
||||
.into_iter()
|
||||
.map(at!(1))
|
||||
.map(|pdu| pdu.to_message_like_event())
|
||||
.map(Event::into_format)
|
||||
.collect(),
|
||||
})
|
||||
}
|
||||
|
||||
async fn visibility_filter(
|
||||
async fn visibility_filter<Pdu: Event + Send + Sync>(
|
||||
services: &Services,
|
||||
sender_user: &UserId,
|
||||
item: PdusIterItem,
|
||||
) -> Option<PdusIterItem> {
|
||||
item: (PduCount, Pdu),
|
||||
) -> Option<(PduCount, Pdu)> {
|
||||
let (_, pdu) = &item;
|
||||
|
||||
services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.user_can_see_event(sender_user, &pdu.room_id, &pdu.event_id)
|
||||
.user_can_see_event(sender_user, pdu.room_id(), pdu.event_id())
|
||||
.await
|
||||
.then_some(item)
|
||||
}
|
||||
|
||||
+12
-25
@@ -2,15 +2,12 @@ use std::time::Duration;
|
||||
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::InsecureClientIp;
|
||||
use conduwuit::{Err, Error, Result, debug_info, info, matrix::pdu::PduEvent, utils::ReadyExt};
|
||||
use conduwuit::{Err, Result, debug_info, info, matrix::pdu::PduEvent, utils::ReadyExt};
|
||||
use conduwuit_service::Services;
|
||||
use rand::Rng;
|
||||
use ruma::{
|
||||
EventId, RoomId, UserId,
|
||||
api::client::{
|
||||
error::ErrorKind,
|
||||
room::{report_content, report_room},
|
||||
},
|
||||
api::client::room::{report_content, report_room},
|
||||
events::room::message,
|
||||
int,
|
||||
};
|
||||
@@ -28,7 +25,7 @@ pub(crate) async fn report_room_route(
|
||||
body: Ruma<report_room::v3::Request>,
|
||||
) -> Result<report_room::v3::Response> {
|
||||
// user authentication
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let sender_user = body.sender_user();
|
||||
|
||||
info!(
|
||||
"Received room report by user {sender_user} for room {} with reason: \"{}\"",
|
||||
@@ -37,9 +34,8 @@ pub(crate) async fn report_room_route(
|
||||
);
|
||||
|
||||
if body.reason.as_ref().is_some_and(|s| s.len() > 750) {
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::InvalidParam,
|
||||
"Reason too long, should be 750 characters or fewer",
|
||||
return Err!(Request(
|
||||
InvalidParam("Reason too long, should be 750 characters or fewer",)
|
||||
));
|
||||
}
|
||||
|
||||
@@ -82,7 +78,7 @@ pub(crate) async fn report_event_route(
|
||||
body: Ruma<report_content::v3::Request>,
|
||||
) -> Result<report_content::v3::Response> {
|
||||
// user authentication
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let sender_user = body.sender_user();
|
||||
|
||||
info!(
|
||||
"Received event report by user {sender_user} for room {} and event ID {}, with reason: \
|
||||
@@ -151,23 +147,16 @@ async fn is_event_report_valid(
|
||||
);
|
||||
|
||||
if room_id != pdu.room_id {
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::NotFound,
|
||||
"Event ID does not belong to the reported room",
|
||||
));
|
||||
return Err!(Request(NotFound("Event ID does not belong to the reported room",)));
|
||||
}
|
||||
|
||||
if score.is_some_and(|s| s > int!(0) || s < int!(-100)) {
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::InvalidParam,
|
||||
"Invalid score, must be within 0 to -100",
|
||||
));
|
||||
return Err!(Request(InvalidParam("Invalid score, must be within 0 to -100",)));
|
||||
}
|
||||
|
||||
if reason.as_ref().is_some_and(|s| s.len() > 750) {
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::InvalidParam,
|
||||
"Reason too long, should be 750 characters or fewer",
|
||||
return Err!(Request(
|
||||
InvalidParam("Reason too long, should be 750 characters or fewer",)
|
||||
));
|
||||
}
|
||||
|
||||
@@ -178,10 +167,7 @@ async fn is_event_report_valid(
|
||||
.ready_any(|user_id| user_id == sender_user)
|
||||
.await
|
||||
{
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::NotFound,
|
||||
"You are not in the room you are reporting.",
|
||||
));
|
||||
return Err!(Request(NotFound("You are not in the room you are reporting.",)));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -196,5 +182,6 @@ async fn delay_response() {
|
||||
"Got successful /report request, waiting {time_to_wait} seconds before sending \
|
||||
successful response."
|
||||
);
|
||||
|
||||
sleep(Duration::from_secs(time_to_wait)).await;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use axum::extract::State;
|
||||
use conduwuit::{Error, Result};
|
||||
use conduwuit::{Err, Result};
|
||||
use futures::StreamExt;
|
||||
use ruma::api::client::{error::ErrorKind, room::aliases};
|
||||
use ruma::api::client::room::aliases;
|
||||
|
||||
use crate::Ruma;
|
||||
|
||||
@@ -15,7 +15,7 @@ pub(crate) async fn get_room_aliases_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<aliases::v3::Request>,
|
||||
) -> Result<aliases::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let sender_user = body.sender_user();
|
||||
|
||||
if !services
|
||||
.rooms
|
||||
@@ -23,10 +23,7 @@ pub(crate) async fn get_room_aliases_route(
|
||||
.user_can_see_state_events(sender_user, &body.room_id)
|
||||
.await
|
||||
{
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::forbidden(),
|
||||
"You don't have permission to view this room.",
|
||||
));
|
||||
return Err!(Request(Forbidden("You don't have permission to view this room.",)));
|
||||
}
|
||||
|
||||
Ok(aliases::v3::Response {
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::collections::BTreeMap;
|
||||
|
||||
use axum::extract::State;
|
||||
use conduwuit::{
|
||||
Err, Error, Result, debug_info, debug_warn, err, error, info,
|
||||
Err, Result, debug_info, debug_warn, err, info,
|
||||
matrix::{StateKey, pdu::PduBuilder},
|
||||
warn,
|
||||
};
|
||||
@@ -10,10 +10,7 @@ use conduwuit_service::{Services, appservice::RegistrationInfo};
|
||||
use futures::FutureExt;
|
||||
use ruma::{
|
||||
CanonicalJsonObject, Int, OwnedRoomAliasId, OwnedRoomId, OwnedUserId, RoomId, RoomVersionId,
|
||||
api::client::{
|
||||
error::ErrorKind,
|
||||
room::{self, create_room},
|
||||
},
|
||||
api::client::room::{self, create_room},
|
||||
events::{
|
||||
TimelineEventType,
|
||||
room::{
|
||||
@@ -58,16 +55,13 @@ pub(crate) async fn create_room_route(
|
||||
) -> Result<create_room::v3::Response> {
|
||||
use create_room::v3::RoomPreset;
|
||||
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
let sender_user = body.sender_user();
|
||||
|
||||
if !services.globals.allow_room_creation()
|
||||
&& body.appservice_info.is_none()
|
||||
&& !services.users.is_admin(sender_user).await
|
||||
{
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::forbidden(),
|
||||
"Room creation has been disabled.",
|
||||
));
|
||||
return Err!(Request(Forbidden("Room creation has been disabled.",)));
|
||||
}
|
||||
|
||||
let room_id: OwnedRoomId = match &body.room_id {
|
||||
@@ -77,10 +71,7 @@ pub(crate) async fn create_room_route(
|
||||
|
||||
// check if room ID doesn't already exist instead of erroring on auth check
|
||||
if services.rooms.short.get_shortroomid(&room_id).await.is_ok() {
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::RoomInUse,
|
||||
"Room with that custom room ID already exists",
|
||||
));
|
||||
return Err!(Request(RoomInUse("Room with that custom room ID already exists",)));
|
||||
}
|
||||
|
||||
if body.visibility == room::Visibility::Public
|
||||
@@ -88,19 +79,17 @@ pub(crate) async fn create_room_route(
|
||||
&& !services.users.is_admin(sender_user).await
|
||||
&& body.appservice_info.is_none()
|
||||
{
|
||||
info!(
|
||||
"Non-admin user {sender_user} tried to publish {0} to the room directory while \
|
||||
\"lockdown_public_room_directory\" is enabled",
|
||||
&room_id
|
||||
warn!(
|
||||
"Non-admin user {sender_user} tried to publish {room_id} to the room directory \
|
||||
while \"lockdown_public_room_directory\" is enabled"
|
||||
);
|
||||
|
||||
if services.server.config.admin_room_notices {
|
||||
services
|
||||
.admin
|
||||
.send_text(&format!(
|
||||
"Non-admin user {sender_user} tried to publish {0} to the room directory \
|
||||
while \"lockdown_public_room_directory\" is enabled",
|
||||
&room_id
|
||||
.notice(&format!(
|
||||
"Non-admin user {sender_user} tried to publish {room_id} to the room \
|
||||
directory while \"lockdown_public_room_directory\" is enabled"
|
||||
))
|
||||
.await;
|
||||
}
|
||||
@@ -125,10 +114,9 @@ pub(crate) async fn create_room_route(
|
||||
if services.server.supported_room_version(&room_version) {
|
||||
room_version
|
||||
} else {
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::UnsupportedRoomVersion,
|
||||
"This server does not support that room version.",
|
||||
));
|
||||
return Err!(Request(UnsupportedRoomVersion(
|
||||
"This server does not support that room version."
|
||||
)));
|
||||
},
|
||||
| None => services.server.config.default_room_version.clone(),
|
||||
};
|
||||
@@ -140,16 +128,17 @@ pub(crate) async fn create_room_route(
|
||||
let mut content = content
|
||||
.deserialize_as::<CanonicalJsonObject>()
|
||||
.map_err(|e| {
|
||||
error!("Failed to deserialise content as canonical JSON: {}", e);
|
||||
Error::bad_database("Failed to deserialise content as canonical JSON.")
|
||||
err!(Request(BadJson(error!(
|
||||
"Failed to deserialise content as canonical JSON: {e}"
|
||||
))))
|
||||
})?;
|
||||
|
||||
match room_version {
|
||||
| V1 | V2 | V3 | V4 | V5 | V6 | V7 | V8 | V9 | V10 => {
|
||||
content.insert(
|
||||
"creator".into(),
|
||||
json!(&sender_user).try_into().map_err(|e| {
|
||||
info!("Invalid creation content: {e}");
|
||||
Error::BadRequest(ErrorKind::BadJson, "Invalid creation content")
|
||||
err!(Request(BadJson(debug_error!("Invalid creation content: {e}"))))
|
||||
})?,
|
||||
);
|
||||
},
|
||||
@@ -159,9 +148,9 @@ pub(crate) async fn create_room_route(
|
||||
}
|
||||
content.insert(
|
||||
"room_version".into(),
|
||||
json!(room_version.as_str()).try_into().map_err(|_| {
|
||||
Error::BadRequest(ErrorKind::BadJson, "Invalid creation content")
|
||||
})?,
|
||||
json!(room_version.as_str())
|
||||
.try_into()
|
||||
.map_err(|e| err!(Request(BadJson("Invalid creation content: {e}"))))?,
|
||||
);
|
||||
content
|
||||
},
|
||||
@@ -170,21 +159,13 @@ pub(crate) async fn create_room_route(
|
||||
|
||||
let content = match room_version {
|
||||
| V1 | V2 | V3 | V4 | V5 | V6 | V7 | V8 | V9 | V10 =>
|
||||
RoomCreateEventContent::new_v1(sender_user.clone()),
|
||||
RoomCreateEventContent::new_v1(sender_user.to_owned()),
|
||||
| _ => RoomCreateEventContent::new_v11(),
|
||||
};
|
||||
let mut content = serde_json::from_str::<CanonicalJsonObject>(
|
||||
to_raw_value(&content)
|
||||
.expect("we just created this as content was None")
|
||||
.get(),
|
||||
)
|
||||
.unwrap();
|
||||
content.insert(
|
||||
"room_version".into(),
|
||||
json!(room_version.as_str())
|
||||
.try_into()
|
||||
.expect("we just created this as content was None"),
|
||||
);
|
||||
let mut content =
|
||||
serde_json::from_str::<CanonicalJsonObject>(to_raw_value(&content)?.get())
|
||||
.unwrap();
|
||||
content.insert("room_version".into(), json!(room_version.as_str()).try_into()?);
|
||||
content
|
||||
},
|
||||
};
|
||||
@@ -196,8 +177,7 @@ pub(crate) async fn create_room_route(
|
||||
.build_and_append_pdu(
|
||||
PduBuilder {
|
||||
event_type: TimelineEventType::RoomCreate,
|
||||
content: to_raw_value(&create_content)
|
||||
.expect("create event content serialization"),
|
||||
content: to_raw_value(&create_content)?,
|
||||
state_key: Some(StateKey::new()),
|
||||
..Default::default()
|
||||
},
|
||||
@@ -235,7 +215,7 @@ pub(crate) async fn create_room_route(
|
||||
| _ => RoomPreset::PrivateChat, // Room visibility should not be custom
|
||||
});
|
||||
|
||||
let mut users = BTreeMap::from_iter([(sender_user.clone(), int!(100))]);
|
||||
let mut users = BTreeMap::from_iter([(sender_user.to_owned(), int!(100))]);
|
||||
|
||||
if preset == RoomPreset::TrustedPrivateChat {
|
||||
for invite in &body.invite {
|
||||
@@ -263,8 +243,7 @@ pub(crate) async fn create_room_route(
|
||||
.build_and_append_pdu(
|
||||
PduBuilder {
|
||||
event_type: TimelineEventType::RoomPowerLevels,
|
||||
content: to_raw_value(&power_levels_content)
|
||||
.expect("serialized power_levels event content"),
|
||||
content: to_raw_value(&power_levels_content)?,
|
||||
state_key: Some(StateKey::new()),
|
||||
..Default::default()
|
||||
},
|
||||
@@ -353,8 +332,7 @@ pub(crate) async fn create_room_route(
|
||||
// 6. Events listed in initial_state
|
||||
for event in &body.initial_state {
|
||||
let mut pdu_builder = event.deserialize_as::<PduBuilder>().map_err(|e| {
|
||||
warn!("Invalid initial state event: {:?}", e);
|
||||
Error::BadRequest(ErrorKind::InvalidParam, "Invalid initial state event.")
|
||||
err!(Request(InvalidParam(warn!("Invalid initial state event: {e:?}"))))
|
||||
})?;
|
||||
|
||||
debug_info!("Room creation initial state event: {event:?}");
|
||||
@@ -363,7 +341,7 @@ pub(crate) async fn create_room_route(
|
||||
// state event in there with the content of literally `{}` (not null or empty
|
||||
// string), let's just skip it over and warn.
|
||||
if pdu_builder.content.get().eq("{}") {
|
||||
info!("skipping empty initial state event with content of `{{}}`: {event:?}");
|
||||
debug_warn!("skipping empty initial state event with content of `{{}}`: {event:?}");
|
||||
debug_warn!("content: {}", pdu_builder.content.get());
|
||||
continue;
|
||||
}
|
||||
@@ -510,9 +488,7 @@ fn default_power_levels_content(
|
||||
|
||||
if let Some(power_level_content_override) = power_level_content_override {
|
||||
let json: JsonObject = serde_json::from_str(power_level_content_override.json().get())
|
||||
.map_err(|_| {
|
||||
Error::BadRequest(ErrorKind::BadJson, "Invalid power_level_content_override.")
|
||||
})?;
|
||||
.map_err(|e| err!(Request(BadJson("Invalid power_level_content_override: {e:?}"))))?;
|
||||
|
||||
for (key, value) in json {
|
||||
power_levels_content[key] = value;
|
||||
@@ -530,16 +506,14 @@ async fn room_alias_check(
|
||||
) -> Result<OwnedRoomAliasId> {
|
||||
// Basic checks on the room alias validity
|
||||
if room_alias_name.contains(':') {
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::InvalidParam,
|
||||
return Err!(Request(InvalidParam(
|
||||
"Room alias contained `:` which is not allowed. Please note that this expects a \
|
||||
localpart, not the full room alias.",
|
||||
));
|
||||
)));
|
||||
} else if room_alias_name.contains(char::is_whitespace) {
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::InvalidParam,
|
||||
return Err!(Request(InvalidParam(
|
||||
"Room alias contained spaces which is not a valid room alias.",
|
||||
));
|
||||
)));
|
||||
}
|
||||
|
||||
// check if room alias is forbidden
|
||||
@@ -548,7 +522,7 @@ async fn room_alias_check(
|
||||
.forbidden_alias_names()
|
||||
.is_match(room_alias_name)
|
||||
{
|
||||
return Err(Error::BadRequest(ErrorKind::Unknown, "Room alias name is forbidden."));
|
||||
return Err!(Request(Unknown("Room alias name is forbidden.")));
|
||||
}
|
||||
|
||||
let server_name = services.globals.server_name();
|
||||
@@ -568,25 +542,19 @@ async fn room_alias_check(
|
||||
.await
|
||||
.is_ok()
|
||||
{
|
||||
return Err(Error::BadRequest(ErrorKind::RoomInUse, "Room alias already exists."));
|
||||
return Err!(Request(RoomInUse("Room alias already exists.")));
|
||||
}
|
||||
|
||||
if let Some(info) = appservice_info {
|
||||
if !info.aliases.is_match(full_room_alias.as_str()) {
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::Exclusive,
|
||||
"Room alias is not in namespace.",
|
||||
));
|
||||
return Err!(Request(Exclusive("Room alias is not in namespace.")));
|
||||
}
|
||||
} else if services
|
||||
.appservice
|
||||
.is_exclusive_alias(&full_room_alias)
|
||||
.await
|
||||
{
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::Exclusive,
|
||||
"Room alias reserved by appservice.",
|
||||
));
|
||||
return Err!(Request(Exclusive("Room alias reserved by appservice.",)));
|
||||
}
|
||||
|
||||
debug_info!("Full room alias: {full_room_alias}");
|
||||
@@ -602,36 +570,49 @@ fn custom_room_id_check(services: &Services, custom_room_id: &str) -> Result<Own
|
||||
.forbidden_alias_names()
|
||||
.is_match(custom_room_id)
|
||||
{
|
||||
return Err(Error::BadRequest(ErrorKind::Unknown, "Custom room ID is forbidden."));
|
||||
return Err!(Request(Unknown("Custom room ID is forbidden.")));
|
||||
}
|
||||
|
||||
if custom_room_id.contains(':') {
|
||||
return Err!(Request(InvalidParam(
|
||||
"Custom room ID contained `:` which is not allowed. Please note that this expects a \
|
||||
localpart, not the full room ID.",
|
||||
)));
|
||||
} else if custom_room_id.contains(char::is_whitespace) {
|
||||
return Err!(Request(InvalidParam(
|
||||
"Custom room ID contained spaces which is not valid."
|
||||
)));
|
||||
}
|
||||
|
||||
let server_name = services.globals.server_name();
|
||||
let mut room_id = custom_room_id.to_owned();
|
||||
if custom_room_id.contains(':') {
|
||||
if !custom_room_id.starts_with('!') {
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::InvalidParam,
|
||||
return Err!(Request(InvalidParam(
|
||||
"Custom room ID contains an unexpected `:` which is not allowed.",
|
||||
));
|
||||
)));
|
||||
}
|
||||
} else if custom_room_id.starts_with('!'){
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::InvalidParam,
|
||||
"Room ID is prefixed with !, but is not fully qualified. You likely did not want this."));
|
||||
} else if custom_room_id.starts_with('!') {
|
||||
return Err!(Request(InvalidParam(
|
||||
"Room ID is prefixed with !, but is not fully qualified. You likely did not want \
|
||||
this.",
|
||||
)));
|
||||
} else {
|
||||
room_id = format!("!{custom_room_id}:{server_name}");
|
||||
}
|
||||
OwnedRoomId::parse(room_id)
|
||||
.map_err(Into::into)
|
||||
.and_then(
|
||||
|full_room_id| {
|
||||
if full_room_id.server_name().expect("failed to extract server name from room ID") != server_name {
|
||||
Err(Error::BadRequest(ErrorKind::InvalidParam, "Custom room ID must be on this server."))
|
||||
} else {
|
||||
Ok(full_room_id)
|
||||
}
|
||||
.and_then(|full_room_id| {
|
||||
if full_room_id
|
||||
.server_name()
|
||||
.expect("failed to extract server name from room ID")
|
||||
!= server_name
|
||||
{
|
||||
Err!(Request(InvalidParam("Custom room ID must be on this server.",)))
|
||||
} else {
|
||||
Ok(full_room_id)
|
||||
}
|
||||
)
|
||||
})
|
||||
.inspect(|full_room_id| {
|
||||
debug_info!(?full_room_id, "Full custom room ID");
|
||||
})
|
||||
|
||||
@@ -40,5 +40,5 @@ pub(crate) async fn get_room_event_route(
|
||||
|
||||
event.add_age().ok();
|
||||
|
||||
Ok(get_room_event::v3::Response { event: event.into_room_event() })
|
||||
Ok(get_room_event::v3::Response { event: event.into_format() })
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use axum::extract::State;
|
||||
use conduwuit::{
|
||||
Err, PduEvent, Result, at,
|
||||
Err, Event, Result, at,
|
||||
utils::{BoolExt, stream::TryTools},
|
||||
};
|
||||
use futures::TryStreamExt;
|
||||
use futures::{FutureExt, TryStreamExt, future::try_join4};
|
||||
use ruma::api::client::room::initial_sync::v3::{PaginationChunk, Request, Response};
|
||||
|
||||
use crate::Ruma;
|
||||
@@ -25,22 +25,33 @@ pub(crate) async fn room_initial_sync_route(
|
||||
return Err!(Request(Forbidden("No room preview available.")));
|
||||
}
|
||||
|
||||
let membership = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.user_membership(body.sender_user(), room_id)
|
||||
.map(Ok);
|
||||
|
||||
let visibility = services.rooms.directory.visibility(room_id).map(Ok);
|
||||
|
||||
let state = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_full_pdus(room_id)
|
||||
.map_ok(Event::into_format)
|
||||
.try_collect::<Vec<_>>();
|
||||
|
||||
let limit = LIMIT_MAX;
|
||||
let events: Vec<_> = services
|
||||
let events = services
|
||||
.rooms
|
||||
.timeline
|
||||
.pdus_rev(None, room_id, None)
|
||||
.try_take(limit)
|
||||
.try_collect()
|
||||
.await?;
|
||||
.try_collect::<Vec<_>>();
|
||||
|
||||
let state: Vec<_> = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_full_pdus(room_id)
|
||||
.map_ok(PduEvent::into_state_event)
|
||||
.try_collect()
|
||||
.await?;
|
||||
let (membership, visibility, state, events) =
|
||||
try_join4(membership, visibility, state, events)
|
||||
.boxed()
|
||||
.await?;
|
||||
|
||||
let messages = PaginationChunk {
|
||||
start: events.last().map(at!(0)).as_ref().map(ToString::to_string),
|
||||
@@ -55,7 +66,7 @@ pub(crate) async fn room_initial_sync_route(
|
||||
chunk: events
|
||||
.into_iter()
|
||||
.map(at!(1))
|
||||
.map(PduEvent::into_room_event)
|
||||
.map(Event::into_format)
|
||||
.collect(),
|
||||
};
|
||||
|
||||
@@ -64,11 +75,7 @@ pub(crate) async fn room_initial_sync_route(
|
||||
account_data: None,
|
||||
state: state.into(),
|
||||
messages: messages.chunk.is_empty().or_some(messages),
|
||||
visibility: services.rooms.directory.visibility(room_id).await.into(),
|
||||
membership: services
|
||||
.rooms
|
||||
.state_cache
|
||||
.user_membership(body.sender_user(), room_id)
|
||||
.await,
|
||||
visibility: visibility.into(),
|
||||
membership,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -113,13 +113,15 @@ async fn local_room_summary_response(
|
||||
) -> Result<get_summary::msc3266::Response> {
|
||||
trace!(?sender_user, "Sending local room summary response for {room_id:?}");
|
||||
let join_rule = services.rooms.state_accessor.get_join_rules(room_id);
|
||||
|
||||
let world_readable = services.rooms.state_accessor.is_world_readable(room_id);
|
||||
|
||||
let guest_can_join = services.rooms.state_accessor.guest_can_join(room_id);
|
||||
|
||||
let (join_rule, world_readable, guest_can_join) =
|
||||
join3(join_rule, world_readable, guest_can_join).await;
|
||||
trace!("{join_rule:?}, {world_readable:?}, {guest_can_join:?}");
|
||||
|
||||
trace!("{join_rule:?}, {world_readable:?}, {guest_can_join:?}");
|
||||
user_can_see_summary(
|
||||
services,
|
||||
room_id,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user