Compare commits

..

65 commits

Author SHA1 Message Date
nexy7574
5ec8e378c2
Run cargo fmt
Some checks failed
Checks / Prefligit / prefligit (push) Failing after 4s
Release Docker Image / define-variables (push) Failing after 12s
Release Docker Image / build-image (linux/amd64, release, linux-amd64, base) (push) Has been skipped
Release Docker Image / build-image (linux/arm64, release, linux-arm64, base) (push) Has been skipped
Release Docker Image / merge (push) Has been skipped
Checks / Rust / Format (push) Failing after 8s
Checks / Rust / Clippy (push) Failing after 14s
Checks / Rust / Cargo Test (push) Failing after 11s
2025-06-30 17:16:00 +01:00
nexy7574
b243069c6f
no!
(cherry picked from commit a3d8e222a2d3a45ccef6142a55f7ff8a8a265ad4)
2025-06-30 17:16:00 +01:00
nexy7574
8e49b3512e
Policy checks and alsl remove assertatte ion
(cherry picked from commit f0ff0e4599c3db8786693e43691b164bb0b7e3c3)
2025-06-30 17:16:00 +01:00
nexy7574
0fd634cb23
Almost call with the PDU
Need to figure out why signing is sad

(cherry picked from commit 6134b971633c9981871c1c9d060a677588714141)
2025-06-30 17:16:00 +01:00
nexy7574
ba6f98a9aa
Correctly mark things as soft failed when checking the policyserv
(cherry picked from commit 3b876ea029b7ec44db143ead31e1696eb4c44b5e)
2025-06-30 17:16:00 +01:00
nexy7574
2dff2df090
Don't call policy server on ourselves
(cherry picked from commit a8c10f6317011ee9db3db968f7a4f81284211780)
2025-06-30 17:15:59 +01:00
nexy7574
57879c1be5
policy server following maybe???
(cherry picked from commit e1c06e10f6ce27b570682989fed0defda0fe09a1)
2025-06-30 17:15:58 +01:00
Jade Ellis
17930708d8
chore: Add second ko-fi as custom link
Some checks failed
Documentation / Build and Deploy Documentation (push) Has been skipped
Checks / Prefligit / prefligit (push) Failing after 2s
Release Docker Image / define-variables (push) Failing after 12s
Release Docker Image / build-image (linux/amd64, release, linux-amd64, base) (push) Has been skipped
Release Docker Image / build-image (linux/arm64, release, linux-arm64, base) (push) Has been skipped
Release Docker Image / merge (push) Has been skipped
Checks / Rust / Format (push) Failing after 6s
Checks / Rust / Clippy (push) Failing after 14s
Checks / Rust / Cargo Test (push) Failing after 11s
2025-06-29 23:06:26 +01:00
Jade Ellis
ec9d3d613e
chore: Add funding
Some checks failed
Documentation / Build and Deploy Documentation (push) Has been skipped
Checks / Prefligit / prefligit (push) Failing after 3s
Release Docker Image / define-variables (push) Failing after 1s
Release Docker Image / build-image (linux/amd64, release, linux-amd64, base) (push) Has been skipped
Release Docker Image / build-image (linux/arm64, release, linux-arm64, base) (push) Has been skipped
Release Docker Image / merge (push) Has been skipped
Checks / Rust / Format (push) Failing after 3s
Checks / Rust / Clippy (push) Failing after 14s
Checks / Rust / Cargo Test (push) Failing after 15s
2025-06-29 23:02:15 +01:00
nexy7574
d4862b8ead style: Remove redundant, unused functions
Some checks failed
Documentation / Build and Deploy Documentation (push) Has been skipped
Checks / Prefligit / prefligit (push) Failing after 13s
Release Docker Image / define-variables (push) Failing after 5s
Release Docker Image / build-image (linux/amd64, release, linux-amd64, base) (push) Has been skipped
Release Docker Image / build-image (linux/arm64, release, linux-arm64, base) (push) Has been skipped
Release Docker Image / merge (push) Has been skipped
Checks / Rust / Format (push) Failing after 5s
Checks / Rust / Clippy (push) Failing after 11s
Checks / Rust / Cargo Test (push) Failing after 12s
2025-06-29 15:38:01 +00:00
Jade Ellis
acb74faa07 feat: Pass sender through admin commands 2025-06-29 15:38:01 +00:00
Jade Ellis
ecc6fda98b feat: Record metadata about user suspensions 2025-06-29 15:38:01 +00:00
nexy7574
13e17d52e0 style: Remove unnecessary imports (clippy) 2025-06-29 15:38:01 +00:00
nexy7574
d8a27eeb54 fix: Failing open on database errors
oops
2025-06-29 15:38:01 +00:00
nexy7574
eb2e3b3bb7 fix: Missing suspensions shouldn't error
Turns out copying and pasting the function
above verbatim actually introduces more
problems than it solves!
2025-06-29 15:38:01 +00:00
nexy7574
72f8cb3038 feat: Do not allow suspended users to send typing statuses 2025-06-29 15:38:01 +00:00
nexy7574
1124097bd1 feat: Only allow private read receipts when suspended 2025-06-29 15:38:01 +00:00
nexy7574
08527a2880 feat: Prevent suspended users upgrading rooms 2025-06-29 15:38:01 +00:00
nexy7574
8e06571e7c feat: Prevent suspended users uploading media 2025-06-29 15:38:01 +00:00
nexy7574
90180916eb feat: Prevent suspended users performing room changes
Prevents kicks, bans, unbans, and alias modification
2025-06-29 15:38:01 +00:00
nexy7574
d0548ec064 feat: Forbid suspended users from sending state events 2025-06-29 15:38:01 +00:00
nexy7574
1ff8af8e9e style: Remove unneeded statements (clippy) 2025-06-29 15:38:01 +00:00
nexy7574
cc864dc8bb feat: Do not allow suspending admin users 2025-06-29 15:38:01 +00:00
nexy7574
8791a9b851 fix: Inappropriate empty check
I once again, assumed `true` is actually `false`.
2025-06-29 15:38:01 +00:00
nexy7574
968c0e236c fix: Create the column appropriately 2025-06-29 15:38:01 +00:00
nexy7574
5d5350a9fe feat: Prevent suspended users creating new rooms 2025-06-29 15:38:01 +00:00
nexy7574
e127c4e5a2 feat: Add un/suspend admin commands 2025-06-29 15:38:01 +00:00
nexy7574
a94128e698 feat: Prevent suspended users joining/knocking on rooms 2025-06-29 15:38:01 +00:00
nexy7574
a6ba9e3045 feat: Prevent suspended users changing their profile 2025-06-29 15:38:01 +00:00
nexy7574
286974cb9a feat: Prevent suspended users redacting events 2025-06-29 15:38:01 +00:00
nexy7574
accfda2586 feat: Prevent suspended users sending events 2025-06-29 15:38:01 +00:00
nexy7574
fac9e090cd feat: Add suspension helper to user service 2025-06-29 15:38:01 +00:00
nexy7574
b4bdd1ee65
chore: Update ruwuma
Some checks failed
Documentation / Build and Deploy Documentation (push) Has been skipped
Checks / Prefligit / prefligit (push) Failing after 11s
Release Docker Image / define-variables (push) Failing after 3s
Release Docker Image / build-image (linux/amd64, release, linux-amd64, base) (push) Has been skipped
Release Docker Image / build-image (linux/arm64, release, linux-arm64, base) (push) Has been skipped
Release Docker Image / merge (push) Has been skipped
Checks / Rust / Format (push) Failing after 3s
Checks / Rust / Clippy (push) Failing after 20s
Checks / Rust / Cargo Test (push) Failing after 19s
Fixes the wrong field name being serialised
2025-06-29 13:43:27 +01:00
nexy7574
4b5e8df95c
fix: Add missing init fields 2025-06-29 13:29:27 +01:00
nexy7574
d63c8b9fca
feat: Support passing through MSC4293 redact_events 2025-06-29 13:16:31 +01:00
nexy7574
9b6ac6c45f fix: Ignore existing membership when room is disconnected
Some checks failed
Documentation / Build and Deploy Documentation (push) Has been skipped
Checks / Prefligit / prefligit (push) Failing after 7s
Release Docker Image / define-variables (push) Failing after 3s
Release Docker Image / build-image (linux/amd64, release, linux-amd64, base) (push) Has been skipped
Release Docker Image / build-image (linux/arm64, release, linux-arm64, base) (push) Has been skipped
Release Docker Image / merge (push) Has been skipped
Checks / Rust / Format (push) Failing after 7s
Checks / Rust / Clippy (push) Failing after 20s
Checks / Rust / Cargo Test (push) Failing after 16s
2025-06-29 12:14:20 +00:00
nexy7574
52e042cb06 Always calculate state diff IDs in syncv3
Some checks failed
Documentation / Build and Deploy Documentation (push) Has been skipped
Checks / Prefligit / prefligit (push) Failing after 13s
Release Docker Image / define-variables (push) Failing after 5s
Release Docker Image / build-image (linux/amd64, release, linux-amd64, base) (push) Has been skipped
Release Docker Image / build-image (linux/arm64, release, linux-arm64, base) (push) Has been skipped
Release Docker Image / merge (push) Has been skipped
Checks / Rust / Format (push) Failing after 5s
Checks / Rust / Clippy (push) Failing after 11s
Checks / Rust / Cargo Test (push) Failing after 11s
seemingly fixes #779
2025-06-28 20:37:40 +00:00
Jason Volk
f508e7654c fix: off by one.
Some checks failed
Documentation / Build and Deploy Documentation (push) Has been skipped
Checks / Prefligit / prefligit (push) Failing after 2s
Release Docker Image / define-variables (push) Failing after 1s
Release Docker Image / build-image (linux/amd64, release, linux-amd64, base) (push) Has been skipped
Release Docker Image / build-image (linux/arm64, release, linux-arm64, base) (push) Has been skipped
Release Docker Image / merge (push) Has been skipped
Checks / Rust / Format (push) Failing after 2s
Checks / Rust / Clippy (push) Failing after 18s
Checks / Rust / Cargo Test (push) Failing after 21s
2025-06-28 00:38:45 +00:00
nexy7574
543ab27747
fix: Additional sanity checks when creating a PDU
Some checks failed
Documentation / Build and Deploy Documentation (push) Has been skipped
Checks / Prefligit / prefligit (push) Failing after 2s
Release Docker Image / define-variables (push) Failing after 1s
Release Docker Image / build-image (linux/amd64, release, linux-amd64, base) (push) Has been skipped
Release Docker Image / build-image (linux/arm64, release, linux-arm64, base) (push) Has been skipped
Release Docker Image / merge (push) Has been skipped
Checks / Rust / Format (push) Failing after 4s
Checks / Rust / Clippy (push) Failing after 27s
Checks / Rust / Cargo Test (push) Failing after 31s
Prevents creating events that are most likely catastrophically invalid
2025-06-27 20:58:52 +01:00
Jade Ellis
c82ea24069
docs: Add Matrix chat and space badges to README
Some checks failed
Documentation / Build and Deploy Documentation (push) Has been skipped
Checks / Prefligit / prefligit (push) Failing after 22s
Checks / Rust / Format (push) Failing after 5s
Checks / Rust / Clippy (push) Failing after 21s
Checks / Rust / Cargo Test (push) Failing after 17s
2025-06-27 18:44:46 +01:00
Jacob Taylor
db58d841aa
fix: Only load children of nested spaces
Some checks failed
Documentation / Build and Deploy Documentation (push) Has been skipped
Checks / Prefligit / prefligit (push) Failing after 20s
Release Docker Image / define-variables (push) Failing after 3s
Release Docker Image / build-image (linux/amd64, release, linux-amd64, base) (push) Has been skipped
Release Docker Image / build-image (linux/arm64, release, linux-arm64, base) (push) Has been skipped
Release Docker Image / merge (push) Has been skipped
Checks / Rust / Format (push) Failing after 5s
Checks / Rust / Clippy (push) Failing after 18s
Checks / Rust / Cargo Test (push) Failing after 18s
2025-06-25 18:17:06 +01:00
Jade Ellis
f1ca84fcaf
fix: Correct project brand in admin & OTEL
Some checks failed
Documentation / Build and Deploy Documentation (push) Has been skipped
Checks / Prefligit / prefligit (push) Failing after 4s
Release Docker Image / define-variables (push) Failing after 1s
Release Docker Image / build-image (linux/amd64, release, linux-amd64, base) (push) Has been skipped
Release Docker Image / build-image (linux/arm64, release, linux-arm64, base) (push) Has been skipped
Release Docker Image / merge (push) Has been skipped
Checks / Rust / Format (push) Failing after 2s
Checks / Rust / Clippy (push) Failing after 10s
Checks / Rust / Cargo Test (push) Failing after 10s
2025-06-24 23:16:48 +01:00
Jade Ellis
63962fc040
docs: Remove completed items from the README 2025-06-24 23:13:28 +01:00
Jade Ellis
a24278dc1b
docs: Update mirror badges 2025-06-24 23:12:09 +01:00
Jade Ellis
b787e97dc1
chore: Document & enforce conventional commit messages
Some checks failed
Release Docker Image / build-image (linux/amd64, release, linux-amd64, base) (push) Has been skipped
Release Docker Image / build-image (linux/arm64, release, linux-arm64, base) (push) Has been skipped
Release Docker Image / merge (push) Has been skipped
Documentation / Build and Deploy Documentation (push) Has been skipped
Checks / Prefligit / prefligit (push) Failing after 20s
Release Docker Image / define-variables (push) Failing after 2s
Checks / Rust / Format (push) Failing after 17s
Checks / Rust / Clippy (push) Failing after 21s
Checks / Rust / Cargo Test (push) Failing after 29s
2025-06-24 22:43:44 +01:00
Jade Ellis
eb75c4ecb0
chore: Fix typos in commit messages automatically 2025-06-24 22:43:44 +01:00
Jade Ellis
9bbe333082
ci: Don't run docs flow when the secret is inaccessible 2025-06-24 22:43:43 +01:00
Jade Ellis
3177545a6f
chore: Remove clippy pre-commit hook
It's too slow for a good git experience
2025-06-24 21:45:54 +01:00
Kimiblock Moe
4a289a9fee arch systemd: use credentials to load config
Some checks failed
Documentation / Build and Deploy Documentation (push) Failing after 27s
Checks / Prefligit / prefligit (push) Failing after 3s
Release Docker Image / define-variables (push) Failing after 1s
Release Docker Image / build-image (linux/amd64, release, linux-amd64, base) (push) Has been skipped
Release Docker Image / build-image (linux/arm64, release, linux-arm64, base) (push) Has been skipped
Release Docker Image / merge (push) Has been skipped
Checks / Rust / Format (push) Failing after 4s
Checks / Rust / Clippy (push) Failing after 28s
Checks / Rust / Cargo Test (push) Failing after 30s
2025-06-24 11:07:11 +00:00
Jade Ellis
4d69a1ad51
docs: Deduplicate sections
Some checks failed
Checks / Prefligit / prefligit (push) Failing after 2s
Documentation / Build and Deploy Documentation (push) Failing after 9s
Checks / Rust / Format (push) Failing after 2s
Checks / Rust / Clippy (push) Failing after 12s
Checks / Rust / Cargo Test (push) Failing after 12s
2025-06-23 01:25:38 +01:00
Jade Ellis
4f174324ba
docs: Update contributing guide 2025-06-23 01:04:27 +01:00
Jade Ellis
2ecbd75d64
ci: fixes
Some checks failed
Release Docker Image / build-image (linux/amd64, release, linux-amd64, base) (push) Has been skipped
Release Docker Image / build-image (linux/arm64, release, linux-arm64, base) (push) Has been skipped
Release Docker Image / merge (push) Has been skipped
Documentation / Build and Deploy Documentation (push) Failing after 11s
Checks / Prefligit / prefligit (push) Failing after 2s
Release Docker Image / define-variables (push) Failing after 1s
Checks / Rust / Format (push) Failing after 3s
Checks / Rust / Clippy (push) Failing after 11s
Checks / Rust / Cargo Test (push) Failing after 11s
- Install UV
- Verbose run
- Set permissions explicitly
- Check all files
2025-06-21 19:17:21 +01:00
Jade Ellis
a682e9dbb8
chore: Add commit to ignored revs 2025-06-21 18:03:38 +01:00
Jade Ellis
46c193e74b
chore: fix end of files & trailing whitespace 2025-06-21 17:59:01 +01:00
Jade Ellis
93719018a8
ci: Run additional sanity checks on repository 2025-06-21 17:58:28 +01:00
Jade Ellis
70df8364b3
chore: Bump rustyline-async from 0.4.3 to 0.4.6
Some checks failed
Release Docker Image / define-variables (push) Failing after 1s
Release Docker Image / build-image (linux/amd64, release, linux-amd64, base) (push) Has been skipped
Release Docker Image / build-image (linux/arm64, release, linux-arm64, base) (push) Has been skipped
Release Docker Image / merge (push) Has been skipped
Documentation / Build and Deploy Documentation (push) Failing after 8s
Rust Checks / Format (push) Failing after 3s
Rust Checks / Clippy (push) Failing after 12s
Rust Checks / Cargo Test (push) Failing after 12s
2025-06-21 00:50:02 +01:00
Jade Ellis
bae8192fb3
chore: Bump resolv-conf from 0.7.1 to 0.7.4 2025-06-20 23:39:20 +01:00
Jade Ellis
add5c7052c
chore: Update lockfile
Some checks failed
Release Docker Image / define-variables (push) Failing after 3s
Release Docker Image / build-image (linux/amd64, release, linux-amd64, base) (push) Has been skipped
Release Docker Image / build-image (linux/arm64, release, linux-arm64, base) (push) Has been skipped
Release Docker Image / merge (push) Has been skipped
Documentation / Build and Deploy Documentation (push) Failing after 32s
Rust Checks / Format (push) Failing after 4s
Rust Checks / Clippy (push) Failing after 13s
Rust Checks / Cargo Test (push) Failing after 11s
2025-06-20 21:51:53 +01:00
Jade Ellis
01200d9b54
build: Allow specifying build profile
Additionally splits caches by target CPU
2025-06-20 21:48:37 +01:00
Jade Ellis
0ba4a265be
build: Upgrade to Rust 1.87 2025-06-20 21:45:29 +01:00
Jade Ellis
08fbcbba69
build: Use newer LLVM for rust 1.87 2025-06-20 21:35:48 +01:00
Jade Ellis
b526935d45
build: Specify debian version 2025-06-20 21:35:03 +01:00
Jade Ellis
a737d845a4
chore: Don't specify targets in rust-toolchain 2025-06-20 21:25:34 +01:00
nex
e508b1197f feat: allow overriding the "most recent event" when forcing a state download (#853)
Some checks failed
Documentation / Build and Deploy Documentation (push) Failing after 28s
Release Docker Image / define-variables (push) Failing after 2s
Release Docker Image / build-image (linux/amd64, linux-amd64) (push) Has been skipped
Release Docker Image / build-image (linux/arm64, linux-arm64) (push) Has been skipped
Release Docker Image / merge (push) Has been skipped
Rust Checks / Format (push) Failing after 1s
Rust Checks / Clippy (push) Failing after 11s
Rust Checks / Cargo Test (push) Failing after 10s
Add option to select which event to set the state at to, for the force-set-room-state admin command.

This allows us to work around issues where the latest PDU is one that remote servers don't know about (i.e. failed federation for whatever reason)

Closes #852

Reviewed-on: https://forgejo.ellis.link/continuwuation/continuwuity/pulls/853
Reviewed-by: Jade Ellis <jade@ellis.link>
Co-authored-by: nex <nex@noreply.forgejo.ellis.link>
Co-committed-by: nex <nex@noreply.forgejo.ellis.link>
2025-06-19 21:27:50 +00:00
Kimiblock
d6fd30393c Update docs/deploying/arch-linux.md 2025-06-19 12:36:49 +00:00
52 changed files with 655 additions and 225 deletions

View file

@ -23,6 +23,6 @@ indent_size = 2
indent_style = tab
max_line_length = 98
[{.forgejo/**/*.yml,.github/**/*.yml}]
[*.yml]
indent_size = 2
indent_style = space

View file

@ -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

View file

@ -17,6 +17,7 @@ jobs:
docs:
name: Build and Deploy Documentation
runs-on: ubuntu-latest
if: secrets.CLOUDFLARE_API_TOKEN != ''
steps:
- name: Sync repository

View file

@ -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

View file

@ -49,6 +49,7 @@ jobs:
const platforms = ['linux/amd64', 'linux/arm64']
core.setOutput('build_matrix', JSON.stringify({
platform: platforms,
target_cpu: ['base'],
include: platforms.map(platform => { return {
platform,
slug: platform.replace('/', '-')
@ -66,6 +67,8 @@ jobs:
strategy:
matrix:
{
"target_cpu": ["base"],
"profile": ["release"],
"include":
[
{ "platform": "linux/amd64", "slug": "linux-amd64" },
@ -73,6 +76,7 @@ jobs:
],
"platform": ["linux/amd64", "linux/arm64"],
}
steps:
- name: Echo strategy
run: echo '${{ toJSON(fromJSON(needs.define-variables.outputs.build_matrix)) }}'
@ -140,8 +144,8 @@ jobs:
uses: actions/cache@v3
with:
path: |
cargo-target-${{ matrix.slug }}
key: cargo-target-${{ matrix.slug }}-${{hashFiles('**/Cargo.lock') }}-${{steps.rust-toolchain.outputs.rustc_version}}
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
@ -163,9 +167,9 @@ jobs:
{
".cargo/registry": "/usr/local/cargo/registry",
".cargo/git/db": "/usr/local/cargo/git/db",
"cargo-target-${{ matrix.slug }}": {
"cargo-target-${{ matrix.target_cpu }}-${{ matrix.slug }}-${{ matrix.profile }}": {
"target": "/app/target",
"id": "cargo-target-${{ matrix.platform }}"
"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"

View file

@ -1,4 +1,4 @@
name: Rust Checks
name: Checks / Rust
on:
push:

View file

@ -5,3 +5,5 @@ f419c64aca300a338096b4e0db4c73ace54f23d0
# use chain_width 60
162948313c212193965dece50b816ef0903172ba
5998a0d883d31b866f7c8c46433a8857eae51a89
# trailing whitespace and newlines
46c193e74b2ce86c48ce802333a0aabce37fd6e9

5
.github/FUNDING.yml vendored Normal file
View file

@ -0,0 +1,5 @@
github: [JadedBlueEyes]
# Doesn't support an array, so we can only list nex
ko_fi: nexy7574
custom:
- https://ko-fi.com/JadedBlueEyes

47
.pre-commit-config.yaml Normal file
View file

@ -0,0 +1,47 @@
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
- id: typos
name: commit-msg-typos
stages: [commit-msg]
- repo: https://github.com/crate-ci/committed
rev: v1.1.7
hooks:
- id: committed
- repo: local
hooks:
- id: cargo-fmt
name: cargo fmt
entry: cargo +nightly fmt --
language: system
types: [rust]
pass_filenames: false
stages:
- pre-commit

View file

@ -1,6 +1,6 @@
# Contributing guide
This page is for about contributing to Continuwuity. The
This page is 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
@ -10,7 +10,7 @@ and comment on it.
### Linting and Formatting
It is mandatory all your changes satisfy the lints (clippy, rustc, rustdoc, etc)
and your code is formatted via the **nightly** `cargo fmt`. A lot of the
and your code is formatted via the **nightly** rustfmt (`cargo +nightly fmt`). A lot of the
`rustfmt.toml` features depend on nightly toolchain. It would be ideal if they
weren't nightly-exclusive features, but they currently still are. CI's rustfmt
uses nightly.
@ -21,67 +21,91 @@ comment saying why. Do not write inefficient code for the sake of satisfying
lints. If a lint is wrong and provides a more inefficient solution or
suggestion, allow the lint and mention that in a comment.
### Running CI tests locally
### Pre-commit Checks
continuwuity's CI for tests, linting, formatting, audit, etc use
[`engage`][engage]. engage can be installed from nixpkgs or `cargo install
engage`. continuwuity's Nix flake devshell has the nixpkgs engage with `direnv`.
Use `engage --help` for more usage details.
Continuwuity uses pre-commit hooks to enforce various coding standards and catch common issues before they're committed. These checks include:
To test, format, lint, etc that CI would do, install engage, allow the `.envrc`
file using `direnv allow`, and run `engage`.
- Code formatting and linting
- Typo detection (both in code and commit messages)
- Checking for large files
- Ensuring proper line endings and no trailing whitespace
- Validating YAML, JSON, and TOML files
- Checking for merge conflicts
All of the tasks are defined at the [engage.toml][engage.toml] file. You can
view all of them neatly by running `engage list`
You can run these checks locally by installing [prefligit](https://github.com/j178/prefligit):
If you would like to run only a specific engage task group, use `just`:
- `engage just <group>`
- Example: `engage just lints`
```bash
# Install prefligit using cargo-binstall
cargo binstall prefligit
If you would like to run a specific engage task in a specific group, use `just
<GROUP> [TASK]`: `engage just lints cargo-fmt`
# Install git hooks to run checks automatically
prefligit install
The following binaries are used in [`engage.toml`][engage.toml]:
# Run all checks
prefligit --all-files
```
- [`engage`][engage]
- `nix`
- [`direnv`][direnv]
- `rustc`
- `cargo`
- `cargo-fmt`
- `rustdoc`
- `cargo-clippy`
- [`cargo-audit`][cargo-audit]
- [`cargo-deb`][cargo-deb]
- [`lychee`][lychee]
- [`markdownlint-cli`][markdownlint-cli]
- `dpkg`
Alternatively, you can use [pre-commit](https://pre-commit.com/):
```bash
# Install pre-commit
pip install pre-commit
# Install the hooks
pre-commit install
# Run all checks manually
pre-commit run --all-files
```
These same checks are run in CI via the prefligit-checks workflow to ensure consistency.
### Running tests locally
Tests, compilation, and linting can be run with standard Cargo commands:
```bash
# Run tests
cargo test
# Check compilation
cargo check --workspace
# Run lints
cargo clippy --workspace
# Auto-fix: cargo clippy --workspace --fix --allow-staged;
# Format code (must use nightly)
cargo +nightly fmt
```
### Matrix tests
CI runs [Complement][complement], but currently does not fail if results from
the checked-in results differ with the new results. If your changes are done to
fix Matrix tests, note that in your pull request. If more Complement tests start
failing from your changes, please review the logs (they are uploaded as
artifacts) and determine if they're intended or not.
Continuwuity uses [Complement][complement] for Matrix protocol compliance testing. Complement tests are run manually by developers, and documentation on how to run these tests locally is currently being developed.
If you'd like to run Complement locally using Nix, see the
[testing](development/testing.md) page.
If your changes are done to fix Matrix tests, please note that in your pull request. If more Complement tests start failing from your changes, please review the logs and determine if they're intended or not.
[Sytest][sytest] support will come soon.
[Sytest][sytest] is currently unsupported.
### Writing documentation
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.
Continuwuity's website uses [`mdbook`][mdbook] and is deployed via CI using Cloudflare Pages
in the [`documentation.yml`][documentation.yml] workflow file. All documentation is in the `docs/`
directory at the top level.
To build the documentation using Nix, run: `bin/nix-build-and-cache just .#book`
To build the documentation locally:
The output of the mdbook generation is in `result/`. mdbooks can be opened in
your browser from the individual HTML files without any web server needed.
1. Install mdbook if you don't have it already:
```bash
cargo install mdbook # or cargo binstall, or another method
```
2. Build the documentation:
```bash
mdbook build
```
The output of the mdbook generation is in `public/`. You can open the HTML files directly in your browser without needing a web server.
### Inclusivity and Diversity
@ -109,6 +133,40 @@ Rust's default style and standards with regards to [function names, variable
names, comments](https://rust-lang.github.io/api-guidelines/naming.html), etc
applies here.
### Commit Messages
Continuwuity follows the [Conventional Commits](https://www.conventionalcommits.org/) specification for commit messages. This provides a standardized format that makes the commit history more readable and enables automated tools to generate changelogs.
The basic structure is:
```
<type>[(optional scope)]: <description>
[optional body]
[optional footer(s)]
```
The allowed types for commits are:
- `fix`: Bug fixes
- `feat`: New features
- `docs`: Documentation changes
- `style`: Changes that don't affect the meaning of the code (formatting, etc.)
- `refactor`: Code changes that neither fix bugs nor add features
- `perf`: Performance improvements
- `test`: Adding or fixing tests
- `build`: Changes to the build system or dependencies
- `ci`: Changes to CI configuration
- `chore`: Other changes that don't modify source or test files
Examples:
```
feat: add user authentication
fix(database): resolve connection pooling issue
docs: update installation instructions
```
The project uses the `committed` hook to validate commit messages in pre-commit. This ensures all commits follow the conventional format.
### Creating pull requests
Please try to keep contributions to the Forgejo Instance. While the mirrors of continuwuity
@ -118,6 +176,13 @@ 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.
Before submitting a pull request, please ensure:
1. Your code passes all CI checks (formatting, linting, typo detection, etc.)
2. Your commit messages follow the conventional commits format
3. Tests are added for new functionality
4. Documentation is updated if needed
Direct all PRs/MRs to the `main` branch.
@ -125,20 +190,13 @@ 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 continuwuity's Code of Conduct.
Contribution by users who violate either of these code of conducts will not have
Contribution by users who violate either of these code of conducts may not have
their contributions accepted. This includes users who have been banned from
continuwuityMatrix rooms for Code of Conduct violations.
continuwuity Matrix rooms for Code of Conduct violations.
[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://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
[lychee]: https://github.com/lycheeverse/lychee
[markdownlint-cli]: https://github.com/igorshubovych/markdownlint-cli
[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://forgejo.ellis.link/continuwuation/continuwuity/src/branch/main/.forgejo/workflows/documentation.yml

View file

@ -4,6 +4,10 @@
## A community-driven [Matrix](https://matrix.org/) homeserver in Rust
[![Chat on Matrix](https://img.shields.io/matrix/continuwuity%3Acontinuwuity.org?server_fqdn=matrix.continuwuity.org&fetchMode=summary&logo=matrix)](https://matrix.to/#/#continuwuity:continuwuity.org?via=continuwuity.org&via=ellis.link&via=explodie.org&via=matrix.org) [![Join the space](https://img.shields.io/matrix/space%3Acontinuwuity.org?server_fqdn=matrix.continuwuity.org&fetchMode=summary&logo=matrix&label=space)](https://matrix.to/#/#space:continuwuity.org?via=continuwuity.org&via=ellis.link&via=explodie.org&via=matrix.org)
<!-- ANCHOR_END: catchphrase -->
[continuwuity] is a Matrix homeserver written in Rust.
@ -11,11 +15,13 @@ It's a community continuation of the [conduwuit](https://github.com/girlbossceo/
<!-- ANCHOR: body -->
[![forgejo.ellis.link](https://img.shields.io/badge/Ellis%20Git-main+packages-green?style=flat&logo=forgejo&labelColor=fff)](https://forgejo.ellis.link/continuwuation/continuwuity) ![](https://forgejo.ellis.link/continuwuation/continuwuity/badges/stars.svg?style=flat) [![](https://forgejo.ellis.link/continuwuation/continuwuity/badges/issues/open.svg?style=flat)](https://forgejo.ellis.link/continuwuation/continuwuity/issues?state=open) [![](https://forgejo.ellis.link/continuwuation/continuwuity/badges/pulls/open.svg?style=flat)](https://forgejo.ellis.link/continuwuation/continuwuity/pulls?state=open)
[![forgejo.ellis.link](https://img.shields.io/badge/Ellis%20Git-main+packages-green?style=flat&logo=forgejo&labelColor=fff)](https://forgejo.ellis.link/continuwuation/continuwuity) [![Stars](https://forgejo.ellis.link/continuwuation/continuwuity/badges/stars.svg?style=flat)](https://forgejo.ellis.link/continuwuation/continuwuity/stars) [![Issues](https://forgejo.ellis.link/continuwuation/continuwuity/badges/issues/open.svg?style=flat)](https://forgejo.ellis.link/continuwuation/continuwuity/issues?state=open) [![Pull Requests](https://forgejo.ellis.link/continuwuation/continuwuity/badges/pulls/open.svg?style=flat)](https://forgejo.ellis.link/continuwuation/continuwuity/pulls?state=open)
[![GitHub](https://img.shields.io/badge/GitHub-mirror-blue?style=flat&logo=github&labelColor=fff&logoColor=24292f)](https://github.com/continuwuity/continuwuity) ![](https://img.shields.io/github/stars/continuwuity/continuwuity?style=flat)
[![GitHub](https://img.shields.io/badge/GitHub-mirror-blue?style=flat&logo=github&labelColor=fff&logoColor=24292f)](https://github.com/continuwuity/continuwuity) [![Stars](https://img.shields.io/github/stars/continuwuity/continuwuity?style=flat)](https://github.com/continuwuity/continuwuity/stargazers)
[![Codeberg](https://img.shields.io/badge/Codeberg-mirror-2185D0?style=flat&logo=codeberg&labelColor=fff)](https://codeberg.org/nexy7574/continuwuity) ![](https://codeberg.org/nexy7574/continuwuity/badges/stars.svg?style=flat)
[![GitLab](https://img.shields.io/badge/GitLab-mirror-blue?style=flat&logo=gitlab&labelColor=fff)](https://gitlab.com/continuwuity/continuwuity) [![Stars](https://img.shields.io/gitlab/stars/continuwuity/continuwuity?style=flat)](https://gitlab.com/continuwuity/continuwuity/-/starrers)
[![Codeberg](https://img.shields.io/badge/Codeberg-mirror-2185D0?style=flat&logo=codeberg&labelColor=fff)](https://codeberg.org/continuwuity/continuwuity) [![Stars](https://codeberg.org/continuwuity/continuwuity/badges/stars.svg?style=flat)](https://codeberg.org/continuwuity/continuwuity/stars)
### Why does this exist?
@ -59,8 +65,6 @@ There are currently no open registration Continuwuity instances available.
We're working our way through all of the issues in the [Forgejo project](https://forgejo.ellis.link/continuwuation/continuwuity/issues).
- [Replacing old conduwuit links with working continuwuity links](https://forgejo.ellis.link/continuwuation/continuwuity/issues/742)
- [Getting CI and docs deployment working on the new Forgejo project](https://forgejo.ellis.link/continuwuation/continuwuity/issues/740)
- [Packaging & availability in more places](https://forgejo.ellis.link/continuwuation/continuwuity/issues/747)
- [Appservices bugs & features](https://forgejo.ellis.link/continuwuation/continuwuity/issues?q=&type=all&state=open&labels=178&milestone=0&assignee=0&poster=0)
- [Improving compatibility and spec compliance](https://forgejo.ellis.link/continuwuation/continuwuity/issues?labels=119)

View file

@ -6,6 +6,7 @@ After=network-online.target
Documentation=https://continuwuity.org/
RequiresMountsFor=/var/lib/private/conduwuit
Alias=matrix-conduwuit.service
[Service]
DynamicUser=yes
Type=notify-reload
@ -59,7 +60,8 @@ StateDirectory=conduwuit
RuntimeDirectory=conduwuit
RuntimeDirectoryMode=0750
Environment="CONTINUWUITY_CONFIG=/etc/conduwuit/conduwuit.toml"
Environment=CONTINUWUITY_CONFIG=${CREDENTIALS_DIRECTORY}/config.toml
LoadCredential=config.toml:/etc/conduwuit/conduwuit.toml
BindPaths=/var/lib/private/conduwuit:/var/lib/matrix-conduit
BindPaths=/var/lib/private/conduwuit:/var/lib/private/matrix-conduit

2
committed.toml Normal file
View file

@ -0,0 +1,2 @@
style = "conventional"
allowed_types = ["ci", "build", "fix", "feat", "chore", "docs", "style", "refactor", "perf", "test"]

View file

@ -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
@ -19,10 +20,18 @@ ARG LLVM_VERSION=19
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 \
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
@ -39,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
@ -140,11 +149,12 @@ 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,id=cargo-target-${TARGETPLATFORM} \
--mount=type=cache,target=/app/target,id=cargo-target-${TARGET_CPU}-${TARGETPLATFORM}-${RUST_PROFILE} \
bash <<'EOF'
set -o allexport
set -o xtrace
@ -153,7 +163,7 @@ RUN --mount=type=cache,target=/usr/local/cargo/registry \
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"))

View file

@ -1,3 +1,5 @@
# Continuwuity for Arch Linux
Continuwuity does not have any Arch Linux packages at this time.
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`
Simply install the `continuwuity` package. Configure the service in `/etc/conduwuit/conduwuit.toml`, then enable/start the continuwuity.service.

View file

@ -34,4 +34,3 @@ services:
# - "traefik.http.routers.to-element-web.tls.certresolver=letsencrypt"
# vim: ts=2:sw=2:expandtab

View file

@ -68,31 +68,22 @@ do this if Rust supported workspace-level features to begin with.
## List of forked dependencies
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), Continuwuity-specific usecases, or
lack of time to upstream some things.
During Continuwuity (and prior projects) development, we have had to fork some dependencies to support our use-cases.
These forks exist for various reasons including features that upstream projects won't accept,
faster-paced development, Continuwuity-specific usecases, or lack of time to upstream changes.
- [ruma/ruma][1]: <https://github.com/girlbossceo/ruwuma> - various performance
improvements, more features, faster-paced development, better client/server interop
hacks upstream won't accept, etc
- [facebook/rocksdb][2]: <https://github.com/girlbossceo/rocksdb> - liburing
build fixes and GCC debug build fix
- [tikv/jemallocator][3]: <https://github.com/girlbossceo/jemallocator> - musl
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 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
issues, removes unnecessary `gtest` include, and uses our RocksDB and jemallocator
forks.
- [tokio-rs/tracing][6]: <https://github.com/girlbossceo/tracing> - Implements
`Clone` for `EnvFilter` to support dynamically changing tracing envfilter's
alongside other logging/metrics things
All forked dependencies are maintained under the [continuwuation organization on Forgejo](https://forgejo.ellis.link/continuwuation):
- [ruwuma][continuwuation-ruwuma] - Fork of [ruma/ruma][ruma] with various performance improvements, more features and better client/server interop
- [rocksdb][continuwuation-rocksdb] - Fork of [facebook/rocksdb][rocksdb] via [`@zaidoon1`][8] with liburing build fixes and GCC debug build fixes
- [jemallocator][continuwuation-jemallocator] - Fork of [tikv/jemallocator][jemallocator] fixing musl builds, suspicious code,
and adding support for redzones in Valgrind
- [rustyline-async][continuwuation-rustyline-async] - Fork of [zyansheep/rustyline-async][rustyline-async] with tab completion callback
and `CTRL+\` signal quit event for Continuwuity console CLI
- [rust-rocksdb][continuwuation-rust-rocksdb] - Fork of [rust-rocksdb/rust-rocksdb][rust-rocksdb] fixing musl build issues,
removing unnecessary `gtest` include, and using our RocksDB and jemallocator forks
- [tracing][continuwuation-tracing] - Fork of [tokio-rs/tracing][tracing] implementing `Clone` for `EnvFilter` to
support dynamically changing tracing environments
## Debugging with `tokio-console`
@ -113,12 +104,30 @@ You will also need to enable the `tokio_console` config option in Continuwuity w
starting it. This was due to tokio-console causing gradual memory leak/usage
if left enabled.
[1]: https://github.com/ruma/ruma/
[2]: https://github.com/facebook/rocksdb/
[3]: https://github.com/tikv/jemallocator/
[4]: https://github.com/zyansheep/rustyline-async/
[5]: https://github.com/rust-rocksdb/rust-rocksdb/
[6]: https://github.com/tokio-rs/tracing/
## Building Docker Images
To build a Docker image for Continuwuity, use the standard Docker build command:
```bash
docker build -f docker/Dockerfile .
```
The image can be cross-compiled for different architectures.
[continuwuation-ruwuma]: https://forgejo.ellis.link/continuwuation/ruwuma
[continuwuation-rocksdb]: https://forgejo.ellis.link/continuwuation/rocksdb
[continuwuation-jemallocator]: https://forgejo.ellis.link/continuwuation/jemallocator
[continuwuation-rustyline-async]: https://forgejo.ellis.link/continuwuation/rustyline-async
[continuwuation-rust-rocksdb]: https://forgejo.ellis.link/continuwuation/rust-rocksdb
[continuwuation-tracing]: https://forgejo.ellis.link/continuwuation/tracing
[ruma]: https://github.com/ruma/ruma/
[rocksdb]: https://github.com/facebook/rocksdb/
[jemallocator]: https://github.com/tikv/jemallocator/
[rustyline-async]: https://github.com/zyansheep/rustyline-async/
[rust-rocksdb]: https://github.com/rust-rocksdb/rust-rocksdb/
[tracing]: https://github.com/tokio-rs/tracing/
[7]: https://docs.rs/tokio-console/latest/tokio_console/
[8]: https://github.com/zaidoon1/
[9]: https://github.com/rust-lang/cargo/issues/12162

View file

@ -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",
]

View file

@ -9,7 +9,7 @@ use crate::{
};
#[derive(Debug, Parser)]
#[command(name = "conduwuit", version = conduwuit::version())]
#[command(name = conduwuit_core::name(), version = conduwuit_core::version())]
pub(super) enum AdminCommand {
#[command(subcommand)]
/// - Commands for managing appservices

View file

@ -7,13 +7,14 @@ use futures::{
io::{AsyncWriteExt, BufWriter},
lock::Mutex,
};
use ruma::EventId;
use ruma::{EventId, UserId};
pub(crate) struct Context<'a> {
pub(crate) services: &'a Services,
pub(crate) body: &'a [&'a str],
pub(crate) timer: SystemTime,
pub(crate) reply_id: Option<&'a EventId>,
pub(crate) sender: Option<&'a UserId>,
pub(crate) output: Mutex<BufWriter<Vec<u8>>>,
}
@ -36,4 +37,10 @@ impl Context<'_> {
output.write_all(s.as_bytes()).map_err(Into::into).await
})
}
/// Get the sender as a string, or service user ID if not available
pub(crate) fn sender_or_service_user(&self) -> &UserId {
self.sender
.unwrap_or_else(|| self.services.globals.server_user.as_ref())
}
}

View file

@ -239,10 +239,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| {
@ -384,8 +385,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}");
@ -408,8 +410,9 @@ pub(super) async fn change_log_level(&self, filter: Option<String>, reset: bool)
.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}"),
| Err(e) => {
return Err!("Failed to modify and reload the global tracing log level: {e}");
},
}
}
@ -529,6 +532,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 +544,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
.clone(),
};
let room_version = self.services.rooms.state.get_room_version(&room_id).await?;
@ -557,7 +566,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?;

View file

@ -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

View file

@ -63,6 +63,7 @@ async fn process_command(services: Arc<Services>, input: &CommandInput) -> Proce
body: &body,
timer: SystemTime::now(),
reply_id: input.reply_id.as_deref(),
sender: input.sender.as_deref(),
output: BufWriter::new(Vec::new()).into(),
};

View file

@ -224,6 +224,47 @@ pub(super) async fn deactivate(&self, no_leave_rooms: bool, user_id: String) ->
.await
}
#[admin_command]
pub(super) async fn suspend(&self, user_id: String) -> Result {
let user_id = parse_local_user_id(self.services, &user_id)?;
if user_id == self.services.globals.server_user {
return Err!("Not allowed to suspend the server service account.",);
}
if !self.services.users.exists(&user_id).await {
return Err!("User {user_id} does not exist.");
}
if self.services.users.is_admin(&user_id).await {
return Err!("Admin users cannot be suspended.");
}
// TODO: Record the actual user that sent the suspension where possible
self.services
.users
.suspend_account(&user_id, self.sender_or_service_user())
.await;
self.write_str(&format!("User {user_id} has been suspended."))
.await
}
#[admin_command]
pub(super) async fn unsuspend(&self, user_id: String) -> Result {
let user_id = parse_local_user_id(self.services, &user_id)?;
if user_id == self.services.globals.server_user {
return Err!("Not allowed to unsuspend the server service account.",);
}
if !self.services.users.exists(&user_id).await {
return Err!("User {user_id} does not exist.");
}
self.services.users.unsuspend_account(&user_id).await;
self.write_str(&format!("User {user_id} has been unsuspended."))
.await
}
#[admin_command]
pub(super) async fn reset_password(&self, username: String, password: Option<String>) -> Result {
let user_id = parse_local_user_id(self.services, &username)?;

View file

@ -59,6 +59,28 @@ pub(super) enum UserCommand {
force: bool,
},
/// - Suspend a user
///
/// Suspended users are able to log in, sync, and read messages, but are not
/// able to send events nor redact them, cannot change their profile, and
/// are unable to join, invite to, or knock on rooms.
///
/// Suspended users can still leave rooms and deactivate their account.
/// Suspending them effectively makes them read-only.
Suspend {
/// Username of the user to suspend
user_id: String,
},
/// - Unsuspend a user
///
/// Reverses the effects of the `suspend` command, allowing the user to send
/// messages, change their profile, create room invites, etc.
Unsuspend {
/// Username of the user to unsuspend
user_id: String,
},
/// - List local users in the database
#[clap(alias = "list")]
ListUsers,

View file

@ -18,6 +18,9 @@ pub(crate) async fn create_alias_route(
body: Ruma<create_alias::v3::Request>,
) -> Result<create_alias::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
if services.users.is_suspended(sender_user).await? {
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
}
services
.rooms
@ -63,6 +66,9 @@ pub(crate) async fn delete_alias_route(
body: Ruma<delete_alias::v3::Request>,
) -> Result<delete_alias::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
if services.users.is_suspended(sender_user).await? {
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
}
services
.rooms

View file

@ -128,6 +128,9 @@ pub(crate) async fn set_room_visibility_route(
// Return 404 if the room doesn't exist
return Err!(Request(NotFound("Room not found")));
}
if services.users.is_suspended(sender_user).await? {
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
}
if services
.users

View file

@ -52,6 +52,9 @@ pub(crate) async fn create_content_route(
body: Ruma<create_content::v3::Request>,
) -> Result<create_content::v3::Response> {
let user = body.sender_user.as_ref().expect("user is authenticated");
if services.users.is_suspended(user).await? {
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
}
let filename = body.filename.as_deref();
let content_type = body.content_type.as_deref();

View file

@ -178,6 +178,9 @@ pub(crate) async fn join_room_by_id_route(
body: Ruma<join_room_by_id::v3::Request>,
) -> Result<join_room_by_id::v3::Response> {
let sender_user = body.sender_user();
if services.users.is_suspended(sender_user).await? {
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
}
banned_room_check(
&services,
@ -249,6 +252,9 @@ pub(crate) async fn join_room_by_id_or_alias_route(
let sender_user = body.sender_user.as_deref().expect("user is authenticated");
let appservice_info = &body.appservice_info;
let body = body.body;
if services.users.is_suspended(sender_user).await? {
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
}
let (servers, room_id) = match OwnedRoomId::try_from(body.room_id_or_alias) {
| Ok(room_id) => {
@ -369,6 +375,9 @@ pub(crate) async fn knock_room_route(
) -> Result<knock_room::v3::Response> {
let sender_user = body.sender_user();
let body = &body.body;
if services.users.is_suspended(sender_user).await? {
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
}
let (servers, room_id) = match OwnedRoomId::try_from(body.room_id_or_alias.clone()) {
| Ok(room_id) => {
@ -492,6 +501,9 @@ pub(crate) async fn invite_user_route(
body: Ruma<invite_user::v3::Request>,
) -> Result<invite_user::v3::Response> {
let sender_user = body.sender_user();
if services.users.is_suspended(sender_user).await? {
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
}
if !services.users.is_admin(sender_user).await && services.config.block_non_admin_invites {
debug_error!(
@ -566,6 +578,10 @@ pub(crate) async fn kick_user_route(
State(services): State<crate::State>,
body: Ruma<kick_user::v3::Request>,
) -> Result<kick_user::v3::Response> {
let sender_user = body.sender_user();
if services.users.is_suspended(sender_user).await? {
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
}
let state_lock = services.rooms.state.mutex.lock(&body.room_id).await;
let Ok(event) = services
@ -601,7 +617,7 @@ pub(crate) async fn kick_user_route(
third_party_invite: None,
..event
}),
body.sender_user(),
sender_user,
&body.room_id,
&state_lock,
)
@ -625,6 +641,10 @@ pub(crate) async fn ban_user_route(
return Err!(Request(Forbidden("You cannot ban yourself.")));
}
if services.users.is_suspended(sender_user).await? {
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
}
let state_lock = services.rooms.state.mutex.lock(&body.room_id).await;
let current_member_content = services
@ -646,6 +666,7 @@ pub(crate) async fn ban_user_route(
is_direct: None,
join_authorized_via_users_server: None,
third_party_invite: None,
redact_events: body.redact_events,
..current_member_content
}),
sender_user,
@ -666,6 +687,10 @@ pub(crate) async fn unban_user_route(
State(services): State<crate::State>,
body: Ruma<unban_user::v3::Request>,
) -> Result<unban_user::v3::Response> {
let sender_user = body.sender_user();
if services.users.is_suspended(sender_user).await? {
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
}
let state_lock = services.rooms.state.mutex.lock(&body.room_id).await;
let current_member_content = services
@ -694,7 +719,7 @@ pub(crate) async fn unban_user_route(
is_direct: None,
..current_member_content
}),
body.sender_user(),
sender_user,
&body.room_id,
&state_lock,
)
@ -925,24 +950,32 @@ pub async fn join_room_by_id_helper(
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;
// Only check our known membership if we're already in the room.
// See: https://forgejo.ellis.link/continuwuation/continuwuity/issues/855
let membership = if server_in_room {
services
.rooms
.state_accessor
.get_member(room_id, sender_user)
.await
} else {
debug!("Ignoring local state for join {room_id}, we aren't in the room yet.");
Ok(RoomMemberEventContent::new(MembershipState::Leave))
};
if let Ok(m) = membership {
if m.membership == MembershipState::Ban {
debug_warn!("{sender_user} is banned from {room_id} but attempted to join");
// TODO: return reason
return Err!(Request(Forbidden("You are banned from the room.")));
}
}
let local_join = server_in_room
|| servers.is_empty()
|| (servers.len() == 1 && services.globals.server_is_ours(&servers[0]));
@ -1815,6 +1848,7 @@ pub async fn leave_room(
displayname: None,
third_party_invite: None,
blurhash: None,
redact_events: None,
};
let is_banned = services.rooms.metadata.is_banned(room_id);

View file

@ -36,6 +36,9 @@ pub(crate) async fn set_displayname_route(
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");
if services.users.is_suspended(sender_user).await? {
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
}
if *sender_user != body.user_id && body.appservice_info.is_none() {
return Err!(Request(Forbidden("You cannot update the profile of another user")));
@ -125,6 +128,9 @@ pub(crate) async fn set_avatar_url_route(
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");
if services.users.is_suspended(sender_user).await? {
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
}
if *sender_user != body.user_id && body.appservice_info.is_none() {
return Err!(Request(Forbidden("You cannot update the profile of another user")));
@ -343,6 +349,7 @@ pub async fn update_displayname(
reason: None,
is_direct: None,
third_party_invite: None,
redact_events: None,
});
Ok((pdu, room_id))
@ -396,6 +403,7 @@ pub async fn update_avatar_url(
reason: None,
is_direct: None,
third_party_invite: None,
redact_events: None,
});
Ok((pdu, room_id))

View file

@ -58,29 +58,34 @@ pub(crate) async fn set_read_marker_route(
}
if let Some(event) = &body.read_receipt {
let receipt_content = BTreeMap::from_iter([(
event.to_owned(),
BTreeMap::from_iter([(
ReceiptType::Read,
BTreeMap::from_iter([(sender_user.to_owned(), ruma::events::receipt::Receipt {
ts: Some(MilliSecondsSinceUnixEpoch::now()),
thread: ReceiptThread::Unthreaded,
})]),
)]),
)]);
if !services.users.is_suspended(sender_user).await? {
let receipt_content = BTreeMap::from_iter([(
event.to_owned(),
BTreeMap::from_iter([(
ReceiptType::Read,
BTreeMap::from_iter([(
sender_user.to_owned(),
ruma::events::receipt::Receipt {
ts: Some(MilliSecondsSinceUnixEpoch::now()),
thread: ReceiptThread::Unthreaded,
},
)]),
)]),
)]);
services
.rooms
.read_receipt
.readreceipt_update(
sender_user,
&body.room_id,
&ruma::events::receipt::ReceiptEvent {
content: ruma::events::receipt::ReceiptEventContent(receipt_content),
room_id: body.room_id.clone(),
},
)
.await;
services
.rooms
.read_receipt
.readreceipt_update(
sender_user,
&body.room_id,
&ruma::events::receipt::ReceiptEvent {
content: ruma::events::receipt::ReceiptEventContent(receipt_content),
room_id: body.room_id.clone(),
},
)
.await;
}
}
if let Some(event) = &body.private_read_receipt {

View file

@ -1,5 +1,5 @@
use axum::extract::State;
use conduwuit::{Result, matrix::pdu::PduBuilder};
use conduwuit::{Err, Result, matrix::pdu::PduBuilder};
use ruma::{
api::client::redact::redact_event, events::room::redaction::RoomRedactionEventContent,
};
@ -17,6 +17,10 @@ pub(crate) async fn redact_event_route(
) -> Result<redact_event::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let body = body.body;
if services.users.is_suspended(sender_user).await? {
// TODO: Users can redact their own messages while suspended
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
}
let state_lock = services.rooms.state.mutex.lock(&body.room_id).await;

View file

@ -70,6 +70,10 @@ pub(crate) async fn create_room_route(
));
}
if services.users.is_suspended(sender_user).await? {
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
}
let room_id: OwnedRoomId = match &body.room_id {
| Some(custom_room_id) => custom_room_id_check(&services, custom_room_id)?,
| _ => RoomId::new(&services.server.name),

View file

@ -2,7 +2,7 @@ use std::cmp::max;
use axum::extract::State;
use conduwuit::{
Error, Result, err, info,
Err, Error, Result, err, info,
matrix::{StateKey, pdu::PduBuilder},
};
use futures::StreamExt;
@ -63,6 +63,10 @@ pub(crate) async fn upgrade_room_route(
));
}
if services.users.is_suspended(sender_user).await? {
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
}
// Create a replacement room
let replacement_room = RoomId::new(services.globals.server_name());
@ -189,6 +193,7 @@ pub(crate) async fn upgrade_room_route(
blurhash: services.users.blurhash(sender_user).await.ok(),
reason: None,
join_authorized_via_users_server: None,
redact_events: None,
})
.expect("event is valid, we just created it"),
unsigned: None,

View file

@ -23,6 +23,9 @@ pub(crate) async fn send_message_event_route(
let sender_user = body.sender_user();
let sender_device = body.sender_device.as_deref();
let appservice_info = body.appservice_info.as_ref();
if services.users.is_suspended(sender_user).await? {
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
}
// Forbid m.room.encrypted if encryption is disabled
if MessageLikeEventType::RoomEncrypted == body.event_type && !services.config.allow_encryption

View file

@ -121,7 +121,9 @@ where
.map(|(key, val)| (key, val.collect()))
.collect();
if !populate {
if populate {
rooms.push(summary_to_chunk(summary.clone()));
} else {
children = children
.iter()
.rev()
@ -144,10 +146,8 @@ where
.collect();
}
if populate {
rooms.push(summary_to_chunk(summary.clone()));
} else if queue.is_empty() && children.is_empty() {
return Err!(Request(InvalidParam("Room IDs in token were not found.")));
if !populate && queue.is_empty() && children.is_empty() {
break;
}
parents.insert(current_room.clone());

View file

@ -33,6 +33,10 @@ pub(crate) async fn send_state_event_for_key_route(
) -> Result<send_state_event::v3::Response> {
let sender_user = body.sender_user();
if services.users.is_suspended(sender_user).await? {
return Err!(Request(UserSuspended("You cannot perform this action while suspended.")));
}
Ok(send_state_event::v3::Response {
event_id: send_state_event_for_key_helper(
&services,

View file

@ -1009,8 +1009,6 @@ async fn calculate_state_incremental<'a>(
) -> Result<StateChanges> {
let since_shortstatehash = since_shortstatehash.unwrap_or(current_shortstatehash);
let state_changed = since_shortstatehash != current_shortstatehash;
let encrypted_room = services
.rooms
.state_accessor
@ -1042,7 +1040,7 @@ async fn calculate_state_incremental<'a>(
})
.into();
let state_diff_ids: OptionFuture<_> = (!full_state && state_changed)
let state_diff_ids: OptionFuture<_> = (!full_state)
.then(|| {
StreamExt::into_future(
services

View file

@ -26,41 +26,42 @@ pub(crate) async fn create_typing_event_route(
{
return Err!(Request(Forbidden("You are not in this room.")));
}
match body.state {
| Typing::Yes(duration) => {
let duration = utils::clamp(
duration.as_millis().try_into().unwrap_or(u64::MAX),
if !services.users.is_suspended(sender_user).await? {
match body.state {
| Typing::Yes(duration) => {
let duration = utils::clamp(
duration.as_millis().try_into().unwrap_or(u64::MAX),
services
.server
.config
.typing_client_timeout_min_s
.try_mul(1000)?,
services
.server
.config
.typing_client_timeout_max_s
.try_mul(1000)?,
);
services
.server
.config
.typing_client_timeout_min_s
.try_mul(1000)?,
.rooms
.typing
.typing_add(
sender_user,
&body.room_id,
utils::millis_since_unix_epoch()
.checked_add(duration)
.expect("user typing timeout should not get this high"),
)
.await?;
},
| _ => {
services
.server
.config
.typing_client_timeout_max_s
.try_mul(1000)?,
);
services
.rooms
.typing
.typing_add(
sender_user,
&body.room_id,
utils::millis_since_unix_epoch()
.checked_add(duration)
.expect("user typing timeout should not get this high"),
)
.await?;
},
| _ => {
services
.rooms
.typing
.typing_remove(sender_user, &body.room_id)
.await?;
},
.rooms
.typing
.typing_remove(sender_user, &body.room_id)
.await?;
},
}
}
// ping presence

View file

@ -378,6 +378,10 @@ pub(super) static MAPS: &[Descriptor] = &[
name: "userid_password",
..descriptor::RANDOM
},
Descriptor {
name: "userid_suspension",
..descriptor::RANDOM_SMALL
},
Descriptor {
name: "userid_presenceid",
..descriptor::RANDOM_SMALL

View file

@ -77,7 +77,7 @@ pub(crate) fn init(
);
let tracer = opentelemetry_jaeger::new_agent_pipeline()
.with_auto_split_batch(true)
.with_service_name("conduwuit")
.with_service_name(conduwuit_core::name())
.install_batch(opentelemetry_sdk::runtime::Tokio)
.expect("jaeger agent pipeline");
let telemetry = tracing_opentelemetry::layer().with_tracer(tracer);

View file

@ -45,11 +45,13 @@ struct Services {
services: StdRwLock<Option<Weak<crate::Services>>>,
}
/// Inputs to a command are a multi-line string and optional reply_id.
/// Inputs to a command are a multi-line string, optional reply_id, and optional
/// sender.
#[derive(Debug)]
pub struct CommandInput {
pub command: String,
pub reply_id: Option<OwnedEventId>,
pub sender: Option<Box<UserId>>,
}
/// Prototype of the tab-completer. The input is buffered text when tab
@ -162,7 +164,22 @@ impl Service {
pub fn command(&self, command: String, reply_id: Option<OwnedEventId>) -> Result<()> {
self.channel
.0
.send(CommandInput { command, reply_id })
.send(CommandInput { command, reply_id, sender: None })
.map_err(|e| err!("Failed to enqueue admin command: {e:?}"))
}
/// Posts a command to the command processor queue with sender information
/// and returns. Processing will take place on the service worker's task
/// asynchronously. Errors if the queue is full.
pub fn command_with_sender(
&self,
command: String,
reply_id: Option<OwnedEventId>,
sender: Box<UserId>,
) -> Result<()> {
self.channel
.0
.send(CommandInput { command, reply_id, sender: Some(sender) })
.map_err(|e| err!("Failed to enqueue admin command: {e:?}"))
}
@ -173,7 +190,7 @@ impl Service {
command: String,
reply_id: Option<OwnedEventId>,
) -> ProcessorResult {
self.process_command(CommandInput { command, reply_id })
self.process_command(CommandInput { command, reply_id, sender: None })
.await
}

View file

@ -536,9 +536,11 @@ impl Service {
self.services.search.index_pdu(shortroomid, &pdu_id, &body);
if self.services.admin.is_admin_command(pdu, &body).await {
self.services
.admin
.command(body, Some((*pdu.event_id).into()))?;
self.services.admin.command_with_sender(
body,
Some((*pdu.event_id).into()),
pdu.sender.clone().into(),
)?;
}
}
},
@ -733,6 +735,18 @@ impl Service {
);
}
}
if event_type != TimelineEventType::RoomCreate && prev_events.is_empty() {
return Err!(Request(Unknown("Event incorrectly had zero prev_events.")));
}
if state_key.is_none() && depth.lt(&uint!(2)) {
// The first two events in a room are always m.room.create and m.room.member,
// so any other events with that same depth are illegal.
warn!(
"Had unsafe depth {depth} when creating non-state event in {room_id}. Cowardly \
aborting"
);
return Err!(Request(Unknown("Unsafe depth for non-state event.")));
}
let mut pdu = PduEvent {
event_id: ruma::event_id!("$thiswillbefilledinlater").into(),

View file

@ -16,10 +16,21 @@ use ruma::{
},
serde::Raw,
};
use serde::{Deserialize, Serialize};
use serde_json::json;
use crate::{Dep, account_data, admin, globals, rooms};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserSuspension {
/// Whether the user is currently suspended
pub suspended: bool,
/// When the user was suspended (Unix timestamp in milliseconds)
pub suspended_at: u64,
/// User ID of who suspended this user
pub suspended_by: String,
}
pub struct Service {
services: Services,
db: Data,
@ -52,6 +63,7 @@ struct Data {
userid_lastonetimekeyupdate: Arc<Map>,
userid_masterkeyid: Arc<Map>,
userid_password: Arc<Map>,
userid_suspension: Arc<Map>,
userid_selfsigningkeyid: Arc<Map>,
userid_usersigningkeyid: Arc<Map>,
useridprofilekey_value: Arc<Map>,
@ -87,6 +99,7 @@ impl crate::Service for Service {
userid_lastonetimekeyupdate: args.db["userid_lastonetimekeyupdate"].clone(),
userid_masterkeyid: args.db["userid_masterkeyid"].clone(),
userid_password: args.db["userid_password"].clone(),
userid_suspension: args.db["userid_suspension"].clone(),
userid_selfsigningkeyid: args.db["userid_selfsigningkeyid"].clone(),
userid_usersigningkeyid: args.db["userid_usersigningkeyid"].clone(),
useridprofilekey_value: args.db["useridprofilekey_value"].clone(),
@ -143,6 +156,23 @@ impl Service {
Ok(())
}
/// Suspend account, placing it in a read-only state
pub async fn suspend_account(&self, user_id: &UserId, suspending_user: &UserId) {
self.db.userid_suspension.raw_put(
user_id,
Json(UserSuspension {
suspended: true,
suspended_at: MilliSecondsSinceUnixEpoch::now().get().into(),
suspended_by: suspending_user.to_string(),
}),
);
}
/// Unsuspend account, placing it in a read-write state
pub async fn unsuspend_account(&self, user_id: &UserId) {
self.db.userid_suspension.remove(user_id);
}
/// Check if a user has an account on this homeserver.
#[inline]
pub async fn exists(&self, user_id: &UserId) -> bool {
@ -159,6 +189,25 @@ impl Service {
.await
}
/// Check if account is suspended
pub async fn is_suspended(&self, user_id: &UserId) -> Result<bool> {
match self
.db
.userid_suspension
.get(user_id)
.await
.deserialized::<UserSuspension>()
{
| Ok(s) => Ok(s.suspended),
| Err(e) =>
if e.is_not_found() {
Ok(false)
} else {
Err(e)
},
}
}
/// Check if account is active, infallible
pub async fn is_active(&self, user_id: &UserId) -> bool {
!self.is_deactivated(user_id).await.unwrap_or(true)

View file

@ -605,4 +605,3 @@ ul#searchresults span.teaser em {
margin-inline-start: -14px;
width: 14px;
}