mirror of
https://forgejo.ellis.link/continuwuation/continuwuity.git
synced 2026-05-26 20:49:55 +00:00
Compare commits
70 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 00c7e220bb | |||
| 87be4d1a52 | |||
| 205506f206 | |||
| 66181c61af | |||
| b7a0442298 | |||
| 1bc663e1c8 | |||
| 68b0140c42 | |||
| f32f60d056 | |||
| fe06d78c8e | |||
| 99ebe022ed | |||
| f335f45017 | |||
| 1726633c0f | |||
| dfda27fadc | |||
| 9465c5df1f | |||
| 2d475b1220 | |||
| d7fa624fd2 | |||
| cc9202b0c4 | |||
| a3d62ed0d9 | |||
| 78b7175677 | |||
| 74d60f256b | |||
| 732c69f5ca | |||
| 8e7801f323 | |||
| 9017efe45b | |||
| 7e2f04a78a | |||
| d74514f305 | |||
| 95610499c7 | |||
| f593cac58a | |||
| 1c985c59f5 | |||
| b635e825d2 | |||
| 6d29098d1a | |||
| 374fb2745c | |||
| a1d616e3e3 | |||
| 30a8c06fd9 | |||
| 0631094350 | |||
| 9051ce63f7 | |||
| f513cb7598 | |||
| c639228f4d | |||
| 331832616f | |||
| b2b18002ea | |||
| 57868a008c | |||
| f063814d94 | |||
| 3b5335630d | |||
| b2883c3d6e | |||
| 62bdfe1ce8 | |||
| 843e501902 | |||
| 0a8c13ffd2 | |||
| a89ceb93d8 | |||
| 13de0ac822 | |||
| 4a5b122d77 | |||
| 2655acf269 | |||
| 3c320f6d6e | |||
| 946449d3e5 | |||
| b17f278803 | |||
| 6a4905271e | |||
| cfc64ddb40 | |||
| 6aceac3833 | |||
| 5bf20db8e7 | |||
| 1abe8f7835 | |||
| ce84c46459 | |||
| 7b60f5368d | |||
| e61a593932 | |||
| b71186d958 | |||
| c362499cef | |||
| 14774fa153 | |||
| ff805d8ae1 | |||
| f0994355d4 | |||
| 980774a275 | |||
| e4a6abe15e | |||
| df1cb10a8e | |||
| 651d07a609 |
@@ -2,6 +2,6 @@
|
||||
|
||||
dotenv_if_exists
|
||||
|
||||
use flake ".#${DIRENV_DEVSHELL:-default}"
|
||||
# use flake ".#${DIRENV_DEVSHELL:-default}"
|
||||
|
||||
PATH_add bin
|
||||
|
||||
@@ -262,7 +262,7 @@ jobs:
|
||||
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
|
||||
type=raw,value=latest,enable=${{ !startsWith(github.ref, 'refs/tags/v') }}
|
||||
type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/v') }}
|
||||
images: ${{needs.define-variables.outputs.images}}
|
||||
# default labels & annotations: https://github.com/docker/metadata-action/blob/master/src/meta.ts#L509
|
||||
env:
|
||||
|
||||
@@ -73,6 +73,7 @@ jobs:
|
||||
run: |
|
||||
cargo clippy \
|
||||
--workspace \
|
||||
--features full \
|
||||
--locked \
|
||||
--no-deps \
|
||||
--profile test \
|
||||
@@ -132,6 +133,7 @@ jobs:
|
||||
run: |
|
||||
cargo test \
|
||||
--workspace \
|
||||
--features full \
|
||||
--locked \
|
||||
--profile test \
|
||||
--all-targets \
|
||||
|
||||
+15
-1
@@ -1,5 +1,19 @@
|
||||
[files]
|
||||
extend-exclude = ["*.csr"]
|
||||
extend-exclude = ["*.csr", "*.lock", "pnpm-lock.yaml"]
|
||||
|
||||
[default]
|
||||
|
||||
extend-ignore-re = [
|
||||
"(?Rm)^.*(#|//|<!--)\\s*spellchecker:disable-line(\\s*-->)$", # Ignore a line by making it trail with a `spellchecker:disable-line` comment
|
||||
"^[0-9a-f]{7,}$", # Commit hashes
|
||||
|
||||
# some heuristics for base64 strings
|
||||
"[A-Za-z0-9+=]{72,}",
|
||||
"([A-Za-z0-9+=]|\\\\\\s\\*){72,}",
|
||||
"[0-9+][A-Za-z0-9+]{30,}[a-z0-9+]",
|
||||
"\\$[A-Z0-9+][A-Za-z0-9+]{6,}[a-z0-9+]",
|
||||
"\\b[a-z0-9+/=][A-Za-z0-9+/=]{7,}[a-z0-9+/=][A-Z]\\b",
|
||||
]
|
||||
|
||||
[default.extend-words]
|
||||
"allocatedp" = "allocatedp"
|
||||
|
||||
+1
-1
@@ -59,7 +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 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.
|
||||
reported to the community leaders responsible for enforcement over Matrix at [#continuwuity:continuwuity.org](https://matrix.to/#/#continuwuity:continuwuity.org?via=continuwuity.org&via=ellis.link&via=explodie.org&via=matrix.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
|
||||
|
||||
+19
-48
@@ -1,25 +1,15 @@
|
||||
# Contributing guide
|
||||
|
||||
This page is about contributing to Continuwuity. The
|
||||
[development](./development.md) page may be of interest for you as well.
|
||||
[development](./development.md) and [code style guide](./development/code_style.md) pages 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 [#continuwuity:continuwuity.org][continuwuity-matrix],
|
||||
and comment on it.
|
||||
|
||||
### Linting and Formatting
|
||||
### Code Style
|
||||
|
||||
It is mandatory all your changes satisfy the lints (clippy, rustc, rustdoc, etc)
|
||||
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.
|
||||
|
||||
If you need to allow a lint, please make sure it's either obvious as to why
|
||||
(e.g. clippy saying redundant clone but it's actually required) or it has a
|
||||
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.
|
||||
Please review and follow the [code style guide](./development/code_style.md) for formatting, linting, naming conventions, and other code standards.
|
||||
|
||||
### Pre-commit Checks
|
||||
|
||||
@@ -36,6 +26,10 @@ You can run these checks locally by installing [prefligit](https://github.com/j1
|
||||
|
||||
|
||||
```bash
|
||||
# Requires UV: https://docs.astral.sh/uv/getting-started/installation/
|
||||
# Mac/linux: curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
# Windows: powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
|
||||
|
||||
# Install prefligit using cargo-binstall
|
||||
cargo binstall prefligit
|
||||
|
||||
@@ -48,6 +42,8 @@ prefligit --all-files
|
||||
|
||||
Alternatively, you can use [pre-commit](https://pre-commit.com/):
|
||||
```bash
|
||||
# Requires python
|
||||
|
||||
# Install pre-commit
|
||||
pip install pre-commit
|
||||
|
||||
@@ -58,7 +54,7 @@ pre-commit install
|
||||
pre-commit run --all-files
|
||||
```
|
||||
|
||||
These same checks are run in CI via the prefligit-checks workflow to ensure consistency.
|
||||
These same checks are run in CI via the prefligit-checks workflow to ensure consistency. These must pass before the PR is merged.
|
||||
|
||||
### Running tests locally
|
||||
|
||||
@@ -69,11 +65,11 @@ Tests, compilation, and linting can be run with standard Cargo commands:
|
||||
cargo test
|
||||
|
||||
# Check compilation
|
||||
cargo check --workspace
|
||||
cargo check --workspace --features full
|
||||
|
||||
# Run lints
|
||||
cargo clippy --workspace
|
||||
# Auto-fix: cargo clippy --workspace --fix --allow-staged;
|
||||
cargo clippy --workspace --features full
|
||||
# Auto-fix: cargo clippy --workspace --features full --fix --allow-staged;
|
||||
|
||||
# Format code (must use nightly)
|
||||
cargo +nightly fmt
|
||||
@@ -107,37 +103,13 @@ To build the documentation locally:
|
||||
|
||||
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
|
||||
|
||||
All **MUST** code and write with inclusivity and diversity in mind. See the
|
||||
[following page by Google on writing inclusive code and
|
||||
documentation](https://developers.google.com/style/inclusive-documentation).
|
||||
|
||||
This **EXPLICITLY** forbids usage of terms like "blacklist"/"whitelist" and
|
||||
"master"/"slave", [forbids gender-specific words and
|
||||
phrases](https://developers.google.com/style/pronouns#gender-neutral-pronouns),
|
||||
forbids ableist language like "sanity-check", "cripple", or "insane", and
|
||||
forbids culture-specific language (e.g. US-only holidays or cultures).
|
||||
|
||||
No exceptions are allowed. Dependencies that may use these terms are allowed but
|
||||
[do not replicate the name in your functions or
|
||||
variables](https://developers.google.com/style/inclusive-documentation#write-around).
|
||||
|
||||
In addition to language, write and code with the user experience in mind. This
|
||||
is software that intends to be used by everyone, so make it easy and comfortable
|
||||
for everyone to use. 🏳️⚧️
|
||||
|
||||
### Variable, comment, function, etc standards
|
||||
|
||||
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>
|
||||
|
||||
@@ -178,11 +150,10 @@ of it, especially when the CI completed successfully and everything so it
|
||||
|
||||
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
|
||||
|
||||
|
||||
2. Your code follows the [code style guide](./development/code_style.md)
|
||||
3. Your commit messages follow the conventional commits format
|
||||
4. Tests are added for new functionality
|
||||
5. Documentation is updated if needed
|
||||
|
||||
Direct all PRs/MRs to the `main` branch.
|
||||
|
||||
@@ -195,7 +166,7 @@ their contributions accepted. This includes users who have been banned from
|
||||
continuwuity Matrix rooms for Code of Conduct violations.
|
||||
|
||||
[issues]: https://forgejo.ellis.link/continuwuation/continuwuity/issues
|
||||
[continuwuity-matrix]: https://matrix.to/#/#continuwuity:continuwuity.org
|
||||
[continuwuity-matrix]: https://matrix.to/#/#continuwuity:continuwuity.org?via=continuwuity.org&via=ellis.link&via=explodie.org&via=matrix.org
|
||||
[complement]: https://github.com/matrix-org/complement/
|
||||
[sytest]: https://github.com/matrix-org/sytest/
|
||||
[mdbook]: https://rust-lang.github.io/mdBook/
|
||||
|
||||
Generated
+190
-135
@@ -52,9 +52,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.18"
|
||||
version = "0.6.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
|
||||
checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
@@ -73,27 +73,27 @@ checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.6"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
|
||||
checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.2"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
|
||||
checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.8"
|
||||
version = "3.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6680de5231bd6ee4c6191b8a1325daa282b415391ec9d3a37bd34f2060dc73fa"
|
||||
checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell_polyfill",
|
||||
@@ -217,9 +217,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "async-compression"
|
||||
version = "0.4.25"
|
||||
version = "0.4.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40f6024f3f856663b45fd0c9b6f2024034a702f453549449e0d84a305900dad4"
|
||||
checksum = "ddb939d66e4ae03cee6091612804ba446b12878410cfa17f785f4dd67d4014e8"
|
||||
dependencies = [
|
||||
"brotli",
|
||||
"flate2",
|
||||
@@ -301,18 +301,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "avif-serialize"
|
||||
version = "0.8.3"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "98922d6a4cfbcb08820c69d8eeccc05bb1f29bfa06b4f5b1dbfe9a868bd7608e"
|
||||
checksum = "2ea8ef51aced2b9191c08197f55450d830876d9933f8f48a429b354f1d496b42"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aws-lc-rs"
|
||||
version = "1.13.1"
|
||||
version = "1.13.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93fcc8f365936c834db5514fc45aee5b1202d677e6b40e48468aaaa8183ca8c7"
|
||||
checksum = "5c953fe1ba023e6b7730c0d4b031d06f267f23a46167dcbd40316644b10a17ba"
|
||||
dependencies = [
|
||||
"aws-lc-sys",
|
||||
"zeroize",
|
||||
@@ -320,9 +320,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-lc-sys"
|
||||
version = "0.29.0"
|
||||
version = "0.30.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61b1d86e7705efe1be1b569bab41d4fa1e14e220b60a160f78de2db687add079"
|
||||
checksum = "dbfd150b5dbdb988bcc8fb1fe787eb6b7ee6180ca24da683b61ea5405f3d43ff"
|
||||
dependencies = [
|
||||
"bindgen 0.69.5",
|
||||
"cc",
|
||||
@@ -527,9 +527,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bindgen"
|
||||
version = "0.71.1"
|
||||
version = "0.72.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3"
|
||||
checksum = "4f72209734318d0b619a5e0f5129918b848c416e122a3c4ce054e03cb87b726f"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"cexpr",
|
||||
@@ -629,9 +629,9 @@ checksum = "f4ad8f11f288f48ca24471bbd51ac257aaeaaa07adae295591266b792902ae64"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.18.1"
|
||||
version = "3.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee"
|
||||
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
@@ -685,9 +685,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.27"
|
||||
version = "1.2.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc"
|
||||
checksum = "deec109607ca693028562ed836a5f1c4b8bd77755c4e132fc5ce11b0b6211ae7"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
@@ -756,9 +756,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.40"
|
||||
version = "4.5.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f"
|
||||
checksum = "be92d32e80243a54711e5d7ce823c35c41c9d929dc4ab58e1276f625841aadf9"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@@ -775,9 +775,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.40"
|
||||
version = "4.5.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e"
|
||||
checksum = "707eab41e9622f9139419d573eca0900137718000c517d47da73045f54331c3d"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@@ -787,9 +787,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.40"
|
||||
version = "4.5.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce"
|
||||
checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
@@ -805,9 +805,9 @@ checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675"
|
||||
|
||||
[[package]]
|
||||
name = "clap_mangen"
|
||||
version = "0.2.26"
|
||||
version = "0.2.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "724842fa9b144f9b89b3f3d371a89f3455eea660361d13a554f68f8ae5d6c13a"
|
||||
checksum = "e2fb6d3f935bbb9819391528b0e7cf655e78a0bc7a7c3d227211a1d24fc11db1"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"roff",
|
||||
@@ -830,9 +830,9 @@ checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.3"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
||||
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
|
||||
|
||||
[[package]]
|
||||
name = "concurrent-queue"
|
||||
@@ -845,7 +845,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "conduwuit"
|
||||
version = "0.5.0-rc.6"
|
||||
version = "0.5.0-rc.7"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"conduwuit_admin",
|
||||
@@ -875,7 +875,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "conduwuit_admin"
|
||||
version = "0.5.0-rc.6"
|
||||
version = "0.5.0-rc.7"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"conduwuit_api",
|
||||
@@ -896,7 +896,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "conduwuit_api"
|
||||
version = "0.5.0-rc.6"
|
||||
version = "0.5.0-rc.7"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum",
|
||||
@@ -928,14 +928,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "conduwuit_build_metadata"
|
||||
version = "0.5.0-rc.6"
|
||||
version = "0.5.0-rc.7"
|
||||
dependencies = [
|
||||
"built 0.8.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "conduwuit_core"
|
||||
version = "0.5.0-rc.6"
|
||||
version = "0.5.0-rc.7"
|
||||
dependencies = [
|
||||
"argon2",
|
||||
"arrayvec",
|
||||
@@ -963,10 +963,12 @@ dependencies = [
|
||||
"itertools 0.14.0",
|
||||
"libc",
|
||||
"libloading",
|
||||
"lock_api",
|
||||
"log",
|
||||
"maplit",
|
||||
"nix",
|
||||
"num-traits",
|
||||
"parking_lot",
|
||||
"rand 0.8.5",
|
||||
"regex",
|
||||
"reqwest",
|
||||
@@ -994,7 +996,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "conduwuit_database"
|
||||
version = "0.5.0-rc.6"
|
||||
version = "0.5.0-rc.7"
|
||||
dependencies = [
|
||||
"async-channel",
|
||||
"conduwuit_core",
|
||||
@@ -1012,7 +1014,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "conduwuit_macros"
|
||||
version = "0.5.0-rc.6"
|
||||
version = "0.5.0-rc.7"
|
||||
dependencies = [
|
||||
"itertools 0.14.0",
|
||||
"proc-macro2",
|
||||
@@ -1022,7 +1024,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "conduwuit_router"
|
||||
version = "0.5.0-rc.6"
|
||||
version = "0.5.0-rc.7"
|
||||
dependencies = [
|
||||
"axum",
|
||||
"axum-client-ip",
|
||||
@@ -1056,7 +1058,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "conduwuit_service"
|
||||
version = "0.5.0-rc.6"
|
||||
version = "0.5.0-rc.7"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"base64 0.22.1",
|
||||
@@ -1076,6 +1078,7 @@ dependencies = [
|
||||
"loole",
|
||||
"lru-cache",
|
||||
"rand 0.8.5",
|
||||
"recaptcha-verify",
|
||||
"regex",
|
||||
"reqwest",
|
||||
"ruma",
|
||||
@@ -1093,7 +1096,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "conduwuit_web"
|
||||
version = "0.5.0-rc.6"
|
||||
version = "0.5.0-rc.7"
|
||||
dependencies = [
|
||||
"askama",
|
||||
"axum",
|
||||
@@ -1152,15 +1155,15 @@ checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
|
||||
|
||||
[[package]]
|
||||
name = "const-str"
|
||||
version = "0.6.2"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e991226a70654b49d34de5ed064885f0bef0348a8e70018b8ff1ac80aa984a2"
|
||||
checksum = "041fbfcf8e7054df725fb9985297e92422cdc80fcf313665f5ca3d761bb63f4c"
|
||||
|
||||
[[package]]
|
||||
name = "const_panic"
|
||||
version = "0.2.12"
|
||||
version = "0.2.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2459fc9262a1aa204eb4b5764ad4f189caec88aea9634389c0a25f8be7f6265e"
|
||||
checksum = "b98d1483e98c9d67f341ab4b3915cfdc54740bd6f5cccc9226ee0535d86aa8fb"
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
@@ -1217,9 +1220,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.4.2"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
|
||||
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
@@ -1325,7 +1328,7 @@ dependencies = [
|
||||
"futures-core",
|
||||
"mio",
|
||||
"parking_lot",
|
||||
"rustix 1.0.7",
|
||||
"rustix 1.0.8",
|
||||
"signal-hook",
|
||||
"signal-hook-mio",
|
||||
"winapi",
|
||||
@@ -1342,9 +1345,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crunchy"
|
||||
version = "0.2.3"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929"
|
||||
checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
@@ -1510,9 +1513,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ed25519-dalek"
|
||||
version = "2.1.1"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871"
|
||||
checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9"
|
||||
dependencies = [
|
||||
"curve25519-dalek",
|
||||
"ed25519",
|
||||
@@ -1656,6 +1659,12 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fixedbitset"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.1.2"
|
||||
@@ -1858,9 +1867,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gif"
|
||||
version = "0.13.1"
|
||||
version = "0.13.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2"
|
||||
checksum = "4ae047235e33e2829703574b54fdec96bfbad892062d97fed2f76022287de61b"
|
||||
dependencies = [
|
||||
"color_quant",
|
||||
"weezl",
|
||||
@@ -1880,9 +1889,9 @@ checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.4.10"
|
||||
version = "0.4.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9421a676d1b147b16b82c9225157dc629087ef8ec4d5e2960f9437a90dac0a5"
|
||||
checksum = "17da50a276f1e01e0ba6c029e47b7100754904ee8a278f886546e98575380785"
|
||||
dependencies = [
|
||||
"atomic-waker",
|
||||
"bytes",
|
||||
@@ -1890,7 +1899,7 @@ dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"http",
|
||||
"indexmap 2.9.0",
|
||||
"indexmap 2.10.0",
|
||||
"slab",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
@@ -2020,7 +2029,7 @@ dependencies = [
|
||||
"idna",
|
||||
"ipnet",
|
||||
"once_cell",
|
||||
"rand 0.9.1",
|
||||
"rand 0.9.2",
|
||||
"ring",
|
||||
"serde",
|
||||
"thiserror 2.0.12",
|
||||
@@ -2064,7 +2073,7 @@ dependencies = [
|
||||
"moka",
|
||||
"once_cell",
|
||||
"parking_lot",
|
||||
"rand 0.9.1",
|
||||
"rand 0.9.2",
|
||||
"resolv-conf",
|
||||
"serde",
|
||||
"smallvec",
|
||||
@@ -2213,7 +2222,7 @@ dependencies = [
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tower-service",
|
||||
"webpki-roots 1.0.1",
|
||||
"webpki-roots 1.0.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2406,9 +2415,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.9.0"
|
||||
version = "2.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
|
||||
checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.15.4",
|
||||
@@ -2438,6 +2447,17 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "io-uring"
|
||||
version = "0.7.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipaddress"
|
||||
version = "0.1.3"
|
||||
@@ -2521,9 +2541,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "jpeg-decoder"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0"
|
||||
checksum = "00810f1d8b74be64b13dbf3db89ac67740615d6c891f0e7b6179326533011a07"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
@@ -2622,9 +2642,9 @@ checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
||||
|
||||
[[package]]
|
||||
name = "libfuzzer-sys"
|
||||
version = "0.4.9"
|
||||
version = "0.4.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf78f52d400cf2d84a3a973a78a592b4adc535739e0a5597a0da6f0c357adc75"
|
||||
checksum = "5037190e1f70cbeef565bd267599242926f724d3b8a9f510fd7e0b540cfa4404"
|
||||
dependencies = [
|
||||
"arbitrary",
|
||||
"cc",
|
||||
@@ -3104,7 +3124,7 @@ checksum = "1e32339a5dc40459130b3bd269e9892439f55b33e772d2a9d402a789baaf4e8a"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"indexmap 2.9.0",
|
||||
"indexmap 2.10.0",
|
||||
"js-sys",
|
||||
"once_cell",
|
||||
"pin-project-lite",
|
||||
@@ -3217,10 +3237,13 @@ version = "0.9.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"petgraph",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"thread-id",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
@@ -3270,6 +3293,16 @@ version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||
|
||||
[[package]]
|
||||
name = "petgraph"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db"
|
||||
dependencies = [
|
||||
"fixedbitset",
|
||||
"indexmap 2.10.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.11.3"
|
||||
@@ -3358,12 +3391,12 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
||||
|
||||
[[package]]
|
||||
name = "plist"
|
||||
version = "1.7.2"
|
||||
version = "1.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d77244ce2d584cd84f6a15f86195b8c9b2a0dfbfd817c09e0464244091a58ed"
|
||||
checksum = "3af6b589e163c5a788fab00ce0c0366f6efbb9959c2f9874b224936af7fce7e1"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"indexmap 2.9.0",
|
||||
"indexmap 2.10.0",
|
||||
"quick-xml",
|
||||
"serde",
|
||||
"time",
|
||||
@@ -3420,9 +3453,9 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.35"
|
||||
version = "0.2.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "061c1221631e079b26479d25bbf2275bfe5917ae8419cd7e34f13bfc2aa7539a"
|
||||
checksum = "ff24dfcda44452b9816fff4cd4227e1bb73ff5a2f1bc1105aa92fb8565ce44d2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn",
|
||||
@@ -3461,18 +3494,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "profiling"
|
||||
version = "1.0.16"
|
||||
version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d"
|
||||
checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773"
|
||||
dependencies = [
|
||||
"profiling-procmacros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "profiling-procmacros"
|
||||
version = "1.0.16"
|
||||
version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30"
|
||||
checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
@@ -3545,9 +3578,9 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.37.5"
|
||||
version = "0.38.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb"
|
||||
checksum = "8927b0664f5c5a98265138b7e3f90aa19a6b21353182469ace36d4ac527b7b1b"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@@ -3581,7 +3614,7 @@ dependencies = [
|
||||
"bytes",
|
||||
"getrandom 0.3.3",
|
||||
"lru-slab",
|
||||
"rand 0.9.1",
|
||||
"rand 0.9.2",
|
||||
"ring",
|
||||
"rustc-hash 2.1.1",
|
||||
"rustls",
|
||||
@@ -3635,9 +3668,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.9.1"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
|
||||
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
|
||||
dependencies = [
|
||||
"rand_chacha 0.9.0",
|
||||
"rand_core 0.9.3",
|
||||
@@ -3718,9 +3751,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ravif"
|
||||
version = "0.11.12"
|
||||
version = "0.11.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6a5f31fcf7500f9401fea858ea4ab5525c99f2322cfcee732c0e6c74208c0c6"
|
||||
checksum = "5825c26fddd16ab9f515930d49028a630efec172e903483c94796cfe31893e6b"
|
||||
dependencies = [
|
||||
"avif-serialize",
|
||||
"imgref",
|
||||
@@ -3752,10 +3785,21 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.13"
|
||||
name = "recaptcha-verify"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6"
|
||||
checksum = "71e3be7b2e46e24637ac96b0c9f70070f188652018573f36f4e511dcad09738a"
|
||||
dependencies = [
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e8af0dde094006011e6a740d4879319439489813bd0bcdc7d821beaeeff48ec"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
]
|
||||
@@ -3861,9 +3905,9 @@ source = "git+https://forgejo.ellis.link/continuwuation/resolv-conf?rev=56251316
|
||||
|
||||
[[package]]
|
||||
name = "rgb"
|
||||
version = "0.8.50"
|
||||
version = "0.8.52"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a"
|
||||
checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce"
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
@@ -3888,7 +3932,7 @@ checksum = "88f8660c1ff60292143c98d08fc6e2f654d722db50410e3f3797d40baaf9d8f3"
|
||||
[[package]]
|
||||
name = "ruma"
|
||||
version = "0.10.1"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a4b948b40417a65ab0282ae47cc50035dd455e02#a4b948b40417a65ab0282ae47cc50035dd455e02"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=b753738047d1f443aca870896ef27ecaacf027da#b753738047d1f443aca870896ef27ecaacf027da"
|
||||
dependencies = [
|
||||
"assign",
|
||||
"js_int",
|
||||
@@ -3908,7 +3952,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ruma-appservice-api"
|
||||
version = "0.10.0"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a4b948b40417a65ab0282ae47cc50035dd455e02#a4b948b40417a65ab0282ae47cc50035dd455e02"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=b753738047d1f443aca870896ef27ecaacf027da#b753738047d1f443aca870896ef27ecaacf027da"
|
||||
dependencies = [
|
||||
"js_int",
|
||||
"ruma-common",
|
||||
@@ -3920,7 +3964,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ruma-client-api"
|
||||
version = "0.18.0"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a4b948b40417a65ab0282ae47cc50035dd455e02#a4b948b40417a65ab0282ae47cc50035dd455e02"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=b753738047d1f443aca870896ef27ecaacf027da#b753738047d1f443aca870896ef27ecaacf027da"
|
||||
dependencies = [
|
||||
"as_variant",
|
||||
"assign",
|
||||
@@ -3943,7 +3987,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ruma-common"
|
||||
version = "0.13.0"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a4b948b40417a65ab0282ae47cc50035dd455e02#a4b948b40417a65ab0282ae47cc50035dd455e02"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=b753738047d1f443aca870896ef27ecaacf027da#b753738047d1f443aca870896ef27ecaacf027da"
|
||||
dependencies = [
|
||||
"as_variant",
|
||||
"base64 0.22.1",
|
||||
@@ -3951,7 +3995,7 @@ dependencies = [
|
||||
"form_urlencoded",
|
||||
"getrandom 0.2.16",
|
||||
"http",
|
||||
"indexmap 2.9.0",
|
||||
"indexmap 2.10.0",
|
||||
"js_int",
|
||||
"konst",
|
||||
"percent-encoding",
|
||||
@@ -3975,10 +4019,10 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ruma-events"
|
||||
version = "0.28.1"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a4b948b40417a65ab0282ae47cc50035dd455e02#a4b948b40417a65ab0282ae47cc50035dd455e02"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=b753738047d1f443aca870896ef27ecaacf027da#b753738047d1f443aca870896ef27ecaacf027da"
|
||||
dependencies = [
|
||||
"as_variant",
|
||||
"indexmap 2.9.0",
|
||||
"indexmap 2.10.0",
|
||||
"js_int",
|
||||
"js_option",
|
||||
"percent-encoding",
|
||||
@@ -4000,7 +4044,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ruma-federation-api"
|
||||
version = "0.9.0"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a4b948b40417a65ab0282ae47cc50035dd455e02#a4b948b40417a65ab0282ae47cc50035dd455e02"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=b753738047d1f443aca870896ef27ecaacf027da#b753738047d1f443aca870896ef27ecaacf027da"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"headers",
|
||||
@@ -4022,7 +4066,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ruma-identifiers-validation"
|
||||
version = "0.9.5"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a4b948b40417a65ab0282ae47cc50035dd455e02#a4b948b40417a65ab0282ae47cc50035dd455e02"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=b753738047d1f443aca870896ef27ecaacf027da#b753738047d1f443aca870896ef27ecaacf027da"
|
||||
dependencies = [
|
||||
"js_int",
|
||||
"thiserror 2.0.12",
|
||||
@@ -4031,7 +4075,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ruma-identity-service-api"
|
||||
version = "0.9.0"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a4b948b40417a65ab0282ae47cc50035dd455e02#a4b948b40417a65ab0282ae47cc50035dd455e02"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=b753738047d1f443aca870896ef27ecaacf027da#b753738047d1f443aca870896ef27ecaacf027da"
|
||||
dependencies = [
|
||||
"js_int",
|
||||
"ruma-common",
|
||||
@@ -4041,7 +4085,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ruma-macros"
|
||||
version = "0.13.0"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a4b948b40417a65ab0282ae47cc50035dd455e02#a4b948b40417a65ab0282ae47cc50035dd455e02"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=b753738047d1f443aca870896ef27ecaacf027da#b753738047d1f443aca870896ef27ecaacf027da"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"proc-macro-crate",
|
||||
@@ -4056,7 +4100,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ruma-push-gateway-api"
|
||||
version = "0.9.0"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a4b948b40417a65ab0282ae47cc50035dd455e02#a4b948b40417a65ab0282ae47cc50035dd455e02"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=b753738047d1f443aca870896ef27ecaacf027da#b753738047d1f443aca870896ef27ecaacf027da"
|
||||
dependencies = [
|
||||
"js_int",
|
||||
"ruma-common",
|
||||
@@ -4068,7 +4112,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ruma-signatures"
|
||||
version = "0.15.0"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a4b948b40417a65ab0282ae47cc50035dd455e02#a4b948b40417a65ab0282ae47cc50035dd455e02"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=b753738047d1f443aca870896ef27ecaacf027da#b753738047d1f443aca870896ef27ecaacf027da"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"ed25519-dalek",
|
||||
@@ -4083,10 +4127,10 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rust-librocksdb-sys"
|
||||
version = "0.33.0+9.11.1"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/rust-rocksdb-zaidoon1?rev=fc9a99ac54a54208f90fdcba33ae6ee8bc3531dd#fc9a99ac54a54208f90fdcba33ae6ee8bc3531dd"
|
||||
version = "0.38.0+10.4.2"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/rust-rocksdb-zaidoon1?rev=99b0319416b64830dd6f8943e1f65e15aeef18bc#99b0319416b64830dd6f8943e1f65e15aeef18bc"
|
||||
dependencies = [
|
||||
"bindgen 0.71.1",
|
||||
"bindgen 0.72.0",
|
||||
"bzip2-sys",
|
||||
"cc",
|
||||
"glob",
|
||||
@@ -4100,8 +4144,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rust-rocksdb"
|
||||
version = "0.37.0"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/rust-rocksdb-zaidoon1?rev=fc9a99ac54a54208f90fdcba33ae6ee8bc3531dd#fc9a99ac54a54208f90fdcba33ae6ee8bc3531dd"
|
||||
version = "0.42.1"
|
||||
source = "git+https://forgejo.ellis.link/continuwuation/rust-rocksdb-zaidoon1?rev=99b0319416b64830dd6f8943e1f65e15aeef18bc#99b0319416b64830dd6f8943e1f65e15aeef18bc"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rust-librocksdb-sys",
|
||||
@@ -4149,22 +4193,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.0.7"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
|
||||
checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys 0.9.4",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.23.28"
|
||||
version = "0.23.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7160e3e10bf4535308537f3c4e1641468cd0e485175d6163087c0393c7d46643"
|
||||
checksum = "2491382039b29b9b11ff08b76ff6c97cf287671dbb74f0be44bda389fffe9bd1"
|
||||
dependencies = [
|
||||
"aws-lc-rs",
|
||||
"log",
|
||||
@@ -4209,9 +4253,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.103.3"
|
||||
version = "0.103.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435"
|
||||
checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc"
|
||||
dependencies = [
|
||||
"aws-lc-rs",
|
||||
"ring",
|
||||
@@ -4475,7 +4519,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d2de91cf02bbc07cde38891769ccd5d4f073d22a40683aa4bc7a95781aaa2c4"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"indexmap 2.9.0",
|
||||
"indexmap 2.10.0",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
@@ -4483,9 +4527,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.140"
|
||||
version = "1.0.141"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
|
||||
checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
@@ -4540,7 +4584,7 @@ version = "0.9.34+deprecated"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
|
||||
dependencies = [
|
||||
"indexmap 2.9.0",
|
||||
"indexmap 2.10.0",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
@@ -4880,6 +4924,16 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread-id"
|
||||
version = "4.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfe8f25bbdd100db7e1d34acf7fd2dc59c4bf8f7483f505eaa7d4f12f76cc0ea"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "1.1.9"
|
||||
@@ -5008,16 +5062,18 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.45.1"
|
||||
version = "1.46.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779"
|
||||
checksum = "0cc3a2344dafbe23a245241fe8b09735b521110d30fcefbbd5feb1797ca35d17"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
"io-uring",
|
||||
"libc",
|
||||
"mio",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"slab",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"tracing",
|
||||
@@ -5037,9 +5093,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio-metrics"
|
||||
version = "0.4.2"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7817b32d36c9b94744d7aa3f8fc13526aa0f5112009d7045f3c659413a6e44ac"
|
||||
checksum = "23ff82f660c98e4ff60da5eb8fa864a4130f34b56d92d5cd23d6fdfcc14e95fa"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"pin-project-lite",
|
||||
@@ -5120,7 +5176,7 @@ version = "0.22.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
|
||||
dependencies = [
|
||||
"indexmap 2.9.0",
|
||||
"indexmap 2.10.0",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
@@ -5344,9 +5400,9 @@ checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
|
||||
|
||||
[[package]]
|
||||
name = "typewit"
|
||||
version = "1.11.0"
|
||||
version = "1.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb77c29baba9e4d3a6182d51fa75e3215c7fd1dab8f4ea9d107c716878e55fc0"
|
||||
checksum = "97e72ba082eeb9da9dc68ff5a2bf727ef6ce362556e8d29ec1aed3bd05e7d86a"
|
||||
dependencies = [
|
||||
"typewit_proc_macros",
|
||||
]
|
||||
@@ -5658,14 +5714,14 @@ version = "0.26.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9"
|
||||
dependencies = [
|
||||
"webpki-roots 1.0.1",
|
||||
"webpki-roots 1.0.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8782dd5a41a24eed3a4f40b606249b3e236ca61adf1f25ea4d45c73de122b502"
|
||||
checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2"
|
||||
dependencies = [
|
||||
"rustls-pki-types",
|
||||
]
|
||||
@@ -6076,9 +6132,9 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.7.11"
|
||||
version = "0.7.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd"
|
||||
checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@@ -6121,7 +6177,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "xtask"
|
||||
version = "0.5.0-rc.6"
|
||||
version = "0.5.0-rc.7"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"serde",
|
||||
@@ -6130,7 +6186,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "xtask-generate-commands"
|
||||
version = "0.5.0-rc.6"
|
||||
version = "0.5.0-rc.7"
|
||||
dependencies = [
|
||||
"clap-markdown",
|
||||
"clap_builder",
|
||||
@@ -6273,7 +6329,6 @@ version = "2.0.15+zstd.1.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237"
|
||||
dependencies = [
|
||||
"bindgen 0.71.1",
|
||||
"cc",
|
||||
"pkg-config",
|
||||
]
|
||||
@@ -6295,9 +6350,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zune-jpeg"
|
||||
version = "0.4.17"
|
||||
version = "0.4.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f6fe2e33d02a98ee64423802e16df3de99c43e5cf5ff983767e1128b394c8ac"
|
||||
checksum = "2c9e525af0a6a658e031e95f14b7f889976b74a11ba0eca5a5fc9ac8a1c43a6a"
|
||||
dependencies = [
|
||||
"zune-core",
|
||||
]
|
||||
|
||||
+11
-3
@@ -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.6"
|
||||
version = "0.5.0-rc.7"
|
||||
|
||||
[workspace.metadata.crane]
|
||||
name = "conduwuit"
|
||||
@@ -352,7 +352,7 @@ version = "0.1.2"
|
||||
[workspace.dependencies.ruma]
|
||||
git = "https://forgejo.ellis.link/continuwuation/ruwuma"
|
||||
#branch = "conduwuit-changes"
|
||||
rev = "a4b948b40417a65ab0282ae47cc50035dd455e02"
|
||||
rev = "b753738047d1f443aca870896ef27ecaacf027da"
|
||||
features = [
|
||||
"compat",
|
||||
"rand",
|
||||
@@ -391,7 +391,7 @@ features = [
|
||||
|
||||
[workspace.dependencies.rust-rocksdb]
|
||||
git = "https://forgejo.ellis.link/continuwuation/rust-rocksdb-zaidoon1"
|
||||
rev = "fc9a99ac54a54208f90fdcba33ae6ee8bc3531dd"
|
||||
rev = "99b0319416b64830dd6f8943e1f65e15aeef18bc"
|
||||
default-features = false
|
||||
features = [
|
||||
"multi-threaded-cf",
|
||||
@@ -515,6 +515,14 @@ version = "1.0"
|
||||
[workspace.dependencies.proc-macro2]
|
||||
version = "1.0"
|
||||
|
||||
[workspace.dependencies.parking_lot]
|
||||
version = "0.12.4"
|
||||
features = ["hardware-lock-elision", "deadlock_detection"] # TODO: Check if deadlock_detection has a perf impact, if it does only enable with debug_assertions
|
||||
|
||||
# Use this when extending with_lock::WithLock to parking_lot
|
||||
[workspace.dependencies.lock_api]
|
||||
version = "0.4.13"
|
||||
|
||||
[workspace.dependencies.bytesize]
|
||||
version = "2.0"
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ When incorporating code from other forks:
|
||||
|
||||
#### Contact
|
||||
|
||||
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!
|
||||
Join our [Matrix room](https://matrix.to/#/#continuwuity:continuwuity.org?via=continuwuity.org&via=ellis.link&via=explodie.org&via=matrix.org) and [space](https://matrix.to/#/#space:continuwuity.org?via=continuwuity.org&via=ellis.link&via=explodie.org&via=matrix.org) to chat with us about the project!
|
||||
|
||||
<!-- ANCHOR_END: footer -->
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ StandardInput=tty-force
|
||||
StandardOutput=tty
|
||||
StandardError=journal+console
|
||||
|
||||
Environment="CONTINUWUITY_LOG_TO_JOURNALD=1"
|
||||
Environment="CONTINUWUITY_LOG_TO_JOURNALD=true"
|
||||
Environment="CONTINUWUITY_JOURNALD_IDENTIFIER=%N"
|
||||
|
||||
TTYReset=yes
|
||||
|
||||
@@ -325,12 +325,37 @@
|
||||
#
|
||||
#well_known_timeout = 10
|
||||
|
||||
# Federation client connection timeout (seconds). You should not set this
|
||||
# to high values, as dead homeservers can significantly slow down
|
||||
# federation, specifically key retrieval, which will take roughly the
|
||||
# amount of time you configure here given that a homeserver doesn't
|
||||
# respond. This will cause most clients to time out /keys/query, causing
|
||||
# E2EE and device verification to fail.
|
||||
#
|
||||
#federation_conn_timeout = 10
|
||||
|
||||
# Federation client request timeout (seconds). You most definitely want
|
||||
# this to be high to account for extremely large room joins, slow
|
||||
# homeservers, your own resources etc.
|
||||
#
|
||||
#federation_timeout = 300
|
||||
|
||||
# MSC4284 Policy server request timeout (seconds). Generally policy
|
||||
# servers should respond near instantly, however may slow down under
|
||||
# load. If a policy server doesn't respond in a short amount of time, the
|
||||
# room it is configured in may become unusable if this limit is set too
|
||||
# high. 10 seconds is a good default, however dropping this to 3-5 seconds
|
||||
# can be acceptable.
|
||||
#
|
||||
# Please be aware that policy requests are *NOT* currently re-tried, so if
|
||||
# a spam check request fails, the event will be assumed to be not spam,
|
||||
# which in some cases may result in spam being sent to or received from
|
||||
# the room that would typically be prevented.
|
||||
#
|
||||
# About policy servers: https://matrix.org/blog/2025/04/introducing-policy-servers/
|
||||
#
|
||||
#policy_server_request_timeout = 10
|
||||
|
||||
# Federation client idle connection pool timeout (seconds).
|
||||
#
|
||||
#federation_idle_timeout = 25
|
||||
@@ -441,6 +466,26 @@
|
||||
#
|
||||
#registration_token_file =
|
||||
|
||||
# The public site key for reCaptcha. If this is provided, reCaptcha
|
||||
# becomes required during registration. If both captcha *and*
|
||||
# registration token are enabled, both will be required during
|
||||
# registration.
|
||||
#
|
||||
# IMPORTANT: "Verify the origin of reCAPTCHA solutions" **MUST** BE
|
||||
# DISABLED IF YOU WANT THE CAPTCHA TO WORK IN 3RD PARTY CLIENTS, OR
|
||||
# CLIENTS HOSTED ON DOMAINS OTHER THAN YOUR OWN!
|
||||
#
|
||||
# Registration must be enabled (`allow_registration` must be true) for
|
||||
# this to have any effect.
|
||||
#
|
||||
#recaptcha_site_key =
|
||||
|
||||
# The private site key for reCaptcha.
|
||||
# If this is omitted, captcha registration will not work,
|
||||
# even if `recaptcha_site_key` is set.
|
||||
#
|
||||
#recaptcha_private_site_key =
|
||||
|
||||
# Controls whether encrypted rooms and events are allowed.
|
||||
#
|
||||
#allow_encryption = true
|
||||
|
||||
Vendored
+8
-14
@@ -1,29 +1,23 @@
|
||||
# Continuwuity for Debian
|
||||
|
||||
Information about downloading and deploying the Debian package. This may also be
|
||||
referenced for other `apt`-based distros such as Ubuntu.
|
||||
This document provides information about downloading and deploying the Debian package. You can also use this guide for other `apt`-based distributions such as Ubuntu.
|
||||
|
||||
### Installation
|
||||
|
||||
It is recommended to see the [generic deployment guide](../deploying/generic.md)
|
||||
for further information if needed as usage of the Debian package is generally
|
||||
related.
|
||||
See the [generic deployment guide](../deploying/generic.md) for additional information about using the Debian package.
|
||||
|
||||
No `apt` repository is currently offered yet, it is in the works/development.
|
||||
No `apt` repository is currently available. This feature is in development.
|
||||
|
||||
### Configuration
|
||||
|
||||
When installed, the example config is placed at `/etc/conduwuit/conduwuit.toml`
|
||||
as the default config. The config mentions things required to be changed before
|
||||
starting.
|
||||
After installation, Continuwuity places the example configuration at `/etc/conduwuit/conduwuit.toml` as the default configuration file. The configuration file indicates which settings you must change before starting the service.
|
||||
|
||||
You can tweak more detailed settings by uncommenting and setting the config
|
||||
options in `/etc/conduwuit/conduwuit.toml`.
|
||||
You can customize additional settings by uncommenting and modifying the configuration 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 Continuwuity. 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 installs 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.
|
||||
By default, this package assumes that Continuwuity runs behind a reverse proxy. The default configuration options apply (listening on `localhost` and TCP port `6167`). Matrix federation requires a valid domain name and TLS. To federate properly, you must set up TLS certificates and certificate renewal.
|
||||
|
||||
Consult various online documentation and guides on setting up a reverse proxy and TLS. Caddy is documented at the [generic deployment guide](../deploying/generic.md#setting-up-the-reverse-proxy) as it's the easiest and most user friendly.
|
||||
For information about setting up a reverse proxy and TLS, consult online documentation and guides. The [generic deployment guide](../deploying/generic.md#setting-up-the-reverse-proxy) documents Caddy, which is the most user-friendly option for reverse proxy configuration.
|
||||
|
||||
Vendored
+1
-1
@@ -14,7 +14,7 @@ Type=notify
|
||||
|
||||
Environment="CONTINUWUITY_CONFIG=/etc/conduwuit/conduwuit.toml"
|
||||
|
||||
Environment="CONTINUWUITY_LOG_TO_JOURNALD=1"
|
||||
Environment="CONTINUWUITY_LOG_TO_JOURNALD=true"
|
||||
Environment="CONTINUWUITY_JOURNALD_IDENTIFIER=%N"
|
||||
|
||||
ExecStart=/usr/sbin/conduwuit
|
||||
|
||||
+22
-19
@@ -78,7 +78,7 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
|
||||
WORKDIR /app
|
||||
COPY ./rust-toolchain.toml .
|
||||
RUN rustc --version \
|
||||
&& rustup target add $(xx-cargo --print-target-triple)
|
||||
&& xx-cargo --setup-target-triple
|
||||
|
||||
# Build binary
|
||||
# We disable incremental compilation to save disk space, as it only produces a minimal speedup for this case.
|
||||
@@ -87,8 +87,10 @@ 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
|
||||
if command -v "$(xx-info)-pkg-config" >/dev/null 2>/dev/null; then
|
||||
echo "PKG_CONFIG_LIBDIR=/usr/lib/$(xx-info)/pkgconfig" >> /etc/environment
|
||||
echo "PKG_CONFIG=/usr/bin/$(xx-info)-pkg-config" >> /etc/environment
|
||||
fi
|
||||
echo "PKG_CONFIG_ALLOW_CROSS=true" >> /etc/environment
|
||||
EOF
|
||||
|
||||
@@ -109,16 +111,17 @@ RUN <<EOF
|
||||
EOF
|
||||
|
||||
# Apply CPU-specific optimizations if TARGET_CPU is provided
|
||||
ARG TARGET_CPU=
|
||||
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
|
||||
echo "CXXFLAGS='${CXXFLAGS} -march=${TARGET_CPU}'" >> /etc/environment
|
||||
echo "RUSTFLAGS='${RUSTFLAGS} -C target-cpu=${TARGET_CPU}'" >> /etc/environment
|
||||
fi
|
||||
set -o allexport
|
||||
set -o xtrace
|
||||
. /etc/environment
|
||||
if [ -n "${TARGET_CPU}" ]; then
|
||||
echo "CFLAGS='${CFLAGS} -march=${TARGET_CPU}'" >> /etc/environment
|
||||
echo "CXXFLAGS='${CXXFLAGS} -march=${TARGET_CPU}'" >> /etc/environment
|
||||
echo "RUSTFLAGS='${RUSTFLAGS} -C target-cpu=${TARGET_CPU}'" >> /etc/environment
|
||||
fi
|
||||
EOF
|
||||
|
||||
# Prepare output directories
|
||||
@@ -136,12 +139,12 @@ ARG TARGETPLATFORM
|
||||
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=
|
||||
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
|
||||
@@ -169,7 +172,7 @@ RUN --mount=type=cache,target=/usr/local/cargo/registry \
|
||||
jq -r ".packages[] | select(.name == \"$PACKAGE\") | .targets[] | select( .kind | map(. == \"bin\") | any ) | .name"))
|
||||
for BINARY in "${BINARIES[@]}"; do
|
||||
echo $BINARY
|
||||
xx-verify $TARGET_DIR/$(xx-cargo --print-target-triple)/release/$BINARY
|
||||
xx-verify $TARGET_DIR/$(xx-cargo --print-target-triple)/release/$BINARY
|
||||
cp $TARGET_DIR/$(xx-cargo --print-target-triple)/release/$BINARY /out/sbin/$BINARY
|
||||
done
|
||||
EOF
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
# Why does this exist?
|
||||
# Debian doesn't provide prebuilt musl packages
|
||||
# rocksdb requires a prebuilt liburing, and linking fails if a gnu one is provided
|
||||
|
||||
ARG RUST_VERSION=1
|
||||
ARG ALPINE_VERSION=3.22
|
||||
|
||||
FROM --platform=$BUILDPLATFORM docker.io/tonistiigi/xx AS xx
|
||||
FROM --platform=$BUILDPLATFORM rust:${RUST_VERSION}-alpine${ALPINE_VERSION} AS base
|
||||
FROM --platform=$BUILDPLATFORM rust:${RUST_VERSION}-alpine${ALPINE_VERSION} AS toolchain
|
||||
|
||||
# Install repo tools and dependencies
|
||||
RUN --mount=type=cache,target=/etc/apk/cache apk add \
|
||||
build-base pkgconfig make jq bash \
|
||||
curl git file \
|
||||
llvm-dev clang clang-static lld
|
||||
|
||||
|
||||
# Developer tool versions
|
||||
# renovate: datasource=github-releases depName=cargo-bins/cargo-binstall
|
||||
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
|
||||
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
|
||||
EOF
|
||||
|
||||
# Set up xx (cross-compilation scripts)
|
||||
COPY --from=xx / /
|
||||
ARG TARGETPLATFORM
|
||||
|
||||
# Install libraries linked by the binary
|
||||
RUN --mount=type=cache,target=/etc/apk/cache xx-apk add musl-dev gcc g++ liburing-dev
|
||||
|
||||
# Set up Rust toolchain
|
||||
WORKDIR /app
|
||||
COPY ./rust-toolchain.toml .
|
||||
RUN rustc --version \
|
||||
&& xx-cargo --setup-target-triple
|
||||
|
||||
# Build binary
|
||||
# We disable incremental compilation to save disk space, as it only produces a minimal speedup for this case.
|
||||
RUN echo "CARGO_INCREMENTAL=0" >> /etc/environment
|
||||
|
||||
# Configure pkg-config
|
||||
RUN <<EOF
|
||||
set -o xtrace
|
||||
if command -v "$(xx-info)-pkg-config" >/dev/null 2>/dev/null; then
|
||||
echo "PKG_CONFIG_LIBDIR=/usr/lib/$(xx-info)/pkgconfig" >> /etc/environment
|
||||
echo "PKG_CONFIG=/usr/bin/$(xx-info)-pkg-config" >> /etc/environment
|
||||
fi
|
||||
echo "PKG_CONFIG_ALLOW_CROSS=true" >> /etc/environment
|
||||
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
|
||||
echo "RUSTFLAGS='-Clinker-plugin-lto -Clink-arg=-fuse-ld=lld'" >> /etc/environment
|
||||
EOF
|
||||
|
||||
# Apply CPU-specific optimizations if TARGET_CPU is provided
|
||||
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
|
||||
echo "CXXFLAGS='${CXXFLAGS} -march=${TARGET_CPU}'" >> /etc/environment
|
||||
echo "RUSTFLAGS='${RUSTFLAGS} -C target-cpu=${TARGET_CPU}'" >> /etc/environment
|
||||
fi
|
||||
EOF
|
||||
|
||||
# Prepare output directories
|
||||
RUN mkdir /out
|
||||
|
||||
FROM toolchain AS builder
|
||||
|
||||
|
||||
# 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,id=cargo-target-${TARGET_CPU}-${TARGETPLATFORM}-musl-${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 --profile ${RUST_PROFILE} \
|
||||
-p $PACKAGE --no-default-features --features bindgen-static,release_max_log_level,standard;
|
||||
BINARIES=($(cargo metadata --no-deps --format-version 1 | \
|
||||
jq -r ".packages[] | select(.name == \"$PACKAGE\") | .targets[] | select( .kind | map(. == \"bin\") | any ) | .name"))
|
||||
for BINARY in "${BINARIES[@]}"; do
|
||||
echo $BINARY
|
||||
xx-verify $TARGET_DIR/$(xx-cargo --print-target-triple)/release/$BINARY
|
||||
cp $TARGET_DIR/$(xx-cargo --print-target-triple)/release/$BINARY /out/sbin/$BINARY
|
||||
done
|
||||
EOF
|
||||
|
||||
# Generate Software Bill of Materials (SBOM)
|
||||
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
|
||||
BINARY_BASE=$(basename ${BINARY})
|
||||
package=$(cargo metadata --no-deps --format-version 1 | jq -r ".packages[] | select(.targets[] | select( .kind | map(. == \"bin\") | any ) | .name == \"$BINARY_BASE\") | .name")
|
||||
if [ -z "$package" ]; then
|
||||
continue
|
||||
fi
|
||||
PACKAGES[$package]=1
|
||||
done
|
||||
for PACKAGE in $(echo ${!PACKAGES[@]}); do
|
||||
echo $PACKAGE
|
||||
cargo sbom --cargo-package $PACKAGE > /out/sbom/$PACKAGE.spdx.json
|
||||
done
|
||||
EOF
|
||||
|
||||
# Extract dynamically linked dependencies
|
||||
RUN <<EOF
|
||||
set -o xtrace
|
||||
mkdir /out/libs
|
||||
mkdir /out/libs-root
|
||||
for BINARY in /out/sbin/*; do
|
||||
lddtree "$BINARY" | awk '{print $(NF-0) " " $1}' | sort -u -k 1,1 | awk '{print "install", "-D", $1, (($2 ~ /^\//) ? "/out/libs-root" $2 : "/out/libs/" $2)}' | xargs -I {} sh -c {}
|
||||
done
|
||||
EOF
|
||||
|
||||
FROM scratch
|
||||
|
||||
WORKDIR /
|
||||
|
||||
# Copy root certs for tls into image
|
||||
# You can also mount the certs from the host
|
||||
# --volume /etc/ssl/certs:/etc/ssl/certs:ro
|
||||
COPY --from=base /etc/ssl/certs /etc/ssl/certs
|
||||
|
||||
# Copy our build
|
||||
COPY --from=builder /out/sbin/ /sbin/
|
||||
# Copy SBOM
|
||||
COPY --from=builder /out/sbom/ /sbom/
|
||||
|
||||
# Copy dynamic libraries to root
|
||||
COPY --from=builder /out/libs-root/ /
|
||||
COPY --from=builder /out/libs/ /usr/lib/
|
||||
|
||||
# Inform linker where to find libraries
|
||||
ENV LD_LIBRARY_PATH=/usr/lib
|
||||
|
||||
# Continuwuity default port
|
||||
EXPOSE 8008
|
||||
|
||||
CMD ["/sbin/conduwuit"]
|
||||
@@ -18,6 +18,7 @@
|
||||
- [Admin Command Reference](admin_reference.md)
|
||||
- [Development](development.md)
|
||||
- [Contributing](contributing.md)
|
||||
- [Code Style Guide](development/code_style.md)
|
||||
- [Testing](development/testing.md)
|
||||
- [Hot Reloading ("Live" Development)](development/hot_reload.md)
|
||||
- [Community (and Guidelines)](community.md)
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
## Getting help
|
||||
|
||||
If you run into any problems while setting up an Appservice: ask us in
|
||||
[#continuwuity:continuwuity.org](https://matrix.to/#/#continuwuity:continuwuity.org) or
|
||||
[#continuwuity:continuwuity.org](https://matrix.to/#/#continuwuity:continuwuity.org?via=continuwuity.org&via=ellis.link&via=explodie.org&via=matrix.org) or
|
||||
[open an issue on Forgejo](https://forgejo.ellis.link/continuwuation/continuwuity/issues/new).
|
||||
|
||||
## Set up the appservice - general instructions
|
||||
|
||||
+4
-4
@@ -75,9 +75,9 @@ 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 Matrix Space](https://matrix.to/#/#space:continuwuity.org?via=continuwuity.org&via=ellis.link&via=explodie.org&via=matrix.org) and its rooms, including:
|
||||
|
||||
### [#continuwuity:continuwuity.org](https://matrix.to/#/#continuwuity:continuwuity.org)
|
||||
### [#continuwuity:continuwuity.org](https://matrix.to/#/#continuwuity:continuwuity.org?via=continuwuity.org&via=ellis.link&via=explodie.org&via=matrix.org)
|
||||
|
||||
This room is for support and discussions about Continuwuity. Ask questions, share insights, and help
|
||||
each other out while adhering to these guidelines.
|
||||
@@ -85,7 +85,7 @@ 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)
|
||||
### [#offtopic:continuwuity.org](https://matrix.to/#/#offtopic:continuwuity.org?via=continuwuity.org&via=ellis.link&via=explodie.org&via=matrix.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
|
||||
@@ -95,7 +95,7 @@ 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)
|
||||
### [#dev:continuwuity.org](https://matrix.to/#/#dev:continuwuity.org?via=continuwuity.org&via=ellis.link&via=explodie.org&via=matrix.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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Continuwuity for Arch Linux
|
||||
|
||||
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`
|
||||
Continuwuity is available in the `archlinuxcn` repository and AUR with the same package name `continuwuity`, which includes the latest tagged 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.
|
||||
Simply install the `continuwuity` package. Configure the service in `/etc/conduwuit/conduwuit.toml`, then enable and start the continuwuity.service.
|
||||
|
||||
+23
-21
@@ -2,7 +2,7 @@
|
||||
|
||||
## Docker
|
||||
|
||||
To run Continuwuity 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
|
||||
@@ -26,7 +26,7 @@ to pull it to your machine.
|
||||
|
||||
### Run
|
||||
|
||||
When you have the image you can simply run it with
|
||||
When you have the image, you can simply run it with
|
||||
|
||||
```bash
|
||||
docker run -d -p 8448:6167 \
|
||||
@@ -36,7 +36,7 @@ docker run -d -p 8448:6167 \
|
||||
--name continuwuity $LINK
|
||||
```
|
||||
|
||||
or you can use [docker compose](#docker-compose).
|
||||
or you can use [Docker Compose](#docker-compose).
|
||||
|
||||
The `-d` flag lets the container run in detached mode. You may supply an
|
||||
optional `continuwuity.toml` config file, the example config can be found
|
||||
@@ -46,15 +46,15 @@ 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 Continuwuity for a short time, you can use the `--rm`
|
||||
flag, which will clean up everything related to your container after you stop
|
||||
flag, which cleans up everything related to your container after you stop
|
||||
it.
|
||||
|
||||
### Docker-compose
|
||||
|
||||
If the `docker run` command is not for you or your setup, you can also use one
|
||||
If the `docker run` command is not suitable for you or your setup, you can also use one
|
||||
of the provided `docker-compose` files.
|
||||
|
||||
Depending on your proxy setup, you can use one of the following files;
|
||||
Depending on your proxy setup, you can use one of the following files:
|
||||
|
||||
- If you already have a `traefik` instance set up, use
|
||||
[`docker-compose.for-traefik.yml`](docker-compose.for-traefik.yml)
|
||||
@@ -65,7 +65,7 @@ Depending on your proxy setup, you can use one of the following files;
|
||||
`example.com` placeholders with your own domain
|
||||
- For any other reverse proxy, use [`docker-compose.yml`](docker-compose.yml)
|
||||
|
||||
When picking the traefik-related compose file, rename it so it matches
|
||||
When picking the Traefik-related compose file, rename it to
|
||||
`docker-compose.yml`, and rename the override file to
|
||||
`docker-compose.override.yml`. Edit the latter with the values you want for your
|
||||
server.
|
||||
@@ -77,18 +77,18 @@ create the `caddy` network before spinning up the containers:
|
||||
docker network create caddy
|
||||
```
|
||||
|
||||
After that, you can rename it so it matches `docker-compose.yml` and spin up the
|
||||
After that, you can rename it to `docker-compose.yml` and spin up the
|
||||
containers!
|
||||
|
||||
Additional info about deploying Continuwuity can be found [here](generic.md).
|
||||
|
||||
### Build
|
||||
|
||||
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.
|
||||
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 efficient multi-platform builds.
|
||||
|
||||
The resulting images are broadly compatible with Docker and other container runtimes like Podman or containerd.
|
||||
The resulting images are widely compatible with Docker and other container runtimes like Podman or containerd.
|
||||
|
||||
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 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.
|
||||
|
||||
To build an image locally using Docker Buildx, you can typically run a command like:
|
||||
|
||||
@@ -109,8 +109,8 @@ Refer to the Docker Buildx documentation for more advanced build options.
|
||||
|
||||
### Run
|
||||
|
||||
If you already have built the image or want to use one from the registries, you
|
||||
can just start the container and everything else in the compose file in detached
|
||||
If you have already built the image or want to use one from the registries, you
|
||||
can start the container and everything else in the compose file in detached
|
||||
mode with:
|
||||
|
||||
```bash
|
||||
@@ -121,22 +121,24 @@ docker compose up -d
|
||||
|
||||
### Use Traefik as Proxy
|
||||
|
||||
As a container user, you probably know about Traefik. It is a easy to use
|
||||
reverse proxy for making containerized app and services available through the
|
||||
As a container user, you probably know about Traefik. It is an easy-to-use
|
||||
reverse proxy for making containerized apps and services available through the
|
||||
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 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 Continuwuity to federate, we need to either
|
||||
expose ports `443` and `8448` or serve two endpoints `.well-known/matrix/client`
|
||||
to deploy and use Continuwuity, with a small caveat. If you have already looked at
|
||||
the files, you should have seen the `well-known` service, which is the
|
||||
small caveat. Traefik is simply a proxy and load balancer and cannot
|
||||
serve any kind of content. 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`.
|
||||
|
||||
With the service `well-known` we use a single `nginx` container that will serve
|
||||
With the service `well-known`, we use a single `nginx` container that serves
|
||||
those two files.
|
||||
|
||||
Alternatively, you can use Continuwuity's built-in delegation file capability. Set up the delegation files in the configuration file, and then proxy paths under `/.well-known/matrix` to continuwuity. For example, the label ``traefik.http.routers.continuwuity.rule=(Host(`matrix.ellis.link`) || (Host(`ellis.link`) && PathPrefix(`/.well-known/matrix`)))`` does this for the domain `ellis.link`.
|
||||
|
||||
## Voice communication
|
||||
|
||||
See the [TURN](../turn.md) page.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Continuwuity for FreeBSD
|
||||
|
||||
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.
|
||||
Continuwuity currently does not provide FreeBSD builds or FreeBSD packaging. However, Continuwuity does build and work on FreeBSD using the system-provided RocksDB.
|
||||
|
||||
Contributions for getting Continuwuity packaged are welcome.
|
||||
Contributions to get Continuwuity packaged for FreeBSD are welcome.
|
||||
|
||||
+59
-54
@@ -13,31 +13,42 @@
|
||||
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
|
||||
You can download prebuilt fully static musl binaries from the latest tagged
|
||||
release [here](https://forgejo.ellis.link/continuwuation/continuwuity/releases/latest) or
|
||||
`main` CI branch workflow artifact output. These also include Debian/Ubuntu
|
||||
from the `main` CI branch workflow artifact output. These also include Debian/Ubuntu
|
||||
packages.
|
||||
|
||||
These can be curl'd directly from. `ci-bins` are CI workflow binaries by commit
|
||||
You can download these directly using curl. The `ci-bins` are CI workflow binaries organized by commit
|
||||
hash/revision, and `releases` are tagged releases. Sort by descending last
|
||||
modified for the latest.
|
||||
modified date to find the latest.
|
||||
|
||||
These binaries have jemalloc and io_uring statically linked and included with
|
||||
them, so no additional dynamic dependencies need to be installed.
|
||||
|
||||
For the **best** performance; if using an `x86_64` CPU made in the last ~15 years,
|
||||
we recommend using the `-haswell-` optimised binaries. This sets
|
||||
`-march=haswell` which is the most compatible and highest performance with
|
||||
optimised binaries. The database backend, RocksDB, most benefits from this as it
|
||||
will then use hardware accelerated CRC32 hashing/checksumming which is critical
|
||||
For the **best** performance: if you are using an `x86_64` CPU made in the last ~15 years,
|
||||
we recommend using the `-haswell-` optimized binaries. These set
|
||||
`-march=haswell`, which provides the most compatible and highest performance with
|
||||
optimized binaries. The database backend, RocksDB, benefits most from this as it
|
||||
uses hardware-accelerated CRC32 hashing/checksumming, which is critical
|
||||
for performance.
|
||||
|
||||
### Compiling
|
||||
|
||||
Alternatively, you may compile the binary yourself. We recommend using
|
||||
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.
|
||||
Alternatively, you may compile the binary yourself.
|
||||
|
||||
### Building with the Rust toolchain
|
||||
|
||||
If wanting to build using standard Rust toolchains, make sure you install:
|
||||
|
||||
- (On linux) `liburing-dev` on the compiling machine, and `liburing` on the target host
|
||||
- (On linux) `pkg-config` on the compiling machine to allow finding `liburing`
|
||||
- A C++ compiler and (on linux) `libclang` for RocksDB
|
||||
|
||||
You can build Continuwuity using `cargo build --release`.
|
||||
|
||||
### Building with Nix
|
||||
|
||||
If you prefer, you can use Nix (or [Lix](https://lix.systems)) to build Continuwuity. This provides improved reproducibility and makes it easy to set up a build environment and generate output. This approach also allows for easy cross-compilation.
|
||||
|
||||
You can run the `nix build -L .#static-x86_64-linux-musl-all-features` or
|
||||
`nix build -L .#static-aarch64-linux-musl-all-features` commands based
|
||||
@@ -45,17 +56,11 @@ on architecture to cross-compile the necessary static binary located at
|
||||
`result/bin/conduwuit`. This is reproducible with the static binaries produced
|
||||
in our CI.
|
||||
|
||||
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 Continuwuity using `cargo build --release --all-features`
|
||||
|
||||
## Adding a Continuwuity user
|
||||
|
||||
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.
|
||||
While Continuwuity can run as any user, it is better to use dedicated users for
|
||||
different services. This also ensures that the file permissions
|
||||
are set up correctly.
|
||||
|
||||
In Debian, you can use this command to create a Continuwuity user:
|
||||
|
||||
@@ -71,18 +76,18 @@ 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. Continuwuity has config options for doing delegation, or you can configure
|
||||
your reverse proxy to manually serve the necessary JSON files to do delegation
|
||||
Matrix's default federation port is 8448, and clients must use port 443.
|
||||
If you would like to use only port 443 or a different port, you will need to set up
|
||||
delegation. Continuwuity has configuration options for delegation, or you can configure
|
||||
your reverse proxy to manually serve the necessary JSON files for delegation
|
||||
(see the `[global.well_known]` config section).
|
||||
|
||||
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.
|
||||
IP address than the host system, you need to forward these public ports directly
|
||||
or indirectly to the port mentioned in the configuration.
|
||||
|
||||
Note for NAT users; if you have trouble connecting to your server from the inside
|
||||
of your network, you need to research your router and see if it supports "NAT
|
||||
Note for NAT users: if you have trouble connecting to your server from inside
|
||||
your network, check if your router supports "NAT
|
||||
hairpinning" or "NAT loopback".
|
||||
|
||||
If your router does not support this feature, you need to research doing local
|
||||
@@ -92,19 +97,19 @@ on the network level, consider something like NextDNS or Pi-Hole.
|
||||
|
||||
## Setting up a systemd service
|
||||
|
||||
Two example systemd units for Continuwuity can be found
|
||||
You can find two example systemd units for Continuwuity
|
||||
[on the configuration page](../configuration/examples.md#debian-systemd-unit-file).
|
||||
You may need to change the `ExecStart=` path to where you placed the Continuwuity
|
||||
binary if it is not `/usr/bin/conduwuit`.
|
||||
You may need to change the `ExecStart=` path to match where you placed the Continuwuity
|
||||
binary if it is not in `/usr/bin/conduwuit`.
|
||||
|
||||
On systems where rsyslog is used alongside journald (i.e. Red Hat-based distros
|
||||
and OpenSUSE), put `$EscapeControlCharactersOnReceive off` inside
|
||||
`/etc/rsyslog.conf` to allow color in logs.
|
||||
|
||||
If you are using a different `database_path` other than the systemd unit
|
||||
If you are using a different `database_path` than the systemd unit's
|
||||
configured default `/var/lib/conduwuit`, you need to add your path to the
|
||||
systemd unit's `ReadWritePaths=`. This can be done by either directly editing
|
||||
`conduwuit.service` and reloading systemd, or running `systemctl edit conduwuit.service`
|
||||
systemd unit's `ReadWritePaths=`. You can do this by either directly editing
|
||||
`conduwuit.service` and reloading systemd, or by running `systemctl edit conduwuit.service`
|
||||
and entering the following:
|
||||
|
||||
```
|
||||
@@ -114,8 +119,8 @@ ReadWritePaths=/path/to/custom/database/path
|
||||
|
||||
## Creating the Continuwuity configuration file
|
||||
|
||||
Now we need to create the Continuwuity's config file in
|
||||
`/etc/continuwuity/continuwuity.toml`. The example config can be found at
|
||||
Now you need to create the Continuwuity configuration file in
|
||||
`/etc/continuwuity/continuwuity.toml`. You can find an example configuration at
|
||||
[conduwuit-example.toml](../configuration/examples.md).
|
||||
|
||||
**Please take a moment to read the config. You need to change at least the
|
||||
@@ -125,8 +130,8 @@ RocksDB is the only supported database backend.
|
||||
|
||||
## Setting the correct file permissions
|
||||
|
||||
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:
|
||||
If you are using a dedicated user for Continuwuity, you need to allow it to
|
||||
read the configuration. To do this, run:
|
||||
|
||||
```bash
|
||||
sudo chown -R root:root /etc/conduwuit
|
||||
@@ -143,13 +148,13 @@ sudo chmod 700 /var/lib/conduwuit/
|
||||
|
||||
## Setting up the Reverse Proxy
|
||||
|
||||
We recommend Caddy as a reverse proxy, as it is trivial to use, handling TLS certificates, reverse proxy headers, etc transparently with proper defaults.
|
||||
We recommend Caddy as a reverse proxy because it is trivial to use and handles TLS certificates, reverse proxy headers, etc. transparently with proper defaults.
|
||||
For other software, please refer to their respective documentation or online guides.
|
||||
|
||||
### Caddy
|
||||
|
||||
After installing Caddy via your preferred method, create `/etc/caddy/conf.d/conduwuit_caddyfile`
|
||||
and enter this (substitute for your server name).
|
||||
and enter the following (substitute your actual server name):
|
||||
|
||||
```caddyfile
|
||||
your.server.name, your.server.name:8448 {
|
||||
@@ -168,11 +173,11 @@ sudo systemctl enable --now caddy
|
||||
|
||||
### Other Reverse Proxies
|
||||
|
||||
As we would prefer our users to use Caddy, we will not provide configuration files for other proxys.
|
||||
As we prefer our users to use Caddy, we do not provide configuration files for other proxies.
|
||||
|
||||
You will need to reverse proxy everything under following routes:
|
||||
You will need to reverse proxy everything under the following routes:
|
||||
- `/_matrix/` - core Matrix C-S and S-S APIs
|
||||
- `/_conduwuit/` - ad-hoc Continuwuity routes such as `/local_user_count` and
|
||||
- `/_conduwuit/` and/or `/_continuwuity/` - ad-hoc Continuwuity routes such as `/local_user_count` and
|
||||
`/server_version`
|
||||
|
||||
You can optionally reverse proxy the following individual routes:
|
||||
@@ -193,16 +198,16 @@ Examples of delegation:
|
||||
|
||||
For Apache and Nginx there are many examples available online.
|
||||
|
||||
Lighttpd is not supported as it seems to mess with the `X-Matrix` Authorization
|
||||
header, making federation non-functional. If a workaround is found, feel free to share to get it added to the documentation here.
|
||||
Lighttpd is not supported as it appears to interfere with the `X-Matrix` Authorization
|
||||
header, making federation non-functional. If you find a workaround, please share it so we can add it to this documentation.
|
||||
|
||||
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 Apache, you need to use `nocanon` in your `ProxyPass` directive to prevent httpd from interfering with the `X-Matrix` header (note that Apache is not ideal as a general reverse proxy, so we discourage using it if alternatives are available).
|
||||
|
||||
If using Nginx, you need to give Continuwuity the request URI using `$request_uri`, or like so:
|
||||
If using Nginx, you need to pass the request URI to Continuwuity using `$request_uri`, like this:
|
||||
- `proxy_pass http://127.0.0.1:6167$request_uri;`
|
||||
- `proxy_pass http://127.0.0.1:6167;`
|
||||
|
||||
Nginx users need to increase `client_max_body_size` (default is 1M) to match
|
||||
Nginx users need to increase the `client_max_body_size` setting (default is 1M) to match the
|
||||
`max_request_size` defined in conduwuit.toml.
|
||||
|
||||
## You're done
|
||||
@@ -222,7 +227,7 @@ sudo systemctl enable conduwuit
|
||||
## How do I know it works?
|
||||
|
||||
You can open [a Matrix client](https://matrix.org/ecosystem/clients), enter your
|
||||
homeserver and try to register.
|
||||
homeserver address, and try to register.
|
||||
|
||||
You can also use these commands as a quick health check (replace
|
||||
`your.server.name`).
|
||||
@@ -237,10 +242,10 @@ curl https://your.server.name:8448/_conduwuit/server_version
|
||||
curl https://your.server.name:8448/_matrix/federation/v1/version
|
||||
```
|
||||
|
||||
- To check if your server can talk with other homeservers, you can use the
|
||||
- To check if your server can communicate with other homeservers, use the
|
||||
[Matrix Federation Tester](https://federationtester.matrix.org/). If you can
|
||||
register but cannot join federated rooms check your config again and also check
|
||||
if the port 8448 is open and forwarded correctly.
|
||||
register but cannot join federated rooms, check your configuration and verify
|
||||
that port 8448 is open and forwarded correctly.
|
||||
|
||||
# What's next?
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# Continuwuity for Kubernetes
|
||||
|
||||
Continuwuity doesn't support horizontal scalability or distributed loading
|
||||
natively, however a community maintained Helm Chart is available here to run
|
||||
natively. However, a community-maintained Helm Chart is available here to run
|
||||
conduwuit on Kubernetes: <https://gitlab.cronce.io/charts/conduwuit>
|
||||
|
||||
This should be compatible with continuwuity, but you will need to change the image reference.
|
||||
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.
|
||||
If changes need to be made, please reach out to the maintainer, as this is not maintained or controlled by the Continuwuity maintainers.
|
||||
|
||||
+103
-48
@@ -1,75 +1,130 @@
|
||||
# Continuwuity for NixOS
|
||||
|
||||
Continuwuity can be acquired by Nix (or [Lix][lix]) from various places:
|
||||
NixOS packages Continuwuity as `matrix-continuwuity`. This package includes both the Continuwuity software and a dedicated NixOS module for configuration and deployment.
|
||||
|
||||
* The `flake.nix` at the root of the repo
|
||||
* The `default.nix` at the root of the repo
|
||||
* From Continuwuity's binary cache
|
||||
## Installation methods
|
||||
|
||||
### NixOS module
|
||||
You can acquire Continuwuity with Nix (or [Lix][lix]) from these sources:
|
||||
|
||||
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
|
||||
Continuwuity.
|
||||
* Directly from Nixpkgs using the official package (`pkgs.matrix-continuwuity`)
|
||||
* The `flake.nix` at the root of the Continuwuity repo
|
||||
* The `default.nix` at the root of the Continuwuity repo
|
||||
|
||||
### Conduit NixOS Config Module and SQLite
|
||||
## NixOS module
|
||||
|
||||
Beware! The [`services.matrix-conduit`][module] module defaults to SQLite as a database backend.
|
||||
Continuwuity dropped SQLite support in favor of exclusively supporting the much faster RocksDB.
|
||||
Make sure that you are using the RocksDB backend before migrating!
|
||||
Continuwuity now has an official NixOS module that simplifies configuration and deployment. The module is available in Nixpkgs as `services.matrix-continuwuity` from NixOS 25.05.
|
||||
|
||||
There is a [tool to migrate a Conduit SQLite database to
|
||||
RocksDB](https://github.com/ShadowJonathan/conduit_toolbox/).
|
||||
Here's a basic example of how to use the module:
|
||||
|
||||
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 Continuwuity instead of Conduit.
|
||||
```nix
|
||||
{ config, pkgs, ... }:
|
||||
|
||||
{
|
||||
services.matrix-continuwuity = {
|
||||
enable = true;
|
||||
settings = {
|
||||
global = {
|
||||
server_name = "example.com";
|
||||
# Listening on localhost by default
|
||||
# address and port are handled automatically
|
||||
allow_registration = false;
|
||||
allow_encryption = true;
|
||||
allow_federation = true;
|
||||
trusted_servers = [ "matrix.org" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### Available options
|
||||
|
||||
The NixOS module provides these configuration options:
|
||||
|
||||
- `enable`: Enable the Continuwuity service
|
||||
- `user`: The user to run Continuwuity as (defaults to "continuwuity")
|
||||
- `group`: The group to run Continuwuity as (defaults to "continuwuity")
|
||||
- `extraEnvironment`: Extra environment variables to pass to the Continuwuity server
|
||||
- `package`: The Continuwuity package to use
|
||||
- `settings`: The Continuwuity configuration (in TOML format)
|
||||
|
||||
Use the `settings` option to configure Continuwuity itself. See the [example configuration file](../configuration/examples.md#example-configuration) for all available options.
|
||||
|
||||
### UNIX sockets
|
||||
|
||||
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
|
||||
`port` config options.
|
||||
The NixOS module natively supports UNIX sockets through the `global.unix_socket_path` option. When using UNIX sockets, set `global.address` to `null`:
|
||||
|
||||
```nix
|
||||
options.services.matrix-conduit.settings = lib.mkOption {
|
||||
apply = old: old // (
|
||||
if (old.global ? "unix_socket_path")
|
||||
then { global = builtins.removeAttrs old.global [ "address" "port" ]; }
|
||||
else { }
|
||||
);
|
||||
services.matrix-continuwuity = {
|
||||
enable = true;
|
||||
settings = {
|
||||
global = {
|
||||
server_name = "example.com";
|
||||
address = null; # Must be null when using unix_socket_path
|
||||
unix_socket_path = "/run/continuwuity/continuwuity.sock";
|
||||
unix_socket_perms = 660; # Default permissions for the socket
|
||||
# ...
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
Additionally, the [`matrix-conduit` systemd unit][systemd-unit] in the module does not allow
|
||||
the `AF_UNIX` socket address family in their systemd unit's `RestrictAddressFamilies=` which
|
||||
disallows the namespace from accessing or creating UNIX sockets and has to be enabled like so:
|
||||
The module automatically sets the correct `RestrictAddressFamilies` in the systemd service configuration to allow access to UNIX sockets.
|
||||
|
||||
```nix
|
||||
systemd.services.conduit.serviceConfig.RestrictAddressFamilies = [ "AF_UNIX" ];
|
||||
```
|
||||
### RocksDB database
|
||||
|
||||
Even though those workarounds are feasible a Continuwuity NixOS configuration module, developed and
|
||||
published by the community, would be appreciated.
|
||||
Continuwuity exclusively uses RocksDB as its database backend. The system configures the database path automatically to `/var/lib/continuwuity/` and you cannot change it due to the service's reliance on systemd's StateDir.
|
||||
|
||||
If you're migrating from Conduit with SQLite, use this [tool to migrate a Conduit SQLite database to RocksDB](https://github.com/ShadowJonathan/conduit_toolbox/).
|
||||
|
||||
### jemalloc and hardened profile
|
||||
|
||||
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:
|
||||
Continuwuity uses jemalloc by default. This may interfere with the [`hardened.nix` profile][hardened.nix] because it uses `scudo` by default. Either disable/hide `scudo` from Continuwuity or disable jemalloc like this:
|
||||
|
||||
```nix
|
||||
let
|
||||
conduwuit = pkgs.unstable.conduwuit.override {
|
||||
enableJemalloc = false;
|
||||
};
|
||||
in
|
||||
services.matrix-continuwuity = {
|
||||
enable = true;
|
||||
package = pkgs.matrix-continuwuity.override {
|
||||
enableJemalloc = false;
|
||||
};
|
||||
# ...
|
||||
};
|
||||
```
|
||||
|
||||
## Upgrading from Conduit
|
||||
|
||||
If you previously used Conduit with the `services.matrix-conduit` module:
|
||||
|
||||
1. Ensure your Conduit uses the RocksDB backend, or migrate from SQLite using the [migration tool](https://github.com/ShadowJonathan/conduit_toolbox/)
|
||||
2. Switch to the new module by changing `services.matrix-conduit` to `services.matrix-continuwuity` in your configuration
|
||||
3. Update any custom configuration to match the new module's structure
|
||||
|
||||
## Reverse proxy configuration
|
||||
|
||||
You'll need to set up a reverse proxy (like nginx or caddy) to expose Continuwuity to the internet. Configure your reverse proxy to forward requests to `/_matrix` on port 443 and 8448 to your Continuwuity instance.
|
||||
|
||||
Here's an example nginx configuration:
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
listen 8448 ssl;
|
||||
listen [::]:8448 ssl;
|
||||
|
||||
server_name example.com;
|
||||
|
||||
# SSL configuration here...
|
||||
|
||||
location /_matrix/ {
|
||||
proxy_pass http://127.0.0.1:6167$request_uri;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
[lix]: https://lix.systems/
|
||||
[module]: https://search.nixos.org/options?channel=unstable&query=services.matrix-conduit
|
||||
[package]: https://search.nixos.org/options?channel=unstable&query=services.matrix-conduit.package
|
||||
[hardened.nix]: https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/profiles/hardened.nix#L22
|
||||
[systemd-unit]: https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/matrix/conduit.nix#L132
|
||||
[hardened.nix]: https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/profiles/hardened.nix
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
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).
|
||||
[contributor's guide](./contributing.md) and [code style guide](./development/code_style.md).
|
||||
|
||||
## Continuwuity project layout
|
||||
|
||||
|
||||
@@ -0,0 +1,331 @@
|
||||
# Code Style Guide
|
||||
|
||||
This guide outlines the coding standards and best practices for Continuwuity development. These guidelines help avoid bugs and maintain code consistency, readability, and quality across the project.
|
||||
|
||||
These guidelines apply to new code on a best-effort basis. When modifying existing code, follow existing patterns in the immediate area you're changing and then gradually improve code style when making substantial changes.
|
||||
|
||||
## General Principles
|
||||
|
||||
- **Clarity over cleverness**: Write code that is easy to understand and maintain
|
||||
- **Consistency**: Pragmatically follow existing patterns in the codebase, rather than adding new dependencies.
|
||||
- **Safety**: Prefer safe, explicit code over unsafe code with implicit requirements
|
||||
- **Performance**: Consider performance implications, but not at the expense of correctness or maintainability
|
||||
|
||||
## Formatting and Linting
|
||||
|
||||
All code must satisfy lints (clippy, rustc, rustdoc, etc) and be formatted using **nightly** rustfmt (`cargo +nightly fmt`). Many of the `rustfmt.toml` features depend on the nightly toolchain.
|
||||
|
||||
If you need to allow a lint, ensure it's either obvious why (e.g. clippy saying redundant clone but it's actually required) or add a comment explaining the reason. Do not write inefficient code just to satisfy lints. If a lint is wrong and provides a less efficient solution, allow the lint and mention that in a comment.
|
||||
|
||||
If making large formatting changes across unrelated files, create a separate commit so it can be added to the `.git-blame-ignore-revs` file.
|
||||
|
||||
## Rust-Specific Guidelines
|
||||
|
||||
### Naming Conventions
|
||||
|
||||
Follow standard Rust naming conventions as outlined in the [Rust API Guidelines](https://rust-lang.github.io/api-guidelines/naming.html):
|
||||
|
||||
- Use `snake_case` for functions, variables, and modules
|
||||
- Use `PascalCase` for types, traits, and enum variants
|
||||
- Use `SCREAMING_SNAKE_CASE` for constants and statics
|
||||
- Use descriptive names that clearly indicate purpose
|
||||
|
||||
```rs
|
||||
// Good
|
||||
fn process_user_request(user_id: &UserId) -> Result<Response, Error> { ... }
|
||||
|
||||
const MAX_RETRY_ATTEMPTS: usize = 3;
|
||||
|
||||
struct UserSession {
|
||||
session_id: String,
|
||||
created_at: SystemTime,
|
||||
}
|
||||
|
||||
// Avoid
|
||||
fn proc_reqw(id: &str) -> Result<Resp, Err> { ... }
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
|
||||
- Use `Result<T, E>` for operations that can fail
|
||||
- Prefer specific error types over generic ones
|
||||
- Use `?` operator for error propagation
|
||||
- Provide meaningful error messages
|
||||
- If needed, create or use an error enum.
|
||||
|
||||
```rs
|
||||
// Good
|
||||
fn parse_server_name(input: &str) -> Result<ServerName, InvalidServerNameError> {
|
||||
ServerName::parse(input)
|
||||
.map_err(|_| InvalidServerNameError::new(input))
|
||||
}
|
||||
|
||||
// Avoid
|
||||
fn parse_server_name(input: &str) -> Result<ServerName, Box<dyn Error>> {
|
||||
Ok(ServerName::parse(input).unwrap())
|
||||
}
|
||||
```
|
||||
|
||||
### Option Handling
|
||||
|
||||
- Prefer explicit `Option` handling over unwrapping
|
||||
- Use combinators like `map`, `and_then`, `unwrap_or_else` when appropriate
|
||||
|
||||
```rs
|
||||
// Good
|
||||
let display_name = user.display_name
|
||||
.as_ref()
|
||||
.map(|name| name.trim())
|
||||
.filter(|name| !name.is_empty())
|
||||
.unwrap_or(&user.localpart);
|
||||
|
||||
// Avoid
|
||||
let display_name = if user.display_name.is_some() {
|
||||
user.display_name.as_ref().unwrap()
|
||||
} else {
|
||||
&user.localpart
|
||||
};
|
||||
```
|
||||
|
||||
## Logging Guidelines
|
||||
|
||||
### Structured Logging
|
||||
|
||||
**Always use structured logging instead of string interpolation.** This improves log parsing, filtering, and observability.
|
||||
|
||||
```rs
|
||||
// Good - structured parameters
|
||||
debug!(
|
||||
room_id = %room_id,
|
||||
user_id = %user_id,
|
||||
event_type = ?event.event_type(),
|
||||
"Processing room event"
|
||||
);
|
||||
|
||||
info!(
|
||||
server_name = %server_name,
|
||||
response_time_ms = response_time.as_millis(),
|
||||
"Federation request completed successfully"
|
||||
);
|
||||
|
||||
// Avoid - string interpolation
|
||||
debug!("Processing room event for {room_id} from {user_id}");
|
||||
info!("Federation request to {server_name} took {response_time:?}");
|
||||
```
|
||||
|
||||
### Log Levels
|
||||
|
||||
Use appropriate log levels:
|
||||
|
||||
- `error!`: Unrecoverable errors that affect functionality
|
||||
- `warn!`: Potentially problematic situations that don't stop execution
|
||||
- `info!`: General information about application flow
|
||||
- `debug!`: Detailed information for debugging
|
||||
- `trace!`: Very detailed information, typically only useful during development
|
||||
|
||||
Keep in mind the frequency that the log will be reached, and the relevancy to a server operator.
|
||||
|
||||
```rs
|
||||
// Good
|
||||
error!(
|
||||
error = %err,
|
||||
room_id = %room_id,
|
||||
"Failed to send event to room"
|
||||
);
|
||||
|
||||
warn!(
|
||||
server_name = %server_name,
|
||||
attempt = retry_count,
|
||||
"Federation request failed, retrying"
|
||||
);
|
||||
|
||||
info!(
|
||||
user_id = %user_id,
|
||||
"User registered successfully"
|
||||
);
|
||||
|
||||
debug!(
|
||||
event_id = %event_id,
|
||||
auth_events = ?auth_event_ids,
|
||||
"Validating event authorization"
|
||||
);
|
||||
```
|
||||
|
||||
### Sensitive Information
|
||||
|
||||
Never log sensitive information such as:
|
||||
- Access tokens
|
||||
- Passwords
|
||||
- Private keys
|
||||
- Personal user data (unless specifically needed for debugging)
|
||||
|
||||
```rs
|
||||
// Good
|
||||
debug!(
|
||||
user_id = %user_id,
|
||||
session_id = %session_id,
|
||||
"Processing authenticated request"
|
||||
);
|
||||
|
||||
// Avoid
|
||||
debug!(
|
||||
user_id = %user_id,
|
||||
access_token = %access_token,
|
||||
"Processing authenticated request"
|
||||
);
|
||||
```
|
||||
|
||||
## Lock Management
|
||||
|
||||
### Explicit Lock Scopes
|
||||
|
||||
**Always use closure guards instead of implicitly dropped guards.** This makes lock scopes explicit and helps prevent deadlocks.
|
||||
|
||||
Use the `WithLock` trait from `core::utils::with_lock`:
|
||||
|
||||
```rs
|
||||
use conduwuit::utils::with_lock::WithLock;
|
||||
|
||||
// Good - explicit closure guard
|
||||
shared_data.with_lock(|data| {
|
||||
data.counter += 1;
|
||||
data.last_updated = SystemTime::now();
|
||||
// Lock is explicitly released here
|
||||
});
|
||||
|
||||
// Avoid - implicit guard
|
||||
{
|
||||
let mut data = shared_data.lock().unwrap();
|
||||
data.counter += 1;
|
||||
data.last_updated = SystemTime::now();
|
||||
// Lock released when guard goes out of scope - less explicit
|
||||
}
|
||||
```
|
||||
|
||||
For async contexts, use the async variant:
|
||||
|
||||
```rs
|
||||
use conduwuit::utils::with_lock::WithLockAsync;
|
||||
|
||||
// Good - async closure guard
|
||||
async_shared_data.with_lock(|data| {
|
||||
data.process_async_update();
|
||||
}).await;
|
||||
```
|
||||
|
||||
### Lock Ordering
|
||||
|
||||
When acquiring multiple locks, always acquire them in a consistent order to prevent deadlocks:
|
||||
|
||||
```rs
|
||||
// Good - consistent ordering (e.g., by memory address or logical hierarchy)
|
||||
let locks = [&lock_a, &lock_b, &lock_c];
|
||||
locks.sort_by_key(|lock| lock as *const _ as usize);
|
||||
|
||||
for lock in locks {
|
||||
lock.with_lock(|data| {
|
||||
// Process data
|
||||
});
|
||||
}
|
||||
|
||||
// Avoid - inconsistent ordering that can cause deadlocks
|
||||
lock_b.with_lock(|data_b| {
|
||||
lock_a.with_lock(|data_a| {
|
||||
// Deadlock risk if another thread acquires in A->B order
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
### Code Comments
|
||||
|
||||
- Reference related documentation or parts of the specification
|
||||
- When a task has multiple ways of being acheved, explain your reasoning for your decision
|
||||
- Update comments when code changes
|
||||
|
||||
```rs
|
||||
/// Processes a federation request with automatic retries and backoff.
|
||||
///
|
||||
/// Implements exponential backoff to handle temporary
|
||||
/// network issues and server overload gracefully.
|
||||
pub async fn send_federation_request(
|
||||
destination: &ServerName,
|
||||
request: FederationRequest,
|
||||
) -> Result<FederationResponse, FederationError> {
|
||||
// Retry with exponential backoff because federation can be flaky
|
||||
// due to network issues or temporary server overload
|
||||
let mut retry_delay = Duration::from_millis(100);
|
||||
|
||||
for attempt in 1..=MAX_RETRIES {
|
||||
match try_send_request(destination, &request).await {
|
||||
Ok(response) => return Ok(response),
|
||||
Err(err) if err.is_retriable() && attempt < MAX_RETRIES => {
|
||||
warn!(
|
||||
destination = %destination,
|
||||
attempt = attempt,
|
||||
error = %err,
|
||||
retry_delay_ms = retry_delay.as_millis(),
|
||||
"Federation request failed, retrying"
|
||||
);
|
||||
|
||||
tokio::time::sleep(retry_delay).await;
|
||||
retry_delay *= 2; // Exponential backoff
|
||||
}
|
||||
Err(err) => return Err(err),
|
||||
}
|
||||
}
|
||||
|
||||
unreachable!("Loop should have returned or failed by now")
|
||||
}
|
||||
```
|
||||
|
||||
### Async Patterns
|
||||
|
||||
- Use `async`/`await` appropriately
|
||||
- Avoid blocking operations in async contexts
|
||||
- Consider using `tokio::task::spawn_blocking` for CPU-intensive work
|
||||
|
||||
```rs
|
||||
// Good - non-blocking async operation
|
||||
pub async fn fetch_user_profile(
|
||||
&self,
|
||||
user_id: &UserId,
|
||||
) -> Result<UserProfile, Error> {
|
||||
let profile = self.db
|
||||
.get_user_profile(user_id)
|
||||
.await?;
|
||||
|
||||
Ok(profile)
|
||||
}
|
||||
|
||||
// Good - CPU-intensive work moved to blocking thread
|
||||
pub async fn generate_thumbnail(
|
||||
&self,
|
||||
image_data: Vec<u8>,
|
||||
) -> Result<Vec<u8>, Error> {
|
||||
tokio::task::spawn_blocking(move || {
|
||||
image::generate_thumbnail(image_data)
|
||||
})
|
||||
.await
|
||||
.map_err(|_| Error::TaskJoinError)?
|
||||
}
|
||||
```
|
||||
|
||||
## Inclusivity and Diversity Guidelines
|
||||
|
||||
All code and documentation must be written with inclusivity and diversity in mind. This ensures our software is welcoming and accessible to all users and contributors. Follow the [Google guide on writing inclusive code and documentation](https://developers.google.com/style/inclusive-documentation) for comprehensive guidance.
|
||||
|
||||
The following types of language are explicitly forbidden in all code, comments, documentation, and commit messages:
|
||||
|
||||
**Ableist language:** Avoid terms like "sanity check", "crazy", "insane", "cripple", or "blind to". Use alternatives like "validation", "unexpected", "disable", or "unaware of".
|
||||
|
||||
**Socially-charged technical terms:** Replace overly divisive terminology with neutral alternatives:
|
||||
- "whitelist/blacklist" → "allowlist/denylist" or "permitted/blocked"
|
||||
- "master/slave" → "primary/replica", "controller/worker", or "parent/child"
|
||||
|
||||
When working with external dependencies that use non-inclusive terminology, avoid propagating them in your own APIs and variable names.
|
||||
|
||||
Use diverse examples in documentation that avoid culturally-specific references, assumptions about user demographics, or unnecessarily gendered language. Design with accessibility and inclusivity in mind by providing clear error messages and considering diverse user needs.
|
||||
|
||||
This software is intended to be used by everyone regardless of background, identity, or ability. Write code and documentation that reflects this commitment to inclusivity.
|
||||
@@ -196,5 +196,5 @@ The initial implementation PR is available [here][1].
|
||||
[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/#/#continuwuity:continuwuity.org
|
||||
[7]: https://matrix.to/#/#continuwuity:continuwuity.org?via=continuwuity.org&via=ellis.link&via=explodie.org&via=matrix.org
|
||||
[8]: https://crates.io/crates/libloading
|
||||
|
||||
@@ -68,3 +68,27 @@ documentation](https://github.com/coturn/coturn/blob/master/docker/coturn/README
|
||||
|
||||
For security recommendations see Synapse's [Coturn
|
||||
documentation](https://element-hq.github.io/synapse/latest/turn-howto.html).
|
||||
|
||||
### Testing
|
||||
|
||||
To make sure turn credentials are being correctly served to clients, you can manually make a HTTP request to the turnServer endpoint.
|
||||
|
||||
`curl "https://<matrix.example.com>/_matrix/client/r0/voip/turnServer" -H 'Authorization: Bearer <your_client_token>' | jq`
|
||||
|
||||
You should get a response like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"username": "1752792167:@jade:example.com",
|
||||
"password": "KjlDlawdPbU9mvP4bhdV/2c/h65=",
|
||||
"uris": [
|
||||
"turns:coturn.example.com?transport=udp",
|
||||
"turns:coturn.example.com?transport=tcp",
|
||||
"turn:coturn.example.com?transport=udp",
|
||||
"turn:coturn.example.com?transport=tcp"
|
||||
],
|
||||
"ttl": 86400
|
||||
}
|
||||
```
|
||||
|
||||
You can test these credentials work using [Trickle ICE](https://webrtc.github.io/samples/src/content/peerconnection/trickle-ice/)
|
||||
|
||||
+3
-2
@@ -83,7 +83,7 @@ env DIRENV_DEVSHELL=all-features \
|
||||
--workspace \
|
||||
--locked \
|
||||
--profile test \
|
||||
--all-features \
|
||||
--features full \
|
||||
--no-deps \
|
||||
--document-private-items \
|
||||
--color always
|
||||
@@ -96,6 +96,7 @@ script = """
|
||||
direnv exec . \
|
||||
cargo clippy \
|
||||
--workspace \
|
||||
--features full \
|
||||
--locked \
|
||||
--profile test \
|
||||
--color=always \
|
||||
@@ -113,7 +114,7 @@ env DIRENV_DEVSHELL=all-features \
|
||||
--workspace \
|
||||
--locked \
|
||||
--profile test \
|
||||
--all-features \
|
||||
--features full \
|
||||
--color=always \
|
||||
-- \
|
||||
-D warnings
|
||||
|
||||
@@ -26,8 +26,7 @@ pub(super) async fn incoming_federation(&self) -> Result {
|
||||
.rooms
|
||||
.event_handler
|
||||
.federation_handletime
|
||||
.read()
|
||||
.expect("locked");
|
||||
.read();
|
||||
|
||||
let mut msg = format!("Handling {} incoming pdus:\n", map.len());
|
||||
for (r, (e, i)) in map.iter() {
|
||||
|
||||
+2
-10
@@ -37,11 +37,7 @@ pub use crate::admin::AdminCommand;
|
||||
|
||||
/// Install the admin command processor
|
||||
pub async fn init(admin_service: &service::admin::Service) {
|
||||
_ = admin_service
|
||||
.complete
|
||||
.write()
|
||||
.expect("locked for writing")
|
||||
.insert(processor::complete);
|
||||
_ = admin_service.complete.write().insert(processor::complete);
|
||||
_ = admin_service
|
||||
.handle
|
||||
.write()
|
||||
@@ -52,9 +48,5 @@ pub async fn init(admin_service: &service::admin::Service) {
|
||||
/// Uninstall the admin command handler
|
||||
pub async fn fini(admin_service: &service::admin::Service) {
|
||||
_ = admin_service.handle.write().await.take();
|
||||
_ = admin_service
|
||||
.complete
|
||||
.write()
|
||||
.expect("locked for writing")
|
||||
.take();
|
||||
_ = admin_service.complete.write().take();
|
||||
}
|
||||
|
||||
+5
-11
@@ -1,14 +1,8 @@
|
||||
use std::{
|
||||
fmt::Write,
|
||||
mem::take,
|
||||
panic::AssertUnwindSafe,
|
||||
sync::{Arc, Mutex},
|
||||
time::SystemTime,
|
||||
};
|
||||
use std::{fmt::Write, mem::take, panic::AssertUnwindSafe, sync::Arc, time::SystemTime};
|
||||
|
||||
use clap::{CommandFactory, Parser};
|
||||
use conduwuit::{
|
||||
Error, Result, debug, error,
|
||||
Error, Result, SyncMutex, debug, error,
|
||||
log::{
|
||||
capture,
|
||||
capture::Capture,
|
||||
@@ -123,7 +117,7 @@ async fn process(
|
||||
let mut output = String::new();
|
||||
|
||||
// Prepend the logs only if any were captured
|
||||
let logs = logs.lock().expect("locked");
|
||||
let logs = logs.lock();
|
||||
if logs.lines().count() > 2 {
|
||||
writeln!(&mut output, "{logs}").expect("failed to format logs to command output");
|
||||
}
|
||||
@@ -132,7 +126,7 @@ async fn process(
|
||||
(result, output)
|
||||
}
|
||||
|
||||
fn capture_create(context: &Context<'_>) -> (Arc<Capture>, Arc<Mutex<String>>) {
|
||||
fn capture_create(context: &Context<'_>) -> (Arc<Capture>, Arc<SyncMutex<String>>) {
|
||||
let env_config = &context.services.server.config.admin_log_capture;
|
||||
let env_filter = EnvFilter::try_new(env_config).unwrap_or_else(|e| {
|
||||
warn!("admin_log_capture filter invalid: {e:?}");
|
||||
@@ -152,7 +146,7 @@ fn capture_create(context: &Context<'_>) -> (Arc<Capture>, Arc<Mutex<String>>) {
|
||||
data.level() <= log_level && data.our_modules() && data.scope.contains(&"admin")
|
||||
};
|
||||
|
||||
let logs = Arc::new(Mutex::new(
|
||||
let logs = Arc::new(SyncMutex::new(
|
||||
collect_stream(|s| markdown_table_head(s)).expect("markdown table header"),
|
||||
));
|
||||
|
||||
|
||||
+32
-15
@@ -291,20 +291,38 @@ pub(crate) async fn register_route(
|
||||
}
|
||||
|
||||
// UIAA
|
||||
let mut uiaainfo;
|
||||
let skip_auth = if services.globals.registration_token.is_some() {
|
||||
let mut uiaainfo = UiaaInfo {
|
||||
flows: Vec::new(),
|
||||
completed: Vec::new(),
|
||||
params: Box::default(),
|
||||
session: None,
|
||||
auth_error: None,
|
||||
};
|
||||
let skip_auth = body.appservice_info.is_some() || is_guest;
|
||||
|
||||
// Populate required UIAA flows
|
||||
if services.globals.registration_token.is_some() {
|
||||
// Registration token required
|
||||
uiaainfo = UiaaInfo {
|
||||
flows: vec![AuthFlow {
|
||||
stages: vec![AuthType::RegistrationToken],
|
||||
}],
|
||||
completed: Vec::new(),
|
||||
params: Box::default(),
|
||||
session: None,
|
||||
auth_error: None,
|
||||
};
|
||||
body.appservice_info.is_some()
|
||||
} else {
|
||||
uiaainfo.flows.push(AuthFlow {
|
||||
stages: vec![AuthType::RegistrationToken],
|
||||
});
|
||||
}
|
||||
if services.config.recaptcha_private_site_key.is_some() {
|
||||
if let Some(pubkey) = &services.config.recaptcha_site_key {
|
||||
// ReCaptcha required
|
||||
uiaainfo
|
||||
.flows
|
||||
.push(AuthFlow { stages: vec![AuthType::ReCaptcha] });
|
||||
uiaainfo.params = serde_json::value::to_raw_value(&serde_json::json!({
|
||||
"m.login.recaptcha": {
|
||||
"public_key": pubkey,
|
||||
},
|
||||
}))
|
||||
.expect("Failed to serialize recaptcha params");
|
||||
}
|
||||
}
|
||||
|
||||
if uiaainfo.flows.is_empty() && !skip_auth {
|
||||
// No registration token necessary, but clients must still go through the flow
|
||||
uiaainfo = UiaaInfo {
|
||||
flows: vec![AuthFlow { stages: vec![AuthType::Dummy] }],
|
||||
@@ -313,8 +331,7 @@ pub(crate) async fn register_route(
|
||||
session: None,
|
||||
auth_error: None,
|
||||
};
|
||||
body.appservice_info.is_some() || is_guest
|
||||
};
|
||||
}
|
||||
|
||||
if !skip_auth {
|
||||
match &body.auth {
|
||||
|
||||
+126
-31
@@ -2,10 +2,10 @@ use std::cmp::max;
|
||||
|
||||
use axum::extract::State;
|
||||
use conduwuit::{
|
||||
Err, Error, Event, Result, err, info,
|
||||
Err, Error, Event, Result, debug, err, info,
|
||||
matrix::{StateKey, pdu::PduBuilder},
|
||||
};
|
||||
use futures::StreamExt;
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use ruma::{
|
||||
CanonicalJsonObject, RoomId, RoomVersionId,
|
||||
api::client::{error::ErrorKind, room::upgrade_room},
|
||||
@@ -16,15 +16,16 @@ use ruma::{
|
||||
power_levels::RoomPowerLevelsEventContent,
|
||||
tombstone::RoomTombstoneEventContent,
|
||||
},
|
||||
space::child::{RedactedSpaceChildEventContent, SpaceChildEventContent},
|
||||
},
|
||||
int,
|
||||
};
|
||||
use serde_json::{json, value::to_raw_value};
|
||||
|
||||
use crate::Ruma;
|
||||
use crate::router::Ruma;
|
||||
|
||||
/// Recommended transferable state events list from the spec
|
||||
const TRANSFERABLE_STATE_EVENTS: &[StateEventType; 9] = &[
|
||||
const TRANSFERABLE_STATE_EVENTS: &[StateEventType; 11] = &[
|
||||
StateEventType::RoomAvatar,
|
||||
StateEventType::RoomEncryption,
|
||||
StateEventType::RoomGuestAccess,
|
||||
@@ -34,6 +35,9 @@ const TRANSFERABLE_STATE_EVENTS: &[StateEventType; 9] = &[
|
||||
StateEventType::RoomPowerLevels,
|
||||
StateEventType::RoomServerAcl,
|
||||
StateEventType::RoomTopic,
|
||||
// Not explicitly recommended in spec, but very useful.
|
||||
StateEventType::SpaceChild,
|
||||
StateEventType::SpaceParent, // TODO: m.room.policy?
|
||||
];
|
||||
|
||||
/// # `POST /_matrix/client/r0/rooms/{roomId}/upgrade`
|
||||
@@ -50,10 +54,7 @@ pub(crate) async fn upgrade_room_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<upgrade_room::v3::Request>,
|
||||
) -> Result<upgrade_room::v3::Response> {
|
||||
debug_assert!(
|
||||
TRANSFERABLE_STATE_EVENTS.is_sorted(),
|
||||
"TRANSFERABLE_STATE_EVENTS is not sorted"
|
||||
);
|
||||
// TODO[v12]: Handle additional creators
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
if !services.server.supported_room_version(&body.new_version) {
|
||||
@@ -128,7 +129,7 @@ pub(crate) async fn upgrade_room_route(
|
||||
);
|
||||
},
|
||||
| _ => {
|
||||
// "creator" key no longer exists in V11+ rooms
|
||||
// "creator" key no longer exists in V11 rooms
|
||||
create_event_content.remove("creator");
|
||||
},
|
||||
}
|
||||
@@ -175,6 +176,7 @@ pub(crate) async fn upgrade_room_route(
|
||||
&replacement_room,
|
||||
&state_lock,
|
||||
)
|
||||
.boxed()
|
||||
.await?;
|
||||
|
||||
// Join the new room
|
||||
@@ -205,35 +207,48 @@ pub(crate) async fn upgrade_room_route(
|
||||
&replacement_room,
|
||||
&state_lock,
|
||||
)
|
||||
.boxed()
|
||||
.await?;
|
||||
|
||||
// Replicate transferable state events to the new room
|
||||
for event_type in TRANSFERABLE_STATE_EVENTS {
|
||||
let event_content = match services
|
||||
let state_keys = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get(&body.room_id, event_type, "")
|
||||
.await
|
||||
{
|
||||
| Ok(v) => v.content().to_owned(),
|
||||
| Err(_) => continue, // Skipping missing events.
|
||||
};
|
||||
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder {
|
||||
event_type: event_type.to_string().into(),
|
||||
content: event_content,
|
||||
state_key: Some(StateKey::new()),
|
||||
..Default::default()
|
||||
},
|
||||
sender_user,
|
||||
&replacement_room,
|
||||
&state_lock,
|
||||
)
|
||||
.room_state_keys(&body.room_id, event_type)
|
||||
.await?;
|
||||
for state_key in state_keys {
|
||||
let event_content = match services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get(&body.room_id, event_type, &state_key)
|
||||
.await
|
||||
{
|
||||
| Ok(v) => v.content().to_owned(),
|
||||
| Err(_) => continue, // Skipping missing events.
|
||||
};
|
||||
if event_content.get() == "{}" {
|
||||
// If the event content is empty, we skip it
|
||||
continue;
|
||||
}
|
||||
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder {
|
||||
event_type: event_type.to_string().into(),
|
||||
content: event_content,
|
||||
state_key: Some(StateKey::from(state_key)),
|
||||
..Default::default()
|
||||
},
|
||||
sender_user,
|
||||
&replacement_room,
|
||||
&state_lock,
|
||||
)
|
||||
.boxed()
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
// Moves any local aliases to the new room
|
||||
@@ -290,10 +305,90 @@ pub(crate) async fn upgrade_room_route(
|
||||
&body.room_id,
|
||||
&state_lock,
|
||||
)
|
||||
.boxed()
|
||||
.await?;
|
||||
|
||||
drop(state_lock);
|
||||
|
||||
// Check if the old room has a space parent, and if so, whether we should update
|
||||
// it (m.space.parent, room_id)
|
||||
let parents = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_keys(&body.room_id, &StateEventType::SpaceParent)
|
||||
.await?;
|
||||
|
||||
for raw_space_id in parents {
|
||||
let space_id = RoomId::parse(&raw_space_id)?;
|
||||
let Ok(child) = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get_content::<SpaceChildEventContent>(
|
||||
space_id,
|
||||
&StateEventType::SpaceChild,
|
||||
body.room_id.as_str(),
|
||||
)
|
||||
.await
|
||||
else {
|
||||
// If the space does not have a child event for this room, we can skip it
|
||||
continue;
|
||||
};
|
||||
debug!(
|
||||
"Updating space {space_id} child event for room {} to {replacement_room}",
|
||||
&body.room_id
|
||||
);
|
||||
// First, drop the space's child event
|
||||
let state_lock = services.rooms.state.mutex.lock(space_id).await;
|
||||
debug!("Removing space child event for room {} in space {space_id}", &body.room_id);
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder {
|
||||
event_type: StateEventType::SpaceChild.into(),
|
||||
content: to_raw_value(&RedactedSpaceChildEventContent {})
|
||||
.expect("event is valid, we just created it"),
|
||||
state_key: Some(body.room_id.clone().as_str().into()),
|
||||
..Default::default()
|
||||
},
|
||||
sender_user,
|
||||
space_id,
|
||||
&state_lock,
|
||||
)
|
||||
.boxed()
|
||||
.await
|
||||
.ok();
|
||||
// Now, add a new child event for the replacement room
|
||||
debug!("Adding space child event for room {replacement_room} in space {space_id}");
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder {
|
||||
event_type: StateEventType::SpaceChild.into(),
|
||||
content: to_raw_value(&SpaceChildEventContent {
|
||||
via: vec![sender_user.server_name().to_owned()],
|
||||
order: child.order,
|
||||
suggested: child.suggested,
|
||||
})
|
||||
.expect("event is valid, we just created it"),
|
||||
state_key: Some(replacement_room.as_str().into()),
|
||||
..Default::default()
|
||||
},
|
||||
sender_user,
|
||||
space_id,
|
||||
&state_lock,
|
||||
)
|
||||
.boxed()
|
||||
.await
|
||||
.ok();
|
||||
debug!(
|
||||
"Finished updating space {space_id} child event for room {} to {replacement_room}",
|
||||
&body.room_id
|
||||
);
|
||||
drop(state_lock);
|
||||
}
|
||||
|
||||
// Return the replacement room id
|
||||
Ok(upgrade_room::v3::Response { replacement_room })
|
||||
}
|
||||
|
||||
+5
-2
@@ -187,6 +187,7 @@ pub fn build(router: Router<State>, server: &Server) -> Router<State> {
|
||||
.ruma_route(&client::well_known_support)
|
||||
.ruma_route(&client::well_known_client)
|
||||
.route("/_conduwuit/server_version", get(client::conduwuit_server_version))
|
||||
.route("/_continuwuity/server_version", get(client::conduwuit_server_version))
|
||||
.ruma_route(&client::room_initial_sync_route)
|
||||
.route("/client/server.json", get(client::syncv3_client_server_json));
|
||||
|
||||
@@ -226,13 +227,15 @@ pub fn build(router: Router<State>, server: &Server) -> Router<State> {
|
||||
.ruma_route(&server::well_known_server)
|
||||
.ruma_route(&server::get_content_route)
|
||||
.ruma_route(&server::get_content_thumbnail_route)
|
||||
.route("/_conduwuit/local_user_count", get(client::conduwuit_local_user_count));
|
||||
.route("/_conduwuit/local_user_count", get(client::conduwuit_local_user_count))
|
||||
.route("/_continuwuity/local_user_count", get(client::conduwuit_local_user_count));
|
||||
} else {
|
||||
router = router
|
||||
.route("/_matrix/federation/*path", any(federation_disabled))
|
||||
.route("/.well-known/matrix/server", any(federation_disabled))
|
||||
.route("/_matrix/key/*path", any(federation_disabled))
|
||||
.route("/_conduwuit/local_user_count", any(federation_disabled));
|
||||
.route("/_conduwuit/local_user_count", any(federation_disabled))
|
||||
.route("/_continuwuity/local_user_count", any(federation_disabled));
|
||||
}
|
||||
|
||||
if config.allow_legacy_media {
|
||||
|
||||
@@ -110,6 +110,8 @@ tracing-core.workspace = true
|
||||
tracing-subscriber.workspace = true
|
||||
tracing.workspace = true
|
||||
url.workspace = true
|
||||
parking_lot.workspace = true
|
||||
lock_api.workspace = true
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
nix.workspace = true
|
||||
|
||||
@@ -4,7 +4,6 @@ use std::{
|
||||
cell::OnceCell,
|
||||
ffi::{CStr, c_char, c_void},
|
||||
fmt::Debug,
|
||||
sync::RwLock,
|
||||
};
|
||||
|
||||
use arrayvec::ArrayVec;
|
||||
@@ -13,7 +12,7 @@ use tikv_jemalloc_sys as ffi;
|
||||
use tikv_jemallocator as jemalloc;
|
||||
|
||||
use crate::{
|
||||
Result, err, is_equal_to, is_nonzero,
|
||||
Result, SyncRwLock, err, is_equal_to, is_nonzero,
|
||||
utils::{math, math::Tried},
|
||||
};
|
||||
|
||||
@@ -40,7 +39,7 @@ const MALLOC_CONF_PROF: &str = "";
|
||||
|
||||
#[global_allocator]
|
||||
static JEMALLOC: jemalloc::Jemalloc = jemalloc::Jemalloc;
|
||||
static CONTROL: RwLock<()> = RwLock::new(());
|
||||
static CONTROL: SyncRwLock<()> = SyncRwLock::new(());
|
||||
|
||||
type Name = ArrayVec<u8, NAME_MAX>;
|
||||
type Key = ArrayVec<usize, KEY_SEGS>;
|
||||
@@ -332,7 +331,7 @@ fn set<T>(key: &Key, val: T) -> Result<T>
|
||||
where
|
||||
T: Copy + Debug,
|
||||
{
|
||||
let _lock = CONTROL.write()?;
|
||||
let _lock = CONTROL.write();
|
||||
let res = xchg(key, val)?;
|
||||
inc_epoch()?;
|
||||
|
||||
|
||||
@@ -180,19 +180,28 @@ pub fn check(config: &Config) -> Result {
|
||||
}
|
||||
}
|
||||
|
||||
if config.recaptcha_site_key.is_some() && config.recaptcha_private_site_key.is_none() {
|
||||
return Err!(Config(
|
||||
"recaptcha_private_site_key",
|
||||
"reCAPTCHA private site key is required when reCAPTCHA site key is set."
|
||||
));
|
||||
}
|
||||
|
||||
if config.allow_registration
|
||||
&& !config.yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse
|
||||
&& config.registration_token.is_none()
|
||||
&& config.registration_token_file.is_none()
|
||||
&& config.recaptcha_site_key.is_none()
|
||||
{
|
||||
return Err!(Config(
|
||||
"registration_token",
|
||||
"!! You have `allow_registration` enabled without a token configured in your config \
|
||||
which means you are allowing ANYONE to register on your conduwuit instance without \
|
||||
any 2nd-step (e.g. registration token). If this is not the intended behaviour, \
|
||||
please set a registration token. For security and safety reasons, conduwuit will \
|
||||
shut down. If you are extra sure this is the desired behaviour you want, please \
|
||||
set the following config option to true:
|
||||
"!! You have `allow_registration` enabled without a token or captcha configured \
|
||||
which means you are allowing ANYONE to register on your continuwuity instance \
|
||||
without any 2nd-step (e.g. registration token, captcha), which is FREQUENTLY \
|
||||
abused by malicious actors. If this is not the intended behaviour, please set a \
|
||||
registration token. For security and safety reasons, continuwuity will shut down. \
|
||||
If you are extra sure this is the desired behaviour you want, please set the \
|
||||
following config option to true:
|
||||
`yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse`"
|
||||
));
|
||||
}
|
||||
|
||||
@@ -412,6 +412,17 @@ pub struct Config {
|
||||
#[serde(default = "default_well_known_timeout")]
|
||||
pub well_known_timeout: u64,
|
||||
|
||||
/// Federation client connection timeout (seconds). You should not set this
|
||||
/// to high values, as dead homeservers can significantly slow down
|
||||
/// federation, specifically key retrieval, which will take roughly the
|
||||
/// amount of time you configure here given that a homeserver doesn't
|
||||
/// respond. This will cause most clients to time out /keys/query, causing
|
||||
/// E2EE and device verification to fail.
|
||||
///
|
||||
/// default: 10
|
||||
#[serde(default = "default_federation_conn_timeout")]
|
||||
pub federation_conn_timeout: u64,
|
||||
|
||||
/// Federation client request timeout (seconds). You most definitely want
|
||||
/// this to be high to account for extremely large room joins, slow
|
||||
/// homeservers, your own resources etc.
|
||||
@@ -420,6 +431,23 @@ pub struct Config {
|
||||
#[serde(default = "default_federation_timeout")]
|
||||
pub federation_timeout: u64,
|
||||
|
||||
/// MSC4284 Policy server request timeout (seconds). Generally policy
|
||||
/// servers should respond near instantly, however may slow down under
|
||||
/// load. If a policy server doesn't respond in a short amount of time, the
|
||||
/// room it is configured in may become unusable if this limit is set too
|
||||
/// high. 10 seconds is a good default, however dropping this to 3-5 seconds
|
||||
/// can be acceptable.
|
||||
///
|
||||
/// Please be aware that policy requests are *NOT* currently re-tried, so if
|
||||
/// a spam check request fails, the event will be assumed to be not spam,
|
||||
/// which in some cases may result in spam being sent to or received from
|
||||
/// the room that would typically be prevented.
|
||||
///
|
||||
/// About policy servers: https://matrix.org/blog/2025/04/introducing-policy-servers/
|
||||
/// default: 10
|
||||
#[serde(default = "default_policy_server_request_timeout")]
|
||||
pub policy_server_request_timeout: u64,
|
||||
|
||||
/// Federation client idle connection pool timeout (seconds).
|
||||
///
|
||||
/// default: 25
|
||||
@@ -556,6 +584,24 @@ pub struct Config {
|
||||
/// example: "/etc/continuwuity/.reg_token"
|
||||
pub registration_token_file: Option<PathBuf>,
|
||||
|
||||
/// The public site key for reCaptcha. If this is provided, reCaptcha
|
||||
/// becomes required during registration. If both captcha *and*
|
||||
/// registration token are enabled, both will be required during
|
||||
/// registration.
|
||||
///
|
||||
/// IMPORTANT: "Verify the origin of reCAPTCHA solutions" **MUST** BE
|
||||
/// DISABLED IF YOU WANT THE CAPTCHA TO WORK IN 3RD PARTY CLIENTS, OR
|
||||
/// CLIENTS HOSTED ON DOMAINS OTHER THAN YOUR OWN!
|
||||
///
|
||||
/// Registration must be enabled (`allow_registration` must be true) for
|
||||
/// this to have any effect.
|
||||
pub recaptcha_site_key: Option<String>,
|
||||
|
||||
/// The private site key for reCaptcha.
|
||||
/// If this is omitted, captcha registration will not work,
|
||||
/// even if `recaptcha_site_key` is set.
|
||||
pub recaptcha_private_site_key: Option<String>,
|
||||
|
||||
/// Controls whether encrypted rooms and events are allowed.
|
||||
#[serde(default = "true_fn")]
|
||||
pub allow_encryption: bool,
|
||||
@@ -2175,8 +2221,12 @@ fn default_well_known_conn_timeout() -> u64 { 6 }
|
||||
|
||||
fn default_well_known_timeout() -> u64 { 10 }
|
||||
|
||||
fn default_federation_conn_timeout() -> u64 { 10 }
|
||||
|
||||
fn default_federation_timeout() -> u64 { 25 }
|
||||
|
||||
fn default_policy_server_request_timeout() -> u64 { 10 }
|
||||
|
||||
fn default_federation_idle_timeout() -> u64 { 25 }
|
||||
|
||||
fn default_federation_idle_per_host() -> u16 { 1 }
|
||||
|
||||
@@ -3,18 +3,15 @@
|
||||
//! several crates, lower-level information is supplied from each crate during
|
||||
//! static initialization.
|
||||
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
sync::{Mutex, OnceLock},
|
||||
};
|
||||
use std::{collections::BTreeMap, sync::OnceLock};
|
||||
|
||||
use crate::utils::exchange;
|
||||
use crate::{SyncMutex, utils::exchange};
|
||||
|
||||
/// Raw capture of rustc flags used to build each crate in the project. Informed
|
||||
/// by rustc_flags_capture macro (one in each crate's mod.rs). This is
|
||||
/// done during static initialization which is why it's mutex-protected and pub.
|
||||
/// Should not be written to by anything other than our macro.
|
||||
pub static FLAGS: Mutex<BTreeMap<&str, &[&str]>> = Mutex::new(BTreeMap::new());
|
||||
pub static FLAGS: SyncMutex<BTreeMap<&str, &[&str]>> = SyncMutex::new(BTreeMap::new());
|
||||
|
||||
/// Processed list of enabled features across all project crates. This is
|
||||
/// generated from the data in FLAGS.
|
||||
@@ -27,7 +24,6 @@ fn init_features() -> Vec<&'static str> {
|
||||
let mut features = Vec::new();
|
||||
FLAGS
|
||||
.lock()
|
||||
.expect("locked")
|
||||
.iter()
|
||||
.for_each(|(_, flags)| append_features(&mut features, flags));
|
||||
|
||||
|
||||
@@ -40,7 +40,6 @@ where
|
||||
self.state
|
||||
.active
|
||||
.read()
|
||||
.expect("shared lock")
|
||||
.iter()
|
||||
.filter(|capture| filter(self, capture, event, &ctx))
|
||||
.for_each(|capture| handle(self, capture, event, &ctx));
|
||||
@@ -55,7 +54,7 @@ where
|
||||
let mut visitor = Visitor { values: Values::new() };
|
||||
event.record(&mut visitor);
|
||||
|
||||
let mut closure = capture.closure.lock().expect("exclusive lock");
|
||||
let mut closure = capture.closure.lock();
|
||||
closure(Data {
|
||||
layer,
|
||||
event,
|
||||
|
||||
@@ -4,7 +4,7 @@ pub mod layer;
|
||||
pub mod state;
|
||||
pub mod util;
|
||||
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::Arc;
|
||||
|
||||
pub use data::Data;
|
||||
use guard::Guard;
|
||||
@@ -12,6 +12,8 @@ pub use layer::{Layer, Value};
|
||||
pub use state::State;
|
||||
pub use util::*;
|
||||
|
||||
use crate::SyncMutex;
|
||||
|
||||
pub type Filter = dyn Fn(Data<'_>) -> bool + Send + Sync + 'static;
|
||||
pub type Closure = dyn FnMut(Data<'_>) + Send + Sync + 'static;
|
||||
|
||||
@@ -19,7 +21,7 @@ pub type Closure = dyn FnMut(Data<'_>) + Send + Sync + 'static;
|
||||
pub struct Capture {
|
||||
state: Arc<State>,
|
||||
filter: Option<Box<Filter>>,
|
||||
closure: Mutex<Box<Closure>>,
|
||||
closure: SyncMutex<Box<Closure>>,
|
||||
}
|
||||
|
||||
impl Capture {
|
||||
@@ -34,7 +36,7 @@ impl Capture {
|
||||
Arc::new(Self {
|
||||
state: state.clone(),
|
||||
filter: filter.map(|p| -> Box<Filter> { Box::new(p) }),
|
||||
closure: Mutex::new(Box::new(closure)),
|
||||
closure: SyncMutex::new(Box::new(closure)),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::Capture;
|
||||
use crate::SyncRwLock;
|
||||
|
||||
/// Capture layer state.
|
||||
pub struct State {
|
||||
pub(super) active: RwLock<Vec<Arc<Capture>>>,
|
||||
pub(super) active: SyncRwLock<Vec<Arc<Capture>>>,
|
||||
}
|
||||
|
||||
impl Default for State {
|
||||
@@ -13,17 +14,14 @@ impl Default for State {
|
||||
|
||||
impl State {
|
||||
#[must_use]
|
||||
pub fn new() -> Self { Self { active: RwLock::new(Vec::new()) } }
|
||||
pub fn new() -> Self { Self { active: SyncRwLock::new(Vec::new()) } }
|
||||
|
||||
pub(super) fn add(&self, capture: &Arc<Capture>) {
|
||||
self.active
|
||||
.write()
|
||||
.expect("locked for writing")
|
||||
.push(capture.clone());
|
||||
self.active.write().push(capture.clone());
|
||||
}
|
||||
|
||||
pub(super) fn del(&self, capture: &Arc<Capture>) {
|
||||
let mut vec = self.active.write().expect("locked for writing");
|
||||
let mut vec = self.active.write();
|
||||
if let Some(pos) = vec.iter().position(|v| Arc::ptr_eq(v, capture)) {
|
||||
vec.swap_remove(pos);
|
||||
}
|
||||
|
||||
@@ -1,31 +1,31 @@
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::{
|
||||
super::{Level, fmt},
|
||||
Closure, Data,
|
||||
};
|
||||
use crate::Result;
|
||||
use crate::{Result, SyncMutex};
|
||||
|
||||
pub fn fmt_html<S>(out: Arc<Mutex<S>>) -> Box<Closure>
|
||||
pub fn fmt_html<S>(out: Arc<SyncMutex<S>>) -> Box<Closure>
|
||||
where
|
||||
S: std::fmt::Write + Send + 'static,
|
||||
{
|
||||
fmt(fmt::html, out)
|
||||
}
|
||||
|
||||
pub fn fmt_markdown<S>(out: Arc<Mutex<S>>) -> Box<Closure>
|
||||
pub fn fmt_markdown<S>(out: Arc<SyncMutex<S>>) -> Box<Closure>
|
||||
where
|
||||
S: std::fmt::Write + Send + 'static,
|
||||
{
|
||||
fmt(fmt::markdown, out)
|
||||
}
|
||||
|
||||
pub fn fmt<F, S>(fun: F, out: Arc<Mutex<S>>) -> Box<Closure>
|
||||
pub fn fmt<F, S>(fun: F, out: Arc<SyncMutex<S>>) -> Box<Closure>
|
||||
where
|
||||
F: Fn(&mut S, &Level, &str, &str) -> Result<()> + Send + Sync + Copy + 'static,
|
||||
S: std::fmt::Write + Send + 'static,
|
||||
{
|
||||
Box::new(move |data| call(fun, &mut *out.lock().expect("locked"), &data))
|
||||
Box::new(move |data| call(fun, &mut *out.lock(), &data))
|
||||
}
|
||||
|
||||
fn call<F, S>(fun: F, out: &mut S, data: &Data<'_>)
|
||||
|
||||
+4
-12
@@ -1,11 +1,8 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use tracing_subscriber::{EnvFilter, reload};
|
||||
|
||||
use crate::{Result, error};
|
||||
use crate::{Result, SyncMutex, error};
|
||||
|
||||
/// We need to store a reload::Handle value, but can't name it's type explicitly
|
||||
/// because the S type parameter depends on the subscriber's previous layers. In
|
||||
@@ -35,7 +32,7 @@ impl<L: Clone, S> ReloadHandle<L> for reload::Handle<L, S> {
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct LogLevelReloadHandles {
|
||||
handles: Arc<Mutex<HandleMap>>,
|
||||
handles: Arc<SyncMutex<HandleMap>>,
|
||||
}
|
||||
|
||||
type HandleMap = HashMap<String, Handle>;
|
||||
@@ -43,16 +40,12 @@ type Handle = Box<dyn ReloadHandle<EnvFilter> + Send + Sync>;
|
||||
|
||||
impl LogLevelReloadHandles {
|
||||
pub fn add(&self, name: &str, handle: Handle) {
|
||||
self.handles
|
||||
.lock()
|
||||
.expect("locked")
|
||||
.insert(name.into(), handle);
|
||||
self.handles.lock().insert(name.into(), handle);
|
||||
}
|
||||
|
||||
pub fn reload(&self, new_value: &EnvFilter, names: Option<&[&str]>) -> Result<()> {
|
||||
self.handles
|
||||
.lock()
|
||||
.expect("locked")
|
||||
.iter()
|
||||
.filter(|(name, _)| names.is_some_and(|names| names.contains(&name.as_str())))
|
||||
.for_each(|(_, handle)| {
|
||||
@@ -66,7 +59,6 @@ impl LogLevelReloadHandles {
|
||||
pub fn current(&self, name: &str) -> Option<EnvFilter> {
|
||||
self.handles
|
||||
.lock()
|
||||
.expect("locked")
|
||||
.get(name)
|
||||
.map(|handle| handle.current())?
|
||||
}
|
||||
|
||||
@@ -149,8 +149,8 @@ where
|
||||
for<'a> &'a E: Event + Send,
|
||||
{
|
||||
debug!(
|
||||
event_id = format!("{}", incoming_event.event_id()),
|
||||
event_type = format!("{}", incoming_event.event_type()),
|
||||
event_id = %incoming_event.event_id(),
|
||||
event_type = ?incoming_event.event_type(),
|
||||
"auth_check beginning"
|
||||
);
|
||||
|
||||
@@ -217,8 +217,9 @@ where
|
||||
}
|
||||
|
||||
/*
|
||||
// TODO: In the past this code caused problems federating with synapse, maybe this has been
|
||||
// resolved already. Needs testing.
|
||||
// TODO: In the past this code was commented as it caused problems with Synapse. This is no
|
||||
// longer the case. This needs to be implemented.
|
||||
// See also: https://github.com/ruma/ruma/pull/2064
|
||||
//
|
||||
// 2. Reject if auth_events
|
||||
// a. auth_events cannot have duplicate keys since it's a BTree
|
||||
@@ -256,6 +257,18 @@ where
|
||||
| Some(e) => e,
|
||||
};
|
||||
|
||||
if incoming_event.room_id() != room_create_event.room_id() {
|
||||
warn!("room_id of incoming event does not match room_id of m.room.create event");
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
if let Some(ref pe) = power_levels_event {
|
||||
if pe.room_id() != room_create_event.room_id() {
|
||||
warn!("room_id of power levels event does not match room_id of m.room.create event");
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. If event does not have m.room.create in auth_events reject
|
||||
if !incoming_event
|
||||
.auth_events()
|
||||
@@ -381,6 +394,11 @@ where
|
||||
},
|
||||
};
|
||||
|
||||
if sender_member_event.room_id() != room_create_event.room_id() {
|
||||
warn!("room_id of incoming event does not match room_id of m.room.create event");
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let sender_membership_event_content: RoomMemberContentFields =
|
||||
from_json_str(sender_member_event.content().get())?;
|
||||
let Some(membership_state) = sender_membership_event_content.membership else {
|
||||
|
||||
@@ -28,6 +28,7 @@ pub use info::{
|
||||
pub use matrix::{
|
||||
Event, EventTypeExt, Pdu, PduCount, PduEvent, PduId, RoomVersion, pdu, state_res,
|
||||
};
|
||||
pub use parking_lot::{Mutex as SyncMutex, RwLock as SyncRwLock};
|
||||
pub use server::Server;
|
||||
pub use utils::{ctor, dtor, implement, result, result::Result};
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ pub mod sys;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
pub mod time;
|
||||
pub mod with_lock;
|
||||
|
||||
pub use ::conduwuit_macros::implement;
|
||||
pub use ::ctor::{ctor, dtor};
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
use std::{
|
||||
fmt::Debug,
|
||||
hash::Hash,
|
||||
sync::{Arc, TryLockError::WouldBlock},
|
||||
};
|
||||
use std::{fmt::Debug, hash::Hash, sync::Arc};
|
||||
|
||||
use tokio::sync::OwnedMutexGuard as Omg;
|
||||
|
||||
use crate::{Result, err};
|
||||
use crate::{Result, SyncMutex, err};
|
||||
|
||||
/// Map of Mutexes
|
||||
pub struct MutexMap<Key, Val> {
|
||||
@@ -19,7 +15,7 @@ pub struct Guard<Key, Val> {
|
||||
}
|
||||
|
||||
type Map<Key, Val> = Arc<MapMutex<Key, Val>>;
|
||||
type MapMutex<Key, Val> = std::sync::Mutex<HashMap<Key, Val>>;
|
||||
type MapMutex<Key, Val> = SyncMutex<HashMap<Key, Val>>;
|
||||
type HashMap<Key, Val> = std::collections::HashMap<Key, Value<Val>>;
|
||||
type Value<Val> = Arc<tokio::sync::Mutex<Val>>;
|
||||
|
||||
@@ -45,7 +41,6 @@ where
|
||||
let val = self
|
||||
.map
|
||||
.lock()
|
||||
.expect("locked")
|
||||
.entry(k.try_into().expect("failed to construct key"))
|
||||
.or_default()
|
||||
.clone();
|
||||
@@ -66,7 +61,6 @@ where
|
||||
let val = self
|
||||
.map
|
||||
.lock()
|
||||
.expect("locked")
|
||||
.entry(k.try_into().expect("failed to construct key"))
|
||||
.or_default()
|
||||
.clone();
|
||||
@@ -87,10 +81,7 @@ where
|
||||
let val = self
|
||||
.map
|
||||
.try_lock()
|
||||
.map_err(|e| match e {
|
||||
| WouldBlock => err!("would block"),
|
||||
| _ => panic!("{e:?}"),
|
||||
})?
|
||||
.ok_or_else(|| err!("would block"))?
|
||||
.entry(k.try_into().expect("failed to construct key"))
|
||||
.or_default()
|
||||
.clone();
|
||||
@@ -102,13 +93,13 @@ where
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn contains(&self, k: &Key) -> bool { self.map.lock().expect("locked").contains_key(k) }
|
||||
pub fn contains(&self, k: &Key) -> bool { self.map.lock().contains_key(k) }
|
||||
|
||||
#[must_use]
|
||||
pub fn is_empty(&self) -> bool { self.map.lock().expect("locked").is_empty() }
|
||||
pub fn is_empty(&self) -> bool { self.map.lock().is_empty() }
|
||||
|
||||
#[must_use]
|
||||
pub fn len(&self) -> usize { self.map.lock().expect("locked").len() }
|
||||
pub fn len(&self) -> usize { self.map.lock().len() }
|
||||
}
|
||||
|
||||
impl<Key, Val> Default for MutexMap<Key, Val>
|
||||
@@ -123,7 +114,7 @@ impl<Key, Val> Drop for Guard<Key, Val> {
|
||||
#[tracing::instrument(name = "unlock", level = "trace", skip_all)]
|
||||
fn drop(&mut self) {
|
||||
if Arc::strong_count(Omg::mutex(&self.val)) <= 2 {
|
||||
self.map.lock().expect("locked").retain(|_, val| {
|
||||
self.map.lock().retain(|_, val| {
|
||||
!Arc::ptr_eq(val, Omg::mutex(&self.val)) || Arc::strong_count(val) > 2
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
//! Traits for explicitly scoping the lifetime of locks.
|
||||
|
||||
use std::{
|
||||
future::Future,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
pub trait WithLock<T: ?Sized> {
|
||||
/// Acquires a lock and executes the given closure with the locked data,
|
||||
/// returning the result.
|
||||
fn with_lock<R, F>(&self, f: F) -> R
|
||||
where
|
||||
F: FnMut(&mut T) -> R;
|
||||
}
|
||||
|
||||
impl<T> WithLock<T> for Mutex<T> {
|
||||
fn with_lock<R, F>(&self, mut f: F) -> R
|
||||
where
|
||||
F: FnMut(&mut T) -> R,
|
||||
{
|
||||
// The locking and unlocking logic is hidden inside this function.
|
||||
let mut data_guard = self.lock().unwrap();
|
||||
f(&mut data_guard)
|
||||
// Lock is released here when `data_guard` goes out of scope.
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> WithLock<T> for Arc<Mutex<T>> {
|
||||
fn with_lock<R, F>(&self, mut f: F) -> R
|
||||
where
|
||||
F: FnMut(&mut T) -> R,
|
||||
{
|
||||
// The locking and unlocking logic is hidden inside this function.
|
||||
let mut data_guard = self.lock().unwrap();
|
||||
f(&mut data_guard)
|
||||
// Lock is released here when `data_guard` goes out of scope.
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: lock_api::RawMutex, T: ?Sized> WithLock<T> for lock_api::Mutex<R, T> {
|
||||
fn with_lock<Ret, F>(&self, mut f: F) -> Ret
|
||||
where
|
||||
F: FnMut(&mut T) -> Ret,
|
||||
{
|
||||
// The locking and unlocking logic is hidden inside this function.
|
||||
let mut data_guard = self.lock();
|
||||
f(&mut data_guard)
|
||||
// Lock is released here when `data_guard` goes out of scope.
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: lock_api::RawMutex, T: ?Sized> WithLock<T> for Arc<lock_api::Mutex<R, T>> {
|
||||
fn with_lock<Ret, F>(&self, mut f: F) -> Ret
|
||||
where
|
||||
F: FnMut(&mut T) -> Ret,
|
||||
{
|
||||
// The locking and unlocking logic is hidden inside this function.
|
||||
let mut data_guard = self.lock();
|
||||
f(&mut data_guard)
|
||||
// Lock is released here when `data_guard` goes out of scope.
|
||||
}
|
||||
}
|
||||
|
||||
pub trait WithLockAsync<T> {
|
||||
/// Acquires a lock and executes the given closure with the locked data,
|
||||
/// returning the result.
|
||||
fn with_lock<R, F>(&self, f: F) -> impl Future<Output = R>
|
||||
where
|
||||
F: FnMut(&mut T) -> R;
|
||||
|
||||
/// Acquires a lock and executes the given async closure with the locked
|
||||
/// data.
|
||||
fn with_lock_async<R, F>(&self, f: F) -> impl std::future::Future<Output = R>
|
||||
where
|
||||
F: AsyncFnMut(&mut T) -> R;
|
||||
}
|
||||
|
||||
impl<T> WithLockAsync<T> for futures::lock::Mutex<T> {
|
||||
async fn with_lock<R, F>(&self, mut f: F) -> R
|
||||
where
|
||||
F: FnMut(&mut T) -> R,
|
||||
{
|
||||
// The locking and unlocking logic is hidden inside this function.
|
||||
let mut data_guard = self.lock().await;
|
||||
f(&mut data_guard)
|
||||
// Lock is released here when `data_guard` goes out of scope.
|
||||
}
|
||||
|
||||
async fn with_lock_async<R, F>(&self, mut f: F) -> R
|
||||
where
|
||||
F: AsyncFnMut(&mut T) -> R,
|
||||
{
|
||||
// The locking and unlocking logic is hidden inside this function.
|
||||
let mut data_guard = self.lock().await;
|
||||
f(&mut data_guard).await
|
||||
// Lock is released here when `data_guard` goes out of scope.
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> WithLockAsync<T> for Arc<futures::lock::Mutex<T>> {
|
||||
async fn with_lock<R, F>(&self, mut f: F) -> R
|
||||
where
|
||||
F: FnMut(&mut T) -> R,
|
||||
{
|
||||
// The locking and unlocking logic is hidden inside this function.
|
||||
let mut data_guard = self.lock().await;
|
||||
f(&mut data_guard)
|
||||
// Lock is released here when `data_guard` goes out of scope.
|
||||
}
|
||||
|
||||
async fn with_lock_async<R, F>(&self, mut f: F) -> R
|
||||
where
|
||||
F: AsyncFnMut(&mut T) -> R,
|
||||
{
|
||||
// The locking and unlocking logic is hidden inside this function.
|
||||
let mut data_guard = self.lock().await;
|
||||
f(&mut data_guard).await
|
||||
// Lock is released here when `data_guard` goes out of scope.
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_with_lock_return_value() {
|
||||
let mutex = Mutex::new(5);
|
||||
let result = mutex.with_lock(|v| {
|
||||
*v += 1;
|
||||
*v * 2
|
||||
});
|
||||
assert_eq!(result, 12);
|
||||
let value = mutex.lock().unwrap();
|
||||
assert_eq!(*value, 6);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_with_lock_unit_return() {
|
||||
let mutex = Mutex::new(10);
|
||||
mutex.with_lock(|v| {
|
||||
*v += 2;
|
||||
});
|
||||
let value = mutex.lock().unwrap();
|
||||
assert_eq!(*value, 12);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_with_lock_arc_mutex() {
|
||||
let mutex = Arc::new(Mutex::new(1));
|
||||
let result = mutex.with_lock(|v| {
|
||||
*v *= 10;
|
||||
*v
|
||||
});
|
||||
assert_eq!(result, 10);
|
||||
assert_eq!(*mutex.lock().unwrap(), 10);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_with_lock_async_return_value() {
|
||||
use futures::lock::Mutex as AsyncMutex;
|
||||
let mutex = AsyncMutex::new(7);
|
||||
let result = mutex
|
||||
.with_lock(|v| {
|
||||
*v += 3;
|
||||
*v * 2
|
||||
})
|
||||
.await;
|
||||
assert_eq!(result, 20);
|
||||
let value = mutex.lock().await;
|
||||
assert_eq!(*value, 10);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_with_lock_async_unit_return() {
|
||||
use futures::lock::Mutex as AsyncMutex;
|
||||
let mutex = AsyncMutex::new(100);
|
||||
mutex
|
||||
.with_lock(|v| {
|
||||
*v -= 50;
|
||||
})
|
||||
.await;
|
||||
let value = mutex.lock().await;
|
||||
assert_eq!(*value, 50);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_with_lock_async_closure() {
|
||||
use futures::lock::Mutex as AsyncMutex;
|
||||
let mutex = AsyncMutex::new(1);
|
||||
mutex
|
||||
.with_lock_async(async |v| {
|
||||
*v += 9;
|
||||
})
|
||||
.await;
|
||||
let value = mutex.lock().await;
|
||||
assert_eq!(*value, 10);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_with_lock_async_arc_mutex() {
|
||||
use futures::lock::Mutex as AsyncMutex;
|
||||
let mutex = Arc::new(AsyncMutex::new(2));
|
||||
mutex
|
||||
.with_lock_async(async |v: &mut i32| {
|
||||
*v *= 5;
|
||||
})
|
||||
.await;
|
||||
let value = mutex.lock().await;
|
||||
assert_eq!(*value, 10);
|
||||
}
|
||||
}
|
||||
@@ -44,6 +44,14 @@ zstd_compression = [
|
||||
"conduwuit-core/zstd_compression",
|
||||
"rust-rocksdb/zstd",
|
||||
]
|
||||
bindgen-static = [
|
||||
# "bindgen/static"
|
||||
# "clang-sys/static"
|
||||
"rust-rocksdb/bindgen-static"
|
||||
]
|
||||
bindgen-runtime = [
|
||||
"rust-rocksdb/bindgen-runtime"
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
async-channel.workspace = true
|
||||
|
||||
@@ -71,7 +71,7 @@ pub fn backup_count(&self) -> Result<usize> {
|
||||
fn backup_engine(&self) -> Result<BackupEngine> {
|
||||
let path = self.backup_path()?;
|
||||
let options = BackupEngineOptions::new(path).map_err(map_err)?;
|
||||
BackupEngine::open(&options, &*self.ctx.env.lock()?).map_err(map_err)
|
||||
BackupEngine::open(&options, &self.ctx.env.lock()).map_err(map_err)
|
||||
}
|
||||
|
||||
#[implement(Engine)]
|
||||
|
||||
@@ -232,7 +232,7 @@ fn get_cache(ctx: &Context, desc: &Descriptor) -> Option<Cache> {
|
||||
cache_opts.set_num_shard_bits(shard_bits);
|
||||
cache_opts.set_capacity(size);
|
||||
|
||||
let mut caches = ctx.col_cache.lock().expect("locked");
|
||||
let mut caches = ctx.col_cache.lock();
|
||||
match desc.cache_disp {
|
||||
| CacheDisp::Unique if desc.cache_size == 0 => None,
|
||||
| CacheDisp::Unique => {
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
use std::{collections::BTreeMap, sync::Arc};
|
||||
|
||||
use conduwuit::{Result, Server, debug, utils::math::usize_from_f64};
|
||||
use conduwuit::{Result, Server, SyncMutex, debug, utils::math::usize_from_f64};
|
||||
use rocksdb::{Cache, Env, LruCacheOptions};
|
||||
|
||||
use crate::{or_else, pool::Pool};
|
||||
@@ -14,9 +11,9 @@ use crate::{or_else, pool::Pool};
|
||||
/// These assets are housed in the shared Context.
|
||||
pub(crate) struct Context {
|
||||
pub(crate) pool: Arc<Pool>,
|
||||
pub(crate) col_cache: Mutex<BTreeMap<String, Cache>>,
|
||||
pub(crate) row_cache: Mutex<Cache>,
|
||||
pub(crate) env: Mutex<Env>,
|
||||
pub(crate) col_cache: SyncMutex<BTreeMap<String, Cache>>,
|
||||
pub(crate) row_cache: SyncMutex<Cache>,
|
||||
pub(crate) env: SyncMutex<Env>,
|
||||
pub(crate) server: Arc<Server>,
|
||||
}
|
||||
|
||||
@@ -68,7 +65,7 @@ impl Drop for Context {
|
||||
debug!("Closing frontend pool");
|
||||
self.pool.close();
|
||||
|
||||
let mut env = self.env.lock().expect("locked");
|
||||
let mut env = self.env.lock();
|
||||
|
||||
debug!("Shutting down background threads");
|
||||
env.set_high_priority_background_threads(0);
|
||||
|
||||
@@ -9,7 +9,7 @@ use crate::or_else;
|
||||
#[implement(Engine)]
|
||||
pub fn memory_usage(&self) -> Result<String> {
|
||||
let mut res = String::new();
|
||||
let stats = get_memory_usage_stats(Some(&[&self.db]), Some(&[&*self.ctx.row_cache.lock()?]))
|
||||
let stats = get_memory_usage_stats(Some(&[&self.db]), Some(&[&*self.ctx.row_cache.lock()]))
|
||||
.or_else(or_else)?;
|
||||
let mibs = |input| f64::from(u32::try_from(input / 1024).unwrap_or(0)) / 1024.0;
|
||||
writeln!(
|
||||
@@ -19,10 +19,10 @@ pub fn memory_usage(&self) -> Result<String> {
|
||||
mibs(stats.mem_table_total),
|
||||
mibs(stats.mem_table_unflushed),
|
||||
mibs(stats.mem_table_readers_total),
|
||||
mibs(u64::try_from(self.ctx.row_cache.lock()?.get_usage())?),
|
||||
mibs(u64::try_from(self.ctx.row_cache.lock().get_usage())?),
|
||||
)?;
|
||||
|
||||
for (name, cache) in &*self.ctx.col_cache.lock()? {
|
||||
for (name, cache) in &*self.ctx.col_cache.lock() {
|
||||
writeln!(res, "{name} cache: {:.2} MiB", mibs(u64::try_from(cache.get_usage())?))?;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,11 +23,7 @@ pub(crate) async fn open(ctx: Arc<Context>, desc: &[Descriptor]) -> Result<Arc<S
|
||||
let config = &server.config;
|
||||
let path = &config.database_path;
|
||||
|
||||
let db_opts = db_options(
|
||||
config,
|
||||
&ctx.env.lock().expect("environment locked"),
|
||||
&ctx.row_cache.lock().expect("row cache locked"),
|
||||
)?;
|
||||
let db_opts = db_options(config, &ctx.env.lock(), &ctx.row_cache.lock())?;
|
||||
|
||||
let cfds = Self::configure_cfds(&ctx, &db_opts, desc)?;
|
||||
let num_cfds = cfds.len();
|
||||
|
||||
@@ -3,7 +3,7 @@ mod configure;
|
||||
use std::{
|
||||
mem::take,
|
||||
sync::{
|
||||
Arc, Mutex,
|
||||
Arc,
|
||||
atomic::{AtomicUsize, Ordering},
|
||||
},
|
||||
thread,
|
||||
@@ -12,7 +12,7 @@ use std::{
|
||||
|
||||
use async_channel::{QueueStrategy, Receiver, RecvError, Sender};
|
||||
use conduwuit::{
|
||||
Error, Result, Server, debug, err, error, implement,
|
||||
Error, Result, Server, SyncMutex, debug, err, error, implement,
|
||||
result::DebugInspect,
|
||||
smallvec::SmallVec,
|
||||
trace,
|
||||
@@ -31,7 +31,7 @@ use crate::{Handle, Map, keyval::KeyBuf, stream};
|
||||
pub(crate) struct Pool {
|
||||
server: Arc<Server>,
|
||||
queues: Vec<Sender<Cmd>>,
|
||||
workers: Mutex<Vec<JoinHandle<()>>>,
|
||||
workers: SyncMutex<Vec<JoinHandle<()>>>,
|
||||
topology: Vec<usize>,
|
||||
busy: AtomicUsize,
|
||||
queued_max: AtomicUsize,
|
||||
@@ -115,7 +115,7 @@ impl Drop for Pool {
|
||||
#[implement(Pool)]
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub(crate) fn close(&self) {
|
||||
let workers = take(&mut *self.workers.lock().expect("locked"));
|
||||
let workers = take(&mut *self.workers.lock());
|
||||
|
||||
let senders = self.queues.iter().map(Sender::sender_count).sum::<usize>();
|
||||
|
||||
@@ -154,7 +154,7 @@ pub(crate) fn close(&self) {
|
||||
|
||||
#[implement(Pool)]
|
||||
fn spawn_until(self: &Arc<Self>, recv: &[Receiver<Cmd>], count: usize) -> Result {
|
||||
let mut workers = self.workers.lock().expect("locked");
|
||||
let mut workers = self.workers.lock();
|
||||
while workers.len() < count {
|
||||
self.clone().spawn_one(&mut workers, recv)?;
|
||||
}
|
||||
|
||||
@@ -2,12 +2,12 @@ use std::{
|
||||
collections::{HashMap, hash_map},
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
sync::RwLock,
|
||||
};
|
||||
|
||||
use conduwuit::SyncRwLock;
|
||||
use tokio::sync::watch;
|
||||
|
||||
type Watcher = RwLock<HashMap<Vec<u8>, (watch::Sender<()>, watch::Receiver<()>)>>;
|
||||
type Watcher = SyncRwLock<HashMap<Vec<u8>, (watch::Sender<()>, watch::Receiver<()>)>>;
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct Watchers {
|
||||
@@ -19,7 +19,7 @@ impl Watchers {
|
||||
&'a self,
|
||||
prefix: &[u8],
|
||||
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||
let mut rx = match self.watchers.write().unwrap().entry(prefix.to_vec()) {
|
||||
let mut rx = match self.watchers.write().entry(prefix.to_vec()) {
|
||||
| hash_map::Entry::Occupied(o) => o.get().1.clone(),
|
||||
| hash_map::Entry::Vacant(v) => {
|
||||
let (tx, rx) = watch::channel(());
|
||||
@@ -35,7 +35,7 @@ impl Watchers {
|
||||
}
|
||||
|
||||
pub(crate) fn wake(&self, key: &[u8]) {
|
||||
let watchers = self.watchers.read().unwrap();
|
||||
let watchers = self.watchers.read();
|
||||
let mut triggered = Vec::new();
|
||||
for length in 0..=key.len() {
|
||||
if watchers.contains_key(&key[..length]) {
|
||||
@@ -46,7 +46,7 @@ impl Watchers {
|
||||
drop(watchers);
|
||||
|
||||
if !triggered.is_empty() {
|
||||
let mut watchers = self.watchers.write().unwrap();
|
||||
let mut watchers = self.watchers.write();
|
||||
for prefix in triggered {
|
||||
if let Some(tx) = watchers.remove(prefix) {
|
||||
tx.0.send(()).expect("channel should still be open");
|
||||
|
||||
+2
-2
@@ -15,13 +15,13 @@ pub(super) fn flags_capture(args: TokenStream) -> TokenStream {
|
||||
|
||||
#[conduwuit_core::ctor]
|
||||
fn _set_rustc_flags() {
|
||||
conduwuit_core::info::rustc::FLAGS.lock().expect("locked").insert(#crate_name, &RUSTC_FLAGS);
|
||||
conduwuit_core::info::rustc::FLAGS.lock().insert(#crate_name, &RUSTC_FLAGS);
|
||||
}
|
||||
|
||||
// static strings have to be yanked on module unload
|
||||
#[conduwuit_core::dtor]
|
||||
fn _unset_rustc_flags() {
|
||||
conduwuit_core::info::rustc::FLAGS.lock().expect("locked").remove(#crate_name);
|
||||
conduwuit_core::info::rustc::FLAGS.lock().remove(#crate_name);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+26
-2
@@ -43,6 +43,11 @@ assets = [
|
||||
|
||||
[features]
|
||||
default = [
|
||||
"standard",
|
||||
"release_max_log_level",
|
||||
"bindgen-runtime", # replace with bindgen-static on alpine
|
||||
]
|
||||
standard = [
|
||||
"blurhashing",
|
||||
"brotli_compression",
|
||||
"element_hacks",
|
||||
@@ -52,10 +57,17 @@ default = [
|
||||
"jemalloc_conf",
|
||||
"journald",
|
||||
"media_thumbnail",
|
||||
"release_max_log_level",
|
||||
"systemd",
|
||||
"url_preview",
|
||||
"zstd_compression",
|
||||
"zstd_compression"
|
||||
]
|
||||
full = [
|
||||
"standard",
|
||||
"hardened_malloc",
|
||||
"jemalloc_prof",
|
||||
"perf_measurements",
|
||||
"tokio_console"
|
||||
# sentry_telemetry
|
||||
]
|
||||
|
||||
blurhashing = [
|
||||
@@ -161,6 +173,18 @@ zstd_compression = [
|
||||
conduwuit_mods = [
|
||||
"conduwuit-core/conduwuit_mods",
|
||||
]
|
||||
bindgen-static = [
|
||||
# "bindgen/static"
|
||||
# "clang-sys/static"
|
||||
"conduwuit-database/bindgen-static"
|
||||
]
|
||||
bindgen-runtime = [
|
||||
"conduwuit-database/bindgen-runtime"
|
||||
]
|
||||
|
||||
[build-dependencies]
|
||||
# bindgen = {version = "0.71.1", default-features = false}
|
||||
# clang-sys = {version = "1", default-features = false}
|
||||
|
||||
[dependencies]
|
||||
conduwuit-admin.workspace = true
|
||||
|
||||
@@ -111,6 +111,7 @@ webpage.workspace = true
|
||||
webpage.optional = true
|
||||
blurhash.workspace = true
|
||||
blurhash.optional = true
|
||||
recaptcha-verify = { version = "0.1.5", default-features = false }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
#![cfg(feature = "console")]
|
||||
|
||||
use std::{
|
||||
collections::VecDeque,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
use std::{collections::VecDeque, sync::Arc};
|
||||
|
||||
use conduwuit::{Server, debug, defer, error, log, log::is_systemd_mode};
|
||||
use conduwuit::{Server, SyncMutex, debug, defer, error, log, log::is_systemd_mode};
|
||||
use futures::future::{AbortHandle, Abortable};
|
||||
use ruma::events::room::message::RoomMessageEventContent;
|
||||
use rustyline_async::{Readline, ReadlineError, ReadlineEvent};
|
||||
@@ -17,10 +14,10 @@ use crate::{Dep, admin};
|
||||
pub struct Console {
|
||||
server: Arc<Server>,
|
||||
admin: Dep<admin::Service>,
|
||||
worker_join: Mutex<Option<JoinHandle<()>>>,
|
||||
input_abort: Mutex<Option<AbortHandle>>,
|
||||
command_abort: Mutex<Option<AbortHandle>>,
|
||||
history: Mutex<VecDeque<String>>,
|
||||
worker_join: SyncMutex<Option<JoinHandle<()>>>,
|
||||
input_abort: SyncMutex<Option<AbortHandle>>,
|
||||
command_abort: SyncMutex<Option<AbortHandle>>,
|
||||
history: SyncMutex<VecDeque<String>>,
|
||||
output: MadSkin,
|
||||
}
|
||||
|
||||
@@ -50,7 +47,7 @@ impl Console {
|
||||
}
|
||||
|
||||
pub async fn start(self: &Arc<Self>) {
|
||||
let mut worker_join = self.worker_join.lock().expect("locked");
|
||||
let mut worker_join = self.worker_join.lock();
|
||||
if worker_join.is_none() {
|
||||
let self_ = Arc::clone(self);
|
||||
_ = worker_join.insert(self.server.runtime().spawn(self_.worker()));
|
||||
@@ -60,7 +57,7 @@ impl Console {
|
||||
pub async fn close(self: &Arc<Self>) {
|
||||
self.interrupt();
|
||||
|
||||
let Some(worker_join) = self.worker_join.lock().expect("locked").take() else {
|
||||
let Some(worker_join) = self.worker_join.lock().take() else {
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -70,22 +67,18 @@ impl Console {
|
||||
pub fn interrupt(self: &Arc<Self>) {
|
||||
self.interrupt_command();
|
||||
self.interrupt_readline();
|
||||
self.worker_join
|
||||
.lock()
|
||||
.expect("locked")
|
||||
.as_ref()
|
||||
.map(JoinHandle::abort);
|
||||
self.worker_join.lock().as_ref().map(JoinHandle::abort);
|
||||
}
|
||||
|
||||
pub fn interrupt_readline(self: &Arc<Self>) {
|
||||
if let Some(input_abort) = self.input_abort.lock().expect("locked").take() {
|
||||
if let Some(input_abort) = self.input_abort.lock().take() {
|
||||
debug!("Interrupting console readline...");
|
||||
input_abort.abort();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn interrupt_command(self: &Arc<Self>) {
|
||||
if let Some(command_abort) = self.command_abort.lock().expect("locked").take() {
|
||||
if let Some(command_abort) = self.command_abort.lock().take() {
|
||||
debug!("Interrupting console command...");
|
||||
command_abort.abort();
|
||||
}
|
||||
@@ -120,7 +113,7 @@ impl Console {
|
||||
}
|
||||
|
||||
debug!("session ending");
|
||||
self.worker_join.lock().expect("locked").take();
|
||||
self.worker_join.lock().take();
|
||||
}
|
||||
|
||||
async fn readline(self: &Arc<Self>) -> Result<ReadlineEvent, ReadlineError> {
|
||||
@@ -135,9 +128,9 @@ impl Console {
|
||||
|
||||
let (abort, abort_reg) = AbortHandle::new_pair();
|
||||
let future = Abortable::new(future, abort_reg);
|
||||
_ = self.input_abort.lock().expect("locked").insert(abort);
|
||||
_ = self.input_abort.lock().insert(abort);
|
||||
defer! {{
|
||||
_ = self.input_abort.lock().expect("locked").take();
|
||||
_ = self.input_abort.lock().take();
|
||||
}}
|
||||
|
||||
let Ok(result) = future.await else {
|
||||
@@ -158,9 +151,9 @@ impl Console {
|
||||
|
||||
let (abort, abort_reg) = AbortHandle::new_pair();
|
||||
let future = Abortable::new(future, abort_reg);
|
||||
_ = self.command_abort.lock().expect("locked").insert(abort);
|
||||
_ = self.command_abort.lock().insert(abort);
|
||||
defer! {{
|
||||
_ = self.command_abort.lock().expect("locked").take();
|
||||
_ = self.command_abort.lock().take();
|
||||
}}
|
||||
|
||||
_ = future.await;
|
||||
@@ -184,20 +177,15 @@ impl Console {
|
||||
}
|
||||
|
||||
fn set_history(&self, readline: &mut Readline) {
|
||||
self.history
|
||||
.lock()
|
||||
.expect("locked")
|
||||
.iter()
|
||||
.rev()
|
||||
.for_each(|entry| {
|
||||
readline
|
||||
.add_history_entry(entry.clone())
|
||||
.expect("added history entry");
|
||||
});
|
||||
self.history.lock().iter().rev().for_each(|entry| {
|
||||
readline
|
||||
.add_history_entry(entry.clone())
|
||||
.expect("added history entry");
|
||||
});
|
||||
}
|
||||
|
||||
fn add_history(&self, line: String) {
|
||||
let mut history = self.history.lock().expect("locked");
|
||||
let mut history = self.history.lock();
|
||||
history.push_front(line);
|
||||
history.truncate(HISTORY_LIMIT);
|
||||
}
|
||||
|
||||
@@ -192,8 +192,9 @@ pub async fn revoke_admin(&self, user_id: &UserId) -> Result {
|
||||
|
||||
| Err(e) => return Err!(error!(?e, "Failure occurred while attempting revoke.")),
|
||||
|
||||
| Ok(event) if !matches!(event.membership, Invite | Knock | Join) =>
|
||||
return Err!("Cannot revoke {user_id} in membership state {:?}.", event.membership),
|
||||
| Ok(event) if !matches!(event.membership, Invite | Knock | Join) => {
|
||||
return Err!("Cannot revoke {user_id} in membership state {:?}.", event.membership);
|
||||
},
|
||||
|
||||
| Ok(event) => {
|
||||
assert!(
|
||||
|
||||
+114
-22
@@ -5,10 +5,11 @@ mod grant;
|
||||
|
||||
use std::{
|
||||
pin::Pin,
|
||||
sync::{Arc, RwLock as StdRwLock, Weak},
|
||||
sync::{Arc, Weak},
|
||||
};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use conduwuit::{Err, SyncRwLock, utils};
|
||||
use conduwuit_core::{
|
||||
Error, Event, Result, Server, debug, err, error, error::default_log, pdu::PduBuilder,
|
||||
};
|
||||
@@ -16,21 +17,26 @@ pub use create::create_admin_room;
|
||||
use futures::{Future, FutureExt, TryFutureExt};
|
||||
use loole::{Receiver, Sender};
|
||||
use ruma::{
|
||||
OwnedEventId, OwnedRoomId, RoomId, UserId,
|
||||
Mxc, OwnedEventId, OwnedMxcUri, OwnedRoomId, RoomId, UInt, UserId,
|
||||
events::{
|
||||
Mentions,
|
||||
room::message::{Relation, RoomMessageEventContent},
|
||||
room::{
|
||||
MediaSource,
|
||||
message::{
|
||||
FileInfo, FileMessageEventContent, MessageType, Relation, RoomMessageEventContent,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::{Dep, account_data, globals, rooms, rooms::state::RoomMutexGuard};
|
||||
use crate::{Dep, account_data, globals, media::MXC_LENGTH, rooms, rooms::state::RoomMutexGuard};
|
||||
|
||||
pub struct Service {
|
||||
services: Services,
|
||||
channel: (Sender<CommandInput>, Receiver<CommandInput>),
|
||||
pub handle: RwLock<Option<Processor>>,
|
||||
pub complete: StdRwLock<Option<Completer>>,
|
||||
pub complete: SyncRwLock<Option<Completer>>,
|
||||
#[cfg(feature = "console")]
|
||||
pub console: Arc<console::Console>,
|
||||
}
|
||||
@@ -44,7 +50,8 @@ struct Services {
|
||||
state_cache: Dep<rooms::state_cache::Service>,
|
||||
state_accessor: Dep<rooms::state_accessor::Service>,
|
||||
account_data: Dep<account_data::Service>,
|
||||
services: StdRwLock<Option<Weak<crate::Services>>>,
|
||||
services: SyncRwLock<Option<Weak<crate::Services>>>,
|
||||
media: Dep<crate::media::Service>,
|
||||
}
|
||||
|
||||
/// Inputs to a command are a multi-line string, optional reply_id, and optional
|
||||
@@ -94,10 +101,11 @@ impl crate::Service for Service {
|
||||
.depend::<rooms::state_accessor::Service>("rooms::state_accessor"),
|
||||
account_data: args.depend::<account_data::Service>("account_data"),
|
||||
services: None.into(),
|
||||
media: args.depend::<crate::media::Service>("media"),
|
||||
},
|
||||
channel: loole::bounded(COMMAND_QUEUE_LIMIT),
|
||||
handle: RwLock::new(None),
|
||||
complete: StdRwLock::new(None),
|
||||
complete: SyncRwLock::new(None),
|
||||
#[cfg(feature = "console")]
|
||||
console: console::Console::new(&args),
|
||||
}))
|
||||
@@ -157,8 +165,65 @@ impl Service {
|
||||
.ok();
|
||||
}
|
||||
|
||||
/// Sends a message to the admin room as the admin user (see send_text() for
|
||||
/// convenience).
|
||||
/// Either returns a small-enough message, or converts a large message into
|
||||
/// a file
|
||||
pub async fn text_or_file(
|
||||
&self,
|
||||
message_content: RoomMessageEventContent,
|
||||
) -> RoomMessageEventContent {
|
||||
let body_len = Self::collate_msg_size(&message_content);
|
||||
if body_len > 60000 {
|
||||
// Intercept and send as file
|
||||
let file = self
|
||||
.text_to_file(message_content.body())
|
||||
.await
|
||||
.expect("failed to create text file");
|
||||
let size_u64: u64 = message_content.body().len().try_into().map_or(0, |n| n);
|
||||
let metadata = FileInfo {
|
||||
mimetype: Some("text/markdown".to_owned()),
|
||||
size: Some(UInt::new_saturating(size_u64)),
|
||||
thumbnail_info: None,
|
||||
thumbnail_source: None,
|
||||
};
|
||||
let content = FileMessageEventContent {
|
||||
body: "Output was too large to send as text.".to_owned(),
|
||||
formatted: None,
|
||||
filename: Some("output.md".to_owned()),
|
||||
source: MediaSource::Plain(file),
|
||||
info: Some(Box::new(metadata)),
|
||||
};
|
||||
RoomMessageEventContent::new(MessageType::File(content))
|
||||
} else {
|
||||
message_content
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn collate_msg_size(content: &RoomMessageEventContent) -> u64 {
|
||||
content
|
||||
.body()
|
||||
.len()
|
||||
.saturating_add(match &content.msgtype {
|
||||
| MessageType::Text(t) =>
|
||||
if t.formatted.is_some() {
|
||||
t.formatted.as_ref().map_or(0, |f| f.body.len())
|
||||
} else {
|
||||
0
|
||||
},
|
||||
| MessageType::Notice(n) =>
|
||||
if n.formatted.is_some() {
|
||||
n.formatted.as_ref().map_or(0, |f| f.body.len())
|
||||
} else {
|
||||
0
|
||||
},
|
||||
| _ => 0,
|
||||
})
|
||||
.try_into()
|
||||
.expect("size too large")
|
||||
}
|
||||
|
||||
/// Sends a message to the admin room as the admin user (see send_text()
|
||||
/// for convenience).
|
||||
pub async fn send_message(&self, message_content: RoomMessageEventContent) -> Result<()> {
|
||||
let user_id = &self.services.globals.server_user;
|
||||
let room_id = self.get_admin_room().await?;
|
||||
@@ -178,6 +243,36 @@ impl Service {
|
||||
self.send_message(message_content).await
|
||||
}
|
||||
|
||||
/// Casts a text body into a file and creates a file for it.
|
||||
pub async fn text_to_file(&self, body: &str) -> Result<OwnedMxcUri> {
|
||||
let mxc = Mxc {
|
||||
server_name: self.services.globals.server_name(),
|
||||
media_id: &utils::random_string(MXC_LENGTH),
|
||||
};
|
||||
match self
|
||||
.services
|
||||
.media
|
||||
.create(
|
||||
&mxc,
|
||||
Some(self.services.globals.server_user.as_ref()),
|
||||
Some(&utils::content_disposition::make_content_disposition(
|
||||
None,
|
||||
Some("text/markdown"),
|
||||
Some("output.md"),
|
||||
)),
|
||||
Some("text/markdown"),
|
||||
body.as_bytes(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
| Ok(()) => Ok(mxc.to_string().into()),
|
||||
| Err(e) => {
|
||||
error!("Failed to upload text to file: {e}");
|
||||
Err!(Request(Unknown("Failed to upload text to file")))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Posts a command to the command processor queue and returns. Processing
|
||||
/// will take place on the service worker's task asynchronously. Errors if
|
||||
/// the queue is full.
|
||||
@@ -217,10 +312,7 @@ impl Service {
|
||||
/// Invokes the tab-completer to complete the command. When unavailable,
|
||||
/// None is returned.
|
||||
pub fn complete_command(&self, command: &str) -> Option<String> {
|
||||
self.complete
|
||||
.read()
|
||||
.expect("locked for reading")
|
||||
.map(|complete| complete(command))
|
||||
self.complete.read().map(|complete| complete(command))
|
||||
}
|
||||
|
||||
async fn handle_signal(&self, sig: &'static str) {
|
||||
@@ -243,17 +335,13 @@ impl Service {
|
||||
}
|
||||
|
||||
async fn process_command(&self, command: CommandInput) -> ProcessorResult {
|
||||
let handle = &self
|
||||
.handle
|
||||
.read()
|
||||
.await
|
||||
.expect("Admin module is not loaded");
|
||||
let handle_guard = self.handle.read().await;
|
||||
let handle = handle_guard.as_ref().expect("Admin module is not loaded");
|
||||
|
||||
let services = self
|
||||
.services
|
||||
.services
|
||||
.read()
|
||||
.expect("locked")
|
||||
.as_ref()
|
||||
.and_then(Weak::upgrade)
|
||||
.expect("Services self-reference not initialized.");
|
||||
@@ -325,11 +413,15 @@ impl Service {
|
||||
assert!(self.user_is_admin(user_id).await, "sender is not admin");
|
||||
|
||||
let state_lock = self.services.state.mutex.lock(room_id).await;
|
||||
|
||||
if let Err(e) = self
|
||||
.services
|
||||
.timeline
|
||||
.build_and_append_pdu(PduBuilder::timeline(&content), user_id, room_id, &state_lock)
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::timeline(&self.text_or_file(content).await),
|
||||
user_id,
|
||||
room_id,
|
||||
&state_lock,
|
||||
)
|
||||
.await
|
||||
{
|
||||
self.handle_response_error(e, room_id, user_id, &state_lock)
|
||||
@@ -424,7 +516,7 @@ impl Service {
|
||||
/// Sets the self-reference to crate::Services which will provide context to
|
||||
/// the admin commands.
|
||||
pub(super) fn set_services(&self, services: Option<&Arc<crate::Services>>) {
|
||||
let receiver = &mut *self.services.services.write().expect("locked for writing");
|
||||
let receiver = &mut *self.services.services.write();
|
||||
let weak = services.map(Arc::downgrade);
|
||||
*receiver = weak;
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ impl crate::Service for Service {
|
||||
|
||||
federation: base(config)?
|
||||
.dns_resolver(resolver.resolver.hooked.clone())
|
||||
.connect_timeout(Duration::from_secs(config.federation_conn_timeout))
|
||||
.read_timeout(Duration::from_secs(config.federation_timeout))
|
||||
.pool_max_idle_per_host(config.federation_idle_per_host.into())
|
||||
.pool_idle_timeout(Duration::from_secs(config.federation_idle_timeout))
|
||||
@@ -74,6 +75,7 @@ impl crate::Service for Service {
|
||||
|
||||
synapse: base(config)?
|
||||
.dns_resolver(resolver.resolver.hooked.clone())
|
||||
.connect_timeout(Duration::from_secs(config.federation_conn_timeout))
|
||||
.read_timeout(Duration::from_secs(305))
|
||||
.pool_max_idle_per_host(0)
|
||||
.redirect(redirect::Policy::limited(3))
|
||||
@@ -81,6 +83,7 @@ impl crate::Service for Service {
|
||||
|
||||
sender: base(config)?
|
||||
.dns_resolver(resolver.resolver.hooked.clone())
|
||||
.connect_timeout(Duration::from_secs(config.federation_conn_timeout))
|
||||
.read_timeout(Duration::from_secs(config.sender_timeout))
|
||||
.timeout(Duration::from_secs(config.sender_timeout))
|
||||
.pool_max_idle_per_host(1)
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::sync::Arc;
|
||||
|
||||
use conduwuit::{Result, utils};
|
||||
use conduwuit::{Result, SyncRwLock, utils};
|
||||
use database::{Database, Deserialized, Map};
|
||||
|
||||
pub struct Data {
|
||||
global: Arc<Map>,
|
||||
counter: RwLock<u64>,
|
||||
counter: SyncRwLock<u64>,
|
||||
pub(super) db: Arc<Database>,
|
||||
}
|
||||
|
||||
@@ -16,25 +16,21 @@ impl Data {
|
||||
let db = &args.db;
|
||||
Self {
|
||||
global: db["global"].clone(),
|
||||
counter: RwLock::new(
|
||||
Self::stored_count(&db["global"]).expect("initialized global counter"),
|
||||
),
|
||||
counter: SyncRwLock::new(Self::stored_count(&db["global"]).unwrap_or_default()),
|
||||
db: args.db.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_count(&self) -> Result<u64> {
|
||||
let _cork = self.db.cork();
|
||||
let mut lock = self.counter.write().expect("locked");
|
||||
let mut lock = self.counter.write();
|
||||
let counter: &mut u64 = &mut lock;
|
||||
debug_assert!(
|
||||
*counter == Self::stored_count(&self.global).expect("database failure"),
|
||||
*counter == Self::stored_count(&self.global).unwrap_or_default(),
|
||||
"counter mismatch"
|
||||
);
|
||||
|
||||
*counter = counter
|
||||
.checked_add(1)
|
||||
.expect("counter must not overflow u64");
|
||||
*counter = counter.checked_add(1).unwrap_or(*counter);
|
||||
|
||||
self.global.insert(COUNTER, counter.to_be_bytes());
|
||||
|
||||
@@ -43,10 +39,10 @@ impl Data {
|
||||
|
||||
#[inline]
|
||||
pub fn current_count(&self) -> u64 {
|
||||
let lock = self.counter.read().expect("locked");
|
||||
let lock = self.counter.read();
|
||||
let counter: &u64 = &lock;
|
||||
debug_assert!(
|
||||
*counter == Self::stored_count(&self.global).expect("database failure"),
|
||||
*counter == Self::stored_count(&self.global).unwrap_or_default(),
|
||||
"counter mismatch"
|
||||
);
|
||||
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
mod data;
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fmt::Write,
|
||||
sync::{Arc, RwLock},
|
||||
time::Instant,
|
||||
};
|
||||
use std::{collections::HashMap, fmt::Write, sync::Arc, time::Instant};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use conduwuit::{Result, Server, error, utils::bytes::pretty};
|
||||
use conduwuit::{Result, Server, SyncRwLock, error, utils::bytes::pretty};
|
||||
use data::Data;
|
||||
use regex::RegexSet;
|
||||
use ruma::{OwnedEventId, OwnedRoomAliasId, OwnedServerName, OwnedUserId, ServerName, UserId};
|
||||
@@ -19,7 +14,7 @@ pub struct Service {
|
||||
pub db: Data,
|
||||
server: Arc<Server>,
|
||||
|
||||
pub bad_event_ratelimiter: Arc<RwLock<HashMap<OwnedEventId, RateLimitState>>>,
|
||||
pub bad_event_ratelimiter: Arc<SyncRwLock<HashMap<OwnedEventId, RateLimitState>>>,
|
||||
pub server_user: OwnedUserId,
|
||||
pub admin_alias: OwnedRoomAliasId,
|
||||
pub turn_secret: String,
|
||||
@@ -62,7 +57,7 @@ impl crate::Service for Service {
|
||||
Ok(Arc::new(Self {
|
||||
db,
|
||||
server: args.server.clone(),
|
||||
bad_event_ratelimiter: Arc::new(RwLock::new(HashMap::new())),
|
||||
bad_event_ratelimiter: Arc::new(SyncRwLock::new(HashMap::new())),
|
||||
admin_alias: OwnedRoomAliasId::try_from(format!("#admins:{}", &args.server.name))
|
||||
.expect("#admins:server_name is valid alias name"),
|
||||
server_user: UserId::parse_with_server_name(
|
||||
@@ -76,7 +71,7 @@ impl crate::Service for Service {
|
||||
}
|
||||
|
||||
async fn memory_usage(&self, out: &mut (dyn Write + Send)) -> Result {
|
||||
let (ber_count, ber_bytes) = self.bad_event_ratelimiter.read()?.iter().fold(
|
||||
let (ber_count, ber_bytes) = self.bad_event_ratelimiter.read().iter().fold(
|
||||
(0_usize, 0_usize),
|
||||
|(mut count, mut bytes), (event_id, _)| {
|
||||
bytes = bytes.saturating_add(event_id.capacity());
|
||||
@@ -91,12 +86,7 @@ impl crate::Service for Service {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn clear_cache(&self) {
|
||||
self.bad_event_ratelimiter
|
||||
.write()
|
||||
.expect("locked for writing")
|
||||
.clear();
|
||||
}
|
||||
async fn clear_cache(&self) { self.bad_event_ratelimiter.write().clear(); }
|
||||
|
||||
fn name(&self) -> &str { service::make_name(std::module_path!()) }
|
||||
}
|
||||
|
||||
@@ -58,7 +58,6 @@ impl Manager {
|
||||
let services: Vec<Arc<dyn Service>> = self
|
||||
.service
|
||||
.read()
|
||||
.expect("locked for reading")
|
||||
.values()
|
||||
.map(|val| val.0.upgrade())
|
||||
.map(|arc| arc.expect("services available for manager startup"))
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
use std::{
|
||||
mem::size_of,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
use std::{mem::size_of, sync::Arc};
|
||||
|
||||
use conduwuit::{Err, Result, err, utils, utils::math::usize_from_f64};
|
||||
use conduwuit::{Err, Result, SyncMutex, err, utils, utils::math::usize_from_f64};
|
||||
use database::Map;
|
||||
use lru_cache::LruCache;
|
||||
|
||||
@@ -11,7 +8,7 @@ use crate::rooms::short::ShortEventId;
|
||||
|
||||
pub(super) struct Data {
|
||||
shorteventid_authchain: Arc<Map>,
|
||||
pub(super) auth_chain_cache: Mutex<LruCache<Vec<u64>, Arc<[ShortEventId]>>>,
|
||||
pub(super) auth_chain_cache: SyncMutex<LruCache<Vec<u64>, Arc<[ShortEventId]>>>,
|
||||
}
|
||||
|
||||
impl Data {
|
||||
@@ -23,7 +20,7 @@ impl Data {
|
||||
.expect("valid cache size");
|
||||
Self {
|
||||
shorteventid_authchain: db["shorteventid_authchain"].clone(),
|
||||
auth_chain_cache: Mutex::new(LruCache::new(cache_size)),
|
||||
auth_chain_cache: SyncMutex::new(LruCache::new(cache_size)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,12 +31,7 @@ impl Data {
|
||||
debug_assert!(!key.is_empty(), "auth_chain key must not be empty");
|
||||
|
||||
// Check RAM cache
|
||||
if let Some(result) = self
|
||||
.auth_chain_cache
|
||||
.lock()
|
||||
.expect("cache locked")
|
||||
.get_mut(key)
|
||||
{
|
||||
if let Some(result) = self.auth_chain_cache.lock().get_mut(key) {
|
||||
return Ok(Arc::clone(result));
|
||||
}
|
||||
|
||||
@@ -63,7 +55,6 @@ impl Data {
|
||||
// Cache in RAM
|
||||
self.auth_chain_cache
|
||||
.lock()
|
||||
.expect("cache locked")
|
||||
.insert(vec![key[0]], Arc::clone(&chain));
|
||||
|
||||
Ok(chain)
|
||||
@@ -84,9 +75,6 @@ impl Data {
|
||||
}
|
||||
|
||||
// Cache in RAM
|
||||
self.auth_chain_cache
|
||||
.lock()
|
||||
.expect("cache locked")
|
||||
.insert(key, auth_chain);
|
||||
self.auth_chain_cache.lock().insert(key, auth_chain);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,10 +248,10 @@ pub fn cache_auth_chain_vec(&self, key: Vec<u64>, auth_chain: &[ShortEventId]) {
|
||||
|
||||
#[implement(Service)]
|
||||
pub fn get_cache_usage(&self) -> (usize, usize) {
|
||||
let cache = self.db.auth_chain_cache.lock().expect("locked");
|
||||
let cache = self.db.auth_chain_cache.lock();
|
||||
|
||||
(cache.len(), cache.capacity())
|
||||
}
|
||||
|
||||
#[implement(Service)]
|
||||
pub fn clear_cache(&self) { self.db.auth_chain_cache.lock().expect("locked").clear(); }
|
||||
pub fn clear_cache(&self) { self.db.auth_chain_cache.lock().clear(); }
|
||||
|
||||
@@ -41,7 +41,6 @@ where
|
||||
.globals
|
||||
.bad_event_ratelimiter
|
||||
.write()
|
||||
.expect("locked")
|
||||
.entry(id)
|
||||
{
|
||||
| hash_map::Entry::Vacant(e) => {
|
||||
@@ -76,7 +75,6 @@ where
|
||||
.globals
|
||||
.bad_event_ratelimiter
|
||||
.read()
|
||||
.expect("locked")
|
||||
.get(&*next_id)
|
||||
{
|
||||
// Exponential backoff
|
||||
@@ -187,7 +185,6 @@ where
|
||||
.globals
|
||||
.bad_event_ratelimiter
|
||||
.read()
|
||||
.expect("locked")
|
||||
.get(&*next_id)
|
||||
{
|
||||
// Exponential backoff
|
||||
|
||||
@@ -160,7 +160,6 @@ pub async fn handle_incoming_pdu<'a>(
|
||||
.globals
|
||||
.bad_event_ratelimiter
|
||||
.write()
|
||||
.expect("locked")
|
||||
.entry(prev_id.into())
|
||||
{
|
||||
| hash_map::Entry::Vacant(e) => {
|
||||
@@ -181,13 +180,11 @@ pub async fn handle_incoming_pdu<'a>(
|
||||
let start_time = Instant::now();
|
||||
self.federation_handletime
|
||||
.write()
|
||||
.expect("locked")
|
||||
.insert(room_id.into(), (event_id.to_owned(), start_time));
|
||||
|
||||
defer! {{
|
||||
self.federation_handletime
|
||||
.write()
|
||||
.expect("locked")
|
||||
.remove(room_id);
|
||||
}};
|
||||
|
||||
|
||||
@@ -42,7 +42,6 @@ where
|
||||
.globals
|
||||
.bad_event_ratelimiter
|
||||
.read()
|
||||
.expect("locked")
|
||||
.get(prev_id)
|
||||
{
|
||||
// Exponential backoff
|
||||
@@ -70,13 +69,11 @@ where
|
||||
let start_time = Instant::now();
|
||||
self.federation_handletime
|
||||
.write()
|
||||
.expect("locked")
|
||||
.insert(room_id.into(), ((*prev_id).to_owned(), start_time));
|
||||
|
||||
defer! {{
|
||||
self.federation_handletime
|
||||
.write()
|
||||
.expect("locked")
|
||||
.remove(room_id);
|
||||
}};
|
||||
|
||||
|
||||
@@ -6,19 +6,15 @@ mod handle_incoming_pdu;
|
||||
mod handle_outlier_pdu;
|
||||
mod handle_prev_pdu;
|
||||
mod parse_incoming_pdu;
|
||||
mod policy_server;
|
||||
mod resolve_state;
|
||||
mod state_at_incoming;
|
||||
mod upgrade_outlier_pdu;
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fmt::Write,
|
||||
sync::{Arc, RwLock as StdRwLock},
|
||||
time::Instant,
|
||||
};
|
||||
use std::{collections::HashMap, fmt::Write, sync::Arc, time::Instant};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use conduwuit::{Err, Event, PduEvent, Result, RoomVersion, Server, utils::MutexMap};
|
||||
use conduwuit::{Err, Event, PduEvent, Result, RoomVersion, Server, SyncRwLock, utils::MutexMap};
|
||||
use ruma::{
|
||||
OwnedEventId, OwnedRoomId, RoomId, RoomVersionId,
|
||||
events::room::create::RoomCreateEventContent,
|
||||
@@ -28,7 +24,7 @@ use crate::{Dep, globals, rooms, sending, server_keys};
|
||||
|
||||
pub struct Service {
|
||||
pub mutex_federation: RoomMutexMap,
|
||||
pub federation_handletime: StdRwLock<HandleTimeMap>,
|
||||
pub federation_handletime: SyncRwLock<HandleTimeMap>,
|
||||
services: Services,
|
||||
}
|
||||
|
||||
@@ -42,6 +38,7 @@ struct Services {
|
||||
server_keys: Dep<server_keys::Service>,
|
||||
short: Dep<rooms::short::Service>,
|
||||
state: Dep<rooms::state::Service>,
|
||||
state_cache: Dep<rooms::state_cache::Service>,
|
||||
state_accessor: Dep<rooms::state_accessor::Service>,
|
||||
state_compressor: Dep<rooms::state_compressor::Service>,
|
||||
timeline: Dep<rooms::timeline::Service>,
|
||||
@@ -67,6 +64,7 @@ impl crate::Service for Service {
|
||||
pdu_metadata: args.depend::<rooms::pdu_metadata::Service>("rooms::pdu_metadata"),
|
||||
short: args.depend::<rooms::short::Service>("rooms::short"),
|
||||
state: args.depend::<rooms::state::Service>("rooms::state"),
|
||||
state_cache: args.depend::<rooms::state_cache::Service>("rooms::state_cache"),
|
||||
state_accessor: args
|
||||
.depend::<rooms::state_accessor::Service>("rooms::state_accessor"),
|
||||
state_compressor: args
|
||||
@@ -81,11 +79,7 @@ impl crate::Service for Service {
|
||||
let mutex_federation = self.mutex_federation.len();
|
||||
writeln!(out, "federation_mutex: {mutex_federation}")?;
|
||||
|
||||
let federation_handletime = self
|
||||
.federation_handletime
|
||||
.read()
|
||||
.expect("locked for reading")
|
||||
.len();
|
||||
let federation_handletime = self.federation_handletime.read().len();
|
||||
writeln!(out, "federation_handletime: {federation_handletime}")?;
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
//! Policy server integration for event spam checking in Matrix rooms.
|
||||
//!
|
||||
//! This module implements a check against a room-specific policy server, as
|
||||
//! described in the relevant Matrix spec proposal (see: https://github.com/matrix-org/matrix-spec-proposals/pull/4284).
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use conduwuit::{Err, Event, PduEvent, Result, debug, implement, warn};
|
||||
use ruma::{
|
||||
RoomId, ServerName,
|
||||
api::federation::room::policy::v1::Request as PolicyRequest,
|
||||
events::{StateEventType, room::policy::RoomPolicyEventContent},
|
||||
};
|
||||
|
||||
/// Asks a remote policy server if the event is allowed.
|
||||
///
|
||||
/// If the event is the `org.matrix.msc4284.policy` configuration state event,
|
||||
/// this check is skipped. Similarly, if there is no policy server configured in
|
||||
/// the PDU's room, or the configured server is not present in the room, the
|
||||
/// check is also skipped.
|
||||
///
|
||||
/// If the policy server marks the event as spam, Ok(false) is returned,
|
||||
/// otherwise Ok(true) allows the event. If the policy server cannot be
|
||||
/// contacted for whatever reason, Err(e) is returned, which generally is a
|
||||
/// fail-open operation.
|
||||
#[implement(super::Service)]
|
||||
#[tracing::instrument(skip_all, level = "debug")]
|
||||
pub async fn ask_policy_server(&self, pdu: &PduEvent, room_id: &RoomId) -> Result<bool> {
|
||||
if *pdu.event_type() == StateEventType::RoomPolicy.into() {
|
||||
debug!(
|
||||
room_id = %room_id,
|
||||
event_type = ?pdu.event_type(),
|
||||
"Skipping spam check for policy server meta-event"
|
||||
);
|
||||
return Ok(true);
|
||||
}
|
||||
let Ok(policyserver) = self
|
||||
.services
|
||||
.state_accessor
|
||||
.room_state_get_content(room_id, &StateEventType::RoomPolicy, "")
|
||||
.await
|
||||
.map(|c: RoomPolicyEventContent| c)
|
||||
else {
|
||||
return Ok(true);
|
||||
};
|
||||
|
||||
let via = match policyserver.via {
|
||||
| Some(ref via) => ServerName::parse(via)?,
|
||||
| None => {
|
||||
debug!("No policy server configured for room {room_id}");
|
||||
return Ok(true);
|
||||
},
|
||||
};
|
||||
if via.is_empty() {
|
||||
debug!("Policy server is empty for room {room_id}, skipping spam check");
|
||||
return Ok(true);
|
||||
}
|
||||
if !self.services.state_cache.server_in_room(via, room_id).await {
|
||||
debug!(
|
||||
room_id = %room_id,
|
||||
via = %via,
|
||||
"Policy server is not in the room, skipping spam check"
|
||||
);
|
||||
return Ok(true);
|
||||
}
|
||||
let outgoing = self
|
||||
.services
|
||||
.sending
|
||||
.convert_to_outgoing_federation_event(pdu.to_canonical_object())
|
||||
.await;
|
||||
debug!(
|
||||
room_id = %room_id,
|
||||
via = %via,
|
||||
outgoing = ?outgoing,
|
||||
"Checking event for spam with policy server"
|
||||
);
|
||||
let response = tokio::time::timeout(
|
||||
Duration::from_secs(self.services.server.config.policy_server_request_timeout),
|
||||
self.services
|
||||
.sending
|
||||
.send_federation_request(via, PolicyRequest {
|
||||
event_id: pdu.event_id().to_owned(),
|
||||
pdu: Some(outgoing),
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
let response = match response {
|
||||
| Ok(Ok(response)) => response,
|
||||
| Ok(Err(e)) => {
|
||||
warn!(
|
||||
via = %via,
|
||||
event_id = %pdu.event_id(),
|
||||
room_id = %room_id,
|
||||
"Failed to contact policy server: {e}"
|
||||
);
|
||||
// Network or policy server errors are treated as non-fatal: event is allowed by
|
||||
// default.
|
||||
return Err(e);
|
||||
},
|
||||
| Err(_) => {
|
||||
warn!(
|
||||
via = %via,
|
||||
event_id = %pdu.event_id(),
|
||||
room_id = %room_id,
|
||||
"Policy server request timed out after 10 seconds"
|
||||
);
|
||||
return Err!("Request to policy server timed out");
|
||||
},
|
||||
};
|
||||
if response.recommendation == "spam" {
|
||||
warn!(
|
||||
via = %via,
|
||||
event_id = %pdu.event_id(),
|
||||
room_id = %room_id,
|
||||
"Event was marked as spam by policy server",
|
||||
);
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::{borrow::Borrow, collections::BTreeMap, iter::once, sync::Arc, time::Instant};
|
||||
|
||||
use conduwuit::{
|
||||
Err, Result, debug, debug_info, err, implement, is_equal_to,
|
||||
Err, Result, debug, debug_info, err, implement, info, is_equal_to,
|
||||
matrix::{Event, EventTypeExt, PduEvent, StateKey, state_res},
|
||||
trace,
|
||||
utils::stream::{BroadbandExt, ReadyExt},
|
||||
@@ -47,7 +47,10 @@ where
|
||||
return Err!(Request(InvalidParam("Event has been soft failed")));
|
||||
}
|
||||
|
||||
debug!("Upgrading to timeline pdu");
|
||||
debug!(
|
||||
event_id = %incoming_pdu.event_id,
|
||||
"Upgrading PDU from outlier to timeline"
|
||||
);
|
||||
let timer = Instant::now();
|
||||
let room_version_id = get_room_version_id(create_event)?;
|
||||
|
||||
@@ -55,7 +58,10 @@ where
|
||||
// backwards extremities doing all the checks in this list starting at 1.
|
||||
// These are not timeline events.
|
||||
|
||||
debug!("Resolving state at event");
|
||||
debug!(
|
||||
event_id = %incoming_pdu.event_id,
|
||||
"Resolving state at event"
|
||||
);
|
||||
let mut state_at_incoming_event = if incoming_pdu.prev_events().count() == 1 {
|
||||
self.state_at_incoming_degree_one(&incoming_pdu).await?
|
||||
} else {
|
||||
@@ -74,7 +80,10 @@ where
|
||||
|
||||
let room_version = to_room_version(&room_version_id);
|
||||
|
||||
debug!("Performing auth check");
|
||||
debug!(
|
||||
event_id = %incoming_pdu.event_id,
|
||||
"Performing auth check to upgrade"
|
||||
);
|
||||
// 11. Check the auth of the event passes based on the state of the event
|
||||
let state_fetch_state = &state_at_incoming_event;
|
||||
let state_fetch = |k: StateEventType, s: StateKey| async move {
|
||||
@@ -84,6 +93,10 @@ where
|
||||
self.services.timeline.get_pdu(event_id).await.ok()
|
||||
};
|
||||
|
||||
debug!(
|
||||
event_id = %incoming_pdu.event_id,
|
||||
"Running initial auth check"
|
||||
);
|
||||
let auth_check = state_res::event_auth::auth_check(
|
||||
&room_version,
|
||||
&incoming_pdu,
|
||||
@@ -97,7 +110,10 @@ where
|
||||
return Err!(Request(Forbidden("Event has failed auth check with state at the event.")));
|
||||
}
|
||||
|
||||
debug!("Gathering auth events");
|
||||
debug!(
|
||||
event_id = %incoming_pdu.event_id,
|
||||
"Gathering auth events"
|
||||
);
|
||||
let auth_events = self
|
||||
.services
|
||||
.state
|
||||
@@ -115,6 +131,10 @@ where
|
||||
ready(auth_events.get(&key).map(ToOwned::to_owned))
|
||||
};
|
||||
|
||||
debug!(
|
||||
event_id = %incoming_pdu.event_id,
|
||||
"Running auth check with claimed state auth"
|
||||
);
|
||||
let auth_check = state_res::event_auth::auth_check(
|
||||
&room_version,
|
||||
&incoming_pdu,
|
||||
@@ -125,8 +145,11 @@ where
|
||||
.map_err(|e| err!(Request(Forbidden("Auth check failed: {e:?}"))))?;
|
||||
|
||||
// Soft fail check before doing state res
|
||||
debug!("Performing soft-fail check");
|
||||
let soft_fail = match (auth_check, incoming_pdu.redacts_id(&room_version_id)) {
|
||||
debug!(
|
||||
event_id = %incoming_pdu.event_id,
|
||||
"Performing soft-fail check"
|
||||
);
|
||||
let mut soft_fail = match (auth_check, incoming_pdu.redacts_id(&room_version_id)) {
|
||||
| (false, _) => true,
|
||||
| (true, None) => false,
|
||||
| (true, Some(redact_id)) =>
|
||||
@@ -140,7 +163,10 @@ where
|
||||
// 13. Use state resolution to find new room state
|
||||
|
||||
// We start looking at current room state now, so lets lock the room
|
||||
trace!("Locking the room");
|
||||
trace!(
|
||||
room_id = %room_id,
|
||||
"Locking the room"
|
||||
);
|
||||
let state_lock = self.services.state.mutex.lock(room_id).await;
|
||||
|
||||
// Now we calculate the set of extremities this room has after the incoming
|
||||
@@ -219,10 +245,63 @@ where
|
||||
.await?;
|
||||
}
|
||||
|
||||
if !soft_fail {
|
||||
// Don't call the below checks on events that have already soft-failed, there's
|
||||
// no reason to re-calculate that.
|
||||
// 14-pre. If the event is not a state event, ask the policy server about it
|
||||
if incoming_pdu.state_key.is_none() {
|
||||
debug!(event_id = %incoming_pdu.event_id, "Checking policy server for event");
|
||||
match self.ask_policy_server(&incoming_pdu, room_id).await {
|
||||
| Ok(false) => {
|
||||
warn!(
|
||||
event_id = %incoming_pdu.event_id,
|
||||
"Event has been marked as spam by policy server"
|
||||
);
|
||||
soft_fail = true;
|
||||
},
|
||||
| _ => {
|
||||
debug!(
|
||||
event_id = %incoming_pdu.event_id,
|
||||
"Event has passed policy server check or the policy server was unavailable."
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Additionally, if this is a redaction for a soft-failed event, we soft-fail it
|
||||
// also.
|
||||
|
||||
// TODO: this is supposed to hide redactions from policy servers, however, for
|
||||
// full efficacy it also needs to hide redactions for unknown events. This
|
||||
// needs to be investigated at a later time.
|
||||
if let Some(redact_id) = incoming_pdu.redacts_id(&room_version_id) {
|
||||
debug!(
|
||||
redact_id = %redact_id,
|
||||
"Checking if redaction is for a soft-failed event"
|
||||
);
|
||||
if self
|
||||
.services
|
||||
.pdu_metadata
|
||||
.is_event_soft_failed(&redact_id)
|
||||
.await
|
||||
{
|
||||
warn!(
|
||||
redact_id = %redact_id,
|
||||
"Redaction is for a soft-failed event, soft failing the redaction"
|
||||
);
|
||||
soft_fail = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 14. Check if the event passes auth based on the "current state" of the room,
|
||||
// if not soft fail it
|
||||
if soft_fail {
|
||||
debug!("Soft failing event");
|
||||
info!(
|
||||
event_id = %incoming_pdu.event_id,
|
||||
"Soft failing event"
|
||||
);
|
||||
// assert!(extremities.is_empty(), "soft_fail extremities empty");
|
||||
let extremities = extremities.iter().map(Borrow::borrow);
|
||||
|
||||
self.services
|
||||
@@ -242,7 +321,10 @@ where
|
||||
.pdu_metadata
|
||||
.mark_event_soft_failed(incoming_pdu.event_id());
|
||||
|
||||
warn!("Event was soft failed: {:?}", incoming_pdu.event_id());
|
||||
warn!(
|
||||
event_id = %incoming_pdu.event_id,
|
||||
"Event was soft failed"
|
||||
);
|
||||
return Err!(Request(InvalidParam("Event has been soft failed")));
|
||||
}
|
||||
|
||||
|
||||
@@ -91,3 +91,22 @@ pub async fn room_state_get(
|
||||
.and_then(|shortstatehash| self.state_get(shortstatehash, event_type, state_key))
|
||||
.await
|
||||
}
|
||||
|
||||
/// Returns all state keys for the given `room_id` and `event_type`.
|
||||
#[implement(super::Service)]
|
||||
#[tracing::instrument(skip(self), level = "debug")]
|
||||
pub async fn room_state_keys(
|
||||
&self,
|
||||
room_id: &RoomId,
|
||||
event_type: &StateEventType,
|
||||
) -> Result<Vec<String>> {
|
||||
let shortstatehash = self.services.state.get_room_shortstatehash(room_id).await?;
|
||||
|
||||
let state_keys: Vec<String> = self
|
||||
.state_keys(shortstatehash, event_type)
|
||||
.map(|state_key| state_key.to_string())
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
Ok(state_keys)
|
||||
}
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
mod update;
|
||||
mod via;
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
sync::{Arc, RwLock},
|
||||
};
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use conduwuit::{
|
||||
Result, implement,
|
||||
Result, SyncRwLock, implement,
|
||||
result::LogErr,
|
||||
utils::{ReadyExt, stream::TryIgnore},
|
||||
warn,
|
||||
@@ -54,14 +51,14 @@ struct Data {
|
||||
userroomid_knockedstate: Arc<Map>,
|
||||
}
|
||||
|
||||
type AppServiceInRoomCache = RwLock<HashMap<OwnedRoomId, HashMap<String, bool>>>;
|
||||
type AppServiceInRoomCache = SyncRwLock<HashMap<OwnedRoomId, HashMap<String, bool>>>;
|
||||
type StrippedStateEventItem = (OwnedRoomId, Vec<Raw<AnyStrippedStateEvent>>);
|
||||
type SyncStateEventItem = (OwnedRoomId, Vec<Raw<AnySyncStateEvent>>);
|
||||
|
||||
impl crate::Service for Service {
|
||||
fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
|
||||
Ok(Arc::new(Self {
|
||||
appservice_in_room_cache: RwLock::new(HashMap::new()),
|
||||
appservice_in_room_cache: SyncRwLock::new(HashMap::new()),
|
||||
services: Services {
|
||||
account_data: args.depend::<account_data::Service>("account_data"),
|
||||
config: args.depend::<config::Service>("config"),
|
||||
@@ -99,7 +96,6 @@ pub async fn appservice_in_room(&self, room_id: &RoomId, appservice: &Registrati
|
||||
if let Some(cached) = self
|
||||
.appservice_in_room_cache
|
||||
.read()
|
||||
.expect("locked")
|
||||
.get(room_id)
|
||||
.and_then(|map| map.get(&appservice.registration.id))
|
||||
.copied()
|
||||
@@ -124,7 +120,6 @@ pub async fn appservice_in_room(&self, room_id: &RoomId, appservice: &Registrati
|
||||
|
||||
self.appservice_in_room_cache
|
||||
.write()
|
||||
.expect("locked")
|
||||
.entry(room_id.into())
|
||||
.or_default()
|
||||
.insert(appservice.registration.id.clone(), in_room);
|
||||
@@ -134,19 +129,14 @@ pub async fn appservice_in_room(&self, room_id: &RoomId, appservice: &Registrati
|
||||
|
||||
#[implement(Service)]
|
||||
pub fn get_appservice_in_room_cache_usage(&self) -> (usize, usize) {
|
||||
let cache = self.appservice_in_room_cache.read().expect("locked");
|
||||
let cache = self.appservice_in_room_cache.read();
|
||||
|
||||
(cache.len(), cache.capacity())
|
||||
}
|
||||
|
||||
#[implement(Service)]
|
||||
#[tracing::instrument(level = "debug", skip_all)]
|
||||
pub fn clear_appservice_in_room_cache(&self) {
|
||||
self.appservice_in_room_cache
|
||||
.write()
|
||||
.expect("locked")
|
||||
.clear();
|
||||
}
|
||||
pub fn clear_appservice_in_room_cache(&self) { self.appservice_in_room_cache.write().clear(); }
|
||||
|
||||
/// Returns an iterator of all servers participating in this room.
|
||||
#[implement(Service)]
|
||||
|
||||
@@ -211,10 +211,7 @@ pub async fn update_joined_count(&self, room_id: &RoomId) {
|
||||
self.db.serverroomids.put_raw(serverroom_id, []);
|
||||
}
|
||||
|
||||
self.appservice_in_room_cache
|
||||
.write()
|
||||
.expect("locked")
|
||||
.remove(room_id);
|
||||
self.appservice_in_room_cache.write().remove(room_id);
|
||||
}
|
||||
|
||||
/// Direct DB function to directly mark a user as joined. It is not
|
||||
|
||||
@@ -2,12 +2,12 @@ use std::{
|
||||
collections::{BTreeSet, HashMap},
|
||||
fmt::{Debug, Write},
|
||||
mem::size_of,
|
||||
sync::{Arc, Mutex},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use conduwuit::{
|
||||
Result,
|
||||
Result, SyncMutex,
|
||||
arrayvec::ArrayVec,
|
||||
at, checked, err, expected, implement, utils,
|
||||
utils::{bytes, math::usize_from_f64, stream::IterStream},
|
||||
@@ -23,7 +23,7 @@ use crate::{
|
||||
};
|
||||
|
||||
pub struct Service {
|
||||
pub stateinfo_cache: Mutex<StateInfoLruCache>,
|
||||
pub stateinfo_cache: SyncMutex<StateInfoLruCache>,
|
||||
db: Data,
|
||||
services: Services,
|
||||
}
|
||||
@@ -86,7 +86,7 @@ impl crate::Service for Service {
|
||||
|
||||
async fn memory_usage(&self, out: &mut (dyn Write + Send)) -> Result {
|
||||
let (cache_len, ents) = {
|
||||
let cache = self.stateinfo_cache.lock().expect("locked");
|
||||
let cache = self.stateinfo_cache.lock();
|
||||
let ents = cache.iter().map(at!(1)).flat_map(|vec| vec.iter()).fold(
|
||||
HashMap::new(),
|
||||
|mut ents, ssi| {
|
||||
@@ -110,7 +110,7 @@ impl crate::Service for Service {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn clear_cache(&self) { self.stateinfo_cache.lock().expect("locked").clear(); }
|
||||
async fn clear_cache(&self) { self.stateinfo_cache.lock().clear(); }
|
||||
|
||||
fn name(&self) -> &str { crate::service::make_name(std::module_path!()) }
|
||||
}
|
||||
@@ -123,7 +123,7 @@ pub async fn load_shortstatehash_info(
|
||||
&self,
|
||||
shortstatehash: ShortStateHash,
|
||||
) -> Result<ShortStateInfoVec> {
|
||||
if let Some(r) = self.stateinfo_cache.lock()?.get_mut(&shortstatehash) {
|
||||
if let Some(r) = self.stateinfo_cache.lock().get_mut(&shortstatehash) {
|
||||
return Ok(r.clone());
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ async fn cache_shortstatehash_info(
|
||||
shortstatehash: ShortStateHash,
|
||||
stack: ShortStateInfoVec,
|
||||
) -> Result {
|
||||
self.stateinfo_cache.lock()?.insert(shortstatehash, stack);
|
||||
self.stateinfo_cache.lock().insert(shortstatehash, stack);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -165,6 +165,25 @@ pub async fn create_hash_and_sign_event(
|
||||
return Err!(Request(Forbidden("Event is not authorized.")));
|
||||
}
|
||||
|
||||
// Check with the policy server
|
||||
match self
|
||||
.services
|
||||
.event_handler
|
||||
.ask_policy_server(&pdu, room_id)
|
||||
.await
|
||||
{
|
||||
| Ok(true) => {},
|
||||
| Ok(false) => {
|
||||
return Err!(Request(Forbidden(debug_warn!(
|
||||
"Policy server marked this event as spam"
|
||||
))));
|
||||
},
|
||||
| Err(e) => {
|
||||
// fail open
|
||||
warn!("Failed to check event with policy server: {e}");
|
||||
},
|
||||
}
|
||||
|
||||
// Hash and sign
|
||||
let mut pdu_json = utils::to_canonical_object(&pdu).map_err(|e| {
|
||||
err!(Request(BadJson(warn!("Failed to convert PDU to canonical JSON: {e}"))))
|
||||
|
||||
+23
-27
@@ -3,11 +3,13 @@ use std::{
|
||||
collections::BTreeMap,
|
||||
fmt::Write,
|
||||
ops::Deref,
|
||||
sync::{Arc, OnceLock, RwLock, Weak},
|
||||
sync::{Arc, OnceLock, Weak},
|
||||
};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use conduwuit::{Err, Result, Server, err, error::inspect_log, utils::string::SplitInfallible};
|
||||
use conduwuit::{
|
||||
Err, Result, Server, SyncRwLock, err, error::inspect_log, utils::string::SplitInfallible,
|
||||
};
|
||||
use database::Database;
|
||||
|
||||
/// Abstract interface for a Service
|
||||
@@ -62,7 +64,7 @@ pub(crate) struct Dep<T: Service + Send + Sync> {
|
||||
name: &'static str,
|
||||
}
|
||||
|
||||
pub(crate) type Map = RwLock<MapType>;
|
||||
pub(crate) type Map = SyncRwLock<MapType>;
|
||||
pub(crate) type MapType = BTreeMap<MapKey, MapVal>;
|
||||
pub(crate) type MapVal = (Weak<dyn Service>, Weak<dyn Any + Send + Sync>);
|
||||
pub(crate) type MapKey = String;
|
||||
@@ -143,15 +145,12 @@ pub(crate) fn get<T>(map: &Map, name: &str) -> Option<Arc<T>>
|
||||
where
|
||||
T: Any + Send + Sync + Sized,
|
||||
{
|
||||
map.read()
|
||||
.expect("locked for reading")
|
||||
.get(name)
|
||||
.map(|(_, s)| {
|
||||
s.upgrade().map(|s| {
|
||||
s.downcast::<T>()
|
||||
.expect("Service must be correctly downcast.")
|
||||
})
|
||||
})?
|
||||
map.read().get(name).map(|(_, s)| {
|
||||
s.upgrade().map(|s| {
|
||||
s.downcast::<T>()
|
||||
.expect("Service must be correctly downcast.")
|
||||
})
|
||||
})?
|
||||
}
|
||||
|
||||
/// Reference a Service by name. Returns Err if the Service does not exist or
|
||||
@@ -160,21 +159,18 @@ pub(crate) fn try_get<T>(map: &Map, name: &str) -> Result<Arc<T>>
|
||||
where
|
||||
T: Any + Send + Sync + Sized,
|
||||
{
|
||||
map.read()
|
||||
.expect("locked for reading")
|
||||
.get(name)
|
||||
.map_or_else(
|
||||
|| Err!("Service {name:?} does not exist or has not been built yet."),
|
||||
|(_, s)| {
|
||||
s.upgrade().map_or_else(
|
||||
|| Err!("Service {name:?} no longer exists."),
|
||||
|s| {
|
||||
s.downcast::<T>()
|
||||
.map_err(|_| err!("Service {name:?} must be correctly downcast."))
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
map.read().get(name).map_or_else(
|
||||
|| Err!("Service {name:?} does not exist or has not been built yet."),
|
||||
|(_, s)| {
|
||||
s.upgrade().map_or_else(
|
||||
|| Err!("Service {name:?} no longer exists."),
|
||||
|s| {
|
||||
s.downcast::<T>()
|
||||
.map_err(|_| err!("Service {name:?} must be correctly downcast."))
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Utility for service implementations; see Service::name() in the trait.
|
||||
|
||||
+7
-11
@@ -1,10 +1,8 @@
|
||||
use std::{
|
||||
any::Any,
|
||||
collections::BTreeMap,
|
||||
sync::{Arc, RwLock},
|
||||
};
|
||||
use std::{any::Any, collections::BTreeMap, sync::Arc};
|
||||
|
||||
use conduwuit::{Result, Server, debug, debug_info, info, trace, utils::stream::IterStream};
|
||||
use conduwuit::{
|
||||
Result, Server, SyncRwLock, debug, debug_info, info, trace, utils::stream::IterStream,
|
||||
};
|
||||
use database::Database;
|
||||
use futures::{Stream, StreamExt, TryStreamExt};
|
||||
use tokio::sync::Mutex;
|
||||
@@ -52,7 +50,7 @@ impl Services {
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
pub async fn build(server: Arc<Server>) -> Result<Arc<Self>> {
|
||||
let db = Database::open(&server).await?;
|
||||
let service: Arc<Map> = Arc::new(RwLock::new(BTreeMap::new()));
|
||||
let service: Arc<Map> = Arc::new(SyncRwLock::new(BTreeMap::new()));
|
||||
macro_rules! build {
|
||||
($tyname:ty) => {{
|
||||
let built = <$tyname>::build(Args {
|
||||
@@ -193,7 +191,7 @@ impl Services {
|
||||
|
||||
fn interrupt(&self) {
|
||||
debug!("Interrupting services...");
|
||||
for (name, (service, ..)) in self.service.read().expect("locked for reading").iter() {
|
||||
for (name, (service, ..)) in self.service.read().iter() {
|
||||
if let Some(service) = service.upgrade() {
|
||||
trace!("Interrupting {name}");
|
||||
service.interrupt();
|
||||
@@ -205,7 +203,6 @@ impl Services {
|
||||
fn services(&self) -> impl Stream<Item = Arc<dyn Service>> + Send {
|
||||
self.service
|
||||
.read()
|
||||
.expect("locked for reading")
|
||||
.values()
|
||||
.filter_map(|val| val.0.upgrade())
|
||||
.collect::<Vec<_>>()
|
||||
@@ -233,10 +230,9 @@ impl Services {
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
fn add_service(map: &Arc<Map>, s: Arc<dyn Service>, a: Arc<dyn Any + Send + Sync>) {
|
||||
let name = s.name();
|
||||
let len = map.read().expect("locked for reading").len();
|
||||
let len = map.read().len();
|
||||
|
||||
trace!("built service #{len}: {name:?}");
|
||||
map.write()
|
||||
.expect("locked for writing")
|
||||
.insert(name.to_owned(), (Arc::downgrade(&s), Arc::downgrade(&a)));
|
||||
}
|
||||
|
||||
+29
-32
@@ -2,10 +2,10 @@ mod watch;
|
||||
|
||||
use std::{
|
||||
collections::{BTreeMap, BTreeSet},
|
||||
sync::{Arc, Mutex, Mutex as StdMutex},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use conduwuit::{Result, Server};
|
||||
use conduwuit::{Result, Server, SyncMutex};
|
||||
use database::Map;
|
||||
use ruma::{
|
||||
OwnedDeviceId, OwnedRoomId, OwnedUserId,
|
||||
@@ -62,11 +62,11 @@ struct SnakeSyncCache {
|
||||
extensions: v5::request::Extensions,
|
||||
}
|
||||
|
||||
type DbConnections<K, V> = Mutex<BTreeMap<K, V>>;
|
||||
type DbConnections<K, V> = SyncMutex<BTreeMap<K, V>>;
|
||||
type DbConnectionsKey = (OwnedUserId, OwnedDeviceId, String);
|
||||
type DbConnectionsVal = Arc<Mutex<SlidingSyncCache>>;
|
||||
type DbConnectionsVal = Arc<SyncMutex<SlidingSyncCache>>;
|
||||
type SnakeConnectionsKey = (OwnedUserId, OwnedDeviceId, Option<String>);
|
||||
type SnakeConnectionsVal = Arc<Mutex<SnakeSyncCache>>;
|
||||
type SnakeConnectionsVal = Arc<SyncMutex<SnakeSyncCache>>;
|
||||
|
||||
impl crate::Service for Service {
|
||||
fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
|
||||
@@ -90,8 +90,8 @@ impl crate::Service for Service {
|
||||
state_cache: args.depend::<rooms::state_cache::Service>("rooms::state_cache"),
|
||||
typing: args.depend::<rooms::typing::Service>("rooms::typing"),
|
||||
},
|
||||
connections: StdMutex::new(BTreeMap::new()),
|
||||
snake_connections: StdMutex::new(BTreeMap::new()),
|
||||
connections: SyncMutex::new(BTreeMap::new()),
|
||||
snake_connections: SyncMutex::new(BTreeMap::new()),
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -100,22 +100,19 @@ impl crate::Service for Service {
|
||||
|
||||
impl Service {
|
||||
pub fn snake_connection_cached(&self, key: &SnakeConnectionsKey) -> bool {
|
||||
self.snake_connections
|
||||
.lock()
|
||||
.expect("locked")
|
||||
.contains_key(key)
|
||||
self.snake_connections.lock().contains_key(key)
|
||||
}
|
||||
|
||||
pub fn forget_snake_sync_connection(&self, key: &SnakeConnectionsKey) {
|
||||
self.snake_connections.lock().expect("locked").remove(key);
|
||||
self.snake_connections.lock().remove(key);
|
||||
}
|
||||
|
||||
pub fn remembered(&self, key: &DbConnectionsKey) -> bool {
|
||||
self.connections.lock().expect("locked").contains_key(key)
|
||||
self.connections.lock().contains_key(key)
|
||||
}
|
||||
|
||||
pub fn forget_sync_request_connection(&self, key: &DbConnectionsKey) {
|
||||
self.connections.lock().expect("locked").remove(key);
|
||||
self.connections.lock().remove(key);
|
||||
}
|
||||
|
||||
pub fn update_snake_sync_request_with_cache(
|
||||
@@ -123,13 +120,13 @@ impl Service {
|
||||
snake_key: &SnakeConnectionsKey,
|
||||
request: &mut v5::Request,
|
||||
) -> BTreeMap<String, BTreeMap<OwnedRoomId, u64>> {
|
||||
let mut cache = self.snake_connections.lock().expect("locked");
|
||||
let mut cache = self.snake_connections.lock();
|
||||
let cached = Arc::clone(
|
||||
cache
|
||||
.entry(snake_key.clone())
|
||||
.or_insert_with(|| Arc::new(Mutex::new(SnakeSyncCache::default()))),
|
||||
.or_insert_with(|| Arc::new(SyncMutex::new(SnakeSyncCache::default()))),
|
||||
);
|
||||
let cached = &mut cached.lock().expect("locked");
|
||||
let cached = &mut cached.lock();
|
||||
drop(cache);
|
||||
|
||||
//v5::Request::try_from_http_request(req, path_args);
|
||||
@@ -232,16 +229,16 @@ impl Service {
|
||||
};
|
||||
|
||||
let key = into_db_key(key.0.clone(), key.1.clone(), conn_id);
|
||||
let mut cache = self.connections.lock().expect("locked");
|
||||
let mut cache = self.connections.lock();
|
||||
let cached = Arc::clone(cache.entry(key).or_insert_with(|| {
|
||||
Arc::new(Mutex::new(SlidingSyncCache {
|
||||
Arc::new(SyncMutex::new(SlidingSyncCache {
|
||||
lists: BTreeMap::new(),
|
||||
subscriptions: BTreeMap::new(),
|
||||
known_rooms: BTreeMap::new(),
|
||||
extensions: ExtensionsConfig::default(),
|
||||
}))
|
||||
}));
|
||||
let cached = &mut cached.lock().expect("locked");
|
||||
let cached = &mut cached.lock();
|
||||
drop(cache);
|
||||
|
||||
for (list_id, list) in &mut request.lists {
|
||||
@@ -328,16 +325,16 @@ impl Service {
|
||||
key: &DbConnectionsKey,
|
||||
subscriptions: BTreeMap<OwnedRoomId, sync_events::v4::RoomSubscription>,
|
||||
) {
|
||||
let mut cache = self.connections.lock().expect("locked");
|
||||
let mut cache = self.connections.lock();
|
||||
let cached = Arc::clone(cache.entry(key.clone()).or_insert_with(|| {
|
||||
Arc::new(Mutex::new(SlidingSyncCache {
|
||||
Arc::new(SyncMutex::new(SlidingSyncCache {
|
||||
lists: BTreeMap::new(),
|
||||
subscriptions: BTreeMap::new(),
|
||||
known_rooms: BTreeMap::new(),
|
||||
extensions: ExtensionsConfig::default(),
|
||||
}))
|
||||
}));
|
||||
let cached = &mut cached.lock().expect("locked");
|
||||
let cached = &mut cached.lock();
|
||||
drop(cache);
|
||||
|
||||
cached.subscriptions = subscriptions;
|
||||
@@ -350,16 +347,16 @@ impl Service {
|
||||
new_cached_rooms: BTreeSet<OwnedRoomId>,
|
||||
globalsince: u64,
|
||||
) {
|
||||
let mut cache = self.connections.lock().expect("locked");
|
||||
let mut cache = self.connections.lock();
|
||||
let cached = Arc::clone(cache.entry(key.clone()).or_insert_with(|| {
|
||||
Arc::new(Mutex::new(SlidingSyncCache {
|
||||
Arc::new(SyncMutex::new(SlidingSyncCache {
|
||||
lists: BTreeMap::new(),
|
||||
subscriptions: BTreeMap::new(),
|
||||
known_rooms: BTreeMap::new(),
|
||||
extensions: ExtensionsConfig::default(),
|
||||
}))
|
||||
}));
|
||||
let cached = &mut cached.lock().expect("locked");
|
||||
let cached = &mut cached.lock();
|
||||
drop(cache);
|
||||
|
||||
for (room_id, lastsince) in cached
|
||||
@@ -386,13 +383,13 @@ impl Service {
|
||||
globalsince: u64,
|
||||
) {
|
||||
assert!(key.2.is_some(), "Some(conn_id) required for this call");
|
||||
let mut cache = self.snake_connections.lock().expect("locked");
|
||||
let mut cache = self.snake_connections.lock();
|
||||
let cached = Arc::clone(
|
||||
cache
|
||||
.entry(key.clone())
|
||||
.or_insert_with(|| Arc::new(Mutex::new(SnakeSyncCache::default()))),
|
||||
.or_insert_with(|| Arc::new(SyncMutex::new(SnakeSyncCache::default()))),
|
||||
);
|
||||
let cached = &mut cached.lock().expect("locked");
|
||||
let cached = &mut cached.lock();
|
||||
drop(cache);
|
||||
|
||||
for (room_id, lastsince) in cached
|
||||
@@ -416,13 +413,13 @@ impl Service {
|
||||
key: &SnakeConnectionsKey,
|
||||
subscriptions: BTreeMap<OwnedRoomId, v5::request::RoomSubscription>,
|
||||
) {
|
||||
let mut cache = self.snake_connections.lock().expect("locked");
|
||||
let mut cache = self.snake_connections.lock();
|
||||
let cached = Arc::clone(
|
||||
cache
|
||||
.entry(key.clone())
|
||||
.or_insert_with(|| Arc::new(Mutex::new(SnakeSyncCache::default()))),
|
||||
.or_insert_with(|| Arc::new(SyncMutex::new(SnakeSyncCache::default()))),
|
||||
);
|
||||
let cached = &mut cached.lock().expect("locked");
|
||||
let cached = &mut cached.lock();
|
||||
drop(cache);
|
||||
|
||||
cached.subscriptions = subscriptions;
|
||||
|
||||
+32
-6
@@ -1,10 +1,10 @@
|
||||
use std::{
|
||||
collections::{BTreeMap, HashSet},
|
||||
sync::{Arc, RwLock},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use conduwuit::{
|
||||
Err, Error, Result, err, error, implement, utils,
|
||||
Err, Error, Result, SyncRwLock, err, error, implement, utils,
|
||||
utils::{hash, string::EMPTY},
|
||||
};
|
||||
use database::{Deserialized, Json, Map};
|
||||
@@ -19,7 +19,7 @@ use ruma::{
|
||||
use crate::{Dep, config, globals, users};
|
||||
|
||||
pub struct Service {
|
||||
userdevicesessionid_uiaarequest: RwLock<RequestMap>,
|
||||
userdevicesessionid_uiaarequest: SyncRwLock<RequestMap>,
|
||||
db: Data,
|
||||
services: Services,
|
||||
}
|
||||
@@ -42,7 +42,7 @@ pub const SESSION_ID_LENGTH: usize = 32;
|
||||
impl crate::Service for Service {
|
||||
fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
|
||||
Ok(Arc::new(Self {
|
||||
userdevicesessionid_uiaarequest: RwLock::new(RequestMap::new()),
|
||||
userdevicesessionid_uiaarequest: SyncRwLock::new(RequestMap::new()),
|
||||
db: Data {
|
||||
userdevicesessionid_uiaainfo: args.db["userdevicesessionid_uiaainfo"].clone(),
|
||||
},
|
||||
@@ -177,6 +177,34 @@ pub async fn try_auth(
|
||||
// Password was correct! Let's add it to `completed`
|
||||
uiaainfo.completed.push(AuthType::Password);
|
||||
},
|
||||
| AuthData::ReCaptcha(r) => {
|
||||
if self.services.config.recaptcha_private_site_key.is_none() {
|
||||
return Err!(Request(Forbidden("ReCaptcha is not configured.")));
|
||||
}
|
||||
match recaptcha_verify::verify(
|
||||
self.services
|
||||
.config
|
||||
.recaptcha_private_site_key
|
||||
.as_ref()
|
||||
.unwrap(),
|
||||
r.response.as_str(),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
{
|
||||
| Ok(()) => {
|
||||
uiaainfo.completed.push(AuthType::ReCaptcha);
|
||||
},
|
||||
| Err(e) => {
|
||||
error!("ReCaptcha verification failed: {e:?}");
|
||||
uiaainfo.auth_error = Some(ruma::api::client::error::StandardErrorBody {
|
||||
kind: ErrorKind::forbidden(),
|
||||
message: "ReCaptcha verification failed.".to_owned(),
|
||||
});
|
||||
return Ok((false, uiaainfo));
|
||||
},
|
||||
}
|
||||
},
|
||||
| AuthData::RegistrationToken(t) => {
|
||||
let tokens = self.read_tokens().await?;
|
||||
if tokens.contains(t.token.trim()) {
|
||||
@@ -240,7 +268,6 @@ fn set_uiaa_request(
|
||||
let key = (user_id.to_owned(), device_id.to_owned(), session.to_owned());
|
||||
self.userdevicesessionid_uiaarequest
|
||||
.write()
|
||||
.expect("locked for writing")
|
||||
.insert(key, request.to_owned());
|
||||
}
|
||||
|
||||
@@ -259,7 +286,6 @@ pub fn get_uiaa_request(
|
||||
|
||||
self.userdevicesessionid_uiaarequest
|
||||
.read()
|
||||
.expect("locked for reading")
|
||||
.get(&key)
|
||||
.cloned()
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<p>To get started, you can:</p>
|
||||
<ul>
|
||||
<li>Read the <a href="https://continuwuity.org/introduction">documentation</a></li>
|
||||
<li>Join the <a href="https://matrix.to/#/#continuwuity:continuwuity.org">Continuwuity Matrix room</a> or <a href="https://matrix.to/#/#space:continuwuity.org">space</a></li>
|
||||
<li>Join the <a href="https://matrix.to/#/#continuwuity:continuwuity.org?via=continuwuity.org&via=ellis.link&via=explodie.org&via=matrix.org">Continuwuity Matrix room</a> or <a href="https://matrix.to/#/#space:continuwuity.org?via=continuwuity.org&via=ellis.link&via=explodie.org&via=matrix.org">space</a></li>
|
||||
<li>Log in with a <a href="https://matrix.org/ecosystem/clients/">client</a></li>
|
||||
<li>Ensure <a href="https://federationtester.matrix.org/#{{ server_name }}">federation</a> works</li>
|
||||
</ul>
|
||||
|
||||
@@ -48,7 +48,7 @@ vector () {
|
||||
VECTOR_OPTS=$@
|
||||
element "$TOOLCHAIN" $VECTOR_OPTS --no-default-features
|
||||
element "$TOOLCHAIN" $VECTOR_OPTS --features=default
|
||||
element "$TOOLCHAIN" $VECTOR_OPTS --all-features
|
||||
element "$TOOLCHAIN" $VECTOR_OPTS --features full
|
||||
}
|
||||
|
||||
matrix () {
|
||||
|
||||
@@ -605,3 +605,8 @@ ul#searchresults span.teaser em {
|
||||
margin-inline-start: -14px;
|
||||
width: 14px;
|
||||
}
|
||||
|
||||
/* HACK: Stop the keyboard shortcuts from adding a giant space at the top */
|
||||
#mdbook-help-container {
|
||||
display: none;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user