mirror of
https://forgejo.ellis.link/continuwuation/continuwuity.git
synced 2026-05-26 20:49:55 +00:00
Compare commits
42 Commits
jade/http3
...
jade/snafu
| Author | SHA1 | Date | |
|---|---|---|---|
| 7a8c409ff9 | |||
| 688ef727e5 | |||
| 3de026160e | |||
| 9fe761513d | |||
| abf1e1195a | |||
| d9537e9b55 | |||
| 0d1de70d8f | |||
| 4aa03a71eb | |||
| f847918575 | |||
| 7569a0545b | |||
| b6c5991e1f | |||
| efd879fcd8 | |||
| 92a848f74d | |||
| 776b5865ba | |||
| 722bacbe89 | |||
| 46907e3dce | |||
| 31e2195e56 | |||
| 7ecac93ddc | |||
| 6a0b103722 | |||
| 23d77b614f | |||
| e01aa44b16 | |||
| a08739c246 | |||
| c14864b881 | |||
| 1773e72e68 | |||
| 0f94d55689 | |||
| abfb6377c2 | |||
| 91d64f5b24 | |||
| 9a3f3f6e78 | |||
| b3e31a4aad | |||
| 8cda431cc6 | |||
| 02b9a3f713 | |||
| d40893730c | |||
| 28fae58cf6 | |||
| f458f6ab76 | |||
| fdf9cea533 | |||
| ecb1b73c84 | |||
| e03082480a | |||
| f9e7f019ad | |||
| 12069e7c86 | |||
| 77928a62b4 | |||
| c73cb5c1bf | |||
| a140eacb04 |
@@ -23,7 +23,7 @@ repos:
|
|||||||
- id: check-added-large-files
|
- id: check-added-large-files
|
||||||
|
|
||||||
- repo: https://github.com/crate-ci/typos
|
- repo: https://github.com/crate-ci/typos
|
||||||
rev: v1.43.4
|
rev: v1.43.5
|
||||||
hooks:
|
hooks:
|
||||||
- id: typos
|
- id: typos
|
||||||
- id: typos
|
- id: typos
|
||||||
|
|||||||
+16
-8
@@ -85,24 +85,31 @@ If your changes are done to fix Matrix tests, please note that in your pull requ
|
|||||||
|
|
||||||
### Writing documentation
|
### Writing documentation
|
||||||
|
|
||||||
Continuwuity's website uses [`mdbook`][mdbook] and is deployed via CI using Cloudflare Pages
|
Continuwuity's website uses [`rspress`][rspress] and is deployed via CI using Cloudflare Pages
|
||||||
in the [`documentation.yml`][documentation.yml] workflow file. All documentation is in the `docs/`
|
in the [`documentation.yml`][documentation.yml] workflow file. All documentation is in the `docs/`
|
||||||
directory at the top level.
|
directory at the top level.
|
||||||
|
|
||||||
To build the documentation locally:
|
To load the documentation locally:
|
||||||
|
|
||||||
|
1. Install NodeJS and npm from their [official website][nodejs-download] or via your package manager of choice
|
||||||
|
|
||||||
|
2. From the project's root directory, install the relevant npm modules
|
||||||
|
|
||||||
1. Install mdbook if you don't have it already:
|
|
||||||
```bash
|
```bash
|
||||||
cargo install mdbook # or cargo binstall, or another method
|
npm ci
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Build the documentation:
|
3. Make changes to the document pages as you see fit
|
||||||
|
|
||||||
|
4. Generate a live preview of the documentation
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
mdbook build
|
npm run docs:dev
|
||||||
```
|
```
|
||||||
|
|
||||||
The output of the mdbook generation is in `public/`. You can open the HTML files directly in your browser without needing a web server.
|
A webserver for the docs will be spun up for you (e.g. at `http://localhost:3000`). Any changes you make to the documentation will be live-reloaded on the webpage.
|
||||||
|
|
||||||
|
Alternatively, you can build the documentation using `npm run docs:build` - the output of this will be in the `/doc_build` directory. Once you're happy with your documentation updates, you can commit the changes.
|
||||||
|
|
||||||
### Commit Messages
|
### Commit Messages
|
||||||
|
|
||||||
@@ -169,5 +176,6 @@ continuwuity Matrix rooms for Code of Conduct violations.
|
|||||||
[continuwuity-matrix]: https://matrix.to/#/#continuwuity:continuwuity.org?via=continuwuity.org&via=ellis.link&via=explodie.org&via=matrix.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/
|
[complement]: https://github.com/matrix-org/complement/
|
||||||
[sytest]: https://github.com/matrix-org/sytest/
|
[sytest]: https://github.com/matrix-org/sytest/
|
||||||
[mdbook]: https://rust-lang.github.io/mdBook/
|
[nodejs-download]: https://nodejs.org/en/download
|
||||||
|
[rspress]: https://rspress.rs/
|
||||||
[documentation.yml]: https://forgejo.ellis.link/continuwuation/continuwuity/src/branch/main/.forgejo/workflows/documentation.yml
|
[documentation.yml]: https://forgejo.ellis.link/continuwuation/continuwuity/src/branch/main/.forgejo/workflows/documentation.yml
|
||||||
|
|||||||
Generated
+511
-529
File diff suppressed because it is too large
Load Diff
+21
-15
@@ -68,7 +68,7 @@ default-features = false
|
|||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
|
|
||||||
[workspace.dependencies.rand]
|
[workspace.dependencies.rand]
|
||||||
version = "0.8.5"
|
version = "0.10.0"
|
||||||
|
|
||||||
# Used for the http request / response body type for Ruma endpoints used with reqwest
|
# Used for the http request / response body type for Ruma endpoints used with reqwest
|
||||||
[workspace.dependencies.bytes]
|
[workspace.dependencies.bytes]
|
||||||
@@ -84,7 +84,7 @@ version = "1.3.1"
|
|||||||
version = "1.11.1"
|
version = "1.11.1"
|
||||||
|
|
||||||
[workspace.dependencies.axum]
|
[workspace.dependencies.axum]
|
||||||
version = "0.7.9"
|
version = "0.8.8"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = [
|
features = [
|
||||||
"form",
|
"form",
|
||||||
@@ -97,7 +97,7 @@ features = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[workspace.dependencies.axum-extra]
|
[workspace.dependencies.axum-extra]
|
||||||
version = "0.9.6"
|
version = "0.10.1"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["typed-header", "tracing"]
|
features = ["typed-header", "tracing"]
|
||||||
|
|
||||||
@@ -110,7 +110,7 @@ default-features = false
|
|||||||
version = "0.7"
|
version = "0.7"
|
||||||
|
|
||||||
[workspace.dependencies.axum-client-ip]
|
[workspace.dependencies.axum-client-ip]
|
||||||
version = "0.6.1"
|
version = "0.7"
|
||||||
|
|
||||||
[workspace.dependencies.tower]
|
[workspace.dependencies.tower]
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
@@ -118,7 +118,7 @@ default-features = false
|
|||||||
features = ["util"]
|
features = ["util"]
|
||||||
|
|
||||||
[workspace.dependencies.tower-http]
|
[workspace.dependencies.tower-http]
|
||||||
version = "0.6.2"
|
version = "0.6.8"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = [
|
features = [
|
||||||
"add-extension",
|
"add-extension",
|
||||||
@@ -253,7 +253,7 @@ features = [
|
|||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
|
||||||
[workspace.dependencies.libloading]
|
[workspace.dependencies.libloading]
|
||||||
version = "0.8.6"
|
version = "0.9.0"
|
||||||
|
|
||||||
# Validating urls in config, was already a transitive dependency
|
# Validating urls in config, was already a transitive dependency
|
||||||
[workspace.dependencies.url]
|
[workspace.dependencies.url]
|
||||||
@@ -298,7 +298,7 @@ default-features = false
|
|||||||
features = ["env", "toml"]
|
features = ["env", "toml"]
|
||||||
|
|
||||||
[workspace.dependencies.hickory-resolver]
|
[workspace.dependencies.hickory-resolver]
|
||||||
version = "0.25.1"
|
version = "0.25.2"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = [
|
features = [
|
||||||
"serde",
|
"serde",
|
||||||
@@ -307,9 +307,14 @@ features = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
# Used for conduwuit::Error type
|
# Used for conduwuit::Error type
|
||||||
[workspace.dependencies.thiserror]
|
[workspace.dependencies.snafu]
|
||||||
version = "2.0.12"
|
version = "0.8"
|
||||||
default-features = false
|
default-features = false
|
||||||
|
features = ["std", "rust_1_81"]
|
||||||
|
|
||||||
|
# Used for macro name generation
|
||||||
|
[workspace.dependencies.paste]
|
||||||
|
version = "1.0"
|
||||||
|
|
||||||
# Used when hashing the state
|
# Used when hashing the state
|
||||||
[workspace.dependencies.ring]
|
[workspace.dependencies.ring]
|
||||||
@@ -342,7 +347,8 @@ version = "0.1.2"
|
|||||||
# Used for matrix spec type definitions and helpers
|
# Used for matrix spec type definitions and helpers
|
||||||
[workspace.dependencies.ruma]
|
[workspace.dependencies.ruma]
|
||||||
git = "https://forgejo.ellis.link/continuwuation/ruwuma"
|
git = "https://forgejo.ellis.link/continuwuation/ruwuma"
|
||||||
rev = "b496b7f38d517149361a882e75d3fd4faf210441"
|
#branch = "conduwuit-changes"
|
||||||
|
rev = "e087ff15888156942ca2ffe6097d1b4c3fd27628"
|
||||||
features = [
|
features = [
|
||||||
"compat",
|
"compat",
|
||||||
"rand",
|
"rand",
|
||||||
@@ -424,7 +430,7 @@ features = ["http", "grpc-tonic", "trace", "logs", "metrics"]
|
|||||||
|
|
||||||
# optional sentry metrics for crash/panic reporting
|
# optional sentry metrics for crash/panic reporting
|
||||||
[workspace.dependencies.sentry]
|
[workspace.dependencies.sentry]
|
||||||
version = "0.45.0"
|
version = "0.46.0"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = [
|
features = [
|
||||||
"backtrace",
|
"backtrace",
|
||||||
@@ -440,9 +446,9 @@ features = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[workspace.dependencies.sentry-tracing]
|
[workspace.dependencies.sentry-tracing]
|
||||||
version = "0.45.0"
|
version = "0.46.0"
|
||||||
[workspace.dependencies.sentry-tower]
|
[workspace.dependencies.sentry-tower]
|
||||||
version = "0.45.0"
|
version = "0.46.0"
|
||||||
|
|
||||||
# jemalloc usage
|
# jemalloc usage
|
||||||
[workspace.dependencies.tikv-jemalloc-sys]
|
[workspace.dependencies.tikv-jemalloc-sys]
|
||||||
@@ -471,7 +477,7 @@ features = ["use_std"]
|
|||||||
version = "0.5"
|
version = "0.5"
|
||||||
|
|
||||||
[workspace.dependencies.nix]
|
[workspace.dependencies.nix]
|
||||||
version = "0.30.1"
|
version = "0.31.0"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["resource"]
|
features = ["resource"]
|
||||||
|
|
||||||
@@ -553,7 +559,7 @@ version = "0.7.5"
|
|||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
|
|
||||||
[workspace.dependencies.askama]
|
[workspace.dependencies.askama]
|
||||||
version = "0.14.0"
|
version = "0.15.0"
|
||||||
|
|
||||||
#
|
#
|
||||||
# Patches
|
# Patches
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
Removed non-compliant nor functional room alias lookups over federation. Contributed by @nex
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
Outgoing presence is now disabled by default, and the config option documentation has been adjusted to more accurately represent the weight of presence, typing indicators, and read receipts. Contributed by @nex.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
Removed ability to set rocksdb as read only. Doing so would cause unintentional and buggy behaviour. Contributed by @Terryiscool160.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
Fixed a startup crash in the sender service if we can't detect the number of CPU cores, even if the `sender_workers' config option is set correctly. Contributed by @katie.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
Updated `list-backups` admin command to output one backup per line.
|
||||||
+27
-20
@@ -1056,14 +1056,6 @@
|
|||||||
#
|
#
|
||||||
#rocksdb_repair = false
|
#rocksdb_repair = false
|
||||||
|
|
||||||
# This item is undocumented. Please contribute documentation for it.
|
|
||||||
#
|
|
||||||
#rocksdb_read_only = false
|
|
||||||
|
|
||||||
# This item is undocumented. Please contribute documentation for it.
|
|
||||||
#
|
|
||||||
#rocksdb_secondary = false
|
|
||||||
|
|
||||||
# Enables idle CPU priority for compaction thread. This is not enabled by
|
# Enables idle CPU priority for compaction thread. This is not enabled by
|
||||||
# default to prevent compaction from falling too far behind on busy
|
# default to prevent compaction from falling too far behind on busy
|
||||||
# systems.
|
# systems.
|
||||||
@@ -1120,27 +1112,34 @@
|
|||||||
|
|
||||||
# Allow local (your server only) presence updates/requests.
|
# Allow local (your server only) presence updates/requests.
|
||||||
#
|
#
|
||||||
# Note that presence on continuwuity is very fast unlike Synapse's. If
|
# Local presence must be enabled for outgoing presence to function.
|
||||||
# using outgoing presence, this MUST be enabled.
|
#
|
||||||
|
# Note that local presence is not as heavy on the CPU as federated
|
||||||
|
# presence, but will still become more expensive the more local users you
|
||||||
|
# have.
|
||||||
#
|
#
|
||||||
#allow_local_presence = true
|
#allow_local_presence = true
|
||||||
|
|
||||||
# Allow incoming federated presence updates/requests.
|
# Allow incoming federated presence updates.
|
||||||
#
|
#
|
||||||
# This option receives presence updates from other servers, but does not
|
# This option enables processing inbound presence updates from other
|
||||||
# send any unless `allow_outgoing_presence` is true. Note that presence on
|
# servers. Without it, remote users will appear as if they are always
|
||||||
# continuwuity is very fast unlike Synapse's.
|
# offline to your local users. This does not affect typing indicators or
|
||||||
|
# read receipts.
|
||||||
#
|
#
|
||||||
#allow_incoming_presence = true
|
#allow_incoming_presence = true
|
||||||
|
|
||||||
# Allow outgoing presence updates/requests.
|
# Allow outgoing presence updates/requests.
|
||||||
#
|
#
|
||||||
# This option sends presence updates to other servers, but does not
|
# This option sends presence updates to other servers, and requires that
|
||||||
# receive any unless `allow_incoming_presence` is true. Note that presence
|
# `allow_local_presence` is also enabled.
|
||||||
# on continuwuity is very fast unlike Synapse's. If using outgoing
|
|
||||||
# presence, you MUST enable `allow_local_presence` as well.
|
|
||||||
#
|
#
|
||||||
#allow_outgoing_presence = true
|
# Note that outgoing presence is very heavy on the CPU and network, and
|
||||||
|
# will typically cause extreme strain and slowdowns for no real benefit.
|
||||||
|
# There are only a few clients that even implement presence, so you
|
||||||
|
# probably don't want to enable this.
|
||||||
|
#
|
||||||
|
#allow_outgoing_presence = false
|
||||||
|
|
||||||
# How many seconds without presence updates before you become idle.
|
# How many seconds without presence updates before you become idle.
|
||||||
# Defaults to 5 minutes.
|
# Defaults to 5 minutes.
|
||||||
@@ -1174,6 +1173,10 @@
|
|||||||
|
|
||||||
# Allow sending read receipts to remote servers.
|
# Allow sending read receipts to remote servers.
|
||||||
#
|
#
|
||||||
|
# Note that sending read receipts to remote servers in large rooms with
|
||||||
|
# lots of other homeservers may cause additional strain on the CPU and
|
||||||
|
# network.
|
||||||
|
#
|
||||||
#allow_outgoing_read_receipts = true
|
#allow_outgoing_read_receipts = true
|
||||||
|
|
||||||
# Allow local typing updates.
|
# Allow local typing updates.
|
||||||
@@ -1185,6 +1188,10 @@
|
|||||||
|
|
||||||
# Allow outgoing typing updates to federation.
|
# Allow outgoing typing updates to federation.
|
||||||
#
|
#
|
||||||
|
# Note that sending typing indicators to remote servers in large rooms
|
||||||
|
# with lots of other homeservers may cause additional strain on the CPU
|
||||||
|
# and network.
|
||||||
|
#
|
||||||
#allow_outgoing_typing = true
|
#allow_outgoing_typing = true
|
||||||
|
|
||||||
# Allow incoming typing updates from federation.
|
# Allow incoming typing updates from federation.
|
||||||
@@ -1318,7 +1325,7 @@
|
|||||||
# sender user's server name, inbound federation X-Matrix origin, and
|
# sender user's server name, inbound federation X-Matrix origin, and
|
||||||
# outbound federation handler.
|
# outbound federation handler.
|
||||||
#
|
#
|
||||||
# You can set this to ["*"] to block all servers by default, and then
|
# You can set this to [".*"] to block all servers by default, and then
|
||||||
# use `allowed_remote_server_names` to allow only specific servers.
|
# use `allowed_remote_server_names` to allow only specific servers.
|
||||||
#
|
#
|
||||||
# example: ["badserver\\.tld$", "badphrase", "19dollarfortnitecards"]
|
# example: ["badserver\\.tld$", "badphrase", "19dollarfortnitecards"]
|
||||||
|
|||||||
+1
-1
@@ -52,7 +52,7 @@ ENV BINSTALL_VERSION=1.17.5
|
|||||||
# renovate: datasource=github-releases depName=psastras/sbom-rs
|
# renovate: datasource=github-releases depName=psastras/sbom-rs
|
||||||
ENV CARGO_SBOM_VERSION=0.9.1
|
ENV CARGO_SBOM_VERSION=0.9.1
|
||||||
# renovate: datasource=crate depName=lddtree
|
# renovate: datasource=crate depName=lddtree
|
||||||
ENV LDDTREE_VERSION=0.4.0
|
ENV LDDTREE_VERSION=0.5.0
|
||||||
# renovate: datasource=crate depName=timelord-cli
|
# renovate: datasource=crate depName=timelord-cli
|
||||||
ENV TIMELORD_VERSION=3.0.1
|
ENV TIMELORD_VERSION=3.0.1
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ ENV BINSTALL_VERSION=1.17.5
|
|||||||
# renovate: datasource=github-releases depName=psastras/sbom-rs
|
# renovate: datasource=github-releases depName=psastras/sbom-rs
|
||||||
ENV CARGO_SBOM_VERSION=0.9.1
|
ENV CARGO_SBOM_VERSION=0.9.1
|
||||||
# renovate: datasource=crate depName=lddtree
|
# renovate: datasource=crate depName=lddtree
|
||||||
ENV LDDTREE_VERSION=0.4.0
|
ENV LDDTREE_VERSION=0.5.0
|
||||||
|
|
||||||
# Install unpackaged tools
|
# Install unpackaged tools
|
||||||
RUN <<EOF
|
RUN <<EOF
|
||||||
|
|||||||
+41
-2
@@ -137,7 +137,7 @@ By default, all routes should be forwarded to Livekit with the exception of the
|
|||||||
# for lk-jwt-service
|
# for lk-jwt-service
|
||||||
@lk-jwt-service path /sfu/get* /healthz* /get_token*
|
@lk-jwt-service path /sfu/get* /healthz* /get_token*
|
||||||
route @lk-jwt-service {
|
route @lk-jwt-service {
|
||||||
reverse_proxy 127.0.0.1:8080
|
reverse_proxy 127.0.0.1:8081
|
||||||
}
|
}
|
||||||
|
|
||||||
# for livekit
|
# for livekit
|
||||||
@@ -146,6 +146,46 @@ By default, all routes should be forwarded to Livekit with the exception of the
|
|||||||
```
|
```
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Example nginx config</summary>
|
||||||
|
```
|
||||||
|
server {
|
||||||
|
server_name matrix-rtc.example.com;
|
||||||
|
|
||||||
|
# for lk-jwt-service
|
||||||
|
location ~ ^/(sfu/get|healthz|get_token) {
|
||||||
|
proxy_pass http://127.0.0.1:8081$request_uri;
|
||||||
|
proxy_set_header X-Forwarded-For $remote_addr;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header Host $http_host;
|
||||||
|
proxy_buffering off;
|
||||||
|
}
|
||||||
|
|
||||||
|
# for livekit
|
||||||
|
location / {
|
||||||
|
proxy_pass http://127.0.0.1:7880$request_uri;
|
||||||
|
proxy_set_header X-Forwarded-For $remote_addr;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header Host $http_host;
|
||||||
|
proxy_buffering off;
|
||||||
|
|
||||||
|
# websocket
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection $connection_upgrade;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that for websockets to work, you need to have this somewhere outside your server block:
|
||||||
|
```
|
||||||
|
map $http_upgrade $connection_upgrade {
|
||||||
|
default upgrade;
|
||||||
|
'' close;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>Example traefik router</summary>
|
<summary>Example traefik router</summary>
|
||||||
```
|
```
|
||||||
@@ -226,4 +266,3 @@ turn:
|
|||||||
- [Synapse documentation](https://github.com/element-hq/element-call/blob/livekit/docs/self-hosting.md)
|
- [Synapse documentation](https://github.com/element-hq/element-call/blob/livekit/docs/self-hosting.md)
|
||||||
- [Community guide](https://tomfos.tr/matrix/livekit/)
|
- [Community guide](https://tomfos.tr/matrix/livekit/)
|
||||||
- [Community guide](https://blog.kimiblock.top/2024/12/24/hosting-element-call/)
|
- [Community guide](https://blog.kimiblock.top/2024/12/24/hosting-element-call/)
|
||||||
-
|
|
||||||
|
|||||||
@@ -3,3 +3,5 @@
|
|||||||
Continuwuity currently does not provide FreeBSD builds or 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 to get Continuwuity packaged for FreeBSD are welcome.
|
Contributions to get Continuwuity packaged for FreeBSD are welcome.
|
||||||
|
|
||||||
|
Please join our [Continuwuity BSD](https://matrix.to/#/%23bsd:continuwuity.org) community room.
|
||||||
|
|||||||
@@ -1,7 +1,109 @@
|
|||||||
# Continuwuity for Kubernetes
|
# Continuwuity for Kubernetes
|
||||||
|
|
||||||
Continuwuity doesn't support horizontal scalability or distributed loading
|
Continuwuity doesn't support horizontal scalability or distributed loading
|
||||||
natively. However, [a community-maintained Helm Chart is available here to run
|
natively. However, a deployment in Kubernetes is very similar to the docker
|
||||||
|
setup. This is because Continuwuity can be fully configured using environment
|
||||||
|
variables. A sample StatefulSet is shared below. The only thing missing is
|
||||||
|
a PVC definition (named `continuwuity-data`) for the volume mounted to
|
||||||
|
the StatefulSet, an Ingress resources to point your webserver to the
|
||||||
|
Continuwuity Pods, and a Service resource (targeting `app.kubernetes.io/name: continuwuity`)
|
||||||
|
to glue the Ingress and Pod together.
|
||||||
|
|
||||||
|
Carefully go through the `env` section and add, change, and remove any env vars you like using the [Configuration reference](https://continuwuity.org/reference/config.html)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: StatefulSet
|
||||||
|
metadata:
|
||||||
|
name: continuwuity
|
||||||
|
namespace: matrix
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: continuwuity
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
serviceName: continuwuity
|
||||||
|
podManagementPolicy: Parallel
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app.kubernetes.io/name: continuwuity
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: continuwuity
|
||||||
|
spec:
|
||||||
|
securityContext:
|
||||||
|
sysctls:
|
||||||
|
- name: net.ipv4.ip_unprivileged_port_start
|
||||||
|
value: "0"
|
||||||
|
containers:
|
||||||
|
- name: continuwuity
|
||||||
|
# use a sha hash <3
|
||||||
|
image: forgejo.ellis.link/continuwuation/continuwuity:latest
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
containerPort: 80
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /data
|
||||||
|
name: data
|
||||||
|
subPath: data
|
||||||
|
securityContext:
|
||||||
|
capabilities:
|
||||||
|
add:
|
||||||
|
- NET_BIND_SERVICE
|
||||||
|
env:
|
||||||
|
- name: TOKIO_WORKER_THREADS
|
||||||
|
value: "2"
|
||||||
|
- name: CONTINUWUITY_SERVER_NAME
|
||||||
|
value: "example.com"
|
||||||
|
- name: CONTINUWUITY_DATABASE_PATH
|
||||||
|
value: "/data/db"
|
||||||
|
- name: CONTINUWUITY_DATABASE_BACKEND
|
||||||
|
value: "rocksdb"
|
||||||
|
- name: CONTINUWUITY_PORT
|
||||||
|
value: "80"
|
||||||
|
- name: CONTINUWUITY_MAX_REQUEST_SIZE
|
||||||
|
value: "20000000"
|
||||||
|
- name: CONTINUWUITY_ALLOW_FEDERATION
|
||||||
|
value: "true"
|
||||||
|
- name: CONTINUWUITY_TRUSTED_SERVERS
|
||||||
|
value: '["matrix.org"]'
|
||||||
|
- name: CONTINUWUITY_ADDRESS
|
||||||
|
value: "0.0.0.0"
|
||||||
|
- name: CONTINUWUITY_ROCKSDB_PARALLELISM_THREADS
|
||||||
|
value: "1"
|
||||||
|
- name: CONTINUWUITY_WELL_KNOWN__SERVER
|
||||||
|
value: "matrix.example.com:443"
|
||||||
|
- name: CONTINUWUITY_WELL_KNOWN__CLIENT
|
||||||
|
value: "https://matrix.example.com"
|
||||||
|
- name: CONTINUWUITY_ALLOW_REGISTRATION
|
||||||
|
value: "false"
|
||||||
|
- name: RUST_LOG
|
||||||
|
value: info
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /_matrix/federation/v1/version
|
||||||
|
port: http
|
||||||
|
periodSeconds: 4
|
||||||
|
failureThreshold: 5
|
||||||
|
resources:
|
||||||
|
# Continuwuity might use quite some RAM :3
|
||||||
|
requests:
|
||||||
|
cpu: "2"
|
||||||
|
memory: "512Mi"
|
||||||
|
limits:
|
||||||
|
cpu: "4"
|
||||||
|
memory: "2048Mi"
|
||||||
|
volumes:
|
||||||
|
- name: data
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: continuwuity-data
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Apart from manually configuring the containers,
|
||||||
|
[a community-maintained Helm Chart is available here to run
|
||||||
conduwuit on Kubernetes](https://gitlab.cronce.io/charts/conduwuit)
|
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.
|
||||||
|
|||||||
@@ -1,13 +1,28 @@
|
|||||||
# Troubleshooting Continuwuity
|
# Troubleshooting Continuwuity
|
||||||
|
|
||||||
> **Docker users ⚠️**
|
:::warning{title="Docker users:"}
|
||||||
>
|
Docker can be difficult to use and debug. It's common for Docker
|
||||||
> Docker can be difficult to use and debug. It's common for Docker
|
misconfigurations to cause issues, particularly with networking and permissions.
|
||||||
> misconfigurations to cause issues, particularly with networking and permissions.
|
Please check that your issues are not due to problems with your Docker setup.
|
||||||
> Please check that your issues are not due to problems with your Docker setup.
|
:::
|
||||||
|
|
||||||
## Continuwuity and Matrix issues
|
## Continuwuity and Matrix issues
|
||||||
|
|
||||||
|
### Slow joins to rooms
|
||||||
|
|
||||||
|
Some slowness is to be expected if you're the first person on your homserver to join a room (which will
|
||||||
|
always be the case for single-user homeservers). In this situation, your homeserver has to verify the signatures of
|
||||||
|
all of the state events sent by other servers before your join. To make this process as fast as possible, make sure you have
|
||||||
|
multiple fast, trusted servers listed in `trusted_servers` in your configuration, and ensure
|
||||||
|
`query_trusted_key_servers_first_on_join` is set to true (the default).
|
||||||
|
If you need suggestions for trusted servers, ask in the Continuwuity main room.
|
||||||
|
|
||||||
|
However, _very_ slow joins, especially to rooms with only a few users in them or rooms created by another user
|
||||||
|
on your homeserver, may be caused by [issue !779](https://forgejo.ellis.link/continuwuation/continuwuity/issues/779),
|
||||||
|
which is a longstanding bug with synchronizing room joins to clients. In this situation, you did succeed in joining the room, but
|
||||||
|
the bug caused your homeserver to forget to tell your client. **To fix this, clear your client's cache.** Both Element and Cinny
|
||||||
|
have a button to clear their cache in the "About" section of their settings.
|
||||||
|
|
||||||
### Lost access to admin room
|
### Lost access to admin room
|
||||||
|
|
||||||
You can reinvite yourself to the admin room through the following methods:
|
You can reinvite yourself to the admin room through the following methods:
|
||||||
|
|||||||
@@ -77,7 +77,12 @@ rec {
|
|||||||
craneLib.buildDepsOnly (
|
craneLib.buildDepsOnly (
|
||||||
(commonAttrs commonAttrsArgs)
|
(commonAttrs commonAttrsArgs)
|
||||||
// {
|
// {
|
||||||
env = uwuenv.buildDepsOnlyEnv // (makeRocksDBEnv { inherit rocksdb; });
|
env = uwuenv.buildDepsOnlyEnv
|
||||||
|
// (makeRocksDBEnv { inherit rocksdb; })
|
||||||
|
// {
|
||||||
|
# required since we started using unstable reqwest apparently ... otherwise the all-features build will fail
|
||||||
|
RUSTFLAGS = "--cfg reqwest_unstable";
|
||||||
|
};
|
||||||
inherit (features) cargoExtraArgs;
|
inherit (features) cargoExtraArgs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,7 +107,13 @@ rec {
|
|||||||
'';
|
'';
|
||||||
cargoArtifacts = deps;
|
cargoArtifacts = deps;
|
||||||
doCheck = true;
|
doCheck = true;
|
||||||
env = uwuenv.buildPackageEnv // rocksdbEnv;
|
env =
|
||||||
|
uwuenv.buildPackageEnv
|
||||||
|
// rocksdbEnv
|
||||||
|
// {
|
||||||
|
# required since we started using unstable reqwest apparently ... otherwise the all-features build will fail
|
||||||
|
RUSTFLAGS = "--cfg reqwest_unstable";
|
||||||
|
};
|
||||||
passthru.env = uwuenv.buildPackageEnv // rocksdbEnv;
|
passthru.env = uwuenv.buildPackageEnv // rocksdbEnv;
|
||||||
meta.mainProgram = crateInfo.pname;
|
meta.mainProgram = crateInfo.pname;
|
||||||
inherit (features) cargoExtraArgs;
|
inherit (features) cargoExtraArgs;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||||
"extends": ["config:recommended", "replacements:all"],
|
"extends": ["config:recommended", "replacements:all"],
|
||||||
|
"dependencyDashboard": true,
|
||||||
"osvVulnerabilityAlerts": true,
|
"osvVulnerabilityAlerts": true,
|
||||||
"lockFileMaintenance": {
|
"lockFileMaintenance": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
@@ -57,12 +58,25 @@
|
|||||||
"matchUpdateTypes": ["minor", "patch"],
|
"matchUpdateTypes": ["minor", "patch"],
|
||||||
"groupName": "github-actions-non-major"
|
"groupName": "github-actions-non-major"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "Batch patch-level Node.js dependency updates",
|
||||||
|
"matchManagers": ["npm"],
|
||||||
|
"matchUpdateTypes": ["patch"],
|
||||||
|
"groupName": "node-patch-updates"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "Pin forgejo artifact actions to prevent breaking changes",
|
"description": "Pin forgejo artifact actions to prevent breaking changes",
|
||||||
"matchManagers": ["github-actions"],
|
"matchManagers": ["github-actions"],
|
||||||
"matchPackageNames": ["forgejo/upload-artifact", "forgejo/download-artifact"],
|
"matchPackageNames": ["forgejo/upload-artifact", "forgejo/download-artifact"],
|
||||||
"enabled": false
|
"enabled": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"description": "Auto-merge crate-ci/typos minor updates",
|
||||||
|
"matchPackageNames": ["crate-ci/typos"],
|
||||||
|
"matchUpdateTypes": ["minor", "patch"],
|
||||||
|
"automerge": true,
|
||||||
|
"automergeStrategy": "fast-forward"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "Auto-merge renovatebot docker image updates",
|
"description": "Auto-merge renovatebot docker image updates",
|
||||||
"matchDatasources": ["docker"],
|
"matchDatasources": ["docker"],
|
||||||
|
|||||||
@@ -89,13 +89,7 @@ async fn ban_room(&self, room: OwnedRoomOrAliasId) -> Result {
|
|||||||
locally, if not using get_alias_helper to fetch room ID remotely"
|
locally, if not using get_alias_helper to fetch room ID remotely"
|
||||||
);
|
);
|
||||||
|
|
||||||
match self
|
match self.services.rooms.alias.resolve_alias(room_alias).await {
|
||||||
.services
|
|
||||||
.rooms
|
|
||||||
.alias
|
|
||||||
.resolve_alias(room_alias, None)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
| Ok((room_id, servers)) => {
|
| Ok((room_id, servers)) => {
|
||||||
debug!(
|
debug!(
|
||||||
%room_id,
|
%room_id,
|
||||||
@@ -235,7 +229,7 @@ async fn ban_list_of_rooms(&self) -> Result {
|
|||||||
.services
|
.services
|
||||||
.rooms
|
.rooms
|
||||||
.alias
|
.alias
|
||||||
.resolve_alias(room_alias, None)
|
.resolve_alias(room_alias)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
| Ok((room_id, servers)) => {
|
| Ok((room_id, servers)) => {
|
||||||
@@ -388,13 +382,7 @@ async fn unban_room(&self, room: OwnedRoomOrAliasId) -> Result {
|
|||||||
room ID over federation"
|
room ID over federation"
|
||||||
);
|
);
|
||||||
|
|
||||||
match self
|
match self.services.rooms.alias.resolve_alias(room_alias).await {
|
||||||
.services
|
|
||||||
.rooms
|
|
||||||
.alias
|
|
||||||
.resolve_alias(room_alias, None)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
| Ok((room_id, servers)) => {
|
| Ok((room_id, servers)) => {
|
||||||
debug!(
|
debug!(
|
||||||
%room_id,
|
%room_id,
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ pub(super) async fn list_backups(&self) -> Result {
|
|||||||
.db
|
.db
|
||||||
.backup_list()?
|
.backup_list()?
|
||||||
.try_stream()
|
.try_stream()
|
||||||
.try_for_each(|result| write!(self, "{result}"))
|
.try_for_each(|result| writeln!(self, "{result}"))
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use std::fmt::Write;
|
|||||||
use axum::extract::State;
|
use axum::extract::State;
|
||||||
use axum_client_ip::InsecureClientIp;
|
use axum_client_ip::InsecureClientIp;
|
||||||
use conduwuit::{
|
use conduwuit::{
|
||||||
Err, Error, Event, Result, debug_info, err, error, info,
|
Err, Event, Result, debug_info, err, error, info,
|
||||||
matrix::pdu::PduBuilder,
|
matrix::pdu::PduBuilder,
|
||||||
utils::{self, ReadyExt, stream::BroadbandExt},
|
utils::{self, ReadyExt, stream::BroadbandExt},
|
||||||
warn,
|
warn,
|
||||||
@@ -252,6 +252,13 @@ pub(crate) async fn register_route(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Don't allow registration with user IDs that aren't local
|
||||||
|
if !services.globals.user_is_local(&user_id) {
|
||||||
|
return Err!(Request(InvalidUsername(
|
||||||
|
"Username {body_username} is not local to this server"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
user_id
|
user_id
|
||||||
},
|
},
|
||||||
| Err(e) => {
|
| Err(e) => {
|
||||||
@@ -380,7 +387,7 @@ pub(crate) async fn register_route(
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
if !worked {
|
if !worked {
|
||||||
return Err(Error::Uiaa(uiaainfo));
|
return Err!(Uiaa(uiaainfo));
|
||||||
}
|
}
|
||||||
// Success!
|
// Success!
|
||||||
},
|
},
|
||||||
@@ -394,7 +401,7 @@ pub(crate) async fn register_route(
|
|||||||
&uiaainfo,
|
&uiaainfo,
|
||||||
json,
|
json,
|
||||||
);
|
);
|
||||||
return Err(Error::Uiaa(uiaainfo));
|
return Err!(Uiaa(uiaainfo));
|
||||||
},
|
},
|
||||||
| _ => {
|
| _ => {
|
||||||
return Err!(Request(NotJson("JSON body is not valid")));
|
return Err!(Request(NotJson("JSON body is not valid")));
|
||||||
@@ -654,7 +661,7 @@ pub(crate) async fn change_password_route(
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if !worked {
|
if !worked {
|
||||||
return Err(Error::Uiaa(uiaainfo));
|
return Err!(Uiaa(uiaainfo));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Success!
|
// Success!
|
||||||
@@ -666,7 +673,7 @@ pub(crate) async fn change_password_route(
|
|||||||
.uiaa
|
.uiaa
|
||||||
.create(sender_user, body.sender_device(), &uiaainfo, json);
|
.create(sender_user, body.sender_device(), &uiaainfo, json);
|
||||||
|
|
||||||
return Err(Error::Uiaa(uiaainfo));
|
return Err!(Uiaa(uiaainfo));
|
||||||
},
|
},
|
||||||
| _ => {
|
| _ => {
|
||||||
return Err!(Request(NotJson("JSON body is not valid")));
|
return Err!(Request(NotJson("JSON body is not valid")));
|
||||||
@@ -784,7 +791,7 @@ pub(crate) async fn deactivate_route(
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if !worked {
|
if !worked {
|
||||||
return Err(Error::Uiaa(uiaainfo));
|
return Err!(Uiaa(uiaainfo));
|
||||||
}
|
}
|
||||||
// Success!
|
// Success!
|
||||||
},
|
},
|
||||||
@@ -795,7 +802,7 @@ pub(crate) async fn deactivate_route(
|
|||||||
.uiaa
|
.uiaa
|
||||||
.create(sender_user, body.sender_device(), &uiaainfo, json);
|
.create(sender_user, body.sender_device(), &uiaainfo, json);
|
||||||
|
|
||||||
return Err(Error::Uiaa(uiaainfo));
|
return Err!(Uiaa(uiaainfo));
|
||||||
},
|
},
|
||||||
| _ => {
|
| _ => {
|
||||||
return Err!(Request(NotJson("JSON body is not valid")));
|
return Err!(Request(NotJson("JSON body is not valid")));
|
||||||
|
|||||||
+3
-65
@@ -1,12 +1,6 @@
|
|||||||
use axum::extract::State;
|
use axum::extract::State;
|
||||||
use conduwuit::{Err, Result, debug};
|
use conduwuit::{Err, Result};
|
||||||
use conduwuit_service::Services;
|
use ruma::api::client::alias::{create_alias, delete_alias, get_alias};
|
||||||
use futures::StreamExt;
|
|
||||||
use rand::seq::SliceRandom;
|
|
||||||
use ruma::{
|
|
||||||
OwnedServerName, RoomAliasId, RoomId,
|
|
||||||
api::client::alias::{create_alias, delete_alias, get_alias},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::Ruma;
|
use crate::Ruma;
|
||||||
|
|
||||||
@@ -96,65 +90,9 @@ pub(crate) async fn get_alias_route(
|
|||||||
) -> Result<get_alias::v3::Response> {
|
) -> Result<get_alias::v3::Response> {
|
||||||
let room_alias = body.body.room_alias;
|
let room_alias = body.body.room_alias;
|
||||||
|
|
||||||
let Ok((room_id, servers)) = services.rooms.alias.resolve_alias(&room_alias, None).await
|
let Ok((room_id, servers)) = services.rooms.alias.resolve_alias(&room_alias).await else {
|
||||||
else {
|
|
||||||
return Err!(Request(NotFound("Room with alias not found.")));
|
return Err!(Request(NotFound("Room with alias not found.")));
|
||||||
};
|
};
|
||||||
|
|
||||||
let servers = room_available_servers(&services, &room_id, &room_alias, servers).await;
|
|
||||||
debug!(%room_alias, %room_id, "available servers: {servers:?}");
|
|
||||||
|
|
||||||
Ok(get_alias::v3::Response::new(room_id, servers))
|
Ok(get_alias::v3::Response::new(room_id, servers))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn room_available_servers(
|
|
||||||
services: &Services,
|
|
||||||
room_id: &RoomId,
|
|
||||||
room_alias: &RoomAliasId,
|
|
||||||
pre_servers: Vec<OwnedServerName>,
|
|
||||||
) -> Vec<OwnedServerName> {
|
|
||||||
// find active servers in room state cache to suggest
|
|
||||||
let mut servers: Vec<OwnedServerName> = services
|
|
||||||
.rooms
|
|
||||||
.state_cache
|
|
||||||
.room_servers(room_id)
|
|
||||||
.map(ToOwned::to_owned)
|
|
||||||
.collect()
|
|
||||||
.await;
|
|
||||||
|
|
||||||
// push any servers we want in the list already (e.g. responded remote alias
|
|
||||||
// servers, room alias server itself)
|
|
||||||
servers.extend(pre_servers);
|
|
||||||
|
|
||||||
servers.sort_unstable();
|
|
||||||
servers.dedup();
|
|
||||||
|
|
||||||
// shuffle list of servers randomly after sort and dedupe
|
|
||||||
servers.shuffle(&mut rand::thread_rng());
|
|
||||||
|
|
||||||
// insert our server as the very first choice if in list, else check if we can
|
|
||||||
// prefer the room alias server first
|
|
||||||
match servers
|
|
||||||
.iter()
|
|
||||||
.position(|server_name| services.globals.server_is_ours(server_name))
|
|
||||||
{
|
|
||||||
| Some(server_index) => {
|
|
||||||
servers.swap_remove(server_index);
|
|
||||||
servers.insert(0, services.globals.server_name().to_owned());
|
|
||||||
},
|
|
||||||
| _ => {
|
|
||||||
match servers
|
|
||||||
.iter()
|
|
||||||
.position(|server| server == room_alias.server_name())
|
|
||||||
{
|
|
||||||
| Some(alias_server_index) => {
|
|
||||||
servers.swap_remove(alias_server_index);
|
|
||||||
servers.insert(0, room_alias.server_name().into());
|
|
||||||
},
|
|
||||||
| _ => {},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
servers
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use axum::extract::State;
|
use axum::extract::State;
|
||||||
use axum_client_ip::InsecureClientIp;
|
use axum_client_ip::InsecureClientIp;
|
||||||
use conduwuit::{Err, Error, Result, debug, err, utils};
|
use conduwuit::{Err, Result, debug, err, utils};
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
MilliSecondsSinceUnixEpoch, OwnedDeviceId,
|
MilliSecondsSinceUnixEpoch, OwnedDeviceId,
|
||||||
@@ -232,7 +232,7 @@ pub(crate) async fn delete_devices_route(
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if !worked {
|
if !worked {
|
||||||
return Err(Error::Uiaa(uiaainfo));
|
return Err!(Uiaa(uiaainfo));
|
||||||
}
|
}
|
||||||
// Success!
|
// Success!
|
||||||
},
|
},
|
||||||
@@ -243,10 +243,10 @@ pub(crate) async fn delete_devices_route(
|
|||||||
.uiaa
|
.uiaa
|
||||||
.create(sender_user, sender_device, &uiaainfo, json);
|
.create(sender_user, sender_device, &uiaainfo, json);
|
||||||
|
|
||||||
return Err(Error::Uiaa(uiaainfo));
|
return Err!(Uiaa(uiaainfo));
|
||||||
},
|
},
|
||||||
| _ => {
|
| _ => {
|
||||||
return Err(Error::BadRequest(ErrorKind::NotJson, "Not json."));
|
return Err!(BadRequest(ErrorKind::NotJson, "Not json."));
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use std::{
|
|||||||
|
|
||||||
use axum::extract::State;
|
use axum::extract::State;
|
||||||
use conduwuit::{
|
use conduwuit::{
|
||||||
Err, Error, Result, debug, debug_warn, err,
|
Err, Result, debug, debug_warn, err,
|
||||||
result::NotFound,
|
result::NotFound,
|
||||||
utils,
|
utils,
|
||||||
utils::{IterStream, stream::WidebandExt},
|
utils::{IterStream, stream::WidebandExt},
|
||||||
@@ -215,7 +215,7 @@ pub(crate) async fn upload_signing_keys_route(
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if !worked {
|
if !worked {
|
||||||
return Err(Error::Uiaa(uiaainfo));
|
return Err!(Uiaa(uiaainfo));
|
||||||
}
|
}
|
||||||
// Success!
|
// Success!
|
||||||
},
|
},
|
||||||
@@ -226,10 +226,10 @@ pub(crate) async fn upload_signing_keys_route(
|
|||||||
.uiaa
|
.uiaa
|
||||||
.create(sender_user, sender_device, &uiaainfo, json);
|
.create(sender_user, sender_device, &uiaainfo, json);
|
||||||
|
|
||||||
return Err(Error::Uiaa(uiaainfo));
|
return Err!(Uiaa(uiaainfo));
|
||||||
},
|
},
|
||||||
| _ => {
|
| _ => {
|
||||||
return Err(Error::BadRequest(ErrorKind::NotJson, "Not json."));
|
return Err!(BadRequest(ErrorKind::NotJson, "Not json."));
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -396,12 +396,12 @@ pub(crate) async fn get_key_changes_route(
|
|||||||
let from = body
|
let from = body
|
||||||
.from
|
.from
|
||||||
.parse()
|
.parse()
|
||||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Invalid `from`."))?;
|
.map_err(|_| err!(BadRequest(ErrorKind::InvalidParam, "Invalid `from`.")))?;
|
||||||
|
|
||||||
let to = body
|
let to = body
|
||||||
.to
|
.to
|
||||||
.parse()
|
.parse()
|
||||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Invalid `to`."))?;
|
.map_err(|_| err!(BadRequest(ErrorKind::InvalidParam, "Invalid `to`.")))?;
|
||||||
|
|
||||||
device_list_updates.extend(
|
device_list_updates.extend(
|
||||||
services
|
services
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use std::time::Duration;
|
|||||||
use axum::extract::State;
|
use axum::extract::State;
|
||||||
use axum_client_ip::InsecureClientIp;
|
use axum_client_ip::InsecureClientIp;
|
||||||
use conduwuit::{
|
use conduwuit::{
|
||||||
Err, Result, err,
|
Err, Result, err, error,
|
||||||
utils::{self, content_disposition::make_content_disposition, math::ruma_from_usize},
|
utils::{self, content_disposition::make_content_disposition, math::ruma_from_usize},
|
||||||
};
|
};
|
||||||
use conduwuit_service::{
|
use conduwuit_service::{
|
||||||
@@ -69,7 +69,7 @@ pub(crate) async fn create_content_route(
|
|||||||
.create(mxc, Some(user), Some(&content_disposition), content_type, &body.file)
|
.create(mxc, Some(user), Some(&content_disposition), content_type, &body.file)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
err!("Failed to save uploaded media: {e}");
|
error!("Failed to save uploaded media: {e}");
|
||||||
return Err!(Request(Unknown("Failed to save uploaded media")));
|
return Err!(Request(Unknown("Failed to save uploaded media")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -198,11 +198,7 @@ pub(crate) async fn join_room_by_id_or_alias_route(
|
|||||||
(servers, room_id)
|
(servers, room_id)
|
||||||
},
|
},
|
||||||
| Err(room_alias) => {
|
| Err(room_alias) => {
|
||||||
let (room_id, mut servers) = services
|
let (room_id, mut servers) = services.rooms.alias.resolve_alias(&room_alias).await?;
|
||||||
.rooms
|
|
||||||
.alias
|
|
||||||
.resolve_alias(&room_alias, Some(body.via.clone()))
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
banned_room_check(
|
banned_room_check(
|
||||||
&services,
|
&services,
|
||||||
|
|||||||
@@ -102,11 +102,7 @@ pub(crate) async fn knock_room_route(
|
|||||||
(servers, room_id)
|
(servers, room_id)
|
||||||
},
|
},
|
||||||
| Err(room_alias) => {
|
| Err(room_alias) => {
|
||||||
let (room_id, mut servers) = services
|
let (room_id, mut servers) = services.rooms.alias.resolve_alias(&room_alias).await?;
|
||||||
.rooms
|
|
||||||
.alias
|
|
||||||
.resolve_alias(&room_alias, Some(body.via.clone()))
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
banned_room_check(
|
banned_room_check(
|
||||||
&services,
|
&services,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use axum::extract::State;
|
use axum::extract::State;
|
||||||
use axum_client_ip::InsecureClientIp;
|
use axum_client_ip::InsecureClientIp;
|
||||||
use conduwuit::{
|
use conduwuit::{
|
||||||
Err, Error, Result, at, debug_warn,
|
Err, Result, at, debug_warn,
|
||||||
matrix::{
|
matrix::{
|
||||||
event::{Event, Matches},
|
event::{Event, Matches},
|
||||||
pdu::PduCount,
|
pdu::PduCount,
|
||||||
@@ -322,7 +322,7 @@ where
|
|||||||
|
|
||||||
if server_ignored {
|
if server_ignored {
|
||||||
// the sender's server is ignored, so ignore this event
|
// the sender's server is ignored, so ignore this event
|
||||||
return Err(Error::BadRequest(
|
return Err!(BadRequest(
|
||||||
ErrorKind::SenderIgnored { sender: None },
|
ErrorKind::SenderIgnored { sender: None },
|
||||||
"The sender's server is ignored by this server.",
|
"The sender's server is ignored by this server.",
|
||||||
));
|
));
|
||||||
@@ -331,7 +331,7 @@ where
|
|||||||
if user_ignored && !services.config.send_messages_from_ignored_users_to_client {
|
if user_ignored && !services.config.send_messages_from_ignored_users_to_client {
|
||||||
// the recipient of this PDU has the sender ignored, and we're not
|
// the recipient of this PDU has the sender ignored, and we're not
|
||||||
// configured to send ignored messages to clients
|
// configured to send ignored messages to clients
|
||||||
return Err(Error::BadRequest(
|
return Err!(BadRequest(
|
||||||
ErrorKind::SenderIgnored { sender: Some(event.sender().to_owned()) },
|
ErrorKind::SenderIgnored { sender: Some(event.sender().to_owned()) },
|
||||||
"You have ignored this sender.",
|
"You have ignored this sender.",
|
||||||
));
|
));
|
||||||
|
|||||||
+16
-16
@@ -1,5 +1,5 @@
|
|||||||
use axum::extract::State;
|
use axum::extract::State;
|
||||||
use conduwuit::{Err, Error, Result, err};
|
use conduwuit::{Err, Result, err};
|
||||||
use conduwuit_service::Services;
|
use conduwuit_service::Services;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
CanonicalJsonObject, CanonicalJsonValue,
|
CanonicalJsonObject, CanonicalJsonValue,
|
||||||
@@ -243,27 +243,27 @@ pub(crate) async fn set_pushrule_route(
|
|||||||
body.before.as_deref(),
|
body.before.as_deref(),
|
||||||
) {
|
) {
|
||||||
let err = match error {
|
let err = match error {
|
||||||
| InsertPushRuleError::ServerDefaultRuleId => Error::BadRequest(
|
| InsertPushRuleError::ServerDefaultRuleId => err!(BadRequest(
|
||||||
ErrorKind::InvalidParam,
|
ErrorKind::InvalidParam,
|
||||||
"Rule IDs starting with a dot are reserved for server-default rules.",
|
"Rule IDs starting with a dot are reserved for server-default rules.",
|
||||||
),
|
)),
|
||||||
| InsertPushRuleError::InvalidRuleId => Error::BadRequest(
|
| InsertPushRuleError::InvalidRuleId => err!(BadRequest(
|
||||||
ErrorKind::InvalidParam,
|
ErrorKind::InvalidParam,
|
||||||
"Rule ID containing invalid characters.",
|
"Rule ID containing invalid characters.",
|
||||||
),
|
)),
|
||||||
| InsertPushRuleError::RelativeToServerDefaultRule => Error::BadRequest(
|
| InsertPushRuleError::RelativeToServerDefaultRule => err!(BadRequest(
|
||||||
ErrorKind::InvalidParam,
|
ErrorKind::InvalidParam,
|
||||||
"Can't place a push rule relatively to a server-default rule.",
|
"Can't place a push rule relatively to a server-default rule.",
|
||||||
),
|
)),
|
||||||
| InsertPushRuleError::UnknownRuleId => Error::BadRequest(
|
| InsertPushRuleError::UnknownRuleId => err!(BadRequest(
|
||||||
ErrorKind::NotFound,
|
ErrorKind::NotFound,
|
||||||
"The before or after rule could not be found.",
|
"The before or after rule could not be found.",
|
||||||
),
|
)),
|
||||||
| InsertPushRuleError::BeforeHigherThanAfter => Error::BadRequest(
|
| InsertPushRuleError::BeforeHigherThanAfter => err!(BadRequest(
|
||||||
ErrorKind::InvalidParam,
|
ErrorKind::InvalidParam,
|
||||||
"The before rule has a higher priority than the after rule.",
|
"The before rule has a higher priority than the after rule.",
|
||||||
),
|
)),
|
||||||
| _ => Error::BadRequest(ErrorKind::InvalidParam, "Invalid data."),
|
| _ => err!(BadRequest(ErrorKind::InvalidParam, "Invalid data.")),
|
||||||
};
|
};
|
||||||
|
|
||||||
return Err(err);
|
return Err(err);
|
||||||
@@ -433,13 +433,13 @@ pub(crate) async fn delete_pushrule_route(
|
|||||||
.remove(body.kind.clone(), &body.rule_id)
|
.remove(body.kind.clone(), &body.rule_id)
|
||||||
{
|
{
|
||||||
let err = match error {
|
let err = match error {
|
||||||
| RemovePushRuleError::ServerDefault => Error::BadRequest(
|
| RemovePushRuleError::ServerDefault => err!(BadRequest(
|
||||||
ErrorKind::InvalidParam,
|
ErrorKind::InvalidParam,
|
||||||
"Cannot delete a server-default pushrule.",
|
"Cannot delete a server-default pushrule.",
|
||||||
),
|
)),
|
||||||
| RemovePushRuleError::NotFound =>
|
| RemovePushRuleError::NotFound =>
|
||||||
Error::BadRequest(ErrorKind::NotFound, "Push rule not found."),
|
err!(BadRequest(ErrorKind::NotFound, "Push rule not found.")),
|
||||||
| _ => Error::BadRequest(ErrorKind::InvalidParam, "Invalid data."),
|
| _ => err!(BadRequest(ErrorKind::InvalidParam, "Invalid data.")),
|
||||||
};
|
};
|
||||||
|
|
||||||
return Err(err);
|
return Err(err);
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ use axum::extract::State;
|
|||||||
use axum_client_ip::InsecureClientIp;
|
use axum_client_ip::InsecureClientIp;
|
||||||
use conduwuit::{Err, Event, Result, debug_info, info, matrix::pdu::PduEvent, utils::ReadyExt};
|
use conduwuit::{Err, Event, Result, debug_info, info, matrix::pdu::PduEvent, utils::ReadyExt};
|
||||||
use conduwuit_service::Services;
|
use conduwuit_service::Services;
|
||||||
use rand::Rng;
|
|
||||||
use ruma::{
|
use ruma::{
|
||||||
EventId, OwnedEventId, OwnedRoomId, OwnedUserId, RoomId, UserId,
|
EventId, OwnedEventId, OwnedRoomId, OwnedUserId, RoomId, UserId,
|
||||||
api::client::{
|
api::client::{
|
||||||
@@ -244,7 +243,7 @@ fn build_report(report: Report) -> RoomMessageEventContent {
|
|||||||
/// random delay sending a response per spec suggestion regarding
|
/// random delay sending a response per spec suggestion regarding
|
||||||
/// enumerating for potential events existing in our server.
|
/// enumerating for potential events existing in our server.
|
||||||
async fn delay_response() {
|
async fn delay_response() {
|
||||||
let time_to_wait = rand::thread_rng().gen_range(2..5);
|
let time_to_wait = rand::random_range(2..5);
|
||||||
debug_info!(
|
debug_info!(
|
||||||
"Got successful /report request, waiting {time_to_wait} seconds before sending \
|
"Got successful /report request, waiting {time_to_wait} seconds before sending \
|
||||||
successful response."
|
successful response."
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use std::cmp::max;
|
|||||||
|
|
||||||
use axum::extract::State;
|
use axum::extract::State;
|
||||||
use conduwuit::{
|
use conduwuit::{
|
||||||
Err, Error, Event, Result, RoomVersion, debug, err, info,
|
Err, Event, Result, RoomVersion, debug, err, info,
|
||||||
matrix::{StateKey, pdu::PduBuilder},
|
matrix::{StateKey, pdu::PduBuilder},
|
||||||
};
|
};
|
||||||
use futures::{FutureExt, StreamExt};
|
use futures::{FutureExt, StreamExt};
|
||||||
@@ -58,7 +58,7 @@ pub(crate) async fn upgrade_room_route(
|
|||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
if !services.server.supported_room_version(&body.new_version) {
|
if !services.server.supported_room_version(&body.new_version) {
|
||||||
return Err(Error::BadRequest(
|
return Err!(BadRequest(
|
||||||
ErrorKind::UnsupportedRoomVersion,
|
ErrorKind::UnsupportedRoomVersion,
|
||||||
"This server does not support that room version.",
|
"This server does not support that room version.",
|
||||||
));
|
));
|
||||||
@@ -170,7 +170,7 @@ pub(crate) async fn upgrade_room_route(
|
|||||||
"creator".into(),
|
"creator".into(),
|
||||||
json!(&sender_user).try_into().map_err(|e| {
|
json!(&sender_user).try_into().map_err(|e| {
|
||||||
info!("Error forming creation event: {e}");
|
info!("Error forming creation event: {e}");
|
||||||
Error::BadRequest(ErrorKind::BadJson, "Error forming creation event")
|
err!(BadRequest(ErrorKind::BadJson, "Error forming creation event"))
|
||||||
})?,
|
})?,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -186,13 +186,13 @@ pub(crate) async fn upgrade_room_route(
|
|||||||
"room_version".into(),
|
"room_version".into(),
|
||||||
json!(&body.new_version)
|
json!(&body.new_version)
|
||||||
.try_into()
|
.try_into()
|
||||||
.map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Error forming creation event"))?,
|
.map_err(|_| err!(BadRequest(ErrorKind::BadJson, "Error forming creation event")))?,
|
||||||
);
|
);
|
||||||
create_event_content.insert(
|
create_event_content.insert(
|
||||||
"predecessor".into(),
|
"predecessor".into(),
|
||||||
json!(predecessor)
|
json!(predecessor)
|
||||||
.try_into()
|
.try_into()
|
||||||
.map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Error forming creation event"))?,
|
.map_err(|_| err!(BadRequest(ErrorKind::BadJson, "Error forming creation event")))?,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Validate creation event content
|
// Validate creation event content
|
||||||
@@ -203,7 +203,7 @@ pub(crate) async fn upgrade_room_route(
|
|||||||
)
|
)
|
||||||
.is_err()
|
.is_err()
|
||||||
{
|
{
|
||||||
return Err(Error::BadRequest(ErrorKind::BadJson, "Error forming creation event"));
|
return Err!(BadRequest(ErrorKind::BadJson, "Error forming creation event"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let create_event_id = services
|
let create_event_id = services
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use std::time::Duration;
|
|||||||
use axum::extract::State;
|
use axum::extract::State;
|
||||||
use axum_client_ip::InsecureClientIp;
|
use axum_client_ip::InsecureClientIp;
|
||||||
use conduwuit::{
|
use conduwuit::{
|
||||||
Err, Error, Result, debug, err, info,
|
Err, Result, debug, err, info,
|
||||||
utils::{self, ReadyExt, hash},
|
utils::{self, ReadyExt, hash},
|
||||||
warn,
|
warn,
|
||||||
};
|
};
|
||||||
@@ -191,7 +191,7 @@ pub(crate) async fn handle_login(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if services.users.is_locked(&user_id).await? {
|
if services.users.is_locked(&user_id).await? {
|
||||||
return Err(Error::BadRequest(ErrorKind::UserLocked, "This account has been locked."));
|
return Err!(BadRequest(ErrorKind::UserLocked, "This account has been locked."));
|
||||||
}
|
}
|
||||||
|
|
||||||
if services.users.is_login_disabled(&user_id).await {
|
if services.users.is_login_disabled(&user_id).await {
|
||||||
@@ -390,7 +390,7 @@ pub(crate) async fn login_token_route(
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if !worked {
|
if !worked {
|
||||||
return Err(Error::Uiaa(uiaainfo));
|
return Err!(Uiaa(uiaainfo));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Success!
|
// Success!
|
||||||
@@ -402,7 +402,7 @@ pub(crate) async fn login_token_route(
|
|||||||
.uiaa
|
.uiaa
|
||||||
.create(sender_user, sender_device, &uiaainfo, json);
|
.create(sender_user, sender_device, &uiaainfo, json);
|
||||||
|
|
||||||
return Err(Error::Uiaa(uiaainfo));
|
return Err!(Uiaa(uiaainfo));
|
||||||
},
|
},
|
||||||
| _ => {
|
| _ => {
|
||||||
return Err!(Request(NotJson("No JSON body was sent when required.")));
|
return Err!(Request(NotJson("No JSON body was sent when required.")));
|
||||||
|
|||||||
@@ -342,10 +342,10 @@ async fn allowed_to_send_state_event(
|
|||||||
}
|
}
|
||||||
|
|
||||||
for alias in aliases {
|
for alias in aliases {
|
||||||
let (alias_room_id, _servers) = services
|
let (alias_room_id, _) = services
|
||||||
.rooms
|
.rooms
|
||||||
.alias
|
.alias
|
||||||
.resolve_alias(&alias, None)
|
.resolve_alias(&alias)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
err!(Request(Unknown("Failed resolving alias \"{alias}\": {e}")))
|
err!(Request(Unknown("Failed resolving alias \"{alias}\": {e}")))
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use axum::extract::State;
|
use axum::extract::State;
|
||||||
use conduwuit::{Error, Result};
|
use conduwuit::{Result, err};
|
||||||
use conduwuit_service::sending::EduBuf;
|
use conduwuit_service::sending::EduBuf;
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
@@ -66,7 +66,7 @@ pub(crate) async fn send_event_to_device_route(
|
|||||||
|
|
||||||
let event = event
|
let event = event
|
||||||
.deserialize_as()
|
.deserialize_as()
|
||||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Event is invalid"))?;
|
.map_err(|_| err!(BadRequest(ErrorKind::InvalidParam, "Event is invalid")))?;
|
||||||
|
|
||||||
match target_device_id_maybe {
|
match target_device_id_maybe {
|
||||||
| DeviceIdOrAllDevices::DeviceId(target_device_id) => {
|
| DeviceIdOrAllDevices::DeviceId(target_device_id) => {
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
use axum::{Json, extract::State, response::IntoResponse};
|
use axum::{Json, extract::State, response::IntoResponse};
|
||||||
use conduwuit::{Error, Result};
|
use conduwuit::{Err, Result};
|
||||||
use ruma::api::client::{
|
use ruma::api::client::discovery::{
|
||||||
discovery::{
|
discover_homeserver::{self, HomeserverInfo, SlidingSyncProxyInfo},
|
||||||
discover_homeserver::{self, HomeserverInfo, SlidingSyncProxyInfo},
|
discover_support::{self, Contact},
|
||||||
discover_support::{self, Contact},
|
|
||||||
},
|
|
||||||
error::ErrorKind,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::Ruma;
|
use crate::Ruma;
|
||||||
@@ -19,7 +16,7 @@ pub(crate) async fn well_known_client(
|
|||||||
) -> Result<discover_homeserver::Response> {
|
) -> Result<discover_homeserver::Response> {
|
||||||
let client_url = match services.config.well_known.client.as_ref() {
|
let client_url = match services.config.well_known.client.as_ref() {
|
||||||
| Some(url) => url.to_string(),
|
| Some(url) => url.to_string(),
|
||||||
| None => return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")),
|
| None => return Err!(BadRequest(ErrorKind::NotFound, "Not found.")),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(discover_homeserver::Response {
|
Ok(discover_homeserver::Response {
|
||||||
@@ -88,7 +85,7 @@ pub(crate) async fn well_known_support(
|
|||||||
|
|
||||||
if contacts.is_empty() && support_page.is_none() {
|
if contacts.is_empty() && support_page.is_none() {
|
||||||
// No admin room, no configured contacts, and no support page
|
// No admin room, no configured contacts, and no support page
|
||||||
return Err(Error::BadRequest(ErrorKind::NotFound, "Not found."));
|
return Err!(BadRequest(ErrorKind::NotFound, "Not found."));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(discover_support::Response { contacts, support_page })
|
Ok(discover_support::Response { contacts, support_page })
|
||||||
@@ -105,7 +102,7 @@ pub(crate) async fn syncv3_client_server_json(
|
|||||||
| Some(url) => url.to_string(),
|
| Some(url) => url.to_string(),
|
||||||
| None => match services.config.well_known.server.as_ref() {
|
| None => match services.config.well_known.server.as_ref() {
|
||||||
| Some(url) => url.to_string(),
|
| Some(url) => url.to_string(),
|
||||||
| None => return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")),
|
| None => return Err!(BadRequest(ErrorKind::NotFound, "Not found.")),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+16
-16
@@ -122,23 +122,23 @@ pub fn build(router: Router<State>, server: &Server) -> Router<State> {
|
|||||||
// Ruma doesn't have support for multiple paths for a single endpoint yet, and these routes
|
// Ruma doesn't have support for multiple paths for a single endpoint yet, and these routes
|
||||||
// share one Ruma request / response type pair with {get,send}_state_event_for_key_route
|
// share one Ruma request / response type pair with {get,send}_state_event_for_key_route
|
||||||
.route(
|
.route(
|
||||||
"/_matrix/client/r0/rooms/:room_id/state/:event_type",
|
"/_matrix/client/r0/rooms/{room_id}/state/{event_type}",
|
||||||
get(client::get_state_events_for_empty_key_route)
|
get(client::get_state_events_for_empty_key_route)
|
||||||
.put(client::send_state_event_for_empty_key_route),
|
.put(client::send_state_event_for_empty_key_route),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/_matrix/client/v3/rooms/:room_id/state/:event_type",
|
"/_matrix/client/v3/rooms/{room_id}/state/{event_type}",
|
||||||
get(client::get_state_events_for_empty_key_route)
|
get(client::get_state_events_for_empty_key_route)
|
||||||
.put(client::send_state_event_for_empty_key_route),
|
.put(client::send_state_event_for_empty_key_route),
|
||||||
)
|
)
|
||||||
// These two endpoints allow trailing slashes
|
// These two endpoints allow trailing slashes
|
||||||
.route(
|
.route(
|
||||||
"/_matrix/client/r0/rooms/:room_id/state/:event_type/",
|
"/_matrix/client/r0/rooms/{room_id}/state/{event_type}/",
|
||||||
get(client::get_state_events_for_empty_key_route)
|
get(client::get_state_events_for_empty_key_route)
|
||||||
.put(client::send_state_event_for_empty_key_route),
|
.put(client::send_state_event_for_empty_key_route),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/_matrix/client/v3/rooms/:room_id/state/:event_type/",
|
"/_matrix/client/v3/rooms/{room_id}/state/{event_type}/",
|
||||||
get(client::get_state_events_for_empty_key_route)
|
get(client::get_state_events_for_empty_key_route)
|
||||||
.put(client::send_state_event_for_empty_key_route),
|
.put(client::send_state_event_for_empty_key_route),
|
||||||
)
|
)
|
||||||
@@ -177,7 +177,7 @@ pub fn build(router: Router<State>, server: &Server) -> Router<State> {
|
|||||||
.ruma_route(&client::get_mutual_rooms_route)
|
.ruma_route(&client::get_mutual_rooms_route)
|
||||||
.ruma_route(&client::get_room_summary)
|
.ruma_route(&client::get_room_summary)
|
||||||
.route(
|
.route(
|
||||||
"/_matrix/client/unstable/im.nheko.summary/rooms/:room_id_or_alias/summary",
|
"/_matrix/client/unstable/im.nheko.summary/rooms/{room_id_or_alias}/summary",
|
||||||
get(client::get_room_summary_legacy)
|
get(client::get_room_summary_legacy)
|
||||||
)
|
)
|
||||||
.ruma_route(&client::get_suspended_status)
|
.ruma_route(&client::get_suspended_status)
|
||||||
@@ -196,7 +196,7 @@ pub fn build(router: Router<State>, server: &Server) -> Router<State> {
|
|||||||
.ruma_route(&server::get_server_version_route)
|
.ruma_route(&server::get_server_version_route)
|
||||||
.route("/_matrix/key/v2/server", get(server::get_server_keys_route))
|
.route("/_matrix/key/v2/server", get(server::get_server_keys_route))
|
||||||
.route(
|
.route(
|
||||||
"/_matrix/key/v2/server/:key_id",
|
"/_matrix/key/v2/server/{key_id}",
|
||||||
get(server::get_server_keys_deprecated_route),
|
get(server::get_server_keys_deprecated_route),
|
||||||
)
|
)
|
||||||
.ruma_route(&server::get_public_rooms_route)
|
.ruma_route(&server::get_public_rooms_route)
|
||||||
@@ -232,9 +232,9 @@ pub fn build(router: Router<State>, server: &Server) -> Router<State> {
|
|||||||
.route("/_continuwuity/local_user_count", get(client::conduwuit_local_user_count));
|
.route("/_continuwuity/local_user_count", get(client::conduwuit_local_user_count));
|
||||||
} else {
|
} else {
|
||||||
router = router
|
router = router
|
||||||
.route("/_matrix/federation/*path", any(federation_disabled))
|
.route("/_matrix/federation/{*path}", any(federation_disabled))
|
||||||
.route("/.well-known/matrix/server", any(federation_disabled))
|
.route("/.well-known/matrix/server", any(federation_disabled))
|
||||||
.route("/_matrix/key/*path", 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));
|
.route("/_continuwuity/local_user_count", any(federation_disabled));
|
||||||
}
|
}
|
||||||
@@ -253,27 +253,27 @@ pub fn build(router: Router<State>, server: &Server) -> Router<State> {
|
|||||||
get(client::get_media_preview_legacy_legacy_route),
|
get(client::get_media_preview_legacy_legacy_route),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/_matrix/media/v1/download/:server_name/:media_id",
|
"/_matrix/media/v1/download/{server_name}/{media_id}",
|
||||||
get(client::get_content_legacy_legacy_route),
|
get(client::get_content_legacy_legacy_route),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/_matrix/media/v1/download/:server_name/:media_id/:file_name",
|
"/_matrix/media/v1/download/{server_name}/{media_id}/{file_name}",
|
||||||
get(client::get_content_as_filename_legacy_legacy_route),
|
get(client::get_content_as_filename_legacy_legacy_route),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/_matrix/media/v1/thumbnail/:server_name/:media_id",
|
"/_matrix/media/v1/thumbnail/{server_name}/{media_id}",
|
||||||
get(client::get_content_thumbnail_legacy_legacy_route),
|
get(client::get_content_thumbnail_legacy_legacy_route),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
router = router
|
router = router
|
||||||
.route("/_matrix/media/v1/*path", any(legacy_media_disabled))
|
.route("/_matrix/media/v1/{*path}", any(legacy_media_disabled))
|
||||||
.route("/_matrix/media/v3/config", any(legacy_media_disabled))
|
.route("/_matrix/media/v3/config", any(legacy_media_disabled))
|
||||||
.route("/_matrix/media/v3/download/*path", any(legacy_media_disabled))
|
.route("/_matrix/media/v3/download/{*path}", any(legacy_media_disabled))
|
||||||
.route("/_matrix/media/v3/thumbnail/*path", any(legacy_media_disabled))
|
.route("/_matrix/media/v3/thumbnail/{*path}", any(legacy_media_disabled))
|
||||||
.route("/_matrix/media/v3/preview_url", any(redirect_legacy_preview))
|
.route("/_matrix/media/v3/preview_url", any(redirect_legacy_preview))
|
||||||
.route("/_matrix/media/r0/config", any(legacy_media_disabled))
|
.route("/_matrix/media/r0/config", any(legacy_media_disabled))
|
||||||
.route("/_matrix/media/r0/download/*path", any(legacy_media_disabled))
|
.route("/_matrix/media/r0/download/{*path}", any(legacy_media_disabled))
|
||||||
.route("/_matrix/media/r0/thumbnail/*path", any(legacy_media_disabled))
|
.route("/_matrix/media/r0/thumbnail/{*path}", any(legacy_media_disabled))
|
||||||
.route("/_matrix/media/r0/preview_url", any(redirect_legacy_preview));
|
.route("/_matrix/media/r0/preview_url", any(redirect_legacy_preview));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use std::{mem, ops::Deref};
|
use std::{mem, ops::Deref};
|
||||||
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use axum::{body::Body, extract::FromRequest};
|
use axum::{body::Body, extract::FromRequest};
|
||||||
use bytes::{BufMut, Bytes, BytesMut};
|
use bytes::{BufMut, Bytes, BytesMut};
|
||||||
use conduwuit::{Error, Result, debug, debug_warn, err, trace, utils::string::EMPTY};
|
use conduwuit::{Error, Result, debug, debug_warn, err, trace, utils::string::EMPTY};
|
||||||
@@ -79,7 +78,6 @@ where
|
|||||||
fn deref(&self) -> &Self::Target { &self.body }
|
fn deref(&self) -> &Self::Target { &self.body }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl<T> FromRequest<State, Body> for Args<T>
|
impl<T> FromRequest<State, Body> for Args<T>
|
||||||
where
|
where
|
||||||
T: IncomingRequest + Send + Sync + 'static,
|
T: IncomingRequest + Send + Sync + 'static,
|
||||||
|
|||||||
+12
-11
@@ -4,7 +4,7 @@ use axum_extra::{
|
|||||||
headers::{Authorization, authorization::Bearer},
|
headers::{Authorization, authorization::Bearer},
|
||||||
typed_header::TypedHeaderRejectionReason,
|
typed_header::TypedHeaderRejectionReason,
|
||||||
};
|
};
|
||||||
use conduwuit::{Err, Error, Result, debug_error, err, warn};
|
use conduwuit::{Err, Result, debug_error, err, warn};
|
||||||
use futures::{
|
use futures::{
|
||||||
TryFutureExt,
|
TryFutureExt,
|
||||||
future::{
|
future::{
|
||||||
@@ -54,7 +54,8 @@ pub(super) async fn auth(
|
|||||||
json_body: Option<&CanonicalJsonValue>,
|
json_body: Option<&CanonicalJsonValue>,
|
||||||
metadata: &Metadata,
|
metadata: &Metadata,
|
||||||
) -> Result<Auth> {
|
) -> Result<Auth> {
|
||||||
let bearer: Option<TypedHeader<Authorization<Bearer>>> = request.parts.extract().await?;
|
let bearer: Option<TypedHeader<Authorization<Bearer>>> =
|
||||||
|
request.parts.extract().await.unwrap_or(None);
|
||||||
let token = match &bearer {
|
let token = match &bearer {
|
||||||
| Some(TypedHeader(Authorization(bearer))) => Some(bearer.token()),
|
| Some(TypedHeader(Authorization(bearer))) => Some(bearer.token()),
|
||||||
| None => request.query.access_token.as_deref(),
|
| None => request.query.access_token.as_deref(),
|
||||||
@@ -76,7 +77,7 @@ pub(super) async fn auth(
|
|||||||
// already
|
// already
|
||||||
},
|
},
|
||||||
| Token::None | Token::Invalid => {
|
| Token::None | Token::Invalid => {
|
||||||
return Err(Error::BadRequest(
|
return Err!(BadRequest(
|
||||||
ErrorKind::MissingToken,
|
ErrorKind::MissingToken,
|
||||||
"Missing or invalid access token.",
|
"Missing or invalid access token.",
|
||||||
));
|
));
|
||||||
@@ -95,7 +96,7 @@ pub(super) async fn auth(
|
|||||||
// already
|
// already
|
||||||
},
|
},
|
||||||
| Token::None | Token::Invalid => {
|
| Token::None | Token::Invalid => {
|
||||||
return Err(Error::BadRequest(
|
return Err!(BadRequest(
|
||||||
ErrorKind::MissingToken,
|
ErrorKind::MissingToken,
|
||||||
"Missing or invalid access token.",
|
"Missing or invalid access token.",
|
||||||
));
|
));
|
||||||
@@ -129,10 +130,10 @@ pub(super) async fn auth(
|
|||||||
appservice_info: None,
|
appservice_info: None,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(Error::BadRequest(ErrorKind::MissingToken, "Missing access token."))
|
Err!(BadRequest(ErrorKind::MissingToken, "Missing access token."))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
| _ => Err(Error::BadRequest(ErrorKind::MissingToken, "Missing access token.")),
|
| _ => Err!(BadRequest(ErrorKind::MissingToken, "Missing access token.")),
|
||||||
},
|
},
|
||||||
| (
|
| (
|
||||||
AuthScheme::AccessToken | AuthScheme::AccessTokenOptional | AuthScheme::None,
|
AuthScheme::AccessToken | AuthScheme::AccessTokenOptional | AuthScheme::None,
|
||||||
@@ -148,7 +149,7 @@ pub(super) async fn auth(
|
|||||||
&ruma::api::client::session::logout::v3::Request::METADATA
|
&ruma::api::client::session::logout::v3::Request::METADATA
|
||||||
| &ruma::api::client::session::logout_all::v3::Request::METADATA
|
| &ruma::api::client::session::logout_all::v3::Request::METADATA
|
||||||
) {
|
) {
|
||||||
return Err(Error::BadRequest(
|
return Err!(BadRequest(
|
||||||
ErrorKind::UserLocked,
|
ErrorKind::UserLocked,
|
||||||
"This account has been locked.",
|
"This account has been locked.",
|
||||||
));
|
));
|
||||||
@@ -173,11 +174,11 @@ pub(super) async fn auth(
|
|||||||
appservice_info: None,
|
appservice_info: None,
|
||||||
}),
|
}),
|
||||||
| (AuthScheme::ServerSignatures, Token::Appservice(_) | Token::User(_)) =>
|
| (AuthScheme::ServerSignatures, Token::Appservice(_) | Token::User(_)) =>
|
||||||
Err(Error::BadRequest(
|
Err!(BadRequest(
|
||||||
ErrorKind::Unauthorized,
|
ErrorKind::Unauthorized,
|
||||||
"Only server signatures should be used on this endpoint.",
|
"Only server signatures should be used on this endpoint.",
|
||||||
)),
|
)),
|
||||||
| (AuthScheme::AppserviceToken, Token::User(_)) => Err(Error::BadRequest(
|
| (AuthScheme::AppserviceToken, Token::User(_)) => Err!(BadRequest(
|
||||||
ErrorKind::Unauthorized,
|
ErrorKind::Unauthorized,
|
||||||
"Only appservice access tokens should be used on this endpoint.",
|
"Only appservice access tokens should be used on this endpoint.",
|
||||||
)),
|
)),
|
||||||
@@ -195,13 +196,13 @@ pub(super) async fn auth(
|
|||||||
appservice_info: None,
|
appservice_info: None,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(Error::BadRequest(
|
Err!(BadRequest(
|
||||||
ErrorKind::UnknownToken { soft_logout: false },
|
ErrorKind::UnknownToken { soft_logout: false },
|
||||||
"Unknown access token.",
|
"Unknown access token.",
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
| (_, Token::Invalid) => Err(Error::BadRequest(
|
| (_, Token::Invalid) => Err!(BadRequest(
|
||||||
ErrorKind::UnknownToken { soft_logout: false },
|
ErrorKind::UnknownToken { soft_logout: false },
|
||||||
"Unknown access token.",
|
"Unknown access token.",
|
||||||
)),
|
)),
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
use std::{borrow::Borrow, iter::once};
|
use std::{borrow::Borrow, iter::once};
|
||||||
|
|
||||||
use axum::extract::State;
|
use axum::extract::State;
|
||||||
use conduwuit::{Err, Error, Result, info, utils::stream::ReadyExt};
|
use conduwuit::{Err, Error, Result, err, info, utils::stream::ReadyExt};
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use ruma::{
|
use ruma::{RoomId, api::federation::authorization::get_event_authorization};
|
||||||
RoomId,
|
|
||||||
api::{client::error::ErrorKind, federation::authorization::get_event_authorization},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::AccessCheck;
|
use super::AccessCheck;
|
||||||
use crate::Ruma;
|
use crate::Ruma;
|
||||||
@@ -47,7 +44,7 @@ pub(crate) async fn get_event_authorization_route(
|
|||||||
.timeline
|
.timeline
|
||||||
.get_pdu_json(&body.event_id)
|
.get_pdu_json(&body.event_id)
|
||||||
.await
|
.await
|
||||||
.map_err(|_| Error::BadRequest(ErrorKind::NotFound, "Event not found."))?;
|
.map_err(|_| err!(BadRequest(ErrorKind::NotFound, "Event not found.")))?;
|
||||||
|
|
||||||
let room_id_str = event
|
let room_id_str = event
|
||||||
.get("room_id")
|
.get("room_id")
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use axum::extract::State;
|
|||||||
use axum_client_ip::InsecureClientIp;
|
use axum_client_ip::InsecureClientIp;
|
||||||
use base64::{Engine as _, engine::general_purpose};
|
use base64::{Engine as _, engine::general_purpose};
|
||||||
use conduwuit::{
|
use conduwuit::{
|
||||||
Err, Error, PduEvent, Result, err, error,
|
Err, PduEvent, Result, err, error,
|
||||||
matrix::{Event, event::gen_event_id},
|
matrix::{Event, event::gen_event_id},
|
||||||
utils::{self, hash::sha256},
|
utils::{self, hash::sha256},
|
||||||
warn,
|
warn,
|
||||||
@@ -33,7 +33,7 @@ pub(crate) async fn create_invite_route(
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if !services.server.supported_room_version(&body.room_version) {
|
if !services.server.supported_room_version(&body.room_version) {
|
||||||
return Err(Error::BadRequest(
|
return Err!(BadRequest(
|
||||||
ErrorKind::IncompatibleRoomVersion { room_version: body.room_version.clone() },
|
ErrorKind::IncompatibleRoomVersion { room_version: body.room_version.clone() },
|
||||||
"Server does not support this room version.",
|
"Server does not support this room version.",
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::borrow::ToOwned;
|
use std::borrow::ToOwned;
|
||||||
|
|
||||||
use axum::extract::State;
|
use axum::extract::State;
|
||||||
use conduwuit::{Err, Error, Result, debug, debug_info, info, matrix::pdu::PduBuilder, warn};
|
use conduwuit::{Err, Result, debug, debug_info, info, matrix::pdu::PduBuilder, warn};
|
||||||
use conduwuit_service::Services;
|
use conduwuit_service::Services;
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
@@ -80,7 +80,7 @@ pub(crate) async fn create_join_event_template_route(
|
|||||||
|
|
||||||
let room_version_id = services.rooms.state.get_room_version(&body.room_id).await?;
|
let room_version_id = services.rooms.state.get_room_version(&body.room_id).await?;
|
||||||
if !body.ver.contains(&room_version_id) {
|
if !body.ver.contains(&room_version_id) {
|
||||||
return Err(Error::BadRequest(
|
return Err!(BadRequest(
|
||||||
ErrorKind::IncompatibleRoomVersion { room_version: room_version_id },
|
ErrorKind::IncompatibleRoomVersion { room_version: room_version_id },
|
||||||
"Room version not supported.",
|
"Room version not supported.",
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use RoomVersionId::*;
|
use RoomVersionId::*;
|
||||||
use axum::extract::State;
|
use axum::extract::State;
|
||||||
use conduwuit::{Err, Error, Result, debug_warn, info, matrix::pdu::PduBuilder, warn};
|
use conduwuit::{Err, Result, debug_warn, info, matrix::pdu::PduBuilder, warn};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
RoomVersionId,
|
RoomVersionId,
|
||||||
api::{client::error::ErrorKind, federation::knock::create_knock_event_template},
|
api::{client::error::ErrorKind, federation::knock::create_knock_event_template},
|
||||||
@@ -67,14 +67,14 @@ pub(crate) async fn create_knock_event_template_route(
|
|||||||
let room_version_id = services.rooms.state.get_room_version(&body.room_id).await?;
|
let room_version_id = services.rooms.state.get_room_version(&body.room_id).await?;
|
||||||
|
|
||||||
if matches!(room_version_id, V1 | V2 | V3 | V4 | V5 | V6) {
|
if matches!(room_version_id, V1 | V2 | V3 | V4 | V5 | V6) {
|
||||||
return Err(Error::BadRequest(
|
return Err!(BadRequest(
|
||||||
ErrorKind::IncompatibleRoomVersion { room_version: room_version_id },
|
ErrorKind::IncompatibleRoomVersion { room_version: room_version_id },
|
||||||
"Room version does not support knocking.",
|
"Room version does not support knocking.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if !body.ver.contains(&room_version_id) {
|
if !body.ver.contains(&room_version_id) {
|
||||||
return Err(Error::BadRequest(
|
return Err!(BadRequest(
|
||||||
ErrorKind::IncompatibleRoomVersion { room_version: room_version_id },
|
ErrorKind::IncompatibleRoomVersion { room_version: room_version_id },
|
||||||
"Your homeserver does not support the features required to knock on this room.",
|
"Your homeserver does not support the features required to knock on this room.",
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use axum::extract::State;
|
use axum::extract::State;
|
||||||
use axum_client_ip::InsecureClientIp;
|
use axum_client_ip::InsecureClientIp;
|
||||||
use conduwuit::{Error, Result};
|
use conduwuit::{Err, Result, err};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::{
|
api::{
|
||||||
client::error::ErrorKind,
|
client::error::ErrorKind,
|
||||||
@@ -25,7 +25,7 @@ pub(crate) async fn get_public_rooms_filtered_route(
|
|||||||
.config
|
.config
|
||||||
.allow_public_room_directory_over_federation
|
.allow_public_room_directory_over_federation
|
||||||
{
|
{
|
||||||
return Err(Error::BadRequest(ErrorKind::forbidden(), "Room directory is not public"));
|
return Err!(BadRequest(ErrorKind::forbidden(), "Room directory is not public"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let response = crate::client::get_public_rooms_filtered_helper(
|
let response = crate::client::get_public_rooms_filtered_helper(
|
||||||
@@ -38,7 +38,10 @@ pub(crate) async fn get_public_rooms_filtered_route(
|
|||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
Error::BadRequest(ErrorKind::Unknown, "Failed to return this server's public room list.")
|
err!(BadRequest(
|
||||||
|
ErrorKind::Unknown,
|
||||||
|
"Failed to return this server's public room list."
|
||||||
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(get_public_rooms_filtered::v1::Response {
|
Ok(get_public_rooms_filtered::v1::Response {
|
||||||
@@ -62,7 +65,7 @@ pub(crate) async fn get_public_rooms_route(
|
|||||||
.globals
|
.globals
|
||||||
.allow_public_room_directory_over_federation()
|
.allow_public_room_directory_over_federation()
|
||||||
{
|
{
|
||||||
return Err(Error::BadRequest(ErrorKind::forbidden(), "Room directory is not public"));
|
return Err!(BadRequest(ErrorKind::forbidden(), "Room directory is not public"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let response = crate::client::get_public_rooms_filtered_helper(
|
let response = crate::client::get_public_rooms_filtered_helper(
|
||||||
@@ -75,7 +78,10 @@ pub(crate) async fn get_public_rooms_route(
|
|||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
Error::BadRequest(ErrorKind::Unknown, "Failed to return this server's public room list.")
|
err!(BadRequest(
|
||||||
|
ErrorKind::Unknown,
|
||||||
|
"Failed to return this server's public room list."
|
||||||
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(get_public_rooms::v1::Response {
|
Ok(get_public_rooms::v1::Response {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use axum::extract::State;
|
use axum::extract::State;
|
||||||
use conduwuit::{Error, Result, err};
|
use conduwuit::{Err, Result, err};
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use get_profile_information::v1::ProfileField;
|
use get_profile_information::v1::ProfileField;
|
||||||
use rand::seq::SliceRandom;
|
use rand::seq::SliceRandom;
|
||||||
@@ -40,7 +40,7 @@ pub(crate) async fn get_room_information_route(
|
|||||||
servers.sort_unstable();
|
servers.sort_unstable();
|
||||||
servers.dedup();
|
servers.dedup();
|
||||||
|
|
||||||
servers.shuffle(&mut rand::thread_rng());
|
servers.shuffle(&mut rand::rng());
|
||||||
|
|
||||||
// insert our server as the very first choice if in list
|
// insert our server as the very first choice if in list
|
||||||
if let Some(server_index) = servers
|
if let Some(server_index) = servers
|
||||||
@@ -67,17 +67,16 @@ pub(crate) async fn get_profile_information_route(
|
|||||||
.config
|
.config
|
||||||
.allow_inbound_profile_lookup_federation_requests
|
.allow_inbound_profile_lookup_federation_requests
|
||||||
{
|
{
|
||||||
return Err(Error::BadRequest(
|
return Err!(BadRequest(
|
||||||
ErrorKind::forbidden(),
|
ErrorKind::forbidden(),
|
||||||
"Profile lookup over federation is not allowed on this homeserver.",
|
"Profile lookup over federation is not allowed on this homeserver.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if !services.globals.server_is_ours(body.user_id.server_name()) {
|
if !services.globals.server_is_ours(body.user_id.server_name()) {
|
||||||
return Err(Error::BadRequest(
|
return Err!(
|
||||||
ErrorKind::InvalidParam,
|
BadRequest(ErrorKind::InvalidParam, "User does not belong to this server.",)
|
||||||
"User does not belong to this server.",
|
);
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut displayname = None;
|
let mut displayname = None;
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ pub(crate) async fn send_transaction_message_route(
|
|||||||
);
|
);
|
||||||
for (id, result) in &results {
|
for (id, result) in &results {
|
||||||
if let Err(e) = result {
|
if let Err(e) = result {
|
||||||
if matches!(e, Error::BadRequest(ErrorKind::NotFound, _)) {
|
if matches!(e, Error::BadRequest { kind: ErrorKind::NotFound, .. }) {
|
||||||
warn!("Incoming PDU failed {id}: {e:?}");
|
warn!("Incoming PDU failed {id}: {e:?}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use axum::extract::State;
|
use axum::extract::State;
|
||||||
use conduwuit::{Error, Result};
|
use conduwuit::{Err, Result};
|
||||||
use futures::{FutureExt, StreamExt, TryFutureExt};
|
use futures::{FutureExt, StreamExt, TryFutureExt};
|
||||||
use ruma::api::{
|
use ruma::api::{
|
||||||
client::error::ErrorKind,
|
client::error::ErrorKind,
|
||||||
@@ -24,7 +24,7 @@ pub(crate) async fn get_devices_route(
|
|||||||
body: Ruma<get_devices::v1::Request>,
|
body: Ruma<get_devices::v1::Request>,
|
||||||
) -> Result<get_devices::v1::Response> {
|
) -> Result<get_devices::v1::Response> {
|
||||||
if !services.globals.user_is_local(&body.user_id) {
|
if !services.globals.user_is_local(&body.user_id) {
|
||||||
return Err(Error::BadRequest(
|
return Err!(BadRequest(
|
||||||
ErrorKind::InvalidParam,
|
ErrorKind::InvalidParam,
|
||||||
"Tried to access user from other server.",
|
"Tried to access user from other server.",
|
||||||
));
|
));
|
||||||
@@ -86,10 +86,9 @@ pub(crate) async fn get_keys_route(
|
|||||||
.iter()
|
.iter()
|
||||||
.any(|(u, _)| !services.globals.user_is_local(u))
|
.any(|(u, _)| !services.globals.user_is_local(u))
|
||||||
{
|
{
|
||||||
return Err(Error::BadRequest(
|
return Err!(
|
||||||
ErrorKind::InvalidParam,
|
BadRequest(ErrorKind::InvalidParam, "User does not belong to this server.",)
|
||||||
"User does not belong to this server.",
|
);
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = get_keys_helper(
|
let result = get_keys_helper(
|
||||||
@@ -121,7 +120,7 @@ pub(crate) async fn claim_keys_route(
|
|||||||
.iter()
|
.iter()
|
||||||
.any(|(u, _)| !services.globals.user_is_local(u))
|
.any(|(u, _)| !services.globals.user_is_local(u))
|
||||||
{
|
{
|
||||||
return Err(Error::BadRequest(
|
return Err!(BadRequest(
|
||||||
ErrorKind::InvalidParam,
|
ErrorKind::InvalidParam,
|
||||||
"Tried to access user from other server.",
|
"Tried to access user from other server.",
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use axum::extract::State;
|
use axum::extract::State;
|
||||||
use conduwuit::{Error, Result};
|
use conduwuit::{Err, Result};
|
||||||
use ruma::api::{client::error::ErrorKind, federation::discovery::discover_homeserver};
|
use ruma::api::federation::discovery::discover_homeserver;
|
||||||
|
|
||||||
use crate::Ruma;
|
use crate::Ruma;
|
||||||
|
|
||||||
@@ -14,7 +14,7 @@ pub(crate) async fn well_known_server(
|
|||||||
Ok(discover_homeserver::Response {
|
Ok(discover_homeserver::Response {
|
||||||
server: match services.server.config.well_known.server.as_ref() {
|
server: match services.server.config.well_known.server.as_ref() {
|
||||||
| Some(server_name) => server_name.to_owned(),
|
| Some(server_name) => server_name.to_owned(),
|
||||||
| None => return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")),
|
| None => return Err!(BadRequest(ErrorKind::NotFound, "Not found.")),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-1
@@ -86,6 +86,7 @@ libloading.optional = true
|
|||||||
log.workspace = true
|
log.workspace = true
|
||||||
num-traits.workspace = true
|
num-traits.workspace = true
|
||||||
rand.workspace = true
|
rand.workspace = true
|
||||||
|
rand_core = { version = "0.6.4", features = ["getrandom"] }
|
||||||
regex.workspace = true
|
regex.workspace = true
|
||||||
reqwest.workspace = true
|
reqwest.workspace = true
|
||||||
ring.workspace = true
|
ring.workspace = true
|
||||||
@@ -97,7 +98,8 @@ serde-saphyr.workspace = true
|
|||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
smallvec.workspace = true
|
smallvec.workspace = true
|
||||||
smallstr.workspace = true
|
smallstr.workspace = true
|
||||||
thiserror.workspace = true
|
snafu.workspace = true
|
||||||
|
paste.workspace = true
|
||||||
tikv-jemallocator.optional = true
|
tikv-jemallocator.optional = true
|
||||||
tikv-jemallocator.workspace = true
|
tikv-jemallocator.workspace = true
|
||||||
tikv-jemalloc-ctl.optional = true
|
tikv-jemalloc-ctl.optional = true
|
||||||
|
|||||||
+27
-18
@@ -1244,12 +1244,6 @@ pub struct Config {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub rocksdb_repair: bool,
|
pub rocksdb_repair: bool,
|
||||||
|
|
||||||
#[serde(default)]
|
|
||||||
pub rocksdb_read_only: bool,
|
|
||||||
|
|
||||||
#[serde(default)]
|
|
||||||
pub rocksdb_secondary: bool,
|
|
||||||
|
|
||||||
/// Enables idle CPU priority for compaction thread. This is not enabled by
|
/// Enables idle CPU priority for compaction thread. This is not enabled by
|
||||||
/// default to prevent compaction from falling too far behind on busy
|
/// default to prevent compaction from falling too far behind on busy
|
||||||
/// systems.
|
/// systems.
|
||||||
@@ -1309,26 +1303,33 @@ pub struct Config {
|
|||||||
|
|
||||||
/// Allow local (your server only) presence updates/requests.
|
/// Allow local (your server only) presence updates/requests.
|
||||||
///
|
///
|
||||||
/// Note that presence on continuwuity is very fast unlike Synapse's. If
|
/// Local presence must be enabled for outgoing presence to function.
|
||||||
/// using outgoing presence, this MUST be enabled.
|
///
|
||||||
|
/// Note that local presence is not as heavy on the CPU as federated
|
||||||
|
/// presence, but will still become more expensive the more local users you
|
||||||
|
/// have.
|
||||||
#[serde(default = "true_fn")]
|
#[serde(default = "true_fn")]
|
||||||
pub allow_local_presence: bool,
|
pub allow_local_presence: bool,
|
||||||
|
|
||||||
/// Allow incoming federated presence updates/requests.
|
/// Allow incoming federated presence updates.
|
||||||
///
|
///
|
||||||
/// This option receives presence updates from other servers, but does not
|
/// This option enables processing inbound presence updates from other
|
||||||
/// send any unless `allow_outgoing_presence` is true. Note that presence on
|
/// servers. Without it, remote users will appear as if they are always
|
||||||
/// continuwuity is very fast unlike Synapse's.
|
/// offline to your local users. This does not affect typing indicators or
|
||||||
|
/// read receipts.
|
||||||
#[serde(default = "true_fn")]
|
#[serde(default = "true_fn")]
|
||||||
pub allow_incoming_presence: bool,
|
pub allow_incoming_presence: bool,
|
||||||
|
|
||||||
/// Allow outgoing presence updates/requests.
|
/// Allow outgoing presence updates/requests.
|
||||||
///
|
///
|
||||||
/// This option sends presence updates to other servers, but does not
|
/// This option sends presence updates to other servers, and requires that
|
||||||
/// receive any unless `allow_incoming_presence` is true. Note that presence
|
/// `allow_local_presence` is also enabled.
|
||||||
/// on continuwuity is very fast unlike Synapse's. If using outgoing
|
///
|
||||||
/// presence, you MUST enable `allow_local_presence` as well.
|
/// Note that outgoing presence is very heavy on the CPU and network, and
|
||||||
#[serde(default = "true_fn")]
|
/// will typically cause extreme strain and slowdowns for no real benefit.
|
||||||
|
/// There are only a few clients that even implement presence, so you
|
||||||
|
/// probably don't want to enable this.
|
||||||
|
#[serde(default)]
|
||||||
pub allow_outgoing_presence: bool,
|
pub allow_outgoing_presence: bool,
|
||||||
|
|
||||||
/// How many seconds without presence updates before you become idle.
|
/// How many seconds without presence updates before you become idle.
|
||||||
@@ -1366,6 +1367,10 @@ pub struct Config {
|
|||||||
pub allow_incoming_read_receipts: bool,
|
pub allow_incoming_read_receipts: bool,
|
||||||
|
|
||||||
/// Allow sending read receipts to remote servers.
|
/// Allow sending read receipts to remote servers.
|
||||||
|
///
|
||||||
|
/// Note that sending read receipts to remote servers in large rooms with
|
||||||
|
/// lots of other homeservers may cause additional strain on the CPU and
|
||||||
|
/// network.
|
||||||
#[serde(default = "true_fn")]
|
#[serde(default = "true_fn")]
|
||||||
pub allow_outgoing_read_receipts: bool,
|
pub allow_outgoing_read_receipts: bool,
|
||||||
|
|
||||||
@@ -1377,6 +1382,10 @@ pub struct Config {
|
|||||||
pub allow_local_typing: bool,
|
pub allow_local_typing: bool,
|
||||||
|
|
||||||
/// Allow outgoing typing updates to federation.
|
/// Allow outgoing typing updates to federation.
|
||||||
|
///
|
||||||
|
/// Note that sending typing indicators to remote servers in large rooms
|
||||||
|
/// with lots of other homeservers may cause additional strain on the CPU
|
||||||
|
/// and network.
|
||||||
#[serde(default = "true_fn")]
|
#[serde(default = "true_fn")]
|
||||||
pub allow_outgoing_typing: bool,
|
pub allow_outgoing_typing: bool,
|
||||||
|
|
||||||
@@ -1516,7 +1525,7 @@ pub struct Config {
|
|||||||
/// sender user's server name, inbound federation X-Matrix origin, and
|
/// sender user's server name, inbound federation X-Matrix origin, and
|
||||||
/// outbound federation handler.
|
/// outbound federation handler.
|
||||||
///
|
///
|
||||||
/// You can set this to ["*"] to block all servers by default, and then
|
/// You can set this to [".*"] to block all servers by default, and then
|
||||||
/// use `allowed_remote_server_names` to allow only specific servers.
|
/// use `allowed_remote_server_names` to allow only specific servers.
|
||||||
///
|
///
|
||||||
/// example: ["badserver\\.tld$", "badphrase", "19dollarfortnitecards"]
|
/// example: ["badserver\\.tld$", "badphrase", "19dollarfortnitecards"]
|
||||||
|
|||||||
+129
-30
@@ -45,63 +45,162 @@ macro_rules! Err {
|
|||||||
macro_rules! err {
|
macro_rules! err {
|
||||||
(Request(Forbidden($level:ident!($($args:tt)+)))) => {{
|
(Request(Forbidden($level:ident!($($args:tt)+)))) => {{
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
$crate::error::Error::Request(
|
$crate::error::Error::Request {
|
||||||
$crate::ruma::api::client::error::ErrorKind::forbidden(),
|
kind: $crate::ruma::api::client::error::ErrorKind::forbidden(),
|
||||||
$crate::err_log!(buf, $level, $($args)+),
|
message: $crate::err_log!(buf, $level, $($args)+),
|
||||||
$crate::http::StatusCode::BAD_REQUEST
|
code: $crate::http::StatusCode::BAD_REQUEST,
|
||||||
)
|
backtrace: Some($crate::snafu::Backtrace::capture()),
|
||||||
|
}
|
||||||
}};
|
}};
|
||||||
|
|
||||||
(Request(Forbidden($($args:tt)+))) => {
|
(Request(Forbidden($($args:tt)+))) => {
|
||||||
$crate::error::Error::Request(
|
{
|
||||||
$crate::ruma::api::client::error::ErrorKind::forbidden(),
|
let message: std::borrow::Cow<'static, str> = $crate::format_maybe!($($args)+);
|
||||||
$crate::format_maybe!($($args)+),
|
$crate::error::Error::Request {
|
||||||
$crate::http::StatusCode::BAD_REQUEST
|
kind: $crate::ruma::api::client::error::ErrorKind::forbidden(),
|
||||||
)
|
message,
|
||||||
|
code: $crate::http::StatusCode::BAD_REQUEST,
|
||||||
|
backtrace: Some($crate::snafu::Backtrace::capture()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
(Request(NotFound($level:ident!($($args:tt)+)))) => {{
|
||||||
|
let mut buf = String::new();
|
||||||
|
$crate::error::Error::Request {
|
||||||
|
kind: $crate::ruma::api::client::error::ErrorKind::NotFound,
|
||||||
|
message: $crate::err_log!(buf, $level, $($args)+),
|
||||||
|
code: $crate::http::StatusCode::BAD_REQUEST,
|
||||||
|
backtrace: None,
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
|
||||||
|
(Request(NotFound($($args:tt)+))) => {
|
||||||
|
{
|
||||||
|
let message: std::borrow::Cow<'static, str> = $crate::format_maybe!($($args)+);
|
||||||
|
$crate::error::Error::Request {
|
||||||
|
kind: $crate::ruma::api::client::error::ErrorKind::NotFound,
|
||||||
|
message,
|
||||||
|
code: $crate::http::StatusCode::BAD_REQUEST,
|
||||||
|
backtrace: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
(Request($variant:ident($level:ident!($($args:tt)+)))) => {{
|
(Request($variant:ident($level:ident!($($args:tt)+)))) => {{
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
$crate::error::Error::Request(
|
$crate::error::Error::Request {
|
||||||
$crate::ruma::api::client::error::ErrorKind::$variant,
|
kind: $crate::ruma::api::client::error::ErrorKind::$variant,
|
||||||
$crate::err_log!(buf, $level, $($args)+),
|
message: $crate::err_log!(buf, $level, $($args)+),
|
||||||
$crate::http::StatusCode::BAD_REQUEST
|
code: $crate::http::StatusCode::BAD_REQUEST,
|
||||||
)
|
backtrace: Some($crate::snafu::Backtrace::capture()),
|
||||||
|
}
|
||||||
}};
|
}};
|
||||||
|
|
||||||
(Request($variant:ident($($args:tt)+))) => {
|
(Request($variant:ident($($args:tt)+))) => {
|
||||||
$crate::error::Error::Request(
|
{
|
||||||
$crate::ruma::api::client::error::ErrorKind::$variant,
|
let message: std::borrow::Cow<'static, str> = $crate::format_maybe!($($args)+);
|
||||||
$crate::format_maybe!($($args)+),
|
$crate::error::Error::Request {
|
||||||
$crate::http::StatusCode::BAD_REQUEST
|
kind: $crate::ruma::api::client::error::ErrorKind::$variant,
|
||||||
)
|
message,
|
||||||
|
code: $crate::http::StatusCode::BAD_REQUEST,
|
||||||
|
backtrace: Some($crate::snafu::Backtrace::capture()),
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
(Config($item:literal, $($args:tt)+)) => {{
|
(Config($item:literal, $($args:tt)+)) => {{
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
$crate::error::Error::Config($item, $crate::err_log!(buf, error, config = %$item, $($args)+))
|
$crate::error::ConfigSnafu {
|
||||||
|
directive: $item,
|
||||||
|
message: $crate::err_log!(buf, error, config = %$item, $($args)+),
|
||||||
|
}.build()
|
||||||
}};
|
}};
|
||||||
|
|
||||||
|
(BadRequest(ErrorKind::NotFound, $($args:tt)+)) => {
|
||||||
|
{
|
||||||
|
let message: std::borrow::Cow<'static, str> = $crate::format_maybe!($($args)+);
|
||||||
|
$crate::error::Error::Request {
|
||||||
|
kind: $crate::ruma::api::client::error::ErrorKind::NotFound,
|
||||||
|
message,
|
||||||
|
code: $crate::http::StatusCode::BAD_REQUEST,
|
||||||
|
backtrace: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
(BadRequest($kind:expr, $($args:tt)+)) => {
|
||||||
|
{
|
||||||
|
let message: std::borrow::Cow<'static, str> = $crate::format_maybe!($($args)+);
|
||||||
|
$crate::error::BadRequestSnafu {
|
||||||
|
kind: $kind,
|
||||||
|
message,
|
||||||
|
}.build()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
(FeatureDisabled($($args:tt)+)) => {
|
||||||
|
{
|
||||||
|
let feature: std::borrow::Cow<'static, str> = $crate::format_maybe!($($args)+);
|
||||||
|
$crate::error::FeatureDisabledSnafu { feature }.build()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
(Federation($server:expr, $error:expr $(,)?)) => {
|
||||||
|
{
|
||||||
|
$crate::error::FederationSnafu {
|
||||||
|
server: $server,
|
||||||
|
error: $error,
|
||||||
|
}.build()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
(InconsistentRoomState($message:expr, $room_id:expr $(,)?)) => {
|
||||||
|
{
|
||||||
|
$crate::error::InconsistentRoomStateSnafu {
|
||||||
|
message: $message,
|
||||||
|
room_id: $room_id,
|
||||||
|
}.build()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
(Uiaa($info:expr $(,)?)) => {
|
||||||
|
{
|
||||||
|
$crate::error::UiaaSnafu {
|
||||||
|
info: $info,
|
||||||
|
}.build()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
($variant:ident($level:ident!($($args:tt)+))) => {{
|
($variant:ident($level:ident!($($args:tt)+))) => {{
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
$crate::error::Error::$variant($crate::err_log!(buf, $level, $($args)+))
|
$crate::paste::paste! {
|
||||||
|
$crate::error::[<$variant Snafu>] {
|
||||||
|
message: $crate::err_log!(buf, $level, $($args)+),
|
||||||
|
}.build()
|
||||||
|
}
|
||||||
}};
|
}};
|
||||||
|
|
||||||
($variant:ident($($args:ident),+)) => {
|
|
||||||
$crate::error::Error::$variant($($args),+)
|
|
||||||
};
|
|
||||||
|
|
||||||
($variant:ident($($args:tt)+)) => {
|
($variant:ident($($args:tt)+)) => {
|
||||||
$crate::error::Error::$variant($crate::format_maybe!($($args)+))
|
$crate::paste::paste! {
|
||||||
|
{
|
||||||
|
let message: std::borrow::Cow<'static, str> = $crate::format_maybe!($($args)+);
|
||||||
|
$crate::error::[<$variant Snafu>] { message }.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
($level:ident!($($args:tt)+)) => {{
|
($level:ident!($($args:tt)+)) => {{
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
$crate::error::Error::Err($crate::err_log!(buf, $level, $($args)+))
|
let message: std::borrow::Cow<'static, str> = $crate::err_log!(buf, $level, $($args)+);
|
||||||
|
$crate::error::ErrSnafu { message }.build()
|
||||||
}};
|
}};
|
||||||
|
|
||||||
($($args:tt)+) => {
|
($($args:tt)+) => {
|
||||||
$crate::error::Error::Err($crate::format_maybe!($($args)+))
|
{
|
||||||
|
let message: std::borrow::Cow<'static, str> = $crate::format_maybe!($($args)+);
|
||||||
|
$crate::error::ErrSnafu { message }.build()
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,7 +233,7 @@ macro_rules! err_log {
|
|||||||
};
|
};
|
||||||
|
|
||||||
($crate::error::visit)(&mut $out, LEVEL, &__CALLSITE, &mut valueset_all!(__CALLSITE.metadata().fields(), $($fields)+));
|
($crate::error::visit)(&mut $out, LEVEL, &__CALLSITE, &mut valueset_all!(__CALLSITE.metadata().fields(), $($fields)+));
|
||||||
($out).into()
|
std::borrow::Cow::<'static, str>::from($out)
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+448
-139
@@ -6,151 +6,391 @@ mod serde;
|
|||||||
|
|
||||||
use std::{any::Any, borrow::Cow, convert::Infallible, sync::PoisonError};
|
use std::{any::Any, borrow::Cow, convert::Infallible, sync::PoisonError};
|
||||||
|
|
||||||
|
use snafu::{IntoError, prelude::*};
|
||||||
|
|
||||||
pub use self::{err::visit, log::*};
|
pub use self::{err::visit, log::*};
|
||||||
|
|
||||||
#[derive(thiserror::Error)]
|
#[derive(Debug, Snafu)]
|
||||||
|
#[snafu(visibility(pub))]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[error("PANIC!")]
|
#[snafu(display("PANIC!"))]
|
||||||
PanicAny(Box<dyn Any + Send>),
|
PanicAny {
|
||||||
#[error("PANIC! {0}")]
|
panic: Box<dyn Any + Send>,
|
||||||
Panic(&'static str, Box<dyn Any + Send + 'static>),
|
backtrace: snafu::Backtrace,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[snafu(display("PANIC! {message}"))]
|
||||||
|
Panic {
|
||||||
|
message: &'static str,
|
||||||
|
panic: Box<dyn Any + Send + 'static>,
|
||||||
|
backtrace: snafu::Backtrace,
|
||||||
|
},
|
||||||
|
|
||||||
// std
|
// std
|
||||||
#[error(transparent)]
|
#[snafu(display("Format error: {source}"))]
|
||||||
Fmt(#[from] std::fmt::Error),
|
Fmt {
|
||||||
#[error(transparent)]
|
source: std::fmt::Error,
|
||||||
FromUtf8(#[from] std::string::FromUtf8Error),
|
backtrace: snafu::Backtrace,
|
||||||
#[error("I/O error: {0}")]
|
},
|
||||||
Io(#[from] std::io::Error),
|
|
||||||
#[error(transparent)]
|
#[snafu(display("UTF-8 conversion error: {source}"))]
|
||||||
ParseFloat(#[from] std::num::ParseFloatError),
|
FromUtf8 {
|
||||||
#[error(transparent)]
|
source: std::string::FromUtf8Error,
|
||||||
ParseInt(#[from] std::num::ParseIntError),
|
backtrace: snafu::Backtrace,
|
||||||
#[error(transparent)]
|
},
|
||||||
Std(#[from] Box<dyn std::error::Error + Send>),
|
|
||||||
#[error(transparent)]
|
#[snafu(display("I/O error: {source}"))]
|
||||||
ThreadAccessError(#[from] std::thread::AccessError),
|
Io {
|
||||||
#[error(transparent)]
|
source: std::io::Error,
|
||||||
TryFromInt(#[from] std::num::TryFromIntError),
|
backtrace: snafu::Backtrace,
|
||||||
#[error(transparent)]
|
},
|
||||||
TryFromSlice(#[from] std::array::TryFromSliceError),
|
|
||||||
#[error(transparent)]
|
#[snafu(display("Parse float error: {source}"))]
|
||||||
Utf8(#[from] std::str::Utf8Error),
|
ParseFloat {
|
||||||
|
source: std::num::ParseFloatError,
|
||||||
|
backtrace: snafu::Backtrace,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[snafu(display("Parse int error: {source}"))]
|
||||||
|
ParseInt {
|
||||||
|
source: std::num::ParseIntError,
|
||||||
|
backtrace: snafu::Backtrace,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[snafu(display("Error: {source}"))]
|
||||||
|
Std {
|
||||||
|
source: Box<dyn std::error::Error + Send>,
|
||||||
|
backtrace: snafu::Backtrace,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[snafu(display("Thread access error: {source}"))]
|
||||||
|
ThreadAccessError {
|
||||||
|
source: std::thread::AccessError,
|
||||||
|
backtrace: snafu::Backtrace,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[snafu(display("Integer conversion error: {source}"))]
|
||||||
|
TryFromInt {
|
||||||
|
source: std::num::TryFromIntError,
|
||||||
|
backtrace: snafu::Backtrace,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[snafu(display("Slice conversion error: {source}"))]
|
||||||
|
TryFromSlice {
|
||||||
|
source: std::array::TryFromSliceError,
|
||||||
|
backtrace: snafu::Backtrace,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[snafu(display("UTF-8 error: {source}"))]
|
||||||
|
Utf8 {
|
||||||
|
source: std::str::Utf8Error,
|
||||||
|
backtrace: snafu::Backtrace,
|
||||||
|
},
|
||||||
|
|
||||||
// third-party
|
// third-party
|
||||||
#[error(transparent)]
|
#[snafu(display("Capacity error: {source}"))]
|
||||||
CapacityError(#[from] arrayvec::CapacityError),
|
CapacityError {
|
||||||
#[error(transparent)]
|
source: arrayvec::CapacityError,
|
||||||
CargoToml(#[from] cargo_toml::Error),
|
backtrace: snafu::Backtrace,
|
||||||
#[error(transparent)]
|
},
|
||||||
Clap(#[from] clap::error::Error),
|
|
||||||
#[error(transparent)]
|
#[snafu(display("Cargo.toml error: {source}"))]
|
||||||
Extension(#[from] axum::extract::rejection::ExtensionRejection),
|
CargoToml {
|
||||||
#[error(transparent)]
|
source: cargo_toml::Error,
|
||||||
Figment(#[from] figment::error::Error),
|
backtrace: snafu::Backtrace,
|
||||||
#[error(transparent)]
|
},
|
||||||
Http(#[from] http::Error),
|
|
||||||
#[error(transparent)]
|
#[snafu(display("Clap error: {source}"))]
|
||||||
HttpHeader(#[from] http::header::InvalidHeaderValue),
|
Clap {
|
||||||
#[error("Join error: {0}")]
|
source: clap::error::Error,
|
||||||
JoinError(#[from] tokio::task::JoinError),
|
backtrace: snafu::Backtrace,
|
||||||
#[error(transparent)]
|
},
|
||||||
Json(#[from] serde_json::Error),
|
|
||||||
#[error(transparent)]
|
#[snafu(display("Extension rejection: {source}"))]
|
||||||
JsParseInt(#[from] ruma::JsParseIntError), // js_int re-export
|
Extension {
|
||||||
#[error(transparent)]
|
source: axum::extract::rejection::ExtensionRejection,
|
||||||
JsTryFromInt(#[from] ruma::JsTryFromIntError), // js_int re-export
|
backtrace: snafu::Backtrace,
|
||||||
#[error(transparent)]
|
},
|
||||||
Path(#[from] axum::extract::rejection::PathRejection),
|
|
||||||
#[error("Mutex poisoned: {0}")]
|
#[snafu(display("Figment error: {source}"))]
|
||||||
Poison(Cow<'static, str>),
|
Figment {
|
||||||
#[error("Regex error: {0}")]
|
source: figment::error::Error,
|
||||||
Regex(#[from] regex::Error),
|
backtrace: snafu::Backtrace,
|
||||||
#[error("Request error: {0}")]
|
},
|
||||||
Reqwest(#[from] reqwest::Error),
|
|
||||||
#[error("{0}")]
|
#[snafu(display("HTTP error: {source}"))]
|
||||||
SerdeDe(Cow<'static, str>),
|
Http {
|
||||||
#[error("{0}")]
|
source: http::Error,
|
||||||
SerdeSer(Cow<'static, str>),
|
backtrace: snafu::Backtrace,
|
||||||
#[error(transparent)]
|
},
|
||||||
TomlDe(#[from] toml::de::Error),
|
|
||||||
#[error(transparent)]
|
#[snafu(display("Invalid HTTP header value: {source}"))]
|
||||||
TomlSer(#[from] toml::ser::Error),
|
HttpHeader {
|
||||||
#[error("Tracing filter error: {0}")]
|
source: http::header::InvalidHeaderValue,
|
||||||
TracingFilter(#[from] tracing_subscriber::filter::ParseError),
|
backtrace: snafu::Backtrace,
|
||||||
#[error("Tracing reload error: {0}")]
|
},
|
||||||
TracingReload(#[from] tracing_subscriber::reload::Error),
|
|
||||||
#[error(transparent)]
|
#[snafu(display("Join error: {source}"))]
|
||||||
TypedHeader(#[from] axum_extra::typed_header::TypedHeaderRejection),
|
JoinError {
|
||||||
#[error(transparent)]
|
source: tokio::task::JoinError,
|
||||||
YamlDe(#[from] serde_saphyr::Error),
|
backtrace: snafu::Backtrace,
|
||||||
#[error(transparent)]
|
},
|
||||||
YamlSer(#[from] serde_saphyr::ser_error::Error),
|
|
||||||
|
#[snafu(display("JSON error: {source}"))]
|
||||||
|
Json {
|
||||||
|
source: serde_json::Error,
|
||||||
|
backtrace: snafu::Backtrace,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[snafu(display("JS parse int error: {source}"))]
|
||||||
|
JsParseInt {
|
||||||
|
source: ruma::JsParseIntError,
|
||||||
|
backtrace: snafu::Backtrace,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[snafu(display("JS try from int error: {source}"))]
|
||||||
|
JsTryFromInt {
|
||||||
|
source: ruma::JsTryFromIntError,
|
||||||
|
backtrace: snafu::Backtrace,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[snafu(display("Path rejection: {source}"))]
|
||||||
|
Path {
|
||||||
|
source: axum::extract::rejection::PathRejection,
|
||||||
|
backtrace: snafu::Backtrace,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[snafu(display("Mutex poisoned: {message}"))]
|
||||||
|
Poison {
|
||||||
|
message: Cow<'static, str>,
|
||||||
|
backtrace: snafu::Backtrace,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[snafu(display("Regex error: {source}"))]
|
||||||
|
Regex {
|
||||||
|
source: regex::Error,
|
||||||
|
backtrace: snafu::Backtrace,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[snafu(display("Request error: {source}"))]
|
||||||
|
Reqwest {
|
||||||
|
source: reqwest::Error,
|
||||||
|
backtrace: snafu::Backtrace,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[snafu(display("{message}"))]
|
||||||
|
SerdeDe {
|
||||||
|
message: Cow<'static, str>,
|
||||||
|
backtrace: snafu::Backtrace,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[snafu(display("{message}"))]
|
||||||
|
SerdeSer {
|
||||||
|
message: Cow<'static, str>,
|
||||||
|
backtrace: snafu::Backtrace,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[snafu(display("TOML deserialization error: {source}"))]
|
||||||
|
TomlDe {
|
||||||
|
source: toml::de::Error,
|
||||||
|
backtrace: snafu::Backtrace,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[snafu(display("TOML serialization error: {source}"))]
|
||||||
|
TomlSer {
|
||||||
|
source: toml::ser::Error,
|
||||||
|
backtrace: snafu::Backtrace,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[snafu(display("Tracing filter error: {source}"))]
|
||||||
|
TracingFilter {
|
||||||
|
source: tracing_subscriber::filter::ParseError,
|
||||||
|
backtrace: snafu::Backtrace,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[snafu(display("Tracing reload error: {source}"))]
|
||||||
|
TracingReload {
|
||||||
|
source: tracing_subscriber::reload::Error,
|
||||||
|
backtrace: snafu::Backtrace,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[snafu(display("Typed header rejection: {source}"))]
|
||||||
|
TypedHeader {
|
||||||
|
source: axum_extra::typed_header::TypedHeaderRejection,
|
||||||
|
backtrace: snafu::Backtrace,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[snafu(display("YAML deserialization error: {source}"))]
|
||||||
|
YamlDe {
|
||||||
|
source: serde_saphyr::Error,
|
||||||
|
backtrace: snafu::Backtrace,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[snafu(display("YAML serialization error: {source}"))]
|
||||||
|
YamlSer {
|
||||||
|
source: serde_saphyr::ser_error::Error,
|
||||||
|
backtrace: snafu::Backtrace,
|
||||||
|
},
|
||||||
|
|
||||||
// ruma/conduwuit
|
// ruma/conduwuit
|
||||||
#[error("Arithmetic operation failed: {0}")]
|
#[snafu(display("Arithmetic operation failed: {message}"))]
|
||||||
Arithmetic(Cow<'static, str>),
|
Arithmetic {
|
||||||
#[error("{0}: {1}")]
|
message: Cow<'static, str>,
|
||||||
BadRequest(ruma::api::client::error::ErrorKind, &'static str), //TODO: remove
|
backtrace: snafu::Backtrace,
|
||||||
#[error("{0}")]
|
},
|
||||||
BadServerResponse(Cow<'static, str>),
|
|
||||||
#[error(transparent)]
|
#[snafu(display("{kind}: {message}"))]
|
||||||
CanonicalJson(#[from] ruma::CanonicalJsonError),
|
BadRequest {
|
||||||
#[error("There was a problem with the '{0}' directive in your configuration: {1}")]
|
kind: ruma::api::client::error::ErrorKind,
|
||||||
Config(&'static str, Cow<'static, str>),
|
message: Cow<'static, str>,
|
||||||
#[error("{0}")]
|
backtrace: snafu::Backtrace,
|
||||||
Conflict(Cow<'static, str>), // This is only needed for when a room alias already exists
|
},
|
||||||
#[error(transparent)]
|
|
||||||
ContentDisposition(#[from] ruma::http_headers::ContentDispositionParseError),
|
#[snafu(display("{message}"))]
|
||||||
#[error("{0}")]
|
BadServerResponse {
|
||||||
Database(Cow<'static, str>),
|
message: Cow<'static, str>,
|
||||||
#[error("Feature '{0}' is not available on this server.")]
|
backtrace: snafu::Backtrace,
|
||||||
FeatureDisabled(Cow<'static, str>),
|
},
|
||||||
#[error("Remote server {0} responded with: {1}")]
|
|
||||||
Federation(ruma::OwnedServerName, ruma::api::client::error::Error),
|
#[snafu(display("Canonical JSON error: {source}"))]
|
||||||
#[error("{0} in {1}")]
|
CanonicalJson {
|
||||||
InconsistentRoomState(&'static str, ruma::OwnedRoomId),
|
source: ruma::CanonicalJsonError,
|
||||||
#[error(transparent)]
|
backtrace: snafu::Backtrace,
|
||||||
IntoHttp(#[from] ruma::api::error::IntoHttpError),
|
},
|
||||||
#[error("{0}")]
|
|
||||||
Ldap(Cow<'static, str>),
|
#[snafu(display(
|
||||||
#[error(transparent)]
|
"There was a problem with the '{directive}' directive in your configuration: {message}"
|
||||||
Mxc(#[from] ruma::MxcUriError),
|
))]
|
||||||
#[error(transparent)]
|
Config {
|
||||||
Mxid(#[from] ruma::IdParseError),
|
directive: &'static str,
|
||||||
#[error("from {0}: {1}")]
|
message: Cow<'static, str>,
|
||||||
Redaction(ruma::OwnedServerName, ruma::canonical_json::RedactionError),
|
backtrace: snafu::Backtrace,
|
||||||
#[error("{0}: {1}")]
|
},
|
||||||
Request(ruma::api::client::error::ErrorKind, Cow<'static, str>, http::StatusCode),
|
|
||||||
#[error(transparent)]
|
#[snafu(display("{message}"))]
|
||||||
Ruma(#[from] ruma::api::client::error::Error),
|
Conflict {
|
||||||
#[error(transparent)]
|
message: Cow<'static, str>,
|
||||||
Signatures(#[from] ruma::signatures::Error),
|
backtrace: snafu::Backtrace,
|
||||||
#[error(transparent)]
|
},
|
||||||
StateRes(#[from] crate::state_res::Error),
|
|
||||||
#[error("uiaa")]
|
#[snafu(display("Content disposition error: {source}"))]
|
||||||
Uiaa(ruma::api::client::uiaa::UiaaInfo),
|
ContentDisposition {
|
||||||
|
source: ruma::http_headers::ContentDispositionParseError,
|
||||||
|
backtrace: snafu::Backtrace,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[snafu(display("{message}"))]
|
||||||
|
Database {
|
||||||
|
message: Cow<'static, str>,
|
||||||
|
backtrace: snafu::Backtrace,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[snafu(display("Feature '{feature}' is not available on this server."))]
|
||||||
|
FeatureDisabled {
|
||||||
|
feature: Cow<'static, str>,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[snafu(display("Remote server {server} responded with: {error}"))]
|
||||||
|
Federation {
|
||||||
|
server: ruma::OwnedServerName,
|
||||||
|
error: ruma::api::client::error::Error,
|
||||||
|
backtrace: snafu::Backtrace,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[snafu(display("{message} in {room_id}"))]
|
||||||
|
InconsistentRoomState {
|
||||||
|
message: &'static str,
|
||||||
|
room_id: ruma::OwnedRoomId,
|
||||||
|
backtrace: snafu::Backtrace,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[snafu(display("HTTP conversion error: {source}"))]
|
||||||
|
IntoHttp {
|
||||||
|
source: ruma::api::error::IntoHttpError,
|
||||||
|
backtrace: snafu::Backtrace,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[snafu(display("{message}"))]
|
||||||
|
Ldap {
|
||||||
|
message: Cow<'static, str>,
|
||||||
|
backtrace: snafu::Backtrace,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[snafu(display("MXC URI error: {source}"))]
|
||||||
|
Mxc {
|
||||||
|
source: ruma::MxcUriError,
|
||||||
|
backtrace: snafu::Backtrace,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[snafu(display("Matrix ID parse error: {source}"))]
|
||||||
|
Mxid {
|
||||||
|
source: ruma::IdParseError,
|
||||||
|
backtrace: snafu::Backtrace,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[snafu(display("from {server}: {error}"))]
|
||||||
|
Redaction {
|
||||||
|
server: ruma::OwnedServerName,
|
||||||
|
error: ruma::canonical_json::RedactionError,
|
||||||
|
backtrace: snafu::Backtrace,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[snafu(display("{kind}: {message}"))]
|
||||||
|
Request {
|
||||||
|
kind: ruma::api::client::error::ErrorKind,
|
||||||
|
message: Cow<'static, str>,
|
||||||
|
code: http::StatusCode,
|
||||||
|
backtrace: Option<snafu::Backtrace>,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[snafu(display("Ruma error: {source}"))]
|
||||||
|
Ruma {
|
||||||
|
source: ruma::api::client::error::Error,
|
||||||
|
backtrace: snafu::Backtrace,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[snafu(display("Signature error: {source}"))]
|
||||||
|
Signatures {
|
||||||
|
source: ruma::signatures::Error,
|
||||||
|
backtrace: snafu::Backtrace,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[snafu(display("State resolution error: {source}"))]
|
||||||
|
#[snafu(context(false))]
|
||||||
|
StateRes {
|
||||||
|
source: crate::state_res::Error,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[snafu(display("uiaa"))]
|
||||||
|
Uiaa {
|
||||||
|
info: ruma::api::client::uiaa::UiaaInfo,
|
||||||
|
},
|
||||||
|
|
||||||
// unique / untyped
|
// unique / untyped
|
||||||
#[error("{0}")]
|
#[snafu(display("{message}"))]
|
||||||
Err(Cow<'static, str>),
|
Err {
|
||||||
|
message: Cow<'static, str>,
|
||||||
|
backtrace: snafu::Backtrace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error {
|
impl Error {
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn from_errno() -> Self { Self::Io(std::io::Error::last_os_error()) }
|
pub fn from_errno() -> Self { IoSnafu {}.into_error(std::io::Error::last_os_error()) }
|
||||||
|
|
||||||
//#[deprecated]
|
//#[deprecated]
|
||||||
|
#[must_use]
|
||||||
pub fn bad_database(message: &'static str) -> Self {
|
pub fn bad_database(message: &'static str) -> Self {
|
||||||
crate::err!(Database(error!("{message}")))
|
let message: Cow<'static, str> = message.into();
|
||||||
|
DatabaseSnafu { message }.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sanitizes public-facing errors that can leak sensitive information.
|
/// Sanitizes public-facing errors that can leak sensitive information.
|
||||||
pub fn sanitized_message(&self) -> String {
|
pub fn sanitized_message(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
| Self::Database(..) => String::from("Database error occurred."),
|
| Self::Database { .. } => String::from("Database error occurred."),
|
||||||
| Self::Io(..) => String::from("I/O error occurred."),
|
| Self::Io { .. } => String::from("I/O error occurred."),
|
||||||
| _ => self.message(),
|
| _ => self.message(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -158,8 +398,8 @@ impl Error {
|
|||||||
/// Generate the error message string.
|
/// Generate the error message string.
|
||||||
pub fn message(&self) -> String {
|
pub fn message(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
| Self::Federation(origin, error) => format!("Answer from {origin}: {error}"),
|
| Self::Federation { server, error, .. } => format!("Answer from {server}: {error}"),
|
||||||
| Self::Ruma(error) => response::ruma_error_message(error),
|
| Self::Ruma { source, .. } => response::ruma_error_message(source),
|
||||||
| _ => format!("{self}"),
|
| _ => format!("{self}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -170,10 +410,10 @@ impl Error {
|
|||||||
use ruma::api::client::error::ErrorKind::{FeatureDisabled, Unknown};
|
use ruma::api::client::error::ErrorKind::{FeatureDisabled, Unknown};
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
| Self::Federation(_, error) | Self::Ruma(error) =>
|
| Self::Federation { error, .. } => response::ruma_error_kind(error).clone(),
|
||||||
response::ruma_error_kind(error).clone(),
|
| Self::Ruma { source, .. } => response::ruma_error_kind(source).clone(),
|
||||||
| Self::BadRequest(kind, ..) | Self::Request(kind, ..) => kind.clone(),
|
| Self::BadRequest { kind, .. } | Self::Request { kind, .. } => kind.clone(),
|
||||||
| Self::FeatureDisabled(..) => FeatureDisabled,
|
| Self::FeatureDisabled { .. } => FeatureDisabled,
|
||||||
| _ => Unknown,
|
| _ => Unknown,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -184,13 +424,15 @@ impl Error {
|
|||||||
use http::StatusCode;
|
use http::StatusCode;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
| Self::Federation(_, error) | Self::Ruma(error) => error.status_code,
|
| Self::Federation { error, .. } => error.status_code,
|
||||||
| Self::Request(kind, _, code) => response::status_code(kind, *code),
|
| Self::Ruma { source, .. } => source.status_code,
|
||||||
| Self::BadRequest(kind, ..) => response::bad_request_code(kind),
|
| Self::Request { kind, code, .. } => response::status_code(kind, *code),
|
||||||
| Self::FeatureDisabled(..) => response::bad_request_code(&self.kind()),
|
| Self::BadRequest { kind, .. } => response::bad_request_code(kind),
|
||||||
| Self::Reqwest(error) => error.status().unwrap_or(StatusCode::INTERNAL_SERVER_ERROR),
|
| Self::FeatureDisabled { .. } => response::bad_request_code(&self.kind()),
|
||||||
| Self::Conflict(_) => StatusCode::CONFLICT,
|
| Self::Reqwest { source, .. } =>
|
||||||
| Self::Io(error) => response::io_error_code(error.kind()),
|
source.status().unwrap_or(StatusCode::INTERNAL_SERVER_ERROR),
|
||||||
|
| Self::Conflict { .. } => StatusCode::CONFLICT,
|
||||||
|
| Self::Io { source, .. } => response::io_error_code(source.kind()),
|
||||||
| _ => StatusCode::INTERNAL_SERVER_ERROR,
|
| _ => StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -203,16 +445,46 @@ impl Error {
|
|||||||
pub fn is_not_found(&self) -> bool { self.status_code() == http::StatusCode::NOT_FOUND }
|
pub fn is_not_found(&self) -> bool { self.status_code() == http::StatusCode::NOT_FOUND }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for Error {
|
// Debug is already derived by Snafu
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(f, "{}", self.message())
|
/// Macro to reduce boilerplate for From implementations using Snafu context
|
||||||
}
|
macro_rules! impl_from_snafu {
|
||||||
|
($source_ty:ty => $context:ident) => {
|
||||||
|
impl From<$source_ty> for Error {
|
||||||
|
fn from(source: $source_ty) -> Self { $context.into_error(source) }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Macro for From impls that format messages into ErrSnafu or other
|
||||||
|
/// message-based contexts
|
||||||
|
macro_rules! impl_from_message {
|
||||||
|
($source_ty:ty => $context:ident, $msg:expr) => {
|
||||||
|
impl From<$source_ty> for Error {
|
||||||
|
fn from(source: $source_ty) -> Self {
|
||||||
|
let message: Cow<'static, str> = format!($msg, source).into();
|
||||||
|
$context { message }.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Macro for From impls with constant messages (no formatting)
|
||||||
|
macro_rules! impl_from_const_message {
|
||||||
|
($source_ty:ty => $context:ident, $msg:expr) => {
|
||||||
|
impl From<$source_ty> for Error {
|
||||||
|
fn from(_source: $source_ty) -> Self {
|
||||||
|
let message: Cow<'static, str> = $msg.into();
|
||||||
|
$context { message }.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> From<PoisonError<T>> for Error {
|
impl<T> From<PoisonError<T>> for Error {
|
||||||
#[cold]
|
#[cold]
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
fn from(e: PoisonError<T>) -> Self { Self::Poison(e.to_string().into()) }
|
fn from(e: PoisonError<T>) -> Self { PoisonSnafu { message: e.to_string() }.build() }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::fallible_impl_from)]
|
#[allow(clippy::fallible_impl_from)]
|
||||||
@@ -224,6 +496,43 @@ impl From<Infallible> for Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Implementations using the macro
|
||||||
|
impl_from_snafu!(std::io::Error => IoSnafu);
|
||||||
|
impl_from_snafu!(std::string::FromUtf8Error => FromUtf8Snafu);
|
||||||
|
impl_from_snafu!(regex::Error => RegexSnafu);
|
||||||
|
impl_from_snafu!(ruma::http_headers::ContentDispositionParseError => ContentDispositionSnafu);
|
||||||
|
impl_from_snafu!(ruma::api::error::IntoHttpError => IntoHttpSnafu);
|
||||||
|
impl_from_snafu!(ruma::JsTryFromIntError => JsTryFromIntSnafu);
|
||||||
|
impl_from_snafu!(ruma::CanonicalJsonError => CanonicalJsonSnafu);
|
||||||
|
impl_from_snafu!(axum::extract::rejection::PathRejection => PathSnafu);
|
||||||
|
impl_from_snafu!(clap::error::Error => ClapSnafu);
|
||||||
|
impl_from_snafu!(ruma::MxcUriError => MxcSnafu);
|
||||||
|
impl_from_snafu!(serde_saphyr::ser_error::Error => YamlSerSnafu);
|
||||||
|
impl_from_snafu!(toml::de::Error => TomlDeSnafu);
|
||||||
|
impl_from_snafu!(http::header::InvalidHeaderValue => HttpHeaderSnafu);
|
||||||
|
impl_from_snafu!(serde_json::Error => JsonSnafu);
|
||||||
|
|
||||||
|
// Custom implementations using message formatting
|
||||||
|
impl_from_const_message!(std::fmt::Error => ErrSnafu, "formatting error");
|
||||||
|
impl_from_message!(std::str::Utf8Error => ErrSnafu, "UTF-8 error: {}");
|
||||||
|
impl_from_message!(std::num::TryFromIntError => ArithmeticSnafu, "integer conversion error: {}");
|
||||||
|
impl_from_message!(tracing_subscriber::reload::Error => ErrSnafu, "tracing reload error: {}");
|
||||||
|
impl_from_message!(reqwest::Error => ErrSnafu, "HTTP client error: {}");
|
||||||
|
impl_from_message!(ruma::signatures::Error => ErrSnafu, "Signature error: {}");
|
||||||
|
impl_from_message!(ruma::IdParseError => ErrSnafu, "ID parse error: {}");
|
||||||
|
impl_from_message!(std::num::ParseIntError => ErrSnafu, "Integer parse error: {}");
|
||||||
|
impl_from_message!(std::array::TryFromSliceError => ErrSnafu, "Slice conversion error: {}");
|
||||||
|
impl_from_message!(tokio::task::JoinError => ErrSnafu, "Task join error: {}");
|
||||||
|
impl_from_message!(serde_saphyr::Error => ErrSnafu, "YAML error: {}");
|
||||||
|
|
||||||
|
// Generic implementation for CapacityError
|
||||||
|
impl<T> From<arrayvec::CapacityError<T>> for Error {
|
||||||
|
fn from(_source: arrayvec::CapacityError<T>) -> Self {
|
||||||
|
let message: Cow<'static, str> = "capacity error: buffer is full".into();
|
||||||
|
ErrSnafu { message }.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cold]
|
#[cold]
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
pub fn infallible(_e: &Infallible) {
|
pub fn infallible(_e: &Infallible) {
|
||||||
|
|||||||
@@ -15,13 +15,16 @@ impl Error {
|
|||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_panic(e: Box<dyn Any + Send>) -> Self { Self::Panic(debug::panic_str(&e), e) }
|
pub fn from_panic(e: Box<dyn Any + Send>) -> Self {
|
||||||
|
use super::PanicSnafu;
|
||||||
|
PanicSnafu { message: debug::panic_str(&e), panic: e }.build()
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn into_panic(self) -> Box<dyn Any + Send + 'static> {
|
pub fn into_panic(self) -> Box<dyn Any + Send + 'static> {
|
||||||
match self {
|
match self {
|
||||||
| Self::Panic(_, e) | Self::PanicAny(e) => e,
|
| Self::Panic { panic, .. } | Self::PanicAny { panic, .. } => panic,
|
||||||
| Self::JoinError(e) => e.into_panic(),
|
| Self::JoinError { source, .. } => source.into_panic(),
|
||||||
| _ => Box::new(self),
|
| _ => Box::new(self),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -37,8 +40,8 @@ impl Error {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_panic(&self) -> bool {
|
pub fn is_panic(&self) -> bool {
|
||||||
match &self {
|
match &self {
|
||||||
| Self::Panic(..) | Self::PanicAny(..) => true,
|
| Self::Panic { .. } | Self::PanicAny { .. } => true,
|
||||||
| Self::JoinError(e) => e.is_panic(),
|
| Self::JoinError { source, .. } => source.is_panic(),
|
||||||
| _ => false,
|
| _ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,8 +47,8 @@ impl axum::response::IntoResponse for Error {
|
|||||||
impl From<Error> for UiaaResponse {
|
impl From<Error> for UiaaResponse {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(error: Error) -> Self {
|
fn from(error: Error) -> Self {
|
||||||
if let Error::Uiaa(uiaainfo) = error {
|
if let Error::Uiaa { info, .. } = error {
|
||||||
return Self::AuthResponse(uiaainfo);
|
return Self::AuthResponse(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
let body = ErrorBody::Standard {
|
let body = ErrorBody::Standard {
|
||||||
|
|||||||
@@ -5,9 +5,15 @@ use serde::{de, ser};
|
|||||||
use crate::Error;
|
use crate::Error;
|
||||||
|
|
||||||
impl de::Error for Error {
|
impl de::Error for Error {
|
||||||
fn custom<T: Display + ToString>(msg: T) -> Self { Self::SerdeDe(msg.to_string().into()) }
|
fn custom<T: Display + ToString>(msg: T) -> Self {
|
||||||
|
let message: std::borrow::Cow<'static, str> = msg.to_string().into();
|
||||||
|
super::SerdeDeSnafu { message }.build()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ser::Error for Error {
|
impl ser::Error for Error {
|
||||||
fn custom<T: Display + ToString>(msg: T) -> Self { Self::SerdeSer(msg.to_string().into()) }
|
fn custom<T: Display + ToString>(msg: T) -> Self {
|
||||||
|
let message: std::borrow::Cow<'static, str> = msg.to_string().into();
|
||||||
|
super::SerdeSerSnafu { message }.build()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use ruma::{RoomVersionId, canonical_json::redact_content_in_place};
|
use ruma::{RoomVersionId, canonical_json::redact_content_in_place};
|
||||||
use serde_json::{Value as JsonValue, json, value::to_raw_value};
|
use serde_json::{Value as JsonValue, json, value::to_raw_value};
|
||||||
|
|
||||||
use crate::{Error, Result, err, implement};
|
use crate::{Result, err, implement};
|
||||||
|
|
||||||
#[implement(super::Pdu)]
|
#[implement(super::Pdu)]
|
||||||
pub fn redact(&mut self, room_version_id: &RoomVersionId, reason: JsonValue) -> Result {
|
pub fn redact(&mut self, room_version_id: &RoomVersionId, reason: JsonValue) -> Result {
|
||||||
@@ -10,8 +10,15 @@ pub fn redact(&mut self, room_version_id: &RoomVersionId, reason: JsonValue) ->
|
|||||||
let mut content = serde_json::from_str(self.content.get())
|
let mut content = serde_json::from_str(self.content.get())
|
||||||
.map_err(|e| err!(Request(BadJson("Failed to deserialize content into type: {e}"))))?;
|
.map_err(|e| err!(Request(BadJson("Failed to deserialize content into type: {e}"))))?;
|
||||||
|
|
||||||
redact_content_in_place(&mut content, room_version_id, self.kind.to_string())
|
redact_content_in_place(&mut content, room_version_id, self.kind.to_string()).map_err(
|
||||||
.map_err(|e| Error::Redaction(self.sender.server_name().to_owned(), e))?;
|
|error| {
|
||||||
|
crate::error::RedactionSnafu {
|
||||||
|
server: self.sender.server_name().to_owned(),
|
||||||
|
error,
|
||||||
|
}
|
||||||
|
.build()
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
let reason = serde_json::to_value(reason).expect("Failed to preserialize reason");
|
let reason = serde_json::to_value(reason).expect("Failed to preserialize reason");
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ use serde_json::{
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
matrix::{Event, Pdu, pdu::EventHash},
|
matrix::{Event, Pdu, pdu::EventHash},
|
||||||
state_res::{self as state_res, Error, Result, StateMap},
|
state_res::{self as state_res, Error, Result, StateMap, error::NotFoundSnafu},
|
||||||
};
|
};
|
||||||
|
|
||||||
static SERVER_TIMESTAMP: AtomicU64 = AtomicU64::new(0);
|
static SERVER_TIMESTAMP: AtomicU64 = AtomicU64::new(0);
|
||||||
@@ -170,10 +170,12 @@ struct TestStore<E: Event>(HashMap<OwnedEventId, E>);
|
|||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
impl<E: Event + Clone> TestStore<E> {
|
impl<E: Event + Clone> TestStore<E> {
|
||||||
fn get_event(&self, room_id: &RoomId, event_id: &EventId) -> Result<E> {
|
fn get_event(&self, room_id: &RoomId, event_id: &EventId) -> Result<E> {
|
||||||
self.0
|
self.0.get(event_id).cloned().ok_or_else(|| {
|
||||||
.get(event_id)
|
NotFoundSnafu {
|
||||||
.cloned()
|
message: format!("{} not found", event_id),
|
||||||
.ok_or_else(|| Error::NotFound(format!("{} not found", event_id)))
|
}
|
||||||
|
.build()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the events that correspond to the `event_ids` sorted in the same
|
/// Returns the events that correspond to the `event_ids` sorted in the same
|
||||||
|
|||||||
@@ -1,23 +1,40 @@
|
|||||||
use serde_json::Error as JsonError;
|
use serde_json::Error as JsonError;
|
||||||
use thiserror::Error;
|
use snafu::{IntoError, prelude::*};
|
||||||
|
|
||||||
/// Represents the various errors that arise when resolving state.
|
/// Represents the various errors that arise when resolving state.
|
||||||
#[derive(Error, Debug)]
|
#[derive(Debug, Snafu)]
|
||||||
|
#[snafu(visibility(pub))]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// A deserialization error.
|
/// A deserialization error.
|
||||||
#[error(transparent)]
|
#[snafu(display("JSON error: {source}"))]
|
||||||
SerdeJson(#[from] JsonError),
|
SerdeJson {
|
||||||
|
source: JsonError,
|
||||||
|
backtrace: snafu::Backtrace,
|
||||||
|
},
|
||||||
|
|
||||||
/// The given option or version is unsupported.
|
/// The given option or version is unsupported.
|
||||||
#[error("Unsupported room version: {0}")]
|
#[snafu(display("Unsupported room version: {version}"))]
|
||||||
Unsupported(String),
|
Unsupported {
|
||||||
|
version: String,
|
||||||
|
backtrace: snafu::Backtrace,
|
||||||
|
},
|
||||||
|
|
||||||
/// The given event was not found.
|
/// The given event was not found.
|
||||||
#[error("Not found error: {0}")]
|
#[snafu(display("Not found error: {message}"))]
|
||||||
NotFound(String),
|
NotFound {
|
||||||
|
message: String,
|
||||||
|
backtrace: snafu::Backtrace,
|
||||||
|
},
|
||||||
|
|
||||||
/// Invalid fields in the given PDU.
|
/// Invalid fields in the given PDU.
|
||||||
#[error("Invalid PDU: {0}")]
|
#[snafu(display("Invalid PDU: {message}"))]
|
||||||
InvalidPdu(String),
|
InvalidPdu {
|
||||||
|
message: String,
|
||||||
|
backtrace: snafu::Backtrace,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<serde_json::Error> for Error {
|
||||||
|
fn from(source: serde_json::Error) -> Self { SerdeJsonSnafu.into_error(source) }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ use serde_json::{from_str as from_json_str, value::RawValue as RawJsonValue};
|
|||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
Error, Event, Result, StateEventType, StateKey, TimelineEventType,
|
Error, Event, Result, StateEventType, StateKey, TimelineEventType,
|
||||||
|
error::InvalidPduSnafu,
|
||||||
power_levels::{
|
power_levels::{
|
||||||
deserialize_power_levels, deserialize_power_levels_content_fields,
|
deserialize_power_levels, deserialize_power_levels_content_fields,
|
||||||
deserialize_power_levels_content_invite, deserialize_power_levels_content_redact,
|
deserialize_power_levels_content_invite, deserialize_power_levels_content_redact,
|
||||||
@@ -383,8 +384,8 @@ where
|
|||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
let target_user =
|
let target_user = <&UserId>::try_from(state_key)
|
||||||
<&UserId>::try_from(state_key).map_err(|e| Error::InvalidPdu(format!("{e}")))?;
|
.map_err(|e| InvalidPduSnafu { message: format!("{e}") }.build())?;
|
||||||
|
|
||||||
let user_for_join_auth = content
|
let user_for_join_auth = content
|
||||||
.join_authorised_via_users_server
|
.join_authorised_via_users_server
|
||||||
@@ -461,7 +462,7 @@ where
|
|||||||
?sender_membership_event_content,
|
?sender_membership_event_content,
|
||||||
"Sender membership event content missing membership field"
|
"Sender membership event content missing membership field"
|
||||||
);
|
);
|
||||||
return Err(Error::InvalidPdu("Missing membership field".to_owned()));
|
return Err(InvalidPduSnafu { message: "Missing membership field" }.build());
|
||||||
};
|
};
|
||||||
let membership_state = membership_state.deserialize()?;
|
let membership_state = membership_state.deserialize()?;
|
||||||
|
|
||||||
|
|||||||
@@ -29,18 +29,18 @@ use ruma::{
|
|||||||
};
|
};
|
||||||
use serde_json::from_str as from_json_str;
|
use serde_json::from_str as from_json_str;
|
||||||
|
|
||||||
pub(crate) use self::error::Error;
|
pub(crate) use self::error::{Error, InvalidPduSnafu, NotFoundSnafu};
|
||||||
use self::power_levels::PowerLevelsContentFields;
|
use self::power_levels::PowerLevelsContentFields;
|
||||||
pub use self::{
|
pub use self::{
|
||||||
event_auth::{auth_check, auth_types_for_event},
|
event_auth::{auth_check, auth_types_for_event},
|
||||||
room_version::RoomVersion,
|
room_version::RoomVersion,
|
||||||
};
|
};
|
||||||
|
use super::{Event, StateKey};
|
||||||
use crate::{
|
use crate::{
|
||||||
debug, debug_error, err,
|
debug, debug_error,
|
||||||
matrix::{Event, StateKey},
|
|
||||||
state_res::room_version::StateResolutionVersion,
|
state_res::room_version::StateResolutionVersion,
|
||||||
trace,
|
trace,
|
||||||
utils::stream::{BroadbandExt, IterStream, ReadyExt, TryBroadbandExt, WidebandExt},
|
utils::stream::{BroadbandExt, IterStream, ReadyExt, TryBroadbandExt},
|
||||||
warn,
|
warn,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -118,7 +118,10 @@ where
|
|||||||
let csg = calculate_conflicted_subgraph(&conflicting, event_fetch)
|
let csg = calculate_conflicted_subgraph(&conflicting, event_fetch)
|
||||||
.await
|
.await
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
Error::InvalidPdu("Failed to calculate conflicted subgraph".to_owned())
|
InvalidPduSnafu {
|
||||||
|
message: "Failed to calculate conflicted subgraph",
|
||||||
|
}
|
||||||
|
.build()
|
||||||
})?;
|
})?;
|
||||||
debug!(count = csg.len(), "conflicted subgraph");
|
debug!(count = csg.len(), "conflicted subgraph");
|
||||||
trace!(set = ?csg, "conflicted subgraph");
|
trace!(set = ?csg, "conflicted subgraph");
|
||||||
@@ -149,10 +152,11 @@ where
|
|||||||
let control_events: Vec<_> = all_conflicted
|
let control_events: Vec<_> = all_conflicted
|
||||||
.iter()
|
.iter()
|
||||||
.stream()
|
.stream()
|
||||||
.wide_filter_map(async |id| {
|
.broad_filter_map(async |id| {
|
||||||
is_power_event_id(id, &event_fetch)
|
event_fetch(id.clone())
|
||||||
.await
|
.await
|
||||||
.then_some(id.clone())
|
.filter(|event| is_power_event(&event))
|
||||||
|
.map(|_| id.clone())
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
.await;
|
.await;
|
||||||
@@ -314,7 +318,10 @@ where
|
|||||||
trace!(event_id = event_id.as_str(), "fetching event for its auth events");
|
trace!(event_id = event_id.as_str(), "fetching event for its auth events");
|
||||||
let evt = fetch_event(event_id.clone()).await;
|
let evt = fetch_event(event_id.clone()).await;
|
||||||
if evt.is_none() {
|
if evt.is_none() {
|
||||||
err!("could not fetch event {} to calculate conflicted subgraph", event_id);
|
tracing::error!(
|
||||||
|
"could not fetch event {} to calculate conflicted subgraph",
|
||||||
|
event_id
|
||||||
|
);
|
||||||
path.pop();
|
path.pop();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -402,11 +409,11 @@ where
|
|||||||
let fetcher = async |event_id: OwnedEventId| {
|
let fetcher = async |event_id: OwnedEventId| {
|
||||||
let pl = *event_to_pl
|
let pl = *event_to_pl
|
||||||
.get(&event_id)
|
.get(&event_id)
|
||||||
.ok_or_else(|| Error::NotFound(String::new()))?;
|
.ok_or_else(|| NotFoundSnafu { message: "" }.build())?;
|
||||||
|
|
||||||
let ev = fetch_event(event_id)
|
let ev = fetch_event(event_id)
|
||||||
.await
|
.await
|
||||||
.ok_or_else(|| Error::NotFound(String::new()))?;
|
.ok_or_else(|| NotFoundSnafu { message: "" }.build())?;
|
||||||
|
|
||||||
Ok((pl, ev.origin_server_ts()))
|
Ok((pl, ev.origin_server_ts()))
|
||||||
};
|
};
|
||||||
@@ -612,9 +619,12 @@ where
|
|||||||
let events_to_check: Vec<_> = events_to_check
|
let events_to_check: Vec<_> = events_to_check
|
||||||
.map(Result::Ok)
|
.map(Result::Ok)
|
||||||
.broad_and_then(async |event_id| {
|
.broad_and_then(async |event_id| {
|
||||||
fetch_event(event_id.to_owned())
|
fetch_event(event_id.to_owned()).await.ok_or_else(|| {
|
||||||
.await
|
NotFoundSnafu {
|
||||||
.ok_or_else(|| Error::NotFound(format!("Failed to find {event_id}")))
|
message: format!("Failed to find {event_id}"),
|
||||||
|
}
|
||||||
|
.build()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
.try_collect()
|
.try_collect()
|
||||||
.boxed()
|
.boxed()
|
||||||
@@ -653,7 +663,7 @@ where
|
|||||||
trace!(event_id = event.event_id().as_str(), "checking event");
|
trace!(event_id = event.event_id().as_str(), "checking event");
|
||||||
let state_key = event
|
let state_key = event
|
||||||
.state_key()
|
.state_key()
|
||||||
.ok_or_else(|| Error::InvalidPdu("State event had no state key".to_owned()))?;
|
.ok_or_else(|| InvalidPduSnafu { message: "State event had no state key" }.build())?;
|
||||||
|
|
||||||
let auth_types = auth_types_for_event(
|
let auth_types = auth_types_for_event(
|
||||||
event.event_type(),
|
event.event_type(),
|
||||||
@@ -669,13 +679,14 @@ where
|
|||||||
trace!("room version uses hashed IDs, manually fetching create event");
|
trace!("room version uses hashed IDs, manually fetching create event");
|
||||||
let create_event_id_raw = event.room_id_or_hash().as_str().replace('!', "$");
|
let create_event_id_raw = event.room_id_or_hash().as_str().replace('!', "$");
|
||||||
let create_event_id = EventId::parse(&create_event_id_raw).map_err(|e| {
|
let create_event_id = EventId::parse(&create_event_id_raw).map_err(|e| {
|
||||||
Error::InvalidPdu(format!(
|
InvalidPduSnafu {
|
||||||
"Failed to parse create event ID from room ID/hash: {e}"
|
message: format!("Failed to parse create event ID from room ID/hash: {e}"),
|
||||||
))
|
}
|
||||||
|
.build()
|
||||||
|
})?;
|
||||||
|
let create_event = fetch_event(create_event_id.into()).await.ok_or_else(|| {
|
||||||
|
NotFoundSnafu { message: "Failed to find create event" }.build()
|
||||||
})?;
|
})?;
|
||||||
let create_event = fetch_event(create_event_id.into())
|
|
||||||
.await
|
|
||||||
.ok_or_else(|| Error::NotFound("Failed to find create event".into()))?;
|
|
||||||
auth_state.insert(create_event.event_type().with_state_key(""), create_event);
|
auth_state.insert(create_event.event_type().with_state_key(""), create_event);
|
||||||
}
|
}
|
||||||
for aid in event.auth_events() {
|
for aid in event.auth_events() {
|
||||||
@@ -686,7 +697,7 @@ where
|
|||||||
auth_state.insert(
|
auth_state.insert(
|
||||||
ev.event_type()
|
ev.event_type()
|
||||||
.with_state_key(ev.state_key().ok_or_else(|| {
|
.with_state_key(ev.state_key().ok_or_else(|| {
|
||||||
Error::InvalidPdu("State event had no state key".to_owned())
|
InvalidPduSnafu { message: "State event had no state key" }.build()
|
||||||
})?),
|
})?),
|
||||||
ev.clone(),
|
ev.clone(),
|
||||||
);
|
);
|
||||||
@@ -801,13 +812,13 @@ where
|
|||||||
|
|
||||||
let event = fetch_event(p.clone())
|
let event = fetch_event(p.clone())
|
||||||
.await
|
.await
|
||||||
.ok_or_else(|| Error::NotFound(format!("Failed to find {p}")))?;
|
.ok_or_else(|| NotFoundSnafu { message: format!("Failed to find {p}") }.build())?;
|
||||||
|
|
||||||
pl = None;
|
pl = None;
|
||||||
for aid in event.auth_events() {
|
for aid in event.auth_events() {
|
||||||
let ev = fetch_event(aid.to_owned())
|
let ev = fetch_event(aid.to_owned()).await.ok_or_else(|| {
|
||||||
.await
|
NotFoundSnafu { message: format!("Failed to find {aid}") }.build()
|
||||||
.ok_or_else(|| Error::NotFound(format!("Failed to find {aid}")))?;
|
})?;
|
||||||
|
|
||||||
if is_type_and_key(&ev, &TimelineEventType::RoomPowerLevels, "") {
|
if is_type_and_key(&ev, &TimelineEventType::RoomPowerLevels, "") {
|
||||||
pl = Some(aid.to_owned());
|
pl = Some(aid.to_owned());
|
||||||
@@ -869,9 +880,9 @@ where
|
|||||||
|
|
||||||
event = None;
|
event = None;
|
||||||
for aid in sort_ev.auth_events() {
|
for aid in sort_ev.auth_events() {
|
||||||
let aev = fetch_event(aid.to_owned())
|
let aev = fetch_event(aid.to_owned()).await.ok_or_else(|| {
|
||||||
.await
|
NotFoundSnafu { message: format!("Failed to find {aid}") }.build()
|
||||||
.ok_or_else(|| Error::NotFound(format!("Failed to find {aid}")))?;
|
})?;
|
||||||
|
|
||||||
if is_type_and_key(&aev, &TimelineEventType::RoomPowerLevels, "") {
|
if is_type_and_key(&aev, &TimelineEventType::RoomPowerLevels, "") {
|
||||||
event = Some(aev);
|
event = Some(aev);
|
||||||
@@ -915,6 +926,7 @@ async fn add_event_and_auth_chain_to_graph<E, F, Fut>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
async fn is_power_event_id<E, F, Fut>(event_id: &EventId, fetch: &F) -> bool
|
async fn is_power_event_id<E, F, Fut>(event_id: &EventId, fetch: &F) -> bool
|
||||||
where
|
where
|
||||||
F: Fn(OwnedEventId) -> Fut + Sync,
|
F: Fn(OwnedEventId) -> Fut + Sync,
|
||||||
@@ -1046,7 +1058,7 @@ mod tests {
|
|||||||
// don't remove any events so we know it sorts them all correctly
|
// don't remove any events so we know it sorts them all correctly
|
||||||
let mut events_to_sort = events.keys().cloned().collect::<Vec<_>>();
|
let mut events_to_sort = events.keys().cloned().collect::<Vec<_>>();
|
||||||
|
|
||||||
events_to_sort.shuffle(&mut rand::thread_rng());
|
events_to_sort.shuffle(&mut rand::rng());
|
||||||
|
|
||||||
let power_level = resolved_power
|
let power_level = resolved_power
|
||||||
.get(&(StateEventType::RoomPowerLevels, "".into()))
|
.get(&(StateEventType::RoomPowerLevels, "".into()))
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use ruma::RoomVersionId;
|
use ruma::RoomVersionId;
|
||||||
|
|
||||||
use super::{Error, Result};
|
use super::{Result, error::UnsupportedSnafu};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[allow(clippy::exhaustive_enums)]
|
#[allow(clippy::exhaustive_enums)]
|
||||||
@@ -163,7 +163,11 @@ impl RoomVersion {
|
|||||||
| RoomVersionId::V10 => Self::V10,
|
| RoomVersionId::V10 => Self::V10,
|
||||||
| RoomVersionId::V11 => Self::V11,
|
| RoomVersionId::V11 => Self::V11,
|
||||||
| RoomVersionId::V12 => Self::V12,
|
| RoomVersionId::V12 => Self::V12,
|
||||||
| ver => return Err(Error::Unsupported(format!("found version `{ver}`"))),
|
| ver =>
|
||||||
|
return Err(UnsupportedSnafu {
|
||||||
|
version: format!("found version `{ver}`"),
|
||||||
|
}
|
||||||
|
.build()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ use serde_json::{
|
|||||||
value::{RawValue as RawJsonValue, to_raw_value as to_raw_json_value},
|
value::{RawValue as RawJsonValue, to_raw_value as to_raw_json_value},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::auth_types_for_event;
|
use super::{auth_types_for_event, error::NotFoundSnafu};
|
||||||
use crate::{
|
use crate::{
|
||||||
Result, RoomVersion, info,
|
Result, RoomVersion, info,
|
||||||
matrix::{Event, EventTypeExt, Pdu, StateMap, pdu::EventHash},
|
matrix::{Event, EventTypeExt, Pdu, StateMap, pdu::EventHash},
|
||||||
@@ -232,7 +232,7 @@ impl<E: Event + Clone> TestStore<E> {
|
|||||||
self.0
|
self.0
|
||||||
.get(event_id)
|
.get(event_id)
|
||||||
.cloned()
|
.cloned()
|
||||||
.ok_or_else(|| super::Error::NotFound(format!("{event_id} not found")))
|
.ok_or_else(|| NotFoundSnafu { message: format!("{event_id} not found") }.build())
|
||||||
.map_err(Into::into)
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,9 +14,11 @@ pub mod utils;
|
|||||||
|
|
||||||
pub use ::arrayvec;
|
pub use ::arrayvec;
|
||||||
pub use ::http;
|
pub use ::http;
|
||||||
|
pub use ::paste;
|
||||||
pub use ::ruma;
|
pub use ::ruma;
|
||||||
pub use ::smallstr;
|
pub use ::smallstr;
|
||||||
pub use ::smallvec;
|
pub use ::smallvec;
|
||||||
|
pub use ::snafu;
|
||||||
pub use ::toml;
|
pub use ::toml;
|
||||||
pub use ::tracing;
|
pub use ::tracing;
|
||||||
pub use config::Config;
|
pub use config::Config;
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ fn init_argon() -> Argon2<'static> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn password(password: &str) -> Result<String> {
|
pub(super) fn password(password: &str) -> Result<String> {
|
||||||
let salt = SaltString::generate(rand::thread_rng());
|
let salt = SaltString::generate(rand_core::OsRng);
|
||||||
ARGON
|
ARGON
|
||||||
.get_or_init(init_argon)
|
.get_or_init(init_argon)
|
||||||
.hash_password(password.as_bytes(), &salt)
|
.hash_password(password.as_bytes(), &salt)
|
||||||
|
|||||||
+7
-10
@@ -4,16 +4,16 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use arrayvec::ArrayString;
|
use arrayvec::ArrayString;
|
||||||
use rand::{Rng, seq::SliceRandom, thread_rng};
|
use rand::{RngExt, seq::SliceRandom};
|
||||||
|
|
||||||
pub fn shuffle<T>(vec: &mut [T]) {
|
pub fn shuffle<T>(vec: &mut [T]) {
|
||||||
let mut rng = thread_rng();
|
let mut rng = rand::rng();
|
||||||
vec.shuffle(&mut rng);
|
vec.shuffle(&mut rng);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn string(length: usize) -> String {
|
pub fn string(length: usize) -> String {
|
||||||
thread_rng()
|
rand::rng()
|
||||||
.sample_iter(&rand::distributions::Alphanumeric)
|
.sample_iter(&rand::distr::Alphanumeric)
|
||||||
.take(length)
|
.take(length)
|
||||||
.map(char::from)
|
.map(char::from)
|
||||||
.collect()
|
.collect()
|
||||||
@@ -22,8 +22,8 @@ pub fn string(length: usize) -> String {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn string_array<const LENGTH: usize>() -> ArrayString<LENGTH> {
|
pub fn string_array<const LENGTH: usize>() -> ArrayString<LENGTH> {
|
||||||
let mut ret = ArrayString::<LENGTH>::new();
|
let mut ret = ArrayString::<LENGTH>::new();
|
||||||
thread_rng()
|
rand::rng()
|
||||||
.sample_iter(&rand::distributions::Alphanumeric)
|
.sample_iter(&rand::distr::Alphanumeric)
|
||||||
.take(LENGTH)
|
.take(LENGTH)
|
||||||
.map(char::from)
|
.map(char::from)
|
||||||
.for_each(|c| ret.push(c));
|
.for_each(|c| ret.push(c));
|
||||||
@@ -40,7 +40,4 @@ pub fn time_from_now_secs(range: Range<u64>) -> SystemTime {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn secs(range: Range<u64>) -> Duration {
|
pub fn secs(range: Range<u64>) -> Duration { Duration::from_secs(rand::random_range(range)) }
|
||||||
let mut rng = thread_rng();
|
|
||||||
Duration::from_secs(rng.gen_range(range))
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
use std::{cell::Cell, fmt::Debug, path::PathBuf, sync::LazyLock};
|
use std::{cell::Cell, fmt::Debug, path::PathBuf, sync::LazyLock};
|
||||||
|
|
||||||
|
use snafu::IntoError;
|
||||||
|
|
||||||
use crate::{Result, is_equal_to};
|
use crate::{Result, is_equal_to};
|
||||||
|
|
||||||
type Id = usize;
|
type Id = usize;
|
||||||
@@ -142,7 +144,9 @@ pub fn getcpu() -> Result<usize> {
|
|||||||
|
|
||||||
#[cfg(not(target_os = "linux"))]
|
#[cfg(not(target_os = "linux"))]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn getcpu() -> Result<usize> { Err(crate::Error::Io(std::io::ErrorKind::Unsupported.into())) }
|
pub fn getcpu() -> Result<usize> {
|
||||||
|
Err(crate::error::IoSnafu.into_error(std::io::ErrorKind::Unsupported.into()))
|
||||||
|
}
|
||||||
|
|
||||||
fn query_cores_available() -> impl Iterator<Item = Id> {
|
fn query_cores_available() -> impl Iterator<Item = Id> {
|
||||||
core_affinity::get_core_ids()
|
core_affinity::get_core_ids()
|
||||||
|
|||||||
+12
-7
@@ -255,7 +255,10 @@ impl<'a, 'de: 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> {
|
|||||||
| "$serde_json::private::RawValue" => visitor.visit_map(self),
|
| "$serde_json::private::RawValue" => visitor.visit_map(self),
|
||||||
| "Cbor" => visitor
|
| "Cbor" => visitor
|
||||||
.visit_newtype_struct(&mut minicbor_serde::Deserializer::new(self.record_trail()))
|
.visit_newtype_struct(&mut minicbor_serde::Deserializer::new(self.record_trail()))
|
||||||
.map_err(|e| Self::Error::SerdeDe(e.to_string().into())),
|
.map_err(|e| {
|
||||||
|
let message: std::borrow::Cow<'static, str> = e.to_string().into();
|
||||||
|
conduwuit_core::error::SerdeDeSnafu { message }.build()
|
||||||
|
}),
|
||||||
|
|
||||||
| _ => visitor.visit_newtype_struct(self),
|
| _ => visitor.visit_newtype_struct(self),
|
||||||
}
|
}
|
||||||
@@ -313,9 +316,10 @@ impl<'a, 'de: 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> {
|
|||||||
|
|
||||||
let end = self.pos.saturating_add(BYTES).min(self.buf.len());
|
let end = self.pos.saturating_add(BYTES).min(self.buf.len());
|
||||||
let bytes: ArrayVec<u8, BYTES> = self.buf[self.pos..end].try_into()?;
|
let bytes: ArrayVec<u8, BYTES> = self.buf[self.pos..end].try_into()?;
|
||||||
let bytes = bytes
|
let bytes = bytes.into_inner().map_err(|_| {
|
||||||
.into_inner()
|
let message: std::borrow::Cow<'static, str> = "i64 buffer underflow".into();
|
||||||
.map_err(|_| Self::Error::SerdeDe("i64 buffer underflow".into()))?;
|
conduwuit_core::error::SerdeDeSnafu { message }.build()
|
||||||
|
})?;
|
||||||
|
|
||||||
self.inc_pos(BYTES);
|
self.inc_pos(BYTES);
|
||||||
visitor.visit_i64(i64::from_be_bytes(bytes))
|
visitor.visit_i64(i64::from_be_bytes(bytes))
|
||||||
@@ -345,9 +349,10 @@ impl<'a, 'de: 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> {
|
|||||||
|
|
||||||
let end = self.pos.saturating_add(BYTES).min(self.buf.len());
|
let end = self.pos.saturating_add(BYTES).min(self.buf.len());
|
||||||
let bytes: ArrayVec<u8, BYTES> = self.buf[self.pos..end].try_into()?;
|
let bytes: ArrayVec<u8, BYTES> = self.buf[self.pos..end].try_into()?;
|
||||||
let bytes = bytes
|
let bytes = bytes.into_inner().map_err(|_| {
|
||||||
.into_inner()
|
let message: std::borrow::Cow<'static, str> = "u64 buffer underflow".into();
|
||||||
.map_err(|_| Self::Error::SerdeDe("u64 buffer underflow".into()))?;
|
conduwuit_core::error::SerdeDeSnafu { message }.build()
|
||||||
|
})?;
|
||||||
|
|
||||||
self.inc_pos(BYTES);
|
self.inc_pos(BYTES);
|
||||||
visitor.visit_u64(u64::from_be_bytes(bytes))
|
visitor.visit_u64(u64::from_be_bytes(bytes))
|
||||||
|
|||||||
@@ -33,8 +33,6 @@ pub struct Engine {
|
|||||||
pub(crate) db: Db,
|
pub(crate) db: Db,
|
||||||
pub(crate) pool: Arc<Pool>,
|
pub(crate) pool: Arc<Pool>,
|
||||||
pub(crate) ctx: Arc<Context>,
|
pub(crate) ctx: Arc<Context>,
|
||||||
pub(super) read_only: bool,
|
|
||||||
pub(super) secondary: bool,
|
|
||||||
pub(crate) checksums: bool,
|
pub(crate) checksums: bool,
|
||||||
corks: AtomicU32,
|
corks: AtomicU32,
|
||||||
}
|
}
|
||||||
@@ -129,14 +127,6 @@ impl Engine {
|
|||||||
|
|
||||||
sequence
|
sequence
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
#[must_use]
|
|
||||||
pub fn is_read_only(&self) -> bool { self.secondary || self.read_only }
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
#[must_use]
|
|
||||||
pub fn is_secondary(&self) -> bool { self.secondary }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Engine {
|
impl Drop for Engine {
|
||||||
|
|||||||
@@ -12,9 +12,8 @@ pub fn backup(&self) -> Result {
|
|||||||
let mut engine = self.backup_engine()?;
|
let mut engine = self.backup_engine()?;
|
||||||
let config = &self.ctx.server.config;
|
let config = &self.ctx.server.config;
|
||||||
if config.database_backups_to_keep > 0 {
|
if config.database_backups_to_keep > 0 {
|
||||||
let flush = !self.is_read_only();
|
|
||||||
engine
|
engine
|
||||||
.create_new_backup_flush(&self.db, flush)
|
.create_new_backup_flush(&self.db, true)
|
||||||
.map_err(map_err)?;
|
.map_err(map_err)?;
|
||||||
|
|
||||||
let engine_info = engine.get_backup_info();
|
let engine_info = engine.get_backup_info();
|
||||||
|
|||||||
@@ -35,14 +35,7 @@ pub(crate) async fn open(ctx: Arc<Context>, desc: &[Descriptor]) -> Result<Arc<S
|
|||||||
}
|
}
|
||||||
|
|
||||||
debug!("Opening database...");
|
debug!("Opening database...");
|
||||||
let db = if config.rocksdb_read_only {
|
let db = Db::open_cf_descriptors(&db_opts, path, cfds).or_else(or_else)?;
|
||||||
Db::open_cf_descriptors_read_only(&db_opts, path, cfds, false)
|
|
||||||
} else if config.rocksdb_secondary {
|
|
||||||
Db::open_cf_descriptors_as_secondary(&db_opts, path, path, cfds)
|
|
||||||
} else {
|
|
||||||
Db::open_cf_descriptors(&db_opts, path, cfds)
|
|
||||||
}
|
|
||||||
.or_else(or_else)?;
|
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
columns = num_cfds,
|
columns = num_cfds,
|
||||||
@@ -55,8 +48,6 @@ pub(crate) async fn open(ctx: Arc<Context>, desc: &[Descriptor]) -> Result<Arc<S
|
|||||||
db,
|
db,
|
||||||
pool: ctx.pool.clone(),
|
pool: ctx.pool.clone(),
|
||||||
ctx: ctx.clone(),
|
ctx: ctx.clone(),
|
||||||
read_only: config.rocksdb_read_only,
|
|
||||||
secondary: config.rocksdb_secondary,
|
|
||||||
checksums: config.rocksdb_checksums,
|
checksums: config.rocksdb_checksums,
|
||||||
corks: AtomicU32::new(0),
|
corks: AtomicU32::new(0),
|
||||||
}))
|
}))
|
||||||
|
|||||||
@@ -74,14 +74,6 @@ impl Database {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn keys(&self) -> impl Iterator<Item = &MapsKey> + Send + '_ { self.maps.keys() }
|
pub fn keys(&self) -> impl Iterator<Item = &MapsKey> + Send + '_ { self.maps.keys() }
|
||||||
|
|
||||||
#[inline]
|
|
||||||
#[must_use]
|
|
||||||
pub fn is_read_only(&self) -> bool { self.db.is_read_only() }
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
#[must_use]
|
|
||||||
pub fn is_secondary(&self) -> bool { self.db.is_secondary() }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Index<&str> for Database {
|
impl Index<&str> for Database {
|
||||||
|
|||||||
+4
-1
@@ -199,7 +199,10 @@ impl<W: Write> ser::Serializer for &mut Serializer<'_, W> {
|
|||||||
|
|
||||||
value
|
value
|
||||||
.serialize(&mut Serializer::new(&mut Writer::new(&mut self.out)))
|
.serialize(&mut Serializer::new(&mut Writer::new(&mut self.out)))
|
||||||
.map_err(|e| Self::Error::SerdeSer(e.to_string().into()))
|
.map_err(|e| {
|
||||||
|
let message: std::borrow::Cow<'static, str> = e.to_string().into();
|
||||||
|
conduwuit_core::error::SerdeSerSnafu { message }.build()
|
||||||
|
})
|
||||||
},
|
},
|
||||||
| _ => unhandled!("Unrecognized serialization Newtype {name:?}"),
|
| _ => unhandled!("Unrecognized serialization Newtype {name:?}"),
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-9
@@ -27,10 +27,6 @@ pub struct Args {
|
|||||||
#[arg(long, short('O'))]
|
#[arg(long, short('O'))]
|
||||||
pub option: Vec<String>,
|
pub option: Vec<String>,
|
||||||
|
|
||||||
/// Run in a stricter read-only --maintenance mode.
|
|
||||||
#[arg(long)]
|
|
||||||
pub read_only: bool,
|
|
||||||
|
|
||||||
/// Run in maintenance mode while refusing connections.
|
/// Run in maintenance mode while refusing connections.
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
pub maintenance: bool,
|
pub maintenance: bool,
|
||||||
@@ -143,11 +139,7 @@ pub(crate) fn parse() -> Args { Args::parse() }
|
|||||||
|
|
||||||
/// Synthesize any command line options with configuration file options.
|
/// Synthesize any command line options with configuration file options.
|
||||||
pub(crate) fn update(mut config: Figment, args: &Args) -> Result<Figment> {
|
pub(crate) fn update(mut config: Figment, args: &Args) -> Result<Figment> {
|
||||||
if args.read_only {
|
if args.maintenance {
|
||||||
config = config.join(("rocksdb_read_only", true));
|
|
||||||
}
|
|
||||||
|
|
||||||
if args.maintenance || args.read_only {
|
|
||||||
config = config.join(("startup_netburst", false));
|
config = config.join(("startup_netburst", false));
|
||||||
config = config.join(("listening", false));
|
config = config.join(("listening", false));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use std::sync::Arc;
|
use std::{borrow::Cow, sync::Arc};
|
||||||
|
|
||||||
use axum::{Router, response::IntoResponse};
|
use axum::{Router, response::IntoResponse};
|
||||||
use conduwuit::Error;
|
use conduwuit::Error;
|
||||||
@@ -18,5 +18,10 @@ pub(crate) fn build(services: &Arc<Services>) -> (Router, Guard) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn not_found(_uri: Uri) -> impl IntoResponse {
|
async fn not_found(_uri: Uri) -> impl IntoResponse {
|
||||||
Error::Request(ErrorKind::Unrecognized, "Not Found".into(), StatusCode::NOT_FOUND)
|
Error::Request {
|
||||||
|
kind: ErrorKind::Unrecognized,
|
||||||
|
message: Cow::Borrowed("Not Found"),
|
||||||
|
code: StatusCode::NOT_FOUND,
|
||||||
|
backtrace: None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ use std::{sync::Arc, time::Duration};
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use conduwuit::{Result, Server, debug, error, warn};
|
use conduwuit::{Result, Server, debug, error, warn};
|
||||||
use database::{Deserialized, Map};
|
use database::{Deserialized, Map};
|
||||||
use rand::Rng;
|
|
||||||
use ruma::events::{Mentions, room::message::RoomMessageEventContent};
|
use ruma::events::{Mentions, room::message::RoomMessageEventContent};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use tokio::{
|
use tokio::{
|
||||||
@@ -100,8 +99,7 @@ impl crate::Service for Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let first_check_jitter = {
|
let first_check_jitter = {
|
||||||
let mut rng = rand::thread_rng();
|
let jitter_percent = rand::random_range(-50.0..=10.0);
|
||||||
let jitter_percent = rng.gen_range(-50.0..=10.0);
|
|
||||||
self.interval.mul_f64(1.0 + jitter_percent / 100.0)
|
self.interval.mul_f64(1.0 + jitter_percent / 100.0)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -147,11 +147,11 @@ impl Service {
|
|||||||
// same appservice)
|
// same appservice)
|
||||||
if let Ok(existing) = self.find_from_token(®istration.as_token).await {
|
if let Ok(existing) = self.find_from_token(®istration.as_token).await {
|
||||||
if existing.registration.id != registration.id {
|
if existing.registration.id != registration.id {
|
||||||
return Err(err!(Request(InvalidParam(
|
return Err!(Request(InvalidParam(
|
||||||
"Cannot register appservice: Token is already used by appservice '{}'. \
|
"Cannot register appservice: Token is already used by appservice '{}'. \
|
||||||
Please generate a different token.",
|
Please generate a different token.",
|
||||||
existing.registration.id
|
existing.registration.id
|
||||||
))));
|
)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,10 +163,10 @@ impl Service {
|
|||||||
.await
|
.await
|
||||||
.is_ok()
|
.is_ok()
|
||||||
{
|
{
|
||||||
return Err(err!(Request(InvalidParam(
|
return Err!(Request(InvalidParam(
|
||||||
"Cannot register appservice: The provided token is already in use by a user \
|
"Cannot register appservice: The provided token is already in use by a user \
|
||||||
device. Please generate a different token for the appservice."
|
device. Please generate a different token for the appservice."
|
||||||
))));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.db
|
self.db
|
||||||
|
|||||||
@@ -37,10 +37,6 @@ impl crate::Service for Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn worker(self: Arc<Self>) -> Result {
|
async fn worker(self: Arc<Self>) -> Result {
|
||||||
if self.services.globals.is_read_only() {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.services.config.ldap.enable {
|
if self.services.config.ldap.enable {
|
||||||
warn!("emergency password feature not available with LDAP enabled.");
|
warn!("emergency password feature not available with LDAP enabled.");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use std::{fmt::Debug, mem};
|
|||||||
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use conduwuit::{
|
use conduwuit::{
|
||||||
Err, Error, Result, debug, debug::INFO_SPAN_LEVEL, debug_error, debug_warn, err,
|
Err, Result, debug, debug::INFO_SPAN_LEVEL, debug_error, debug_warn, err,
|
||||||
error::inspect_debug_log, implement, trace,
|
error::inspect_debug_log, implement, trace,
|
||||||
};
|
};
|
||||||
use http::{HeaderValue, header::AUTHORIZATION};
|
use http::{HeaderValue, header::AUTHORIZATION};
|
||||||
@@ -179,10 +179,7 @@ async fn into_http_response(
|
|||||||
|
|
||||||
debug!("Got {status:?} for {method} {url}");
|
debug!("Got {status:?} for {method} {url}");
|
||||||
if !status.is_success() {
|
if !status.is_success() {
|
||||||
return Err(Error::Federation(
|
return Err!(Federation(dest.to_owned(), RumaError::from_http_response(http_response),));
|
||||||
dest.to_owned(),
|
|
||||||
RumaError::from_http_response(http_response),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(http_response)
|
Ok(http_response)
|
||||||
|
|||||||
@@ -156,7 +156,4 @@ impl Service {
|
|||||||
pub fn server_is_ours(&self, server_name: &ServerName) -> bool {
|
pub fn server_is_ours(&self, server_name: &ServerName) -> bool {
|
||||||
server_name == self.server_name()
|
server_name == self.server_name()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn is_read_only(&self) -> bool { self.db.db.is_read_only() }
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ pub async fn fetch_remote_thumbnail(
|
|||||||
.fetch_thumbnail_authenticated(mxc, user, server, timeout_ms, dim)
|
.fetch_thumbnail_authenticated(mxc, user, server, timeout_ms, dim)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
if let Err(Error::Request(NotFound, ..)) = &result {
|
if let Err(Error::Request { kind: NotFound, .. }) = &result {
|
||||||
return self
|
return self
|
||||||
.fetch_thumbnail_unauthenticated(mxc, user, server, timeout_ms, dim)
|
.fetch_thumbnail_unauthenticated(mxc, user, server, timeout_ms, dim)
|
||||||
.await;
|
.await;
|
||||||
@@ -67,7 +67,7 @@ pub async fn fetch_remote_content(
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Err(Error::Request(Unrecognized, ..)) = &result {
|
if let Err(Error::Request { kind: Unrecognized, .. }) = &result {
|
||||||
return self
|
return self
|
||||||
.fetch_content_unauthenticated(mxc, user, server, timeout_ms)
|
.fetch_content_unauthenticated(mxc, user, server, timeout_ms)
|
||||||
.await;
|
.await;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ mod remote;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use conduwuit::{
|
use conduwuit::{
|
||||||
Err, Event, Result, Server, err,
|
Err, Event, Result, err,
|
||||||
utils::{ReadyExt, stream::TryIgnore},
|
utils::{ReadyExt, stream::TryIgnore},
|
||||||
};
|
};
|
||||||
use database::{Deserialized, Ignore, Interfix, Map};
|
use database::{Deserialized, Ignore, Interfix, Map};
|
||||||
@@ -30,12 +30,12 @@ struct Data {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct Services {
|
struct Services {
|
||||||
server: Arc<Server>,
|
|
||||||
admin: Dep<admin::Service>,
|
admin: Dep<admin::Service>,
|
||||||
appservice: Dep<appservice::Service>,
|
appservice: Dep<appservice::Service>,
|
||||||
globals: Dep<globals::Service>,
|
globals: Dep<globals::Service>,
|
||||||
sending: Dep<sending::Service>,
|
sending: Dep<sending::Service>,
|
||||||
state_accessor: Dep<rooms::state_accessor::Service>,
|
state_accessor: Dep<rooms::state_accessor::Service>,
|
||||||
|
state_cache: Dep<rooms::state_cache::Service>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl crate::Service for Service {
|
impl crate::Service for Service {
|
||||||
@@ -47,13 +47,13 @@ impl crate::Service for Service {
|
|||||||
aliasid_alias: args.db["aliasid_alias"].clone(),
|
aliasid_alias: args.db["aliasid_alias"].clone(),
|
||||||
},
|
},
|
||||||
services: Services {
|
services: Services {
|
||||||
server: args.server.clone(),
|
|
||||||
admin: args.depend::<admin::Service>("admin"),
|
admin: args.depend::<admin::Service>("admin"),
|
||||||
appservice: args.depend::<appservice::Service>("appservice"),
|
appservice: args.depend::<appservice::Service>("appservice"),
|
||||||
globals: args.depend::<globals::Service>("globals"),
|
globals: args.depend::<globals::Service>("globals"),
|
||||||
sending: args.depend::<sending::Service>("sending"),
|
sending: args.depend::<sending::Service>("sending"),
|
||||||
state_accessor: args
|
state_accessor: args
|
||||||
.depend::<rooms::state_accessor::Service>("rooms::state_accessor"),
|
.depend::<rooms::state_accessor::Service>("rooms::state_accessor"),
|
||||||
|
state_cache: args.depend::<rooms::state_cache::Service>("rooms::state_cache"),
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
@@ -117,6 +117,9 @@ impl Service {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resolves the given room ID or alias, returning the resolved room ID.
|
||||||
|
/// Unlike resolve_with_servers (the underlying call), potential resident
|
||||||
|
/// servers are not returned
|
||||||
#[inline]
|
#[inline]
|
||||||
pub async fn resolve(&self, room: &RoomOrAliasId) -> Result<OwnedRoomId> {
|
pub async fn resolve(&self, room: &RoomOrAliasId) -> Result<OwnedRoomId> {
|
||||||
self.resolve_with_servers(room, None)
|
self.resolve_with_servers(room, None)
|
||||||
@@ -124,6 +127,14 @@ impl Service {
|
|||||||
.map(|(room_id, _)| room_id)
|
.map(|(room_id, _)| room_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resolves the given room ID or alias, returning the resolved room ID, and
|
||||||
|
/// any servers that might be able to assist in fetching room data.
|
||||||
|
///
|
||||||
|
/// If the input is a room ID, this simply returns it and <servers>.
|
||||||
|
/// If the input is an alias, this attempts to resolve it locally, then via
|
||||||
|
/// appservices, and finally remotely if the alias is not local.
|
||||||
|
/// If the alias is successfully resolved, the room ID and an empty list of
|
||||||
|
/// servers is returned.
|
||||||
pub async fn resolve_with_servers(
|
pub async fn resolve_with_servers(
|
||||||
&self,
|
&self,
|
||||||
room: &RoomOrAliasId,
|
room: &RoomOrAliasId,
|
||||||
@@ -134,28 +145,26 @@ impl Service {
|
|||||||
Ok((room_id.to_owned(), servers.unwrap_or_default()))
|
Ok((room_id.to_owned(), servers.unwrap_or_default()))
|
||||||
} else {
|
} else {
|
||||||
let alias: &RoomAliasId = room.try_into().expect("valid RoomAliasId");
|
let alias: &RoomAliasId = room.try_into().expect("valid RoomAliasId");
|
||||||
self.resolve_alias(alias, servers).await
|
self.resolve_alias(alias).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resolves the given room alias, returning the resolved room ID and any
|
||||||
|
/// servers that might be in the room.
|
||||||
#[tracing::instrument(skip(self), name = "resolve")]
|
#[tracing::instrument(skip(self), name = "resolve")]
|
||||||
pub async fn resolve_alias(
|
pub async fn resolve_alias(
|
||||||
&self,
|
&self,
|
||||||
room_alias: &RoomAliasId,
|
room_alias: &RoomAliasId,
|
||||||
servers: Option<Vec<OwnedServerName>>,
|
|
||||||
) -> Result<(OwnedRoomId, Vec<OwnedServerName>)> {
|
) -> Result<(OwnedRoomId, Vec<OwnedServerName>)> {
|
||||||
let server_name = room_alias.server_name();
|
let server_is_ours = self
|
||||||
let server_is_ours = self.services.globals.server_is_ours(server_name);
|
.services
|
||||||
let servers_contains_ours = || {
|
.globals
|
||||||
servers
|
.server_is_ours(room_alias.server_name());
|
||||||
.as_ref()
|
|
||||||
.is_some_and(|servers| servers.contains(&self.services.server.name))
|
|
||||||
};
|
|
||||||
|
|
||||||
if !server_is_ours && !servers_contains_ours() {
|
if !server_is_ours {
|
||||||
return self
|
// TODO: The spec advises servers may cache remote room aliases temporarily.
|
||||||
.remote_resolve(room_alias, servers.unwrap_or_default())
|
// We might want to look at doing that.
|
||||||
.await;
|
return self.remote_resolve(room_alias).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
let room_id = match self.resolve_local_alias(room_alias).await {
|
let room_id = match self.resolve_local_alias(room_alias).await {
|
||||||
@@ -163,10 +172,18 @@ impl Service {
|
|||||||
| Err(_) => self.resolve_appservice_alias(room_alias).await?,
|
| Err(_) => self.resolve_appservice_alias(room_alias).await?,
|
||||||
};
|
};
|
||||||
|
|
||||||
room_id.map_or_else(
|
if let Some(room_id) = room_id {
|
||||||
|| Err!(Request(NotFound("Room with alias not found."))),
|
let servers: Vec<OwnedServerName> = self
|
||||||
|room_id| Ok((room_id, Vec::new())),
|
.services
|
||||||
)
|
.state_cache
|
||||||
|
.room_servers(&room_id)
|
||||||
|
.map(ToOwned::to_owned)
|
||||||
|
.collect()
|
||||||
|
.await;
|
||||||
|
return Ok((room_id, servers));
|
||||||
|
}
|
||||||
|
|
||||||
|
Err!(Request(NotFound("Alias does not exist.")))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip(self), level = "debug")]
|
#[tracing::instrument(skip(self), level = "debug")]
|
||||||
@@ -206,12 +223,12 @@ impl Service {
|
|||||||
|
|
||||||
// The creator of an alias can remove it
|
// The creator of an alias can remove it
|
||||||
if self
|
if self
|
||||||
.who_created_alias(alias).await
|
.who_created_alias(alias).await
|
||||||
.is_ok_and(|user| user == user_id)
|
.is_ok_and(|user| user == user_id)
|
||||||
// Server admins can remove any local alias
|
// Server admins can remove any local alias
|
||||||
|| self.services.admin.user_is_admin(user_id).await
|
|| self.services.admin.user_is_admin(user_id).await
|
||||||
// Always allow the server service account to remove the alias, since there may not be an admin room
|
// Always allow the server service account to remove the alias, since there may not be an admin room
|
||||||
|| server_user == user_id
|
|| server_user == user_id
|
||||||
{
|
{
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
use std::iter::once;
|
use conduwuit::{Result, debug, error, implement};
|
||||||
|
|
||||||
use conduwuit::{Result, debug, debug_error, err, implement};
|
|
||||||
use federation::query::get_room_information::v1::Response;
|
use federation::query::get_room_information::v1::Response;
|
||||||
use ruma::{OwnedRoomId, OwnedServerName, RoomAliasId, ServerName, api::federation};
|
use ruma::{OwnedRoomId, OwnedServerName, RoomAliasId, ServerName, api::federation};
|
||||||
|
|
||||||
@@ -8,40 +6,21 @@ use ruma::{OwnedRoomId, OwnedServerName, RoomAliasId, ServerName, api::federatio
|
|||||||
pub(super) async fn remote_resolve(
|
pub(super) async fn remote_resolve(
|
||||||
&self,
|
&self,
|
||||||
room_alias: &RoomAliasId,
|
room_alias: &RoomAliasId,
|
||||||
servers: Vec<OwnedServerName>,
|
|
||||||
) -> Result<(OwnedRoomId, Vec<OwnedServerName>)> {
|
) -> Result<(OwnedRoomId, Vec<OwnedServerName>)> {
|
||||||
debug!(?room_alias, servers = ?servers, "resolve");
|
debug!("Asking {} to resolve {room_alias:?}", room_alias.server_name());
|
||||||
let servers = once(room_alias.server_name())
|
match self
|
||||||
.map(ToOwned::to_owned)
|
.remote_request(room_alias, room_alias.server_name())
|
||||||
.chain(servers.into_iter());
|
.await
|
||||||
|
{
|
||||||
let mut resolved_servers = Vec::new();
|
| Err(e) => {
|
||||||
let mut resolved_room_id: Option<OwnedRoomId> = None;
|
error!("Unable to resolve remote room alias {}: {e}", room_alias);
|
||||||
for server in servers {
|
Err(e)
|
||||||
match self.remote_request(room_alias, &server).await {
|
},
|
||||||
| Err(e) => debug_error!("Failed to query for {room_alias:?} from {server}: {e}"),
|
| Ok(Response { room_id, servers }) => {
|
||||||
| Ok(Response { room_id, servers }) => {
|
debug!("Remote resolved {room_alias:?} to {room_id:?} with servers {servers:?}");
|
||||||
debug!(
|
Ok((room_id, servers))
|
||||||
"Server {server} answered with {room_id:?} for {room_alias:?} servers: \
|
},
|
||||||
{servers:?}"
|
|
||||||
);
|
|
||||||
|
|
||||||
resolved_room_id.get_or_insert(room_id);
|
|
||||||
add_server(&mut resolved_servers, server);
|
|
||||||
|
|
||||||
if !servers.is_empty() {
|
|
||||||
add_servers(&mut resolved_servers, servers);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resolved_room_id
|
|
||||||
.map(|room_id| (room_id, resolved_servers))
|
|
||||||
.ok_or_else(|| {
|
|
||||||
err!(Request(NotFound("No servers could assist in resolving the room alias")))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[implement(super::Service)]
|
#[implement(super::Service)]
|
||||||
@@ -59,15 +38,3 @@ async fn remote_request(
|
|||||||
.send_federation_request(server, request)
|
.send_federation_request(server, request)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_servers(servers: &mut Vec<OwnedServerName>, new: Vec<OwnedServerName>) {
|
|
||||||
for server in new {
|
|
||||||
add_server(servers, server);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_server(servers: &mut Vec<OwnedServerName>, server: OwnedServerName) {
|
|
||||||
if !servers.contains(&server) {
|
|
||||||
servers.push(server);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -112,7 +112,14 @@ where
|
|||||||
{
|
{
|
||||||
let event_fetch = |event_id| self.event_fetch(event_id);
|
let event_fetch = |event_id| self.event_fetch(event_id);
|
||||||
let event_exists = |event_id| self.event_exists(event_id);
|
let event_exists = |event_id| self.event_exists(event_id);
|
||||||
state_res::resolve(room_version, state_sets, auth_chain_sets, &event_fetch, &event_exists)
|
Ok(
|
||||||
.map_err(|e| err!(error!("State resolution failed: {e:?}")))
|
state_res::resolve(
|
||||||
.await
|
room_version,
|
||||||
|
state_sets,
|
||||||
|
auth_chain_sets,
|
||||||
|
&event_fetch,
|
||||||
|
&event_exists,
|
||||||
|
)
|
||||||
|
.await?,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use std::{
|
|||||||
str::FromStr,
|
str::FromStr,
|
||||||
};
|
};
|
||||||
|
|
||||||
use conduwuit::{Error, Result};
|
use conduwuit::{Err, Error, Result};
|
||||||
use ruma::{UInt, api::client::error::ErrorKind};
|
use ruma::{UInt, api::client::error::ErrorKind};
|
||||||
|
|
||||||
use crate::rooms::short::ShortRoomId;
|
use crate::rooms::short::ShortRoomId;
|
||||||
@@ -57,7 +57,7 @@ impl FromStr for PaginationToken {
|
|||||||
if let Some(token) = pag_tok() {
|
if let Some(token) = pag_tok() {
|
||||||
Ok(token)
|
Ok(token)
|
||||||
} else {
|
} else {
|
||||||
Err(Error::BadRequest(ErrorKind::InvalidParam, "invalid token"))
|
Err!(BadRequest(ErrorKind::InvalidParam, "invalid token"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -139,7 +139,12 @@ pub async fn backfill_if_required(&self, room_id: &RoomId, from: PduCount) -> Re
|
|||||||
})
|
})
|
||||||
.boxed();
|
.boxed();
|
||||||
|
|
||||||
|
let mut federated_room = false;
|
||||||
|
|
||||||
while let Some(ref backfill_server) = servers.next().await {
|
while let Some(ref backfill_server) = servers.next().await {
|
||||||
|
if !self.services.globals.server_is_ours(backfill_server) {
|
||||||
|
federated_room = true;
|
||||||
|
}
|
||||||
info!("Asking {backfill_server} for backfill in {room_id}");
|
info!("Asking {backfill_server} for backfill in {room_id}");
|
||||||
let response = self
|
let response = self
|
||||||
.services
|
.services
|
||||||
@@ -168,7 +173,9 @@ pub async fn backfill_if_required(&self, room_id: &RoomId, from: PduCount) -> Re
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
warn!("No servers could backfill, but backfill was needed in room {room_id}");
|
if federated_room {
|
||||||
|
warn!("No servers could backfill, but backfill was needed in room {room_id}");
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -75,10 +75,7 @@ pub async fn create_hash_and_sign_event(
|
|||||||
let content: RoomCreateEventContent = serde_json::from_str(content.get())?;
|
let content: RoomCreateEventContent = serde_json::from_str(content.get())?;
|
||||||
Ok(content.room_version)
|
Ok(content.room_version)
|
||||||
} else {
|
} else {
|
||||||
Err(Error::InconsistentRoomState(
|
Err!(InconsistentRoomState("non-create event for room of unknown version", room_id))
|
||||||
"non-create event for room of unknown version",
|
|
||||||
room_id,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let PduBuilder {
|
let PduBuilder {
|
||||||
@@ -275,7 +272,9 @@ pub async fn create_hash_and_sign_event(
|
|||||||
.hash_and_sign_event(&mut pdu_json, &room_version_id)
|
.hash_and_sign_event(&mut pdu_json, &room_version_id)
|
||||||
{
|
{
|
||||||
return match e {
|
return match e {
|
||||||
| Error::Signatures(ruma::signatures::Error::PduSize) => {
|
| Error::Signatures { source, .. }
|
||||||
|
if matches!(source, ruma::signatures::Error::PduSize) =>
|
||||||
|
{
|
||||||
Err!(Request(TooLarge("Message/PDU is too long (exceeds 65535 bytes)")))
|
Err!(Request(TooLarge("Message/PDU is too long (exceeds 65535 bytes)")))
|
||||||
},
|
},
|
||||||
| _ => Err!(Request(Unknown(warn!("Signing event failed: {e}")))),
|
| _ => Err!(Request(Unknown(warn!("Signing event failed: {e}")))),
|
||||||
|
|||||||
@@ -385,11 +385,13 @@ fn num_senders(args: &crate::Args<'_>) -> usize {
|
|||||||
const MIN_SENDERS: usize = 1;
|
const MIN_SENDERS: usize = 1;
|
||||||
// Limit the number of senders to the number of workers threads or number of
|
// Limit the number of senders to the number of workers threads or number of
|
||||||
// cores, conservatively.
|
// cores, conservatively.
|
||||||
let max_senders = args
|
let mut max_senders = args.server.metrics.num_workers();
|
||||||
.server
|
|
||||||
.metrics
|
// Work around some platforms not returning the number of cores.
|
||||||
.num_workers()
|
let num_cores = available_parallelism();
|
||||||
.min(available_parallelism());
|
if num_cores > 0 {
|
||||||
|
max_senders = max_senders.min(num_cores);
|
||||||
|
}
|
||||||
|
|
||||||
// If the user doesn't override the default 0, this is intended to then default
|
// If the user doesn't override the default 0, this is intended to then default
|
||||||
// to 1 for now as multiple senders is experimental.
|
// to 1 for now as multiple senders is experimental.
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ impl Services {
|
|||||||
|
|
||||||
// reset dormant online/away statuses to offline, and set the server user as
|
// reset dormant online/away statuses to offline, and set the server user as
|
||||||
// online
|
// online
|
||||||
if self.server.config.allow_local_presence && !self.db.is_read_only() {
|
if self.server.config.allow_local_presence {
|
||||||
self.presence.unset_all_presence().await;
|
self.presence.unset_all_presence().await;
|
||||||
_ = self
|
_ = self
|
||||||
.presence
|
.presence
|
||||||
@@ -156,7 +156,7 @@ impl Services {
|
|||||||
info!("Shutting down services...");
|
info!("Shutting down services...");
|
||||||
|
|
||||||
// set the server user as offline
|
// set the server user as offline
|
||||||
if self.server.config.allow_local_presence && !self.db.is_read_only() {
|
if self.server.config.allow_local_presence {
|
||||||
_ = self
|
_ = self
|
||||||
.presence
|
.presence
|
||||||
.ping_presence(&self.globals.server_user, &ruma::presence::PresenceState::Offline)
|
.ping_presence(&self.globals.server_user, &ruma::presence::PresenceState::Offline)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::{collections::BTreeMap, sync::Arc};
|
use std::{collections::BTreeMap, sync::Arc};
|
||||||
|
|
||||||
use conduwuit::{
|
use conduwuit::{
|
||||||
Err, Error, Result, SyncRwLock, err, error, implement, utils,
|
Err, Result, SyncRwLock, err, error, implement, utils,
|
||||||
utils::{hash, string::EMPTY},
|
utils::{hash, string::EMPTY},
|
||||||
};
|
};
|
||||||
use database::{Deserialized, Json, Map};
|
use database::{Deserialized, Json, Map};
|
||||||
@@ -117,7 +117,7 @@ pub async fn try_auth(
|
|||||||
} else if let Some(username) = user {
|
} else if let Some(username) = user {
|
||||||
username
|
username
|
||||||
} else {
|
} else {
|
||||||
return Err(Error::BadRequest(
|
return Err!(BadRequest(
|
||||||
ErrorKind::Unrecognized,
|
ErrorKind::Unrecognized,
|
||||||
"Identifier type not recognized.",
|
"Identifier type not recognized.",
|
||||||
));
|
));
|
||||||
@@ -125,7 +125,7 @@ pub async fn try_auth(
|
|||||||
|
|
||||||
#[cfg(not(feature = "element_hacks"))]
|
#[cfg(not(feature = "element_hacks"))]
|
||||||
let Some(UserIdentifier::UserIdOrLocalpart(username)) = identifier else {
|
let Some(UserIdentifier::UserIdOrLocalpart(username)) = identifier else {
|
||||||
return Err(Error::BadRequest(
|
return Err!(BadRequest(
|
||||||
ErrorKind::Unrecognized,
|
ErrorKind::Unrecognized,
|
||||||
"Identifier type not recognized.",
|
"Identifier type not recognized.",
|
||||||
));
|
));
|
||||||
@@ -135,7 +135,7 @@ pub async fn try_auth(
|
|||||||
username.clone(),
|
username.clone(),
|
||||||
self.services.globals.server_name(),
|
self.services.globals.server_name(),
|
||||||
)
|
)
|
||||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "User ID is invalid."))?;
|
.map_err(|_| err!(BadRequest(ErrorKind::InvalidParam, "User ID is invalid.")))?;
|
||||||
|
|
||||||
// Check if the access token being used matches the credentials used for UIAA
|
// Check if the access token being used matches the credentials used for UIAA
|
||||||
if user_id.localpart() != user_id_from_username.localpart() {
|
if user_id.localpart() != user_id_from_username.localpart() {
|
||||||
|
|||||||
@@ -184,6 +184,12 @@ impl Service {
|
|||||||
password: Option<&str>,
|
password: Option<&str>,
|
||||||
origin: Option<&str>,
|
origin: Option<&str>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
|
if !self.services.globals.user_is_local(user_id)
|
||||||
|
&& (password.is_some() || origin.is_some())
|
||||||
|
{
|
||||||
|
return Err!("Cannot create a nonlocal user with a set password or origin");
|
||||||
|
}
|
||||||
|
|
||||||
self.db
|
self.db
|
||||||
.userid_origin
|
.userid_origin
|
||||||
.insert(user_id, origin.unwrap_or("password"));
|
.insert(user_id, origin.unwrap_or("password"));
|
||||||
@@ -755,13 +761,13 @@ impl Service {
|
|||||||
.keys
|
.keys
|
||||||
.into_values();
|
.into_values();
|
||||||
|
|
||||||
let self_signing_key_id = self_signing_key_ids.next().ok_or(Error::BadRequest(
|
let self_signing_key_id = self_signing_key_ids.next().ok_or(err!(BadRequest(
|
||||||
ErrorKind::InvalidParam,
|
ErrorKind::InvalidParam,
|
||||||
"Self signing key contained no key.",
|
"Self signing key contained no key.",
|
||||||
))?;
|
)))?;
|
||||||
|
|
||||||
if self_signing_key_ids.next().is_some() {
|
if self_signing_key_ids.next().is_some() {
|
||||||
return Err(Error::BadRequest(
|
return Err!(BadRequest(
|
||||||
ErrorKind::InvalidParam,
|
ErrorKind::InvalidParam,
|
||||||
"Self signing key contained more than one key.",
|
"Self signing key contained more than one key.",
|
||||||
));
|
));
|
||||||
@@ -1433,13 +1439,13 @@ pub fn parse_master_key(
|
|||||||
|
|
||||||
let master_key = master_key
|
let master_key = master_key
|
||||||
.deserialize()
|
.deserialize()
|
||||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Invalid master key"))?;
|
.map_err(|_| err!(BadRequest(ErrorKind::InvalidParam, "Invalid master key")))?;
|
||||||
let mut master_key_ids = master_key.keys.values();
|
let mut master_key_ids = master_key.keys.values();
|
||||||
let master_key_id = master_key_ids
|
let master_key_id = master_key_ids
|
||||||
.next()
|
.next()
|
||||||
.ok_or(Error::BadRequest(ErrorKind::InvalidParam, "Master key contained no key."))?;
|
.ok_or(err!(BadRequest(ErrorKind::InvalidParam, "Master key contained no key.")))?;
|
||||||
if master_key_ids.next().is_some() {
|
if master_key_ids.next().is_some() {
|
||||||
return Err(Error::BadRequest(
|
return Err!(BadRequest(
|
||||||
ErrorKind::InvalidParam,
|
ErrorKind::InvalidParam,
|
||||||
"Master key contained more than one key.",
|
"Master key contained more than one key.",
|
||||||
));
|
));
|
||||||
|
|||||||
+1
-1
@@ -25,7 +25,7 @@ axum.workspace = true
|
|||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
tracing.workspace = true
|
tracing.workspace = true
|
||||||
rand.workspace = true
|
rand.workspace = true
|
||||||
thiserror.workspace = true
|
snafu.workspace = true
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|||||||
+12
-4
@@ -8,6 +8,7 @@ use axum::{
|
|||||||
};
|
};
|
||||||
use conduwuit_build_metadata::{GIT_REMOTE_COMMIT_URL, GIT_REMOTE_WEB_URL, version_tag};
|
use conduwuit_build_metadata::{GIT_REMOTE_COMMIT_URL, GIT_REMOTE_WEB_URL, version_tag};
|
||||||
use conduwuit_service::state;
|
use conduwuit_service::state;
|
||||||
|
use snafu::{IntoError, prelude::*};
|
||||||
|
|
||||||
pub fn build() -> Router<state::State> {
|
pub fn build() -> Router<state::State> {
|
||||||
Router::<state::State>::new()
|
Router::<state::State>::new()
|
||||||
@@ -48,10 +49,17 @@ async fn logo_handler() -> impl IntoResponse {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, Snafu)]
|
||||||
enum WebError {
|
enum WebError {
|
||||||
#[error("Failed to render template: {0}")]
|
#[snafu(display("Failed to render template: {source}"))]
|
||||||
Render(#[from] askama::Error),
|
Render {
|
||||||
|
source: askama::Error,
|
||||||
|
backtrace: snafu::Backtrace,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<askama::Error> for WebError {
|
||||||
|
fn from(source: askama::Error) -> Self { RenderSnafu.into_error(source) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IntoResponse for WebError {
|
impl IntoResponse for WebError {
|
||||||
@@ -66,7 +74,7 @@ impl IntoResponse for WebError {
|
|||||||
let nonce = rand::random::<u64>().to_string();
|
let nonce = rand::random::<u64>().to_string();
|
||||||
|
|
||||||
let status = match &self {
|
let status = match &self {
|
||||||
| Self::Render(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
| Self::Render { .. } => StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
};
|
};
|
||||||
let tmpl = Error { nonce: &nonce, err: self };
|
let tmpl = Error { nonce: &nonce, err: self };
|
||||||
if let Ok(body) = tmpl.render() {
|
if let Ok(body) = tmpl.render() {
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ Server Error
|
|||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
{%- match err -%}
|
{%- match err -%}
|
||||||
{% when WebError::Render(err) -%}
|
{% when WebError::Render { source, .. } -%}
|
||||||
<pre>{{ err }}</pre>
|
<pre>{{ source }}</pre>
|
||||||
{% else -%} <p>An error occurred</p>
|
{% else -%} <p>An error occurred</p>
|
||||||
{%- endmatch -%}
|
{%- endmatch -%}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user