mirror of
https://forgejo.ellis.link/continuwuation/continuwuity/
synced 2026-04-26 20:15:11 +00:00
Compare commits
101 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a91ef01a7f | |||
| 4a6295e374 | |||
| 4da955438e | |||
| 9210ee2b42 | |||
| b60af7c282 | |||
| d2d580c76c | |||
| c507ff6687 | |||
| 70c62158d0 | |||
| b242bad6d2 | |||
| a1ad9f0144 | |||
| c85e710760 | |||
| 59346fc766 | |||
| 9c5e735888 | |||
| fe74e82318 | |||
| cb79a3b9d7 | |||
| ebc8df1c4d | |||
| b667a963cf | |||
| 5a6b909b37 | |||
| dba9cf0ad2 | |||
| 287ddd9bc5 | |||
| 79a278b9e8 | |||
| 6c5d658ef2 | |||
| 70c43abca8 | |||
| 6a9b47c52e | |||
| c042de96f8 | |||
| 7a6acd1c82 | |||
| d260c4fcc2 | |||
| fa15de9764 | |||
| e6c7a4ae60 | |||
| 5bed4ad81d | |||
| 587abe9d14 | |||
| c499042a76 | |||
| 86e450a835 | |||
| 4c796029bb | |||
| fc3615c46b | |||
| 7375f7a68e | |||
| ae28fe92d2 | |||
| 00eeeb78de | |||
| a028049e6f | |||
| 7b159bc8c8 | |||
| 66fcedf08b | |||
| 3f790844f3 | |||
| 89be9d1efc | |||
| 60dd6baffd | |||
| 99a10998b4 | |||
| 05c6b5df75 | |||
| 74db426c6b | |||
| 344d68dabc | |||
| d3ee9c407a | |||
| da956b1a2a | |||
| 244bf6ed2f | |||
| 52a51f1de0 | |||
| 6e118f445b | |||
| e3cf288f39 | |||
| 6e6c9ae4cb | |||
| 5a2a1b6240 | |||
| fb536ca1ce | |||
| d22d47954f | |||
| d48cc46643 | |||
| 8cf2d175d6 | |||
| 205ac22008 | |||
| d353446488 | |||
| 77e8fd1744 | |||
| 7fa7b129c0 | |||
| 247bc15659 | |||
| 88a35e139d | |||
| 37574ef5cc | |||
| 1c816850ed | |||
| 3483059e1c | |||
| d865dd4454 | |||
| adc7c5ac49 | |||
| 112403e470 | |||
| ea0a124981 | |||
| bf205fb13c | |||
| 9a6408f98f | |||
| ca77970ff3 | |||
| 42f4ec34cd | |||
| ecf74bb31f | |||
| 8c716befdc | |||
| a8209d1dd9 | |||
| 9552dd7485 | |||
| 88c84f221f | |||
| a10bd71945 | |||
| 2f11bf4d74 | |||
| 1e8748d1a0 | |||
| 70ef6e4211 | |||
| 212c1bc14d | |||
| ce46b6869f | |||
| a18b8254d0 | |||
| 279f7cbfe4 | |||
| 006c57face | |||
| d52e0dc014 | |||
| 4b873a1b95 | |||
| 76865e6f91 | |||
| 99f16c2dfc | |||
| 5ac82f36f3 | |||
| c249dd992e | |||
| 0956779802 | |||
| a83c1f1513 | |||
| 8b5e4d8fe1 | |||
| 7502a944d7 |
@@ -43,7 +43,7 @@ jobs:
|
||||
name: Renovate
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ghcr.io/renovatebot/renovate:42.11.0@sha256:656c1e5b808279eac16c37b89562fb4c699e02fc7e219244f4a1fc2f0a7ce367
|
||||
image: ghcr.io/renovatebot/renovate:42.70.2@sha256:3c2ac1b94fa92ef2fa4d1a0493f2c3ba564454720a32fdbcac2db2846ff1ee47
|
||||
options: --tmpfs /tmp:exec
|
||||
steps:
|
||||
- name: Checkout
|
||||
|
||||
@@ -23,7 +23,7 @@ jobs:
|
||||
persist-credentials: true
|
||||
token: ${{ secrets.FORGEJO_TOKEN }}
|
||||
|
||||
- uses: https://github.com/cachix/install-nix-action@7ab6e7fd29da88e74b1e314a4ae9ac6b5cda3801 # v31.8.0
|
||||
- uses: https://github.com/cachix/install-nix-action@4e002c8ec80594ecd40e759629461e26c8abed15 # v31.9.0
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ repos:
|
||||
stages: [commit-msg]
|
||||
|
||||
- repo: https://github.com/crate-ci/committed
|
||||
rev: v1.1.9
|
||||
rev: v1.1.10
|
||||
hooks:
|
||||
- id: committed
|
||||
|
||||
|
||||
Vendored
+1
-2
@@ -7,6 +7,5 @@
|
||||
"continuwuity",
|
||||
"homeserver",
|
||||
"homeservers"
|
||||
],
|
||||
"rust-analyzer.cargo.features": ["full"]
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,3 +1,49 @@
|
||||
# Continuwuity 0.5.3 (2026-01-12)
|
||||
|
||||
## Features
|
||||
|
||||
- Improve the display of nested configuration with the `!admin server show-config` command. Contributed by @Jade (#1279)
|
||||
|
||||
## Bugfixes
|
||||
|
||||
- Fixed `M_BAD_JSON` error when sending invites to other servers or when providing joins. Contributed by @nex (#1286)
|
||||
|
||||
|
||||
## Docs
|
||||
|
||||
- Improve admin command documentation generation. Contributed by @ginger (#1280)
|
||||
|
||||
|
||||
## Misc
|
||||
|
||||
- Improve timeout-related code for federation and URL previews. Contributed by @Jade (#1278)
|
||||
|
||||
|
||||
# Continuwuity 0.5.2 (2026-01-09)
|
||||
|
||||
## Features
|
||||
|
||||
- Added support for issuing additional registration tokens, stored in the database, which supplement the existing registration token hardcoded in the config file. These tokens may optionally expire after a certain number of uses or after a certain amount of time has passed. Additionally, the `registration_token_file` configuration option is superseded by this feature and **has been removed**. Use the new `!admin token` command family to manage registration tokens. Contributed by @ginger (#783).
|
||||
- Implemented a configuration defined admin list independent of the admin room. Contributed by @Terryiscool160. (#1253)
|
||||
- Added support for invite and join anti-spam via Draupnir and Meowlnir, similar to that of synapse-http-antispam. Contributed by @nex. (#1263)
|
||||
- Implemented account locking functionality, to complement user suspension. Contributed by @nex. (#1266)
|
||||
- Added admin command to forcefully log out all of a user's existing sessions. Contributed by @nex. (#1271)
|
||||
- Implemented toggling the ability for an account to log in without mutating any of its data. Contributed by @nex. (#1272)
|
||||
- Add support for custom room create event timestamps, to allow generating custom prefixes in hashed room IDs. Contributed by @nex. (#1277)
|
||||
- Certain potentially dangerous admin commands are now restricted to only be usable in the admin room and server console. Contributed by @ginger.
|
||||
|
||||
## Bugfixes
|
||||
|
||||
- Fixed unreliable room summary fetching and improved error messages. Contributed by @nex. (#1257)
|
||||
- Client requested timeout parameter is now applied to e2ee key lookups and claims. Related federation requests are now also concurrent. Contributed by @nex. (#1261)
|
||||
- Fixed the whoami endpoint returning HTTP 404 instead of HTTP 403, which confused some appservices. Contributed by @nex. (#1276)
|
||||
|
||||
## Misc
|
||||
|
||||
- The `console` feature is now enabled by default, allowing the server console to be used for running admin commands directly. To automatically open the console on startup, set the `admin_console_automatic` config option to `true`. Contributed by @ginger.
|
||||
- We now (finally) document our container image mirrors. Contributed by @Jade
|
||||
|
||||
|
||||
# Continuwuity 0.5.0 (2025-12-30)
|
||||
|
||||
**This release contains a CRITICAL vulnerability patch, and you must update as soon as possible**
|
||||
|
||||
Generated
+731
-502
File diff suppressed because it is too large
Load Diff
+73
-76
@@ -1,27 +1,18 @@
|
||||
#cargo-features = ["profile-rustflags"]
|
||||
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
members = ["src/*", "xtask/*"]
|
||||
members = ["src/*", "xtask/"]
|
||||
default-members = ["src/*"]
|
||||
|
||||
[workspace.package]
|
||||
authors = [
|
||||
"June Clementine Strawberry <june@girlboss.ceo>",
|
||||
"strawberry <strawberry@puppygock.gay>", # woof
|
||||
"Jason Volk <jason@zemos.net>",
|
||||
]
|
||||
categories = ["network-programming"]
|
||||
description = "a very cool Matrix chat homeserver written in Rust"
|
||||
authors = ["Continuwuity Team and contributors <team@continuwuity.org>"]
|
||||
description = "A Matrix homeserver written in Rust, the official continuation of the conduwuit homeserver."
|
||||
edition = "2024"
|
||||
homepage = "https://continuwuity.org/"
|
||||
keywords = ["chat", "matrix", "networking", "server", "uwu"]
|
||||
license = "Apache-2.0"
|
||||
# See also `rust-toolchain.toml`
|
||||
readme = "README.md"
|
||||
repository = "https://forgejo.ellis.link/continuwuation/continuwuity"
|
||||
rust-version = "1.86.0"
|
||||
version = "0.5.1"
|
||||
version = "0.5.3"
|
||||
|
||||
[workspace.metadata.crane]
|
||||
name = "conduwuit"
|
||||
@@ -33,11 +24,11 @@ features = ["serde"]
|
||||
[workspace.dependencies.smallvec]
|
||||
version = "1.14.0"
|
||||
features = [
|
||||
"const_generics",
|
||||
"const_new",
|
||||
"serde",
|
||||
"union",
|
||||
"write",
|
||||
"const_generics",
|
||||
"const_new",
|
||||
"serde",
|
||||
"union",
|
||||
"write",
|
||||
]
|
||||
|
||||
[workspace.dependencies.smallstr]
|
||||
@@ -96,13 +87,13 @@ version = "1.11.1"
|
||||
version = "0.7.9"
|
||||
default-features = false
|
||||
features = [
|
||||
"form",
|
||||
"http1",
|
||||
"http2",
|
||||
"json",
|
||||
"matched-path",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"form",
|
||||
"http1",
|
||||
"http2",
|
||||
"json",
|
||||
"matched-path",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[workspace.dependencies.axum-extra]
|
||||
@@ -149,10 +140,10 @@ features = ["aws_lc_rs"]
|
||||
version = "0.12.15"
|
||||
default-features = false
|
||||
features = [
|
||||
"rustls-tls-native-roots",
|
||||
"socks",
|
||||
"hickory-dns",
|
||||
"http2",
|
||||
"rustls-tls-native-roots",
|
||||
"socks",
|
||||
"hickory-dns",
|
||||
"http2",
|
||||
]
|
||||
|
||||
[workspace.dependencies.serde]
|
||||
@@ -167,7 +158,7 @@ features = ["raw_value"]
|
||||
|
||||
# Used for appservice registration files
|
||||
[workspace.dependencies.serde-saphyr]
|
||||
version = "0.0.10"
|
||||
version = "0.0.14"
|
||||
|
||||
# Used to load forbidden room/user regex from config
|
||||
[workspace.dependencies.serde_regex]
|
||||
@@ -188,18 +179,18 @@ default-features = false
|
||||
version = "0.25.5"
|
||||
default-features = false
|
||||
features = [
|
||||
"jpeg",
|
||||
"png",
|
||||
"gif",
|
||||
"webp",
|
||||
"jpeg",
|
||||
"png",
|
||||
"gif",
|
||||
"webp",
|
||||
]
|
||||
|
||||
[workspace.dependencies.blurhash]
|
||||
version = "0.2.3"
|
||||
default-features = false
|
||||
features = [
|
||||
"fast-linear-to-srgb",
|
||||
"image",
|
||||
"fast-linear-to-srgb",
|
||||
"image",
|
||||
]
|
||||
|
||||
# logging
|
||||
@@ -229,13 +220,13 @@ default-features = false
|
||||
version = "4.5.35"
|
||||
default-features = false
|
||||
features = [
|
||||
"derive",
|
||||
"env",
|
||||
"error-context",
|
||||
"help",
|
||||
"std",
|
||||
"string",
|
||||
"usage",
|
||||
"derive",
|
||||
"env",
|
||||
"error-context",
|
||||
"help",
|
||||
"std",
|
||||
"string",
|
||||
"usage",
|
||||
]
|
||||
|
||||
[workspace.dependencies.futures]
|
||||
@@ -247,15 +238,15 @@ features = ["std", "async-await"]
|
||||
version = "1.44.2"
|
||||
default-features = false
|
||||
features = [
|
||||
"fs",
|
||||
"net",
|
||||
"macros",
|
||||
"sync",
|
||||
"signal",
|
||||
"time",
|
||||
"rt-multi-thread",
|
||||
"io-util",
|
||||
"tracing",
|
||||
"fs",
|
||||
"net",
|
||||
"macros",
|
||||
"sync",
|
||||
"signal",
|
||||
"time",
|
||||
"rt-multi-thread",
|
||||
"io-util",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[workspace.dependencies.tokio-metrics]
|
||||
@@ -280,18 +271,18 @@ default-features = false
|
||||
version = "1.6.0"
|
||||
default-features = false
|
||||
features = [
|
||||
"server",
|
||||
"http1",
|
||||
"http2",
|
||||
"server",
|
||||
"http1",
|
||||
"http2",
|
||||
]
|
||||
|
||||
[workspace.dependencies.hyper-util]
|
||||
version = "=0.1.17"
|
||||
default-features = false
|
||||
features = [
|
||||
"server-auto",
|
||||
"server-graceful",
|
||||
"tokio",
|
||||
"server-auto",
|
||||
"server-graceful",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
# to support multiple variations of setting a config option
|
||||
@@ -310,9 +301,9 @@ features = ["env", "toml"]
|
||||
version = "0.25.1"
|
||||
default-features = false
|
||||
features = [
|
||||
"serde",
|
||||
"system-config",
|
||||
"tokio",
|
||||
"serde",
|
||||
"system-config",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
# Used for conduwuit::Error type
|
||||
@@ -351,7 +342,7 @@ version = "0.1.2"
|
||||
# Used for matrix spec type definitions and helpers
|
||||
[workspace.dependencies.ruma]
|
||||
git = "https://forgejo.ellis.link/continuwuation/ruwuma"
|
||||
rev = "27abe0dcd33fd4056efc94bab3582646b31b6ce9"
|
||||
rev = "85d00fb5746cba23904234b4fd3c838dcf141541"
|
||||
features = [
|
||||
"compat",
|
||||
"rand",
|
||||
@@ -381,13 +372,13 @@ features = [
|
||||
"unstable-msc4095",
|
||||
"unstable-msc4121",
|
||||
"unstable-msc4125",
|
||||
"unstable-msc4155",
|
||||
"unstable-msc4155",
|
||||
"unstable-msc4186",
|
||||
"unstable-msc4203", # sending to-device events to appservices
|
||||
"unstable-msc4210", # remove legacy mentions
|
||||
"unstable-extensible-events",
|
||||
"unstable-pdu",
|
||||
"unstable-msc4155"
|
||||
"unstable-msc4155"
|
||||
]
|
||||
|
||||
[workspace.dependencies.rust-rocksdb]
|
||||
@@ -395,11 +386,11 @@ git = "https://forgejo.ellis.link/continuwuation/rust-rocksdb-zaidoon1"
|
||||
rev = "61d9d23872197e9ace4a477f2617d5c9f50ecb23"
|
||||
default-features = false
|
||||
features = [
|
||||
"multi-threaded-cf",
|
||||
"mt_static",
|
||||
"lz4",
|
||||
"zstd",
|
||||
"bzip2",
|
||||
"multi-threaded-cf",
|
||||
"mt_static",
|
||||
"lz4",
|
||||
"zstd",
|
||||
"bzip2",
|
||||
]
|
||||
|
||||
[workspace.dependencies.sha2]
|
||||
@@ -458,16 +449,16 @@ git = "https://forgejo.ellis.link/continuwuation/jemallocator"
|
||||
rev = "82af58d6a13ddd5dcdc7d4e91eae3b63292995b8"
|
||||
default-features = false
|
||||
features = [
|
||||
"background_threads_runtime_support",
|
||||
"unprefixed_malloc_on_supported_platforms",
|
||||
"background_threads_runtime_support",
|
||||
"unprefixed_malloc_on_supported_platforms",
|
||||
]
|
||||
[workspace.dependencies.tikv-jemallocator]
|
||||
git = "https://forgejo.ellis.link/continuwuation/jemallocator"
|
||||
rev = "82af58d6a13ddd5dcdc7d4e91eae3b63292995b8"
|
||||
default-features = false
|
||||
features = [
|
||||
"background_threads_runtime_support",
|
||||
"unprefixed_malloc_on_supported_platforms",
|
||||
"background_threads_runtime_support",
|
||||
"unprefixed_malloc_on_supported_platforms",
|
||||
]
|
||||
[workspace.dependencies.tikv-jemalloc-ctl]
|
||||
git = "https://forgejo.ellis.link/continuwuation/jemallocator"
|
||||
@@ -491,9 +482,9 @@ default-features = false
|
||||
version = "0.1.2"
|
||||
default-features = false
|
||||
features = [
|
||||
"static",
|
||||
"gcc",
|
||||
"light",
|
||||
"static",
|
||||
"gcc",
|
||||
"light",
|
||||
]
|
||||
|
||||
[workspace.dependencies.rustyline-async]
|
||||
@@ -557,6 +548,10 @@ features = ["sync", "tls-rustls", "rustls-provider"]
|
||||
[workspace.dependencies.resolv-conf]
|
||||
version = "0.7.5"
|
||||
|
||||
# Used by stitched ordering
|
||||
[workspace.dependencies.indexmap]
|
||||
version = "2.13.0"
|
||||
|
||||
#
|
||||
# Patches
|
||||
#
|
||||
@@ -848,6 +843,8 @@ unknown_lints = "allow"
|
||||
|
||||
###################
|
||||
cargo = { level = "warn", priority = -1 }
|
||||
# Nobody except for us should be consuming these crates, they don't need metadata
|
||||
cargo_common_metadata = { level = "allow" }
|
||||
|
||||
## some sadness
|
||||
multiple_crate_versions = { level = "allow", priority = 1 }
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
The `console` feature is now enabled by default, allowing the server console to be used for running admin commands directly.
|
||||
@@ -1 +0,0 @@
|
||||
Certain potentially dangerous admin commands are now restricted to only be usable in the admin room and server console.
|
||||
@@ -1 +0,0 @@
|
||||
Implemented a configuration defined admin list independent of the admin room. (@Terryiscool160).
|
||||
@@ -1 +0,0 @@
|
||||
Fixed unreliable room summary fetching and improved error messages. Contributed by @nex.
|
||||
@@ -1,2 +0,0 @@
|
||||
Client requested timeout parameter is now applied to e2ee key lookups and claims. Related federation requests are now
|
||||
also concurrent. Contributed by @nex.
|
||||
@@ -0,0 +1 @@
|
||||
The announcement checker will now announce errors it encounters in the first run to the admin room, plus a few other misc improvements. Contributed by @Jade
|
||||
@@ -0,0 +1 @@
|
||||
Fix the generated configuration containing uncommented optional sections. Contributed by @Jade
|
||||
@@ -0,0 +1 @@
|
||||
Fixed specification non-compliance when handling remote media errors. Contributed by @nex.
|
||||
+61
-19
@@ -340,7 +340,9 @@
|
||||
# this to be high to account for extremely large room joins, slow
|
||||
# homeservers, your own resources etc.
|
||||
#
|
||||
#federation_timeout = 300
|
||||
# Joins have 6x the timeout.
|
||||
#
|
||||
#federation_timeout = 60
|
||||
|
||||
# MSC4284 Policy server request timeout (seconds). Generally policy
|
||||
# servers should respond near instantly, however may slow down under
|
||||
@@ -389,7 +391,15 @@
|
||||
#
|
||||
#appservice_idle_timeout = 300
|
||||
|
||||
# Notification gateway pusher idle connection pool timeout.
|
||||
# Notification gateway pusher request connection timeout (seconds).
|
||||
#
|
||||
#pusher_conn_timeout = 15
|
||||
|
||||
# Notification gateway pusher total request timeout (seconds).
|
||||
#
|
||||
#pusher_timeout = 60
|
||||
|
||||
# Notification gateway pusher idle connection pool timeout (seconds).
|
||||
#
|
||||
#pusher_idle_timeout = 15
|
||||
|
||||
@@ -421,7 +431,7 @@
|
||||
# `yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse`
|
||||
#
|
||||
# If you would like registration only via token reg, please configure
|
||||
# `registration_token` or `registration_token_file`.
|
||||
# `registration_token`.
|
||||
#
|
||||
#allow_registration = false
|
||||
|
||||
@@ -452,22 +462,13 @@
|
||||
# `yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse`
|
||||
# to true to allow open registration without any conditions.
|
||||
#
|
||||
# YOU NEED TO EDIT THIS OR USE registration_token_file.
|
||||
# If you do not want to set a static token, the `!admin token` commands
|
||||
# may also be used to manage registration tokens.
|
||||
#
|
||||
# example: "o&^uCtes4HPf0Vu@F20jQeeWE7"
|
||||
#
|
||||
#registration_token =
|
||||
|
||||
# Path to a file on the system that gets read for additional registration
|
||||
# tokens. Multiple tokens can be added if you separate them with
|
||||
# whitespace
|
||||
#
|
||||
# continuwuity must be able to access the file, and it must not be empty
|
||||
#
|
||||
# example: "/etc/continuwuity/.reg_token"
|
||||
#
|
||||
#registration_token_file =
|
||||
|
||||
# The public site key for reCaptcha. If this is provided, reCaptcha
|
||||
# becomes required during registration. If both captcha *and*
|
||||
# registration token are enabled, both will be required during
|
||||
@@ -1455,6 +1456,11 @@
|
||||
#
|
||||
#url_preview_max_spider_size = 256000
|
||||
|
||||
# Total request timeout for URL previews (seconds). This includes
|
||||
# connection, request, and response body reading time.
|
||||
#
|
||||
#url_preview_timeout = 120
|
||||
|
||||
# Option to decide whether you would like to run the domain allowlist
|
||||
# checks (contains and explicit) on the root domain or not. Does not apply
|
||||
# to URL contains allowlist. Defaults to false.
|
||||
@@ -1647,7 +1653,7 @@
|
||||
|
||||
# Enable the tokio-console. This option is only relevant to developers.
|
||||
#
|
||||
# For more information, see:
|
||||
# For more information, see:
|
||||
# https://continuwuity.org/development.html#debugging-with-tokio-console
|
||||
#
|
||||
#tokio_console = false
|
||||
@@ -1753,10 +1759,6 @@
|
||||
#
|
||||
#config_reload_signal = true
|
||||
|
||||
# This item is undocumented. Please contribute documentation for it.
|
||||
#
|
||||
#ldap = false
|
||||
|
||||
[global.tls]
|
||||
|
||||
# Path to a valid TLS certificate file.
|
||||
@@ -1923,3 +1925,43 @@
|
||||
# example: "(objectClass=conduwuitAdmin)" or "(uid={username})"
|
||||
#
|
||||
#admin_filter = ""
|
||||
|
||||
#[global.antispam]
|
||||
|
||||
#[global.antispam.meowlnir]
|
||||
|
||||
# The base URL on which to contact Meowlnir (before /_meowlnir/antispam).
|
||||
#
|
||||
# Example: "http://127.0.0.1:29339"
|
||||
#
|
||||
#base_url =
|
||||
|
||||
# The authentication secret defined in antispam->secret. Required for
|
||||
# continuwuity to talk to Meowlnir.
|
||||
#
|
||||
#secret =
|
||||
|
||||
# The management room for which to send requests
|
||||
#
|
||||
#management_room =
|
||||
|
||||
# If enabled run all federated join attempts (both federated and local)
|
||||
# through the Meowlnir anti-spam checks.
|
||||
#
|
||||
# By default, only join attempts for rooms with the `fi.mau.spam_checker`
|
||||
# restricted join rule are checked.
|
||||
#
|
||||
#check_all_joins = false
|
||||
|
||||
#[global.antispam.draupnir]
|
||||
|
||||
# The base URL on which to contact Draupnir (before /api/).
|
||||
#
|
||||
# Example: "http://127.0.0.1:29339"
|
||||
#
|
||||
#base_url =
|
||||
|
||||
# The authentication secret defined in
|
||||
# web->synapseHTTPAntispam->authorization
|
||||
#
|
||||
#secret =
|
||||
|
||||
+2
-2
@@ -48,11 +48,11 @@ EOF
|
||||
|
||||
# Developer tool versions
|
||||
# renovate: datasource=github-releases depName=cargo-bins/cargo-binstall
|
||||
ENV BINSTALL_VERSION=1.16.6
|
||||
ENV BINSTALL_VERSION=1.16.7
|
||||
# renovate: datasource=github-releases depName=psastras/sbom-rs
|
||||
ENV CARGO_SBOM_VERSION=0.9.1
|
||||
# renovate: datasource=crate depName=lddtree
|
||||
ENV LDDTREE_VERSION=0.3.7
|
||||
ENV LDDTREE_VERSION=0.4.0
|
||||
# renovate: datasource=crate depName=timelord-cli
|
||||
ENV TIMELORD_VERSION=3.0.1
|
||||
|
||||
|
||||
@@ -18,11 +18,11 @@ RUN --mount=type=cache,target=/etc/apk/cache apk add \
|
||||
|
||||
# Developer tool versions
|
||||
# renovate: datasource=github-releases depName=cargo-bins/cargo-binstall
|
||||
ENV BINSTALL_VERSION=1.16.6
|
||||
ENV BINSTALL_VERSION=1.16.7
|
||||
# renovate: datasource=github-releases depName=psastras/sbom-rs
|
||||
ENV CARGO_SBOM_VERSION=0.9.1
|
||||
# renovate: datasource=crate depName=lddtree
|
||||
ENV LDDTREE_VERSION=0.3.7
|
||||
ENV LDDTREE_VERSION=0.4.0
|
||||
|
||||
# Install unpackaged tools
|
||||
RUN <<EOF
|
||||
|
||||
+2
-7
@@ -57,14 +57,9 @@
|
||||
"name": "/reference/config"
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"type": "dir",
|
||||
"label": "Admin Command Reference",
|
||||
"name": "/reference/admin"
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"label": "Server Reference",
|
||||
"name": "/reference/server"
|
||||
"name": "/reference/admin/"
|
||||
},
|
||||
{
|
||||
"type": "divider"
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
},
|
||||
{
|
||||
"text": "Admin Command Reference",
|
||||
"link": "/reference/admin"
|
||||
"link": "/reference/admin/"
|
||||
},
|
||||
{
|
||||
"text": "Server Reference",
|
||||
|
||||
@@ -11,10 +11,10 @@ ### Use a registry
|
||||
|
||||
| Registry | Image | Notes |
|
||||
| --------------- | --------------------------------------------------------------- | -----------------------|
|
||||
| Forgejo Registry| [forgejo.ellis.link/continuwuation/continuwuity:latest][fj] | Latest tagged image. |
|
||||
| Forgejo Registry| [forgejo.ellis.link/continuwuation/continuwuity:main][fj] | Main branch image. |
|
||||
|
||||
[fj]: https://forgejo.ellis.link/continuwuation/-/packages/container/continuwuity
|
||||
| Forgejo Registry| [forgejo.ellis.link/continuwuation/continuwuity:latest](https://forgejo.ellis.link/continuwuation/-/packages/container/continuwuity/latest) | Latest tagged image. |
|
||||
| Forgejo Registry| [forgejo.ellis.link/continuwuation/continuwuity:main](https://forgejo.ellis.link/continuwuation/-/packages/container/continuwuity/main) | Main branch image. |
|
||||
| Forgejo Registry| [forgejo.ellis.link/continuwuation/continuwuity:latest-maxperf](https://forgejo.ellis.link/continuwuation/-/packages/container/continuwuity/latest-maxperf) | [Performance optimised version.](./generic.mdx#performance-optimised-builds) |
|
||||
| Forgejo Registry| [forgejo.ellis.link/continuwuation/continuwuity:main-maxperf](https://forgejo.ellis.link/continuwuation/-/packages/container/continuwuity/main-maxperf) | [Performance optimised version.](./generic.mdx#performance-optimised-builds) |
|
||||
|
||||
Use
|
||||
|
||||
@@ -24,6 +24,15 @@ ### Use a registry
|
||||
|
||||
to pull it to your machine.
|
||||
|
||||
#### Mirrors
|
||||
|
||||
Images are mirrored to multiple locations automatically, on a schedule:
|
||||
|
||||
- `ghcr.io/continuwuity/continuwuity`
|
||||
- `docker.io/jadedblueeyes/continuwuity`
|
||||
- `registry.gitlab.com/continuwuity/continuwuity`
|
||||
- `git.nexy7574.co.uk/mirrored/continuwuity` (releases only, no `main`)
|
||||
|
||||
### Run
|
||||
|
||||
When you have the image, you can simply run it with
|
||||
@@ -49,7 +58,7 @@ ### Run
|
||||
flag, which cleans up everything related to your container after you stop
|
||||
it.
|
||||
|
||||
### Docker-compose
|
||||
### Docker Compose
|
||||
|
||||
If the `docker run` command is not suitable for you or your setup, you can also use one
|
||||
of the provided `docker-compose` files.
|
||||
@@ -158,8 +167,19 @@ # Build for the current platform and load into the local Docker daemon
|
||||
# Example: Build for specific platforms and push to a registry.
|
||||
# docker buildx build --platform linux/amd64,linux/arm64 --tag registry.io/org/continuwuity:latest -f docker/Dockerfile . --push
|
||||
|
||||
# Example: Build binary optimized for the current CPU
|
||||
# docker buildx build --load --tag continuwuity:latest --build-arg TARGET_CPU=native -f docker/Dockerfile .
|
||||
# Example: Build binary optimised for the current CPU (standard release profile)
|
||||
# docker buildx build --load \
|
||||
# --tag continuwuity:latest \
|
||||
# --build-arg TARGET_CPU=native \
|
||||
# -f docker/Dockerfile .
|
||||
|
||||
# Example: Build maxperf variant (release-max-perf profile with LTO)
|
||||
# Optimised for runtime performance and smaller binary size, but requires longer build time
|
||||
# docker buildx build --load \
|
||||
# --tag continuwuity:latest-maxperf \
|
||||
# --build-arg TARGET_CPU=native \
|
||||
# --build-arg RUST_PROFILE=release-max-perf \
|
||||
# -f docker/Dockerfile .
|
||||
```
|
||||
|
||||
Refer to the Docker Buildx documentation for more advanced build options.
|
||||
@@ -198,5 +218,3 @@ ### Use Traefik as Proxy
|
||||
## Voice communication
|
||||
|
||||
See the [TURN](../turn.md) page.
|
||||
|
||||
[nix-buildlayeredimage]: https://ryantm.github.io/nixpkgs/builders/images/dockertools/#ssec-pkgs-dockerTools-buildLayeredImage
|
||||
|
||||
+28
-18
@@ -8,29 +8,39 @@ # Generic deployment documentation
|
||||
|
||||
## Installing Continuwuity
|
||||
|
||||
### Static prebuilt binary
|
||||
### Prebuilt binary
|
||||
|
||||
You may simply download the binary that fits your machine architecture (x86_64
|
||||
or aarch64). Run `uname -m` to see what you need.
|
||||
Download the binary for your architecture (x86_64 or aarch64) -
|
||||
run the `uname -m` to check which you need.
|
||||
|
||||
You can download prebuilt fully static musl binaries from the latest tagged
|
||||
release [here](https://forgejo.ellis.link/continuwuation/continuwuity/releases/latest) or
|
||||
from the `main` CI branch workflow artifact output. These also include Debian/Ubuntu
|
||||
packages.
|
||||
Prebuilt binaries are available from:
|
||||
- **Tagged releases**: [Latest release page](https://forgejo.ellis.link/continuwuation/continuwuity/releases/latest)
|
||||
- **Development builds**: CI artifacts from the `main` branch
|
||||
(includes Debian/Ubuntu packages)
|
||||
|
||||
You can download these directly using curl. The `ci-bins` are CI workflow binaries organized by commit
|
||||
hash/revision, and `releases` are tagged releases. Sort by descending last
|
||||
modified date to find the latest.
|
||||
When browsing CI artifacts, `ci-bins` contains binaries organised
|
||||
by commit hash, while `releases` contains tagged versions. Sort
|
||||
by last modified date to find the most recent builds.
|
||||
|
||||
These binaries have jemalloc and io_uring statically linked and included with
|
||||
them, so no additional dynamic dependencies need to be installed.
|
||||
The binaries require jemalloc and io_uring on the host system. Currently
|
||||
we can't cross-build static binaries - contributions are welcome here.
|
||||
|
||||
For the **best** performance: if you are using an `x86_64` CPU made in the last ~15 years,
|
||||
we recommend using the `-haswell-` optimized binaries. These set
|
||||
`-march=haswell`, which provides the most compatible and highest performance with
|
||||
optimized binaries. The database backend, RocksDB, benefits most from this as it
|
||||
uses hardware-accelerated CRC32 hashing/checksumming, which is critical
|
||||
for performance.
|
||||
#### Performance-optimised builds
|
||||
|
||||
For x86_64 systems with CPUs from the last ~15 years, use the
|
||||
`-haswell-` optimised binaries for best performance. These
|
||||
binaries enable hardware-accelerated CRC32 checksumming in
|
||||
RocksDB, which significantly improves database performance.
|
||||
The haswell instruction set provides an excellent balance of
|
||||
compatibility and speed.
|
||||
|
||||
If you're using Docker instead, equivalent performance-optimised
|
||||
images are available with the `-maxperf` suffix (e.g.
|
||||
`forgejo.ellis.link/continuwuation/continuwuity:latest-maxperf`).
|
||||
These images use the `release-max-perf`
|
||||
build profile with
|
||||
[link-time optimisation (LTO)](https://doc.rust-lang.org/cargo/reference/profiles.html#lto)
|
||||
and, for amd64, target the haswell CPU architecture.
|
||||
|
||||
### Compiling
|
||||
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
"message": "Welcome to Continuwuity! Important announcements about the project will appear here."
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"mention_room": true,
|
||||
"date": "2025-12-30",
|
||||
"message": "Continuwuity v0.5.1 has been released. **The release contains a fix for the critical vulnerability [GHSA-m5p2-vccg-8c9v](https://github.com/continuwuity/continuwuity/security/advisories/GHSA-m5p2-vccg-8c9v) (embargoed) affecting all Conduit-derived servers. Update as soon as possible.**\n\nThis has been *actively exploited* to attempt account takeover and forge events bricking the Continuwuity rooms. The new space is accessible at [Continuwuity (room list)](https://matrix.to/#/!8cR4g-i9ucof69E4JHNg9LbPVkGprHb3SzcrGBDDJgk?via=continuwuity.org&via=starstruck.systems&via=gingershaped.computer)\n"
|
||||
"id": 8,
|
||||
"mention_room": false,
|
||||
"date": "2026-01-12",
|
||||
"message": "Hey everyone!\n\nJust letting you know we've released [v0.5.3](https://forgejo.ellis.link/continuwuation/continuwuity/releases/tag/v0.5.3) - this one is a bit of a hotfix for an issue with inviting and allowing others to join rooms.\n\nIf you appreceate the round-the-clock work we've been doing to keep your servers secure over this holiday period, we'd really appreciate your support - you can sponsor individuals on our team using the 'sponsor' button at the top of [our GitHub repository](https://github.com/continuwuity/continuwuity). If you can't do that, even a star helps - spreading the word and advocating for our project helps keep it going.\n\nHave a lovely rest of your year \\\n[Jade \\(she/her\\)](https://matrix.to/#/%40jade%3Aellis.link) \n🩵"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -8,10 +8,5 @@
|
||||
"type": "file",
|
||||
"name": "admin",
|
||||
"label": "Admin Commands"
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"name": "server",
|
||||
"label": "Server command"
|
||||
}
|
||||
]
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,29 @@
|
||||
<!-- This file is generated by `cargo xtask generate-docs`. Do not edit. -->
|
||||
# `!admin appservices`
|
||||
|
||||
Commands for managing appservices
|
||||
|
||||
|
||||
## `!admin appservices register`
|
||||
|
||||
Register an appservice using its registration YAML
|
||||
|
||||
This command needs a YAML generated by an appservice (such as a bridge), which must be provided in a Markdown code block below the command.
|
||||
|
||||
Registering a new bridge using the ID of an existing bridge will replace the old one.
|
||||
|
||||
## `!admin appservices unregister`
|
||||
|
||||
Unregister an appservice using its ID
|
||||
|
||||
You can find the ID using the `list-appservices` command.
|
||||
|
||||
## `!admin appservices show-appservice-config`
|
||||
|
||||
Show an appservice's config using its ID
|
||||
|
||||
You can find the ID using the `list-appservices` command.
|
||||
|
||||
## `!admin appservices list-registered`
|
||||
|
||||
List all the currently registered appservices
|
||||
@@ -0,0 +1,9 @@
|
||||
<!-- This file is generated by `cargo xtask generate-docs`. Do not edit. -->
|
||||
# `!admin check`
|
||||
|
||||
Commands for checking integrity
|
||||
|
||||
|
||||
## `!admin check check-all-users`
|
||||
|
||||
Uses the iterator in `src/database/key_value/users.rs` to iterator over every user in our database (remote and local). Reports total count, any errors if there were any, etc
|
||||
@@ -0,0 +1,135 @@
|
||||
<!-- This file is generated by `cargo xtask generate-docs`. Do not edit. -->
|
||||
# `!admin debug`
|
||||
|
||||
Commands for debugging things
|
||||
|
||||
|
||||
## `!admin debug echo`
|
||||
|
||||
Echo input of admin command
|
||||
|
||||
## `!admin debug get-auth-chain`
|
||||
|
||||
Get the auth_chain of a PDU
|
||||
|
||||
## `!admin debug parse-pdu`
|
||||
|
||||
Parse and print a PDU from a JSON
|
||||
|
||||
The PDU event is only checked for validity and is not added to the database.
|
||||
|
||||
This command needs a JSON blob provided in a Markdown code block below the command.
|
||||
|
||||
## `!admin debug get-pdu`
|
||||
|
||||
Retrieve and print a PDU by EventID from the Continuwuity database
|
||||
|
||||
## `!admin debug get-short-pdu`
|
||||
|
||||
Retrieve and print a PDU by PduId from the Continuwuity database
|
||||
|
||||
## `!admin debug get-remote-pdu`
|
||||
|
||||
Attempts to retrieve a PDU from a remote server. **Does not** insert it into the database or persist it anywhere
|
||||
|
||||
## `!admin debug get-remote-pdu-list`
|
||||
|
||||
Same as `get-remote-pdu` but accepts a codeblock newline delimited list of PDUs and a single server to fetch from
|
||||
|
||||
## `!admin debug get-room-state`
|
||||
|
||||
Gets all the room state events for the specified room.
|
||||
|
||||
This is functionally equivalent to `GET /_matrix/client/v3/rooms/{roomid}/state`, except the admin command does *not* check if the sender user is allowed to see state events. This is done because it's implied that server admins here have database access and can see/get room info themselves anyways if they were malicious admins.
|
||||
|
||||
Of course the check is still done on the actual client API.
|
||||
|
||||
## `!admin debug get-signing-keys`
|
||||
|
||||
Get and display signing keys from local cache or remote server
|
||||
|
||||
## `!admin debug get-verify-keys`
|
||||
|
||||
Get and display signing keys from local cache or remote server
|
||||
|
||||
## `!admin debug ping`
|
||||
|
||||
Sends a federation request to the remote server's `/_matrix/federation/v1/version` endpoint and measures the latency it took for the server to respond
|
||||
|
||||
## `!admin debug force-device-list-updates`
|
||||
|
||||
Forces device lists for all local and remote users to be updated (as having new keys available)
|
||||
|
||||
## `!admin debug change-log-level`
|
||||
|
||||
Change tracing log level/filter on the fly
|
||||
|
||||
This accepts the same format as the `log` config option.
|
||||
|
||||
## `!admin debug verify-json`
|
||||
|
||||
Verify JSON signatures
|
||||
|
||||
This command needs a JSON blob provided in a Markdown code block below the command.
|
||||
|
||||
## `!admin debug verify-pdu`
|
||||
|
||||
Verify PDU
|
||||
|
||||
This re-verifies a PDU existing in the database found by ID.
|
||||
|
||||
## `!admin debug first-pdu-in-room`
|
||||
|
||||
Prints the very first PDU in the specified room (typically m.room.create)
|
||||
|
||||
## `!admin debug latest-pdu-in-room`
|
||||
|
||||
Prints the latest ("last") PDU in the specified room (typically a message)
|
||||
|
||||
## `!admin debug force-set-room-state-from-server`
|
||||
|
||||
Forcefully replaces the room state of our local copy of the specified room, with the copy (auth chain and room state events) the specified remote server says.
|
||||
|
||||
A common desire for room deletion is to simply "reset" our copy of the room. While this admin command is not a replacement for that, if you know you have split/broken room state and you know another server in the room that has the best/working room state, this command can let you use their room state. Such example is your server saying users are in a room, but other servers are saying they're not in the room in question.
|
||||
|
||||
This command will get the latest PDU in the room we know about, and request the room state at that point in time via `/_matrix/federation/v1/state/{roomId}`.
|
||||
|
||||
## `!admin debug resolve-true-destination`
|
||||
|
||||
Runs a server name through Continuwuity's true destination resolution process
|
||||
|
||||
Useful for debugging well-known issues
|
||||
|
||||
## `!admin debug memory-stats`
|
||||
|
||||
Print extended memory usage
|
||||
|
||||
Optional argument is a character mask (a sequence of characters in any order) which enable additional extended statistics. Known characters are "abdeglmx". For convenience, a '*' will enable everything.
|
||||
|
||||
## `!admin debug runtime-metrics`
|
||||
|
||||
Print general tokio runtime metric totals
|
||||
|
||||
## `!admin debug runtime-interval`
|
||||
|
||||
Print detailed tokio runtime metrics accumulated since last command invocation
|
||||
|
||||
## `!admin debug time`
|
||||
|
||||
Print the current time
|
||||
|
||||
## `!admin debug database-stats`
|
||||
|
||||
Get database statistics
|
||||
|
||||
## `!admin debug trim-memory`
|
||||
|
||||
Trim memory usage
|
||||
|
||||
## `!admin debug database-files`
|
||||
|
||||
List database files
|
||||
|
||||
## `!admin debug tester`
|
||||
|
||||
Developer test stubs
|
||||
@@ -0,0 +1,29 @@
|
||||
<!-- This file is generated by `cargo xtask generate-docs`. Do not edit. -->
|
||||
# `!admin federation`
|
||||
|
||||
Commands for managing federation
|
||||
|
||||
|
||||
## `!admin federation incoming-federation`
|
||||
|
||||
List all rooms we are currently handling an incoming pdu from
|
||||
|
||||
## `!admin federation disable-room`
|
||||
|
||||
Disables incoming federation handling for a room
|
||||
|
||||
## `!admin federation enable-room`
|
||||
|
||||
Enables incoming federation handling for a room again
|
||||
|
||||
## `!admin federation fetch-support-well-known`
|
||||
|
||||
Fetch `/.well-known/matrix/support` from the specified server
|
||||
|
||||
Despite the name, this is not a federation endpoint and does not go through the federation / server resolution process as per-spec this is supposed to be served at the server_name.
|
||||
|
||||
Respecting homeservers put this file here for listing administration, moderation, and security inquiries. This command provides a way to easily fetch that information.
|
||||
|
||||
## `!admin federation remote-user-in-rooms`
|
||||
|
||||
Lists all the rooms we share/track with the specified *remote* user
|
||||
@@ -0,0 +1,23 @@
|
||||
<!-- This file is generated by `cargo xtask generate-docs`. Do not edit. -->
|
||||
# Admin Command Reference
|
||||
|
||||
Admin commands allow server administrators to manage the server from within their Matrix client. "Server administrators" by default means only those users which are members of the admin room, but additional server admins may be added using the `admins_list` configuration option.
|
||||
|
||||
## Running commands
|
||||
|
||||
* All commands listed here may be used by server administrators in the admin room by sending them as messages.
|
||||
* If the `admin_escape_commands` configuration option is enabled, server administrators may run certain commands in public rooms by prefixing them with a single backslash. These commands will only run on _their_ homeserver, even if they are a member of another homeserver's admin room. Some sensitive commands cannot be used outside the admin room and will return an error.
|
||||
* All commands listed here may be used in the server's console, if it is enabled. Commands entered in the console do not require the `!admin` prefix.
|
||||
|
||||
## Categories
|
||||
|
||||
- [`!admin appservices`](appservices/): Commands for managing appservices
|
||||
- [`!admin users`](users/): Commands for managing local users
|
||||
- [`!admin token`](token/): Commands for managing registration tokens
|
||||
- [`!admin rooms`](rooms/): Commands for managing rooms
|
||||
- [`!admin federation`](federation/): Commands for managing federation
|
||||
- [`!admin server`](server/): Commands for managing the server
|
||||
- [`!admin media`](media/): Commands for managing media
|
||||
- [`!admin check`](check/): Commands for checking integrity
|
||||
- [`!admin debug`](debug/): Commands for debugging things
|
||||
- [`!admin query`](query/): Low-level queries for database getters and iterators
|
||||
@@ -0,0 +1,38 @@
|
||||
<!-- This file is generated by `cargo xtask generate-docs`. Do not edit. -->
|
||||
# `!admin media`
|
||||
|
||||
Commands for managing media
|
||||
|
||||
|
||||
## `!admin media delete`
|
||||
|
||||
Deletes a single media file from our database and on the filesystem via a single MXC URL or event ID (not redacted)
|
||||
|
||||
## `!admin media delete-list`
|
||||
|
||||
Deletes a codeblock list of MXC URLs from our database and on the filesystem. This will always ignore errors
|
||||
|
||||
## `!admin media delete-past-remote-media`
|
||||
|
||||
Deletes all remote (and optionally local) media created before/after
|
||||
[duration] ago, using filesystem metadata first created at date, or
|
||||
fallback to last modified date. This will always ignore errors by
|
||||
default.
|
||||
|
||||
* Examples:
|
||||
* Delete all remote media older than a year:
|
||||
|
||||
`!admin media delete-past-remote-media -b 1y`
|
||||
|
||||
* Delete all remote and local media from 3 days ago, up until now:
|
||||
|
||||
`!admin media delete-past-remote-media -a 3d
|
||||
-yes-i-want-to-delete-local-media`
|
||||
|
||||
## `!admin media delete-all-from-user`
|
||||
|
||||
Deletes all the local media from a local user on our server. This will always ignore errors by default
|
||||
|
||||
## `!admin media delete-all-from-server`
|
||||
|
||||
Deletes all remote media from the specified remote server. This will always ignore errors by default
|
||||
@@ -0,0 +1,181 @@
|
||||
<!-- This file is generated by `cargo xtask generate-docs`. Do not edit. -->
|
||||
# `!admin query`
|
||||
|
||||
Low-level queries for database getters and iterators
|
||||
|
||||
|
||||
## `!admin query account-data`
|
||||
|
||||
account_data.rs iterators and getters
|
||||
|
||||
### `!admin query account-data changes-since`
|
||||
|
||||
Returns all changes to the account data that happened after `since`
|
||||
|
||||
### `!admin query account-data account-data-get`
|
||||
|
||||
Searches the account data for a specific kind
|
||||
|
||||
## `!admin query appservice`
|
||||
|
||||
appservice.rs iterators and getters
|
||||
|
||||
### `!admin query appservice get-registration`
|
||||
|
||||
Gets the appservice registration info/details from the ID as a string
|
||||
|
||||
### `!admin query appservice all`
|
||||
|
||||
Gets all appservice registrations with their ID and registration info
|
||||
|
||||
## `!admin query presence`
|
||||
|
||||
presence.rs iterators and getters
|
||||
|
||||
### `!admin query presence get-presence`
|
||||
|
||||
Returns the latest presence event for the given user
|
||||
|
||||
### `!admin query presence presence-since`
|
||||
|
||||
Iterator of the most recent presence updates that happened after the event with id `since`
|
||||
|
||||
## `!admin query room-alias`
|
||||
|
||||
rooms/alias.rs iterators and getters
|
||||
|
||||
### `!admin query room-alias local-aliases-for-room`
|
||||
|
||||
Iterator of all our local room aliases for the room ID
|
||||
|
||||
### `!admin query room-alias all-local-aliases`
|
||||
|
||||
Iterator of all our local aliases in our database with their room IDs
|
||||
|
||||
## `!admin query room-state-cache`
|
||||
|
||||
rooms/state_cache iterators and getters
|
||||
|
||||
## `!admin query room-timeline`
|
||||
|
||||
rooms/timeline iterators and getters
|
||||
|
||||
## `!admin query globals`
|
||||
|
||||
globals.rs iterators and getters
|
||||
|
||||
### `!admin query globals signing-keys-for`
|
||||
|
||||
This returns an empty `Ok(BTreeMap<..>)` when there are no keys found for the server
|
||||
|
||||
## `!admin query sending`
|
||||
|
||||
sending.rs iterators and getters
|
||||
|
||||
### `!admin query sending active-requests`
|
||||
|
||||
Queries database for all `servercurrentevent_data`
|
||||
|
||||
### `!admin query sending active-requests-for`
|
||||
|
||||
Queries database for `servercurrentevent_data` but for a specific destination
|
||||
|
||||
This command takes only *one* format of these arguments:
|
||||
|
||||
appservice_id server_name user_id AND push_key
|
||||
|
||||
See src/service/sending/mod.rs for the definition of the `Destination` enum
|
||||
|
||||
### `!admin query sending queued-requests`
|
||||
|
||||
Queries database for `servernameevent_data` which are the queued up requests that will eventually be sent
|
||||
|
||||
This command takes only *one* format of these arguments:
|
||||
|
||||
appservice_id server_name user_id AND push_key
|
||||
|
||||
See src/service/sending/mod.rs for the definition of the `Destination` enum
|
||||
|
||||
## `!admin query users`
|
||||
|
||||
users.rs iterators and getters
|
||||
|
||||
## `!admin query resolver`
|
||||
|
||||
resolver service
|
||||
|
||||
### `!admin query resolver destinations-cache`
|
||||
|
||||
Query the destinations cache
|
||||
|
||||
### `!admin query resolver overrides-cache`
|
||||
|
||||
Query the overrides cache
|
||||
|
||||
## `!admin query pusher`
|
||||
|
||||
pusher service
|
||||
|
||||
### `!admin query pusher get-pushers`
|
||||
|
||||
Returns all the pushers for the user
|
||||
|
||||
## `!admin query short`
|
||||
|
||||
short service
|
||||
|
||||
## `!admin query raw`
|
||||
|
||||
raw service
|
||||
|
||||
### `!admin query raw raw-maps`
|
||||
|
||||
List database maps
|
||||
|
||||
### `!admin query raw raw-get`
|
||||
|
||||
Raw database query
|
||||
|
||||
### `!admin query raw raw-del`
|
||||
|
||||
Raw database delete (for string keys)
|
||||
|
||||
### `!admin query raw raw-keys`
|
||||
|
||||
Raw database keys iteration
|
||||
|
||||
### `!admin query raw raw-keys-sizes`
|
||||
|
||||
Raw database key size breakdown
|
||||
|
||||
### `!admin query raw raw-keys-total`
|
||||
|
||||
Raw database keys total bytes
|
||||
|
||||
### `!admin query raw raw-vals-sizes`
|
||||
|
||||
Raw database values size breakdown
|
||||
|
||||
### `!admin query raw raw-vals-total`
|
||||
|
||||
Raw database values total bytes
|
||||
|
||||
### `!admin query raw raw-iter`
|
||||
|
||||
Raw database items iteration
|
||||
|
||||
### `!admin query raw raw-keys-from`
|
||||
|
||||
Raw database keys iteration
|
||||
|
||||
### `!admin query raw raw-iter-from`
|
||||
|
||||
Raw database items iteration
|
||||
|
||||
### `!admin query raw raw-count`
|
||||
|
||||
Raw database record count
|
||||
|
||||
### `!admin query raw compact`
|
||||
|
||||
Compact database
|
||||
@@ -0,0 +1,83 @@
|
||||
<!-- This file is generated by `cargo xtask generate-docs`. Do not edit. -->
|
||||
# `!admin rooms`
|
||||
|
||||
Commands for managing rooms
|
||||
|
||||
|
||||
## `!admin rooms list-rooms`
|
||||
|
||||
List all rooms the server knows about
|
||||
|
||||
## `!admin rooms info`
|
||||
|
||||
View information about a room we know about
|
||||
|
||||
### `!admin rooms info list-joined-members`
|
||||
|
||||
List joined members in a room
|
||||
|
||||
### `!admin rooms info view-room-topic`
|
||||
|
||||
Displays room topic
|
||||
|
||||
Room topics can be huge, so this is in its own separate command
|
||||
|
||||
## `!admin rooms moderation`
|
||||
|
||||
Manage moderation of remote or local rooms
|
||||
|
||||
### `!admin rooms moderation ban-room`
|
||||
|
||||
Bans a room from local users joining and evicts all our local users (including server admins) from the room. Also blocks any invites (local and remote) for the banned room, and disables federation entirely with it
|
||||
|
||||
### `!admin rooms moderation ban-list-of-rooms`
|
||||
|
||||
Bans a list of rooms (room IDs and room aliases) from a newline delimited codeblock similar to `user deactivate-all`. Applies the same steps as ban-room
|
||||
|
||||
### `!admin rooms moderation unban-room`
|
||||
|
||||
Unbans a room to allow local users to join again
|
||||
|
||||
### `!admin rooms moderation list-banned-rooms`
|
||||
|
||||
List of all rooms we have banned
|
||||
|
||||
## `!admin rooms alias`
|
||||
|
||||
Manage rooms' aliases
|
||||
|
||||
### `!admin rooms alias set`
|
||||
|
||||
Make an alias point to a room
|
||||
|
||||
### `!admin rooms alias remove`
|
||||
|
||||
Remove a local alias
|
||||
|
||||
### `!admin rooms alias which`
|
||||
|
||||
Show which room is using an alias
|
||||
|
||||
### `!admin rooms alias list`
|
||||
|
||||
List aliases currently being used
|
||||
|
||||
## `!admin rooms directory`
|
||||
|
||||
Manage the room directory
|
||||
|
||||
### `!admin rooms directory publish`
|
||||
|
||||
Publish a room to the room directory
|
||||
|
||||
### `!admin rooms directory unpublish`
|
||||
|
||||
Unpublish a room to the room directory
|
||||
|
||||
### `!admin rooms directory list`
|
||||
|
||||
List rooms that are published
|
||||
|
||||
## `!admin rooms exists`
|
||||
|
||||
Check if we know about a room
|
||||
@@ -0,0 +1,49 @@
|
||||
<!-- This file is generated by `cargo xtask generate-docs`. Do not edit. -->
|
||||
# `!admin server`
|
||||
|
||||
Commands for managing the server
|
||||
|
||||
|
||||
## `!admin server uptime`
|
||||
|
||||
Time elapsed since startup
|
||||
|
||||
## `!admin server show-config`
|
||||
|
||||
Show configuration values
|
||||
|
||||
## `!admin server reload-config`
|
||||
|
||||
Reload configuration values
|
||||
|
||||
## `!admin server memory-usage`
|
||||
|
||||
Print database memory usage statistics
|
||||
|
||||
## `!admin server clear-caches`
|
||||
|
||||
Clears all of Continuwuity's caches
|
||||
|
||||
## `!admin server backup-database`
|
||||
|
||||
Performs an online backup of the database (only available for RocksDB at the moment)
|
||||
|
||||
## `!admin server list-backups`
|
||||
|
||||
List database backups
|
||||
|
||||
## `!admin server admin-notice`
|
||||
|
||||
Send a message to the admin room
|
||||
|
||||
## `!admin server reload-mods`
|
||||
|
||||
Hot-reload the server
|
||||
|
||||
## `!admin server restart`
|
||||
|
||||
Restart the server
|
||||
|
||||
## `!admin server shutdown`
|
||||
|
||||
Shutdown the server
|
||||
@@ -0,0 +1,17 @@
|
||||
<!-- This file is generated by `cargo xtask generate-docs`. Do not edit. -->
|
||||
# `!admin token`
|
||||
|
||||
Commands for managing registration tokens
|
||||
|
||||
|
||||
## `!admin token issue`
|
||||
|
||||
Issue a new registration token
|
||||
|
||||
## `!admin token revoke`
|
||||
|
||||
Revoke a registration token
|
||||
|
||||
## `!admin token list`
|
||||
|
||||
List all registration tokens
|
||||
@@ -0,0 +1,141 @@
|
||||
<!-- This file is generated by `cargo xtask generate-docs`. Do not edit. -->
|
||||
# `!admin users`
|
||||
|
||||
Commands for managing local users
|
||||
|
||||
|
||||
## `!admin users create-user`
|
||||
|
||||
Create a new user
|
||||
|
||||
## `!admin users reset-password`
|
||||
|
||||
Reset user password
|
||||
|
||||
## `!admin users deactivate`
|
||||
|
||||
Deactivate a user
|
||||
|
||||
User will be removed from all rooms by default. Use --no-leave-rooms to not leave all rooms by default.
|
||||
|
||||
## `!admin users deactivate-all`
|
||||
|
||||
Deactivate a list of users
|
||||
|
||||
Recommended to use in conjunction with list-local-users.
|
||||
|
||||
Users will be removed from joined rooms by default.
|
||||
|
||||
Can be overridden with --no-leave-rooms.
|
||||
|
||||
Removing a mass amount of users from a room may cause a significant amount of leave events. The time to leave rooms may depend significantly on joined rooms and servers.
|
||||
|
||||
This command needs a newline separated list of users provided in a Markdown code block below the command.
|
||||
|
||||
## `!admin users logout`
|
||||
|
||||
Forcefully log a user out of all of their devices.
|
||||
|
||||
This will invalidate all access tokens for the specified user, effectively logging them out from all sessions. Note that this is destructive and may result in data loss for the user, such as encryption keys. Use with caution. Can only be used in the admin room.
|
||||
|
||||
## `!admin users suspend`
|
||||
|
||||
Suspend a user
|
||||
|
||||
Suspended users are able to log in, sync, and read messages, but are not able to send events nor redact them, cannot change their profile, and are unable to join, invite to, or knock on rooms.
|
||||
|
||||
Suspended users can still leave rooms and deactivate their account. Suspending them effectively makes them read-only.
|
||||
|
||||
## `!admin users unsuspend`
|
||||
|
||||
Unsuspend a user
|
||||
|
||||
Reverses the effects of the `suspend` command, allowing the user to send messages, change their profile, create room invites, etc.
|
||||
|
||||
## `!admin users lock`
|
||||
|
||||
Lock a user
|
||||
|
||||
Locked users are unable to use their accounts beyond logging out. This is akin to a temporary deactivation that does not change the user's password. This can be used to quickly prevent a user from accessing their account.
|
||||
|
||||
## `!admin users unlock`
|
||||
|
||||
Unlock a user
|
||||
|
||||
Reverses the effects of the `lock` command, allowing the user to use their account again.
|
||||
|
||||
## `!admin users enable-login`
|
||||
|
||||
Enable login for a user
|
||||
|
||||
## `!admin users disable-login`
|
||||
|
||||
Disable login for a user
|
||||
|
||||
Disables login for the specified user without deactivating or locking their account. This prevents the user from obtaining new access tokens, but does not invalidate existing sessions.
|
||||
|
||||
## `!admin users list-users`
|
||||
|
||||
List local users in the database
|
||||
|
||||
## `!admin users list-joined-rooms`
|
||||
|
||||
Lists all the rooms (local and remote) that the specified user is joined in
|
||||
|
||||
## `!admin users force-join-room`
|
||||
|
||||
Manually join a local user to a room
|
||||
|
||||
## `!admin users force-leave-room`
|
||||
|
||||
Manually leave a local user from a room
|
||||
|
||||
## `!admin users force-leave-remote-room`
|
||||
|
||||
Manually leave a remote room for a local user
|
||||
|
||||
## `!admin users force-demote`
|
||||
|
||||
Forces the specified user to drop their power levels to the room default, if their permissions allow and the auth check permits
|
||||
|
||||
## `!admin users make-user-admin`
|
||||
|
||||
Grant server-admin privileges to a user
|
||||
|
||||
## `!admin users put-room-tag`
|
||||
|
||||
Puts a room tag for the specified user and room ID.
|
||||
|
||||
This is primarily useful if you'd like to set your admin room to the special "System Alerts" section in Element as a way to permanently see your admin room without it being buried away in your favourites or rooms. To do this, you would pass your user, your admin room's internal ID, and the tag name `m.server_notice`.
|
||||
|
||||
## `!admin users delete-room-tag`
|
||||
|
||||
Deletes the room tag for the specified user and room ID
|
||||
|
||||
## `!admin users get-room-tags`
|
||||
|
||||
Gets all the room tags for the specified user and room ID
|
||||
|
||||
## `!admin users redact-event`
|
||||
|
||||
Attempts to forcefully redact the specified event ID from the sender user
|
||||
|
||||
This is only valid for local users
|
||||
|
||||
## `!admin users force-join-list-of-local-users`
|
||||
|
||||
Force joins a specified list of local users to join the specified room.
|
||||
|
||||
Specify a codeblock of usernames.
|
||||
|
||||
At least 1 server admin must be in the room to reduce abuse.
|
||||
|
||||
Requires the `--yes-i-want-to-do-this` flag.
|
||||
|
||||
## `!admin users force-join-all-local-users`
|
||||
|
||||
Force joins all local users to the specified room.
|
||||
|
||||
At least 1 server admin must be in the room to reduce abuse.
|
||||
|
||||
Requires the `--yes-i-want-to-do-this` flag.
|
||||
@@ -1,21 +0,0 @@
|
||||
# Command-Line Help for `continuwuity`
|
||||
|
||||
This document contains the help content for the `continuwuity` command-line program.
|
||||
|
||||
**Command Overview:**
|
||||
|
||||
* [`continuwuity`↴](#continuwuity)
|
||||
|
||||
## `continuwuity`
|
||||
|
||||
a very cool Matrix chat homeserver written in Rust
|
||||
|
||||
**Usage:** `continuwuity [OPTIONS]`
|
||||
|
||||
###### **Options:**
|
||||
|
||||
* `-c`, `--config <CONFIG>` — Path to the config TOML file (optional)
|
||||
* `-O`, `--option <OPTION>` — Override a configuration variable using TOML 'key=value' syntax
|
||||
* `--read-only` — Run in a stricter read-only --maintenance mode
|
||||
* `--maintenance` — Run in maintenance mode while refusing connections
|
||||
* `--execute <EXECUTE>` — Execute console command automatically after startup
|
||||
Generated
+18
-38
@@ -47,7 +47,6 @@
|
||||
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
"@babel/generator": "^7.28.5",
|
||||
@@ -714,9 +713,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@remix-run/router": {
|
||||
"version": "1.23.1",
|
||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.1.tgz",
|
||||
"integrity": "sha512-vDbaOzF7yT2Qs4vO6XV1MHcJv+3dgR1sT+l3B8xxOVhUC336prMvqrvsLL/9Dnw2xr6Qhz4J0dmS0llNAbnUmQ==",
|
||||
"version": "1.23.2",
|
||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.2.tgz",
|
||||
"integrity": "sha512-Ic6m2U/rMjTkhERIa/0ZtXJP17QUi2CbWE7cqx4J58M8aA3QTfW+2UlQ4psvTX9IO1RfNVhK3pcpdjej7L+t2w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -729,7 +728,6 @@
|
||||
"integrity": "sha512-wf41bbFIzqQsGkrDal2eVC4cxN6II1k4bUo1g7OFuvWeEOJzjoeK4R5xxKM9g5hRjbGAJs6OiQaGpASvUnDrsw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@rspack/core": "1.6.5",
|
||||
"@rspack/lite-tapable": "~1.1.0",
|
||||
@@ -989,16 +987,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@rspack/lite-tapable": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/lite-tapable/-/lite-tapable-1.0.1.tgz",
|
||||
"integrity": "sha512-VynGOEsVw2s8TAlLf/uESfrgfrq2+rcXB1muPJYBWbsm1Oa6r5qVQhjA5ggM6z/coYPrsVMgovl3Ff7Q7OCp1w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rspack/plugin-react-refresh": {
|
||||
"version": "1.5.3",
|
||||
"resolved": "https://registry.npmjs.org/@rspack/plugin-react-refresh/-/plugin-react-refresh-1.5.3.tgz",
|
||||
@@ -1025,7 +1013,6 @@
|
||||
"integrity": "sha512-vYbHDoAy7fjQC8hYM4rRgkrsN48CZNSFpD1WEr6lZGXDQcWWrrpFjWw3LK2OQ04bZ0OXZeqapP2rgg/jTyPaZA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@mdx-js/mdx": "^3.1.1",
|
||||
"@mdx-js/react": "^3.1.1",
|
||||
@@ -1288,7 +1275,6 @@
|
||||
"integrity": "sha512-m93nmR0iY3N9Y+9Xi2xCA0NfDnTZVYauJl2SJ9bqRhJmxFHAbWe5f4Ik3VI0gK1g3lvfQ3eBpZihkpUtxCJFBw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@rspress/shared": "2.0.0-rc.1",
|
||||
"@unhead/react": "^2.0.19",
|
||||
@@ -1423,7 +1409,6 @@
|
||||
"integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.8.0"
|
||||
}
|
||||
@@ -1592,7 +1577,6 @@
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
@@ -1774,7 +1758,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"baseline-browser-mapping": "^2.8.25",
|
||||
"caniuse-lite": "^1.0.30001754",
|
||||
@@ -1973,7 +1956,8 @@
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
||||
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.4.3",
|
||||
@@ -3260,9 +3244,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/mdast-util-to-hast": {
|
||||
"version": "13.2.0",
|
||||
"resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz",
|
||||
"integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==",
|
||||
"version": "13.2.1",
|
||||
"resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz",
|
||||
"integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -4250,7 +4234,6 @@
|
||||
"integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -4261,7 +4244,6 @@
|
||||
"integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"scheduler": "^0.27.0"
|
||||
},
|
||||
@@ -4298,19 +4280,18 @@
|
||||
"integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-router": {
|
||||
"version": "6.30.2",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.2.tgz",
|
||||
"integrity": "sha512-H2Bm38Zu1bm8KUE5NVWRMzuIyAV8p/JrOaBJAwVmp37AXG72+CZJlEBw6pdn9i5TBgLMhNDgijS4ZlblpHyWTA==",
|
||||
"version": "6.30.3",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.3.tgz",
|
||||
"integrity": "sha512-XRnlbKMTmktBkjCLE8/XcZFlnHvr2Ltdr1eJX4idL55/9BbORzyZEaIkBFDhFGCEWBBItsVrDxwx3gnisMitdw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@remix-run/router": "1.23.1"
|
||||
"@remix-run/router": "1.23.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
@@ -4320,15 +4301,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-router-dom": {
|
||||
"version": "6.30.2",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.2.tgz",
|
||||
"integrity": "sha512-l2OwHn3UUnEVUqc6/1VMmR1cvZryZ3j3NzapC2eUXO1dB0sYp5mvwdjiXhpUbRb21eFow3qSxpP8Yv6oAU824Q==",
|
||||
"version": "6.30.3",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.3.tgz",
|
||||
"integrity": "sha512-pxPcv1AczD4vso7G4Z3TKcvlxK7g7TNt3/FNGMhfqyntocvYKj+GCatfigGDjbLozC4baguJ0ReCigoDJXb0ag==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@remix-run/router": "1.23.1",
|
||||
"react-router": "6.30.2"
|
||||
"@remix-run/router": "1.23.2",
|
||||
"react-router": "6.30.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
@@ -4663,6 +4643,7 @@
|
||||
"integrity": "sha512-16OL3NnUBw8JG1jBLUoZJsLnQq0n5Ua6aHalhJK4fMQkz1lqR7Osz1sA30trBtd9VUDc2NgkuRCn8+/pBwqZ+w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
@@ -4836,7 +4817,6 @@
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
Name: continuwuity
|
||||
Version: {{{ git_repo_version }}}
|
||||
Release: 1%{?dist}
|
||||
Summary: Very cool Matrix chat homeserver written in Rust
|
||||
Summary: A Matrix homeserver written in Rust.
|
||||
|
||||
License: Apache-2.0 AND MIT
|
||||
|
||||
@@ -23,7 +23,7 @@ Requires: glibc
|
||||
Requires: libstdc++
|
||||
|
||||
%global _description %{expand:
|
||||
A cool hard fork of Conduit, a Matrix homeserver written in Rust}
|
||||
A Matrix homeserver written in Rust, the official continuation of the conduwuit homeserver.}
|
||||
|
||||
%description %{_description}
|
||||
|
||||
|
||||
@@ -1 +1,8 @@
|
||||
tag-message = "chore: Release v{{version}}"
|
||||
tag-prefix = ""
|
||||
shared-version = true
|
||||
|
||||
publish = false
|
||||
|
||||
sign-commit = true
|
||||
sign-tag = true
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
[package]
|
||||
name = "conduwuit_admin"
|
||||
categories.workspace = true
|
||||
description.workspace = true
|
||||
edition.workspace = true
|
||||
keywords.workspace = true
|
||||
license.workspace = true
|
||||
readme.workspace = true
|
||||
repository.workspace = true
|
||||
@@ -89,7 +87,6 @@ serde-saphyr.workspace = true
|
||||
tokio.workspace = true
|
||||
tracing-subscriber.workspace = true
|
||||
tracing.workspace = true
|
||||
ctor.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
+29
-13
@@ -2,49 +2,60 @@
|
||||
use conduwuit::Result;
|
||||
|
||||
use crate::{
|
||||
appservice, appservice::AppserviceCommand, check, check::CheckCommand, context::Context,
|
||||
debug, debug::DebugCommand, federation, federation::FederationCommand, media,
|
||||
media::MediaCommand, query, query::QueryCommand, room, room::RoomCommand, server,
|
||||
server::ServerCommand, user, user::UserCommand,
|
||||
appservice::{self, AppserviceCommand},
|
||||
check::{self, CheckCommand},
|
||||
context::Context,
|
||||
debug::{self, DebugCommand},
|
||||
federation::{self, FederationCommand},
|
||||
media::{self, MediaCommand},
|
||||
query::{self, QueryCommand},
|
||||
room::{self, RoomCommand},
|
||||
server::{self, ServerCommand},
|
||||
token::{self, TokenCommand},
|
||||
user::{self, UserCommand},
|
||||
};
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
#[command(name = conduwuit_core::name(), version = conduwuit_core::version())]
|
||||
pub enum AdminCommand {
|
||||
#[command(subcommand)]
|
||||
/// - Commands for managing appservices
|
||||
/// Commands for managing appservices
|
||||
Appservices(AppserviceCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - Commands for managing local users
|
||||
/// Commands for managing local users
|
||||
Users(UserCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - Commands for managing rooms
|
||||
/// Commands for managing registration tokens
|
||||
Token(TokenCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
/// Commands for managing rooms
|
||||
Rooms(RoomCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - Commands for managing federation
|
||||
/// Commands for managing federation
|
||||
Federation(FederationCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - Commands for managing the server
|
||||
/// Commands for managing the server
|
||||
Server(ServerCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - Commands for managing media
|
||||
/// Commands for managing media
|
||||
Media(MediaCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - Commands for checking integrity
|
||||
/// Commands for checking integrity
|
||||
Check(CheckCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - Commands for debugging things
|
||||
/// Commands for debugging things
|
||||
Debug(DebugCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - Low-level queries for database getters and iterators
|
||||
/// Low-level queries for database getters and iterators
|
||||
Query(QueryCommand),
|
||||
}
|
||||
|
||||
@@ -64,6 +75,11 @@ pub(super) async fn process(command: AdminCommand, context: &Context<'_>) -> Res
|
||||
context.bail_restricted()?;
|
||||
user::process(command, context).await
|
||||
},
|
||||
| Token(command) => {
|
||||
// token commands are all restricted
|
||||
context.bail_restricted()?;
|
||||
token::process(command, context).await
|
||||
},
|
||||
| Rooms(command) => room::process(command, context).await,
|
||||
| Federation(command) => federation::process(command, context).await,
|
||||
| Server(command) => server::process(command, context).await,
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#[derive(Debug, Subcommand)]
|
||||
#[admin_command_dispatch]
|
||||
pub enum AppserviceCommand {
|
||||
/// - Register an appservice using its registration YAML
|
||||
/// Register an appservice using its registration YAML
|
||||
///
|
||||
/// This command needs a YAML generated by an appservice (such as a bridge),
|
||||
/// which must be provided in a Markdown code block below the command.
|
||||
@@ -17,7 +17,7 @@ pub enum AppserviceCommand {
|
||||
/// the old one.
|
||||
Register,
|
||||
|
||||
/// - Unregister an appservice using its ID
|
||||
/// Unregister an appservice using its ID
|
||||
///
|
||||
/// You can find the ID using the `list-appservices` command.
|
||||
Unregister {
|
||||
@@ -25,7 +25,7 @@ pub enum AppserviceCommand {
|
||||
appservice_identifier: String,
|
||||
},
|
||||
|
||||
/// - Show an appservice's config using its ID
|
||||
/// Show an appservice's config using its ID
|
||||
///
|
||||
/// You can find the ID using the `list-appservices` command.
|
||||
#[clap(alias("show"))]
|
||||
@@ -34,7 +34,7 @@ pub enum AppserviceCommand {
|
||||
appservice_identifier: String,
|
||||
},
|
||||
|
||||
/// - List all the currently registered appservices
|
||||
/// List all the currently registered appservices
|
||||
#[clap(alias("list"))]
|
||||
ListRegistered,
|
||||
}
|
||||
|
||||
@@ -4,9 +4,6 @@
|
||||
|
||||
use crate::Context;
|
||||
|
||||
/// Uses the iterator in `src/database/key_value/users.rs` to iterator over
|
||||
/// every user in our database (remote and local). Reports total count, any
|
||||
/// errors if there were any, etc
|
||||
#[implement(Context, params = "<'_>")]
|
||||
pub(super) async fn check_all_users(&self) -> Result {
|
||||
let timer = tokio::time::Instant::now();
|
||||
|
||||
@@ -8,5 +8,8 @@
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum CheckCommand {
|
||||
/// Uses the iterator in `src/database/key_value/users.rs` to iterator over
|
||||
/// every user in our database (remote and local). Reports total count, any
|
||||
/// errors if there were any, etc
|
||||
CheckAllUsers,
|
||||
}
|
||||
|
||||
@@ -819,32 +819,6 @@ pub(super) async fn time(&self) -> Result {
|
||||
self.write_str(&now).await
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn list_dependencies(&self, names: bool) -> Result {
|
||||
if names {
|
||||
let out = info::cargo::dependencies_names().join(" ");
|
||||
return self.write_str(&out).await;
|
||||
}
|
||||
|
||||
let mut out = String::new();
|
||||
let deps = info::cargo::dependencies();
|
||||
writeln!(out, "| name | version | features |")?;
|
||||
writeln!(out, "| ---- | ------- | -------- |")?;
|
||||
for (name, dep) in deps {
|
||||
let version = dep.try_req().unwrap_or("*");
|
||||
let feats = dep.req_features();
|
||||
let feats = if !feats.is_empty() {
|
||||
feats.join(" ")
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
writeln!(out, "| {name} | {version} | {feats} |")?;
|
||||
}
|
||||
|
||||
self.write_str(&out).await
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn database_stats(
|
||||
&self,
|
||||
|
||||
+27
-33
@@ -12,18 +12,18 @@
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum DebugCommand {
|
||||
/// - Echo input of admin command
|
||||
/// Echo input of admin command
|
||||
Echo {
|
||||
message: Vec<String>,
|
||||
},
|
||||
|
||||
/// - Get the auth_chain of a PDU
|
||||
/// Get the auth_chain of a PDU
|
||||
GetAuthChain {
|
||||
/// An event ID (the $ character followed by the base64 reference hash)
|
||||
event_id: OwnedEventId,
|
||||
},
|
||||
|
||||
/// - Parse and print a PDU from a JSON
|
||||
/// Parse and print a PDU from a JSON
|
||||
///
|
||||
/// The PDU event is only checked for validity and is not added to the
|
||||
/// database.
|
||||
@@ -32,13 +32,13 @@ pub enum DebugCommand {
|
||||
/// the command.
|
||||
ParsePdu,
|
||||
|
||||
/// - Retrieve and print a PDU by EventID from the Continuwuity database
|
||||
/// Retrieve and print a PDU by EventID from the Continuwuity database
|
||||
GetPdu {
|
||||
/// An event ID (a $ followed by the base64 reference hash)
|
||||
event_id: OwnedEventId,
|
||||
},
|
||||
|
||||
/// - Retrieve and print a PDU by PduId from the Continuwuity database
|
||||
/// Retrieve and print a PDU by PduId from the Continuwuity database
|
||||
GetShortPdu {
|
||||
/// Shortroomid integer
|
||||
shortroomid: ShortRoomId,
|
||||
@@ -47,7 +47,7 @@ pub enum DebugCommand {
|
||||
shorteventid: ShortEventId,
|
||||
},
|
||||
|
||||
/// - Attempts to retrieve a PDU from a remote server. **Does not** insert
|
||||
/// Attempts to retrieve a PDU from a remote server. **Does not** insert
|
||||
/// it into the database
|
||||
/// or persist it anywhere.
|
||||
GetRemotePdu {
|
||||
@@ -59,7 +59,7 @@ pub enum DebugCommand {
|
||||
server: OwnedServerName,
|
||||
},
|
||||
|
||||
/// - Same as `get-remote-pdu` but accepts a codeblock newline delimited
|
||||
/// Same as `get-remote-pdu` but accepts a codeblock newline delimited
|
||||
/// list of PDUs and a single server to fetch from
|
||||
GetRemotePduList {
|
||||
/// Argument for us to attempt to fetch all the events from the
|
||||
@@ -71,7 +71,7 @@ pub enum DebugCommand {
|
||||
force: bool,
|
||||
},
|
||||
|
||||
/// - Gets all the room state events for the specified room.
|
||||
/// Gets all the room state events for the specified room.
|
||||
///
|
||||
/// This is functionally equivalent to `GET
|
||||
/// /_matrix/client/v3/rooms/{roomid}/state`, except the admin command does
|
||||
@@ -86,7 +86,7 @@ pub enum DebugCommand {
|
||||
room_id: OwnedRoomOrAliasId,
|
||||
},
|
||||
|
||||
/// - Get and display signing keys from local cache or remote server.
|
||||
/// Get and display signing keys from local cache or remote server.
|
||||
GetSigningKeys {
|
||||
server_name: Option<OwnedServerName>,
|
||||
|
||||
@@ -97,23 +97,23 @@ pub enum DebugCommand {
|
||||
query: bool,
|
||||
},
|
||||
|
||||
/// - Get and display signing keys from local cache or remote server.
|
||||
/// Get and display signing keys from local cache or remote server.
|
||||
GetVerifyKeys {
|
||||
server_name: Option<OwnedServerName>,
|
||||
},
|
||||
|
||||
/// - Sends a federation request to the remote server's
|
||||
/// Sends a federation request to the remote server's
|
||||
/// `/_matrix/federation/v1/version` endpoint and measures the latency it
|
||||
/// took for the server to respond
|
||||
Ping {
|
||||
server: OwnedServerName,
|
||||
},
|
||||
|
||||
/// - Forces device lists for all local and remote users to be updated (as
|
||||
/// Forces device lists for all local and remote users to be updated (as
|
||||
/// having new keys available)
|
||||
ForceDeviceListUpdates,
|
||||
|
||||
/// - Change tracing log level/filter on the fly
|
||||
/// Change tracing log level/filter on the fly
|
||||
///
|
||||
/// This accepts the same format as the `log` config option.
|
||||
ChangeLogLevel {
|
||||
@@ -125,34 +125,34 @@ pub enum DebugCommand {
|
||||
reset: bool,
|
||||
},
|
||||
|
||||
/// - Verify JSON signatures
|
||||
/// Verify JSON signatures
|
||||
///
|
||||
/// This command needs a JSON blob provided in a Markdown code block below
|
||||
/// the command.
|
||||
VerifyJson,
|
||||
|
||||
/// - Verify PDU
|
||||
/// Verify PDU
|
||||
///
|
||||
/// This re-verifies a PDU existing in the database found by ID.
|
||||
VerifyPdu {
|
||||
event_id: OwnedEventId,
|
||||
},
|
||||
|
||||
/// - Prints the very first PDU in the specified room (typically
|
||||
/// Prints the very first PDU in the specified room (typically
|
||||
/// m.room.create)
|
||||
FirstPduInRoom {
|
||||
/// The room ID
|
||||
room_id: OwnedRoomId,
|
||||
},
|
||||
|
||||
/// - Prints the latest ("last") PDU in the specified room (typically a
|
||||
/// Prints the latest ("last") PDU in the specified room (typically a
|
||||
/// message)
|
||||
LatestPduInRoom {
|
||||
/// The room ID
|
||||
room_id: OwnedRoomId,
|
||||
},
|
||||
|
||||
/// - Forcefully replaces the room state of our local copy of the specified
|
||||
/// Forcefully replaces the room state of our local copy of the specified
|
||||
/// room, with the copy (auth chain and room state events) the specified
|
||||
/// remote server says.
|
||||
///
|
||||
@@ -176,7 +176,7 @@ pub enum DebugCommand {
|
||||
event_id: Option<OwnedEventId>,
|
||||
},
|
||||
|
||||
/// - Runs a server name through Continuwuity's true destination resolution
|
||||
/// Runs a server name through Continuwuity's true destination resolution
|
||||
/// process
|
||||
///
|
||||
/// Useful for debugging well-known issues
|
||||
@@ -187,7 +187,7 @@ pub enum DebugCommand {
|
||||
no_cache: bool,
|
||||
},
|
||||
|
||||
/// - Print extended memory usage
|
||||
/// Print extended memory usage
|
||||
///
|
||||
/// Optional argument is a character mask (a sequence of characters in any
|
||||
/// order) which enable additional extended statistics. Known characters are
|
||||
@@ -196,23 +196,17 @@ pub enum DebugCommand {
|
||||
opts: Option<String>,
|
||||
},
|
||||
|
||||
/// - Print general tokio runtime metric totals.
|
||||
/// Print general tokio runtime metric totals.
|
||||
RuntimeMetrics,
|
||||
|
||||
/// - Print detailed tokio runtime metrics accumulated since last command
|
||||
/// Print detailed tokio runtime metrics accumulated since last command
|
||||
/// invocation.
|
||||
RuntimeInterval,
|
||||
|
||||
/// - Print the current time
|
||||
/// Print the current time
|
||||
Time,
|
||||
|
||||
/// - List dependencies
|
||||
ListDependencies {
|
||||
#[arg(short, long)]
|
||||
names: bool,
|
||||
},
|
||||
|
||||
/// - Get database statistics
|
||||
/// Get database statistics
|
||||
DatabaseStats {
|
||||
property: Option<String>,
|
||||
|
||||
@@ -220,10 +214,10 @@ pub enum DebugCommand {
|
||||
map: Option<String>,
|
||||
},
|
||||
|
||||
/// - Trim memory usage
|
||||
/// Trim memory usage
|
||||
TrimMemory,
|
||||
|
||||
/// - List database files
|
||||
/// List database files
|
||||
DatabaseFiles {
|
||||
map: Option<String>,
|
||||
|
||||
@@ -231,7 +225,7 @@ pub enum DebugCommand {
|
||||
level: Option<i32>,
|
||||
},
|
||||
|
||||
/// - Developer test stubs
|
||||
/// Developer test stubs
|
||||
#[command(subcommand)]
|
||||
#[allow(non_snake_case)]
|
||||
#[clap(hide(true))]
|
||||
|
||||
@@ -9,20 +9,20 @@
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum FederationCommand {
|
||||
/// - List all rooms we are currently handling an incoming pdu from
|
||||
/// List all rooms we are currently handling an incoming pdu from
|
||||
IncomingFederation,
|
||||
|
||||
/// - Disables incoming federation handling for a room.
|
||||
/// Disables incoming federation handling for a room.
|
||||
DisableRoom {
|
||||
room_id: OwnedRoomId,
|
||||
},
|
||||
|
||||
/// - Enables incoming federation handling for a room again.
|
||||
/// Enables incoming federation handling for a room again.
|
||||
EnableRoom {
|
||||
room_id: OwnedRoomId,
|
||||
},
|
||||
|
||||
/// - Fetch `/.well-known/matrix/support` from the specified server
|
||||
/// Fetch `/.well-known/matrix/support` from the specified server
|
||||
///
|
||||
/// Despite the name, this is not a federation endpoint and does not go
|
||||
/// through the federation / server resolution process as per-spec this is
|
||||
@@ -35,7 +35,7 @@ pub enum FederationCommand {
|
||||
server_name: OwnedServerName,
|
||||
},
|
||||
|
||||
/// - Lists all the rooms we share/track with the specified *remote* user
|
||||
/// Lists all the rooms we share/track with the specified *remote* user
|
||||
RemoteUserInRooms {
|
||||
user_id: OwnedUserId,
|
||||
},
|
||||
|
||||
+10
-10
@@ -10,20 +10,20 @@
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum MediaCommand {
|
||||
/// - Deletes a single media file from our database and on the filesystem
|
||||
/// Deletes a single media file from our database and on the filesystem
|
||||
/// via a single MXC URL or event ID (not redacted)
|
||||
Delete {
|
||||
/// The MXC URL to delete
|
||||
#[arg(long)]
|
||||
mxc: Option<OwnedMxcUri>,
|
||||
|
||||
/// - The message event ID which contains the media and thumbnail MXC
|
||||
/// The message event ID which contains the media and thumbnail MXC
|
||||
/// URLs
|
||||
#[arg(long)]
|
||||
event_id: Option<OwnedEventId>,
|
||||
},
|
||||
|
||||
/// - Deletes a codeblock list of MXC URLs from our database and on the
|
||||
/// Deletes a codeblock list of MXC URLs from our database and on the
|
||||
/// filesystem. This will always ignore errors.
|
||||
DeleteList,
|
||||
|
||||
@@ -40,33 +40,33 @@ pub enum MediaCommand {
|
||||
/// * Delete all remote and local media from 3 days ago, up until now:
|
||||
///
|
||||
/// `!admin media delete-past-remote-media -a 3d
|
||||
/// --yes-i-want-to-delete-local-media`
|
||||
///-yes-i-want-to-delete-local-media`
|
||||
#[command(verbatim_doc_comment)]
|
||||
DeletePastRemoteMedia {
|
||||
/// - The relative time (e.g. 30s, 5m, 7d) from now within which to
|
||||
/// The relative time (e.g. 30s, 5m, 7d) from now within which to
|
||||
/// search
|
||||
duration: String,
|
||||
|
||||
/// - Only delete media created before [duration] ago
|
||||
/// Only delete media created before [duration] ago
|
||||
#[arg(long, short)]
|
||||
before: bool,
|
||||
|
||||
/// - Only delete media created after [duration] ago
|
||||
/// Only delete media created after [duration] ago
|
||||
#[arg(long, short)]
|
||||
after: bool,
|
||||
|
||||
/// - Long argument to additionally delete local media
|
||||
/// Long argument to additionally delete local media
|
||||
#[arg(long)]
|
||||
yes_i_want_to_delete_local_media: bool,
|
||||
},
|
||||
|
||||
/// - Deletes all the local media from a local user on our server. This will
|
||||
/// Deletes all the local media from a local user on our server. This will
|
||||
/// always ignore errors by default.
|
||||
DeleteAllFromUser {
|
||||
username: String,
|
||||
},
|
||||
|
||||
/// - Deletes all remote media from the specified remote server. This will
|
||||
/// Deletes all remote media from the specified remote server. This will
|
||||
/// always ignore errors by default.
|
||||
DeleteAllFromServer {
|
||||
server_name: OwnedServerName,
|
||||
|
||||
+1
-3
@@ -17,6 +17,7 @@
|
||||
pub(crate) mod query;
|
||||
pub(crate) mod room;
|
||||
pub(crate) mod server;
|
||||
pub(crate) mod token;
|
||||
pub(crate) mod user;
|
||||
|
||||
extern crate conduwuit_api as api;
|
||||
@@ -29,11 +30,8 @@
|
||||
|
||||
pub(crate) const PAGE_SIZE: usize = 100;
|
||||
|
||||
use ctor::{ctor, dtor};
|
||||
|
||||
conduwuit::mod_ctor! {}
|
||||
conduwuit::mod_dtor! {}
|
||||
conduwuit::rustc_flags_capture! {}
|
||||
|
||||
pub use crate::admin::AdminCommand;
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#[derive(Debug, Subcommand)]
|
||||
/// All the getters and iterators from src/database/key_value/account_data.rs
|
||||
pub enum AccountDataCommand {
|
||||
/// - Returns all changes to the account data that happened after `since`.
|
||||
/// Returns all changes to the account data that happened after `since`.
|
||||
ChangesSince {
|
||||
/// Full user ID
|
||||
user_id: OwnedUserId,
|
||||
@@ -19,7 +19,7 @@ pub enum AccountDataCommand {
|
||||
room_id: Option<OwnedRoomId>,
|
||||
},
|
||||
|
||||
/// - Searches the account data for a specific kind.
|
||||
/// Searches the account data for a specific kind.
|
||||
AccountDataGet {
|
||||
/// Full user ID
|
||||
user_id: OwnedUserId,
|
||||
|
||||
@@ -7,13 +7,13 @@
|
||||
#[derive(Debug, Subcommand)]
|
||||
/// All the getters and iterators from src/database/key_value/appservice.rs
|
||||
pub enum AppserviceCommand {
|
||||
/// - Gets the appservice registration info/details from the ID as a string
|
||||
/// Gets the appservice registration info/details from the ID as a string
|
||||
GetRegistration {
|
||||
/// Appservice registration ID
|
||||
appservice_id: String,
|
||||
},
|
||||
|
||||
/// - Gets all appservice registrations with their ID and registration info
|
||||
/// Gets all appservice registrations with their ID and registration info
|
||||
All,
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ pub enum GlobalsCommand {
|
||||
|
||||
LastCheckForAnnouncementsId,
|
||||
|
||||
/// - This returns an empty `Ok(BTreeMap<..>)` when there are no keys found
|
||||
/// This returns an empty `Ok(BTreeMap<..>)` when there are no keys found
|
||||
/// for the server.
|
||||
SigningKeysFor {
|
||||
origin: OwnedServerName,
|
||||
|
||||
+13
-13
@@ -28,55 +28,55 @@
|
||||
#[derive(Debug, Subcommand)]
|
||||
/// Query tables from database
|
||||
pub enum QueryCommand {
|
||||
/// - account_data.rs iterators and getters
|
||||
/// account_data.rs iterators and getters
|
||||
#[command(subcommand)]
|
||||
AccountData(AccountDataCommand),
|
||||
|
||||
/// - appservice.rs iterators and getters
|
||||
/// appservice.rs iterators and getters
|
||||
#[command(subcommand)]
|
||||
Appservice(AppserviceCommand),
|
||||
|
||||
/// - presence.rs iterators and getters
|
||||
/// presence.rs iterators and getters
|
||||
#[command(subcommand)]
|
||||
Presence(PresenceCommand),
|
||||
|
||||
/// - rooms/alias.rs iterators and getters
|
||||
/// rooms/alias.rs iterators and getters
|
||||
#[command(subcommand)]
|
||||
RoomAlias(RoomAliasCommand),
|
||||
|
||||
/// - rooms/state_cache iterators and getters
|
||||
/// rooms/state_cache iterators and getters
|
||||
#[command(subcommand)]
|
||||
RoomStateCache(RoomStateCacheCommand),
|
||||
|
||||
/// - rooms/timeline iterators and getters
|
||||
/// rooms/timeline iterators and getters
|
||||
#[command(subcommand)]
|
||||
RoomTimeline(RoomTimelineCommand),
|
||||
|
||||
/// - globals.rs iterators and getters
|
||||
/// globals.rs iterators and getters
|
||||
#[command(subcommand)]
|
||||
Globals(GlobalsCommand),
|
||||
|
||||
/// - sending.rs iterators and getters
|
||||
/// sending.rs iterators and getters
|
||||
#[command(subcommand)]
|
||||
Sending(SendingCommand),
|
||||
|
||||
/// - users.rs iterators and getters
|
||||
/// users.rs iterators and getters
|
||||
#[command(subcommand)]
|
||||
Users(UsersCommand),
|
||||
|
||||
/// - resolver service
|
||||
/// resolver service
|
||||
#[command(subcommand)]
|
||||
Resolver(ResolverCommand),
|
||||
|
||||
/// - pusher service
|
||||
/// pusher service
|
||||
#[command(subcommand)]
|
||||
Pusher(PusherCommand),
|
||||
|
||||
/// - short service
|
||||
/// short service
|
||||
#[command(subcommand)]
|
||||
Short(ShortCommand),
|
||||
|
||||
/// - raw service
|
||||
/// raw service
|
||||
#[command(subcommand)]
|
||||
Raw(RawCommand),
|
||||
}
|
||||
|
||||
@@ -8,13 +8,13 @@
|
||||
#[derive(Debug, Subcommand)]
|
||||
/// All the getters and iterators from src/database/key_value/presence.rs
|
||||
pub enum PresenceCommand {
|
||||
/// - Returns the latest presence event for the given user.
|
||||
/// Returns the latest presence event for the given user.
|
||||
GetPresence {
|
||||
/// Full user ID
|
||||
user_id: OwnedUserId,
|
||||
},
|
||||
|
||||
/// - Iterator of the most recent presence updates that happened after the
|
||||
/// Iterator of the most recent presence updates that happened after the
|
||||
/// event with id `since`.
|
||||
PresenceSince {
|
||||
/// UNIX timestamp since (u64)
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum PusherCommand {
|
||||
/// - Returns all the pushers for the user.
|
||||
/// Returns all the pushers for the user.
|
||||
GetPushers {
|
||||
/// Full user ID
|
||||
user_id: OwnedUserId,
|
||||
|
||||
+13
-13
@@ -20,10 +20,10 @@
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
/// Query tables from database
|
||||
pub enum RawCommand {
|
||||
/// - List database maps
|
||||
/// List database maps
|
||||
RawMaps,
|
||||
|
||||
/// - Raw database query
|
||||
/// Raw database query
|
||||
RawGet {
|
||||
/// Map name
|
||||
map: String,
|
||||
@@ -32,7 +32,7 @@ pub enum RawCommand {
|
||||
key: String,
|
||||
},
|
||||
|
||||
/// - Raw database delete (for string keys)
|
||||
/// Raw database delete (for string keys)
|
||||
RawDel {
|
||||
/// Map name
|
||||
map: String,
|
||||
@@ -41,7 +41,7 @@ pub enum RawCommand {
|
||||
key: String,
|
||||
},
|
||||
|
||||
/// - Raw database keys iteration
|
||||
/// Raw database keys iteration
|
||||
RawKeys {
|
||||
/// Map name
|
||||
map: String,
|
||||
@@ -50,7 +50,7 @@ pub enum RawCommand {
|
||||
prefix: Option<String>,
|
||||
},
|
||||
|
||||
/// - Raw database key size breakdown
|
||||
/// Raw database key size breakdown
|
||||
RawKeysSizes {
|
||||
/// Map name
|
||||
map: Option<String>,
|
||||
@@ -59,7 +59,7 @@ pub enum RawCommand {
|
||||
prefix: Option<String>,
|
||||
},
|
||||
|
||||
/// - Raw database keys total bytes
|
||||
/// Raw database keys total bytes
|
||||
RawKeysTotal {
|
||||
/// Map name
|
||||
map: Option<String>,
|
||||
@@ -68,7 +68,7 @@ pub enum RawCommand {
|
||||
prefix: Option<String>,
|
||||
},
|
||||
|
||||
/// - Raw database values size breakdown
|
||||
/// Raw database values size breakdown
|
||||
RawValsSizes {
|
||||
/// Map name
|
||||
map: Option<String>,
|
||||
@@ -77,7 +77,7 @@ pub enum RawCommand {
|
||||
prefix: Option<String>,
|
||||
},
|
||||
|
||||
/// - Raw database values total bytes
|
||||
/// Raw database values total bytes
|
||||
RawValsTotal {
|
||||
/// Map name
|
||||
map: Option<String>,
|
||||
@@ -86,7 +86,7 @@ pub enum RawCommand {
|
||||
prefix: Option<String>,
|
||||
},
|
||||
|
||||
/// - Raw database items iteration
|
||||
/// Raw database items iteration
|
||||
RawIter {
|
||||
/// Map name
|
||||
map: String,
|
||||
@@ -95,7 +95,7 @@ pub enum RawCommand {
|
||||
prefix: Option<String>,
|
||||
},
|
||||
|
||||
/// - Raw database keys iteration
|
||||
/// Raw database keys iteration
|
||||
RawKeysFrom {
|
||||
/// Map name
|
||||
map: String,
|
||||
@@ -108,7 +108,7 @@ pub enum RawCommand {
|
||||
limit: Option<usize>,
|
||||
},
|
||||
|
||||
/// - Raw database items iteration
|
||||
/// Raw database items iteration
|
||||
RawIterFrom {
|
||||
/// Map name
|
||||
map: String,
|
||||
@@ -121,7 +121,7 @@ pub enum RawCommand {
|
||||
limit: Option<usize>,
|
||||
},
|
||||
|
||||
/// - Raw database record count
|
||||
/// Raw database record count
|
||||
RawCount {
|
||||
/// Map name
|
||||
map: Option<String>,
|
||||
@@ -130,7 +130,7 @@ pub enum RawCommand {
|
||||
prefix: Option<String>,
|
||||
},
|
||||
|
||||
/// - Compact database
|
||||
/// Compact database
|
||||
Compact {
|
||||
#[arg(short, long, alias("column"))]
|
||||
map: Option<Vec<String>>,
|
||||
|
||||
@@ -13,13 +13,13 @@ pub enum RoomAliasCommand {
|
||||
alias: OwnedRoomAliasId,
|
||||
},
|
||||
|
||||
/// - Iterator of all our local room aliases for the room ID
|
||||
/// Iterator of all our local room aliases for the room ID
|
||||
LocalAliasesForRoom {
|
||||
/// Full room ID
|
||||
room_id: OwnedRoomId,
|
||||
},
|
||||
|
||||
/// - Iterator of all our local aliases in our database with their room IDs
|
||||
/// Iterator of all our local aliases in our database with their room IDs
|
||||
AllLocalAliases,
|
||||
}
|
||||
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
#[derive(Debug, Subcommand)]
|
||||
/// All the getters and iterators from src/database/key_value/sending.rs
|
||||
pub enum SendingCommand {
|
||||
/// - Queries database for all `servercurrentevent_data`
|
||||
/// Queries database for all `servercurrentevent_data`
|
||||
ActiveRequests,
|
||||
|
||||
/// - Queries database for `servercurrentevent_data` but for a specific
|
||||
/// Queries database for `servercurrentevent_data` but for a specific
|
||||
/// destination
|
||||
///
|
||||
/// This command takes only *one* format of these arguments:
|
||||
@@ -34,7 +34,7 @@ pub enum SendingCommand {
|
||||
push_key: Option<String>,
|
||||
},
|
||||
|
||||
/// - Queries database for `servernameevent_data` which are the queued up
|
||||
/// Queries database for `servernameevent_data` which are the queued up
|
||||
/// requests that will eventually be sent
|
||||
///
|
||||
/// This command takes only *one* format of these arguments:
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum RoomAliasCommand {
|
||||
/// - Make an alias point to a room.
|
||||
/// Make an alias point to a room.
|
||||
Set {
|
||||
#[arg(short, long)]
|
||||
/// Set the alias even if a room is already using it
|
||||
@@ -22,20 +22,20 @@ pub enum RoomAliasCommand {
|
||||
room_alias_localpart: String,
|
||||
},
|
||||
|
||||
/// - Remove a local alias
|
||||
/// Remove a local alias
|
||||
Remove {
|
||||
/// The alias localpart to remove (`alias`, not `#alias:servername.tld`)
|
||||
room_alias_localpart: String,
|
||||
},
|
||||
|
||||
/// - Show which room is using an alias
|
||||
/// Show which room is using an alias
|
||||
Which {
|
||||
/// The alias localpart to look up (`alias`, not
|
||||
/// `#alias:servername.tld`)
|
||||
room_alias_localpart: String,
|
||||
},
|
||||
|
||||
/// - List aliases currently being used
|
||||
/// List aliases currently being used
|
||||
List {
|
||||
/// If set, only list the aliases for this room
|
||||
room_id: Option<OwnedRoomId>,
|
||||
|
||||
@@ -7,19 +7,19 @@
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum RoomDirectoryCommand {
|
||||
/// - Publish a room to the room directory
|
||||
/// Publish a room to the room directory
|
||||
Publish {
|
||||
/// The room id of the room to publish
|
||||
room_id: OwnedRoomId,
|
||||
},
|
||||
|
||||
/// - Unpublish a room to the room directory
|
||||
/// Unpublish a room to the room directory
|
||||
Unpublish {
|
||||
/// The room id of the room to unpublish
|
||||
room_id: OwnedRoomId,
|
||||
},
|
||||
|
||||
/// - List rooms that are published
|
||||
/// List rooms that are published
|
||||
List {
|
||||
page: Option<usize>,
|
||||
},
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum RoomInfoCommand {
|
||||
/// - List joined members in a room
|
||||
/// List joined members in a room
|
||||
ListJoinedMembers {
|
||||
room_id: OwnedRoomId,
|
||||
|
||||
@@ -17,7 +17,7 @@ pub enum RoomInfoCommand {
|
||||
local_only: bool,
|
||||
},
|
||||
|
||||
/// - Displays room topic
|
||||
/// Displays room topic
|
||||
///
|
||||
/// Room topics can be huge, so this is in its
|
||||
/// own separate command
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum RoomCommand {
|
||||
/// - List all rooms the server knows about
|
||||
/// List all rooms the server knows about
|
||||
#[clap(alias = "list")]
|
||||
ListRooms {
|
||||
page: Option<usize>,
|
||||
@@ -37,22 +37,22 @@ pub enum RoomCommand {
|
||||
},
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - View information about a room we know about
|
||||
/// View information about a room we know about
|
||||
Info(RoomInfoCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - Manage moderation of remote or local rooms
|
||||
/// Manage moderation of remote or local rooms
|
||||
Moderation(RoomModerationCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - Manage rooms' aliases
|
||||
/// Manage rooms' aliases
|
||||
Alias(RoomAliasCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - Manage the room directory
|
||||
/// Manage the room directory
|
||||
Directory(RoomDirectoryCommand),
|
||||
|
||||
/// - Check if we know about a room
|
||||
/// Check if we know about a room
|
||||
Exists {
|
||||
room_id: OwnedRoomId,
|
||||
},
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum RoomModerationCommand {
|
||||
/// - Bans a room from local users joining and evicts all our local users
|
||||
/// Bans a room from local users joining and evicts all our local users
|
||||
/// (including server
|
||||
/// admins)
|
||||
/// from the room. Also blocks any invites (local and remote) for the
|
||||
@@ -24,19 +24,19 @@ pub enum RoomModerationCommand {
|
||||
room: OwnedRoomOrAliasId,
|
||||
},
|
||||
|
||||
/// - Bans a list of rooms (room IDs and room aliases) from a newline
|
||||
/// Bans a list of rooms (room IDs and room aliases) from a newline
|
||||
/// delimited codeblock similar to `user deactivate-all`. Applies the same
|
||||
/// steps as ban-room
|
||||
BanListOfRooms,
|
||||
|
||||
/// - Unbans a room to allow local users to join again
|
||||
/// Unbans a room to allow local users to join again
|
||||
UnbanRoom {
|
||||
/// The room in the format of `!roomid:example.com` or a room alias in
|
||||
/// the format of `#roomalias:example.com`
|
||||
room: OwnedRoomOrAliasId,
|
||||
},
|
||||
|
||||
/// - List of all rooms we have banned
|
||||
/// List of all rooms we have banned
|
||||
ListBannedRooms {
|
||||
#[arg(long)]
|
||||
/// Whether to only output room IDs without supplementary room
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::{fmt::Write, path::PathBuf, sync::Arc};
|
||||
use std::{path::PathBuf, sync::Arc};
|
||||
|
||||
use conduwuit::{
|
||||
Err, Result, info,
|
||||
Err, Result,
|
||||
utils::{stream::IterStream, time},
|
||||
warn,
|
||||
};
|
||||
@@ -59,34 +59,6 @@ pub(super) async fn reload_config(&self, path: Option<PathBuf>) -> Result {
|
||||
.await
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn list_features(&self, available: bool, enabled: bool, comma: bool) -> Result {
|
||||
let delim = if comma { "," } else { " " };
|
||||
if enabled && !available {
|
||||
let features = info::rustc::features().join(delim);
|
||||
let out = format!("`\n{features}\n`");
|
||||
return self.write_str(&out).await;
|
||||
}
|
||||
|
||||
if available && !enabled {
|
||||
let features = info::cargo::features().join(delim);
|
||||
let out = format!("`\n{features}\n`");
|
||||
return self.write_str(&out).await;
|
||||
}
|
||||
|
||||
let mut features = String::new();
|
||||
let enabled = info::rustc::features();
|
||||
let available = info::cargo::features();
|
||||
for feature in available {
|
||||
let active = enabled.contains(&feature.as_str());
|
||||
let emoji = if active { "✅" } else { "❌" };
|
||||
let remark = if active { "[enabled]" } else { "" };
|
||||
writeln!(features, "{emoji} {feature} {remark}")?;
|
||||
}
|
||||
|
||||
self.write_str(&features).await
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn memory_usage(&self) -> Result {
|
||||
let services_usage = self.services.memory_usage().await?;
|
||||
|
||||
+11
-23
@@ -10,58 +10,46 @@
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum ServerCommand {
|
||||
/// - Time elapsed since startup
|
||||
/// Time elapsed since startup
|
||||
Uptime,
|
||||
|
||||
/// - Show configuration values
|
||||
/// Show configuration values
|
||||
ShowConfig,
|
||||
|
||||
/// - Reload configuration values
|
||||
/// Reload configuration values
|
||||
ReloadConfig {
|
||||
path: Option<PathBuf>,
|
||||
},
|
||||
|
||||
/// - List the features built into the server
|
||||
ListFeatures {
|
||||
#[arg(short, long)]
|
||||
available: bool,
|
||||
|
||||
#[arg(short, long)]
|
||||
enabled: bool,
|
||||
|
||||
#[arg(short, long)]
|
||||
comma: bool,
|
||||
},
|
||||
|
||||
/// - Print database memory usage statistics
|
||||
/// Print database memory usage statistics
|
||||
MemoryUsage,
|
||||
|
||||
/// - Clears all of Continuwuity's caches
|
||||
/// Clears all of Continuwuity's caches
|
||||
ClearCaches,
|
||||
|
||||
/// - Performs an online backup of the database (only available for RocksDB
|
||||
/// Performs an online backup of the database (only available for RocksDB
|
||||
/// at the moment)
|
||||
BackupDatabase,
|
||||
|
||||
/// - List database backups
|
||||
/// List database backups
|
||||
ListBackups,
|
||||
|
||||
/// - Send a message to the admin room.
|
||||
/// Send a message to the admin room.
|
||||
AdminNotice {
|
||||
message: Vec<String>,
|
||||
},
|
||||
|
||||
/// - Hot-reload the server
|
||||
/// Hot-reload the server
|
||||
#[clap(alias = "reload")]
|
||||
ReloadMods,
|
||||
|
||||
#[cfg(unix)]
|
||||
/// - Restart the server
|
||||
/// Restart the server
|
||||
Restart {
|
||||
#[arg(short, long)]
|
||||
force: bool,
|
||||
},
|
||||
|
||||
/// - Shutdown the server
|
||||
/// Shutdown the server
|
||||
Shutdown,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
use conduwuit::{Err, Result, utils};
|
||||
use conduwuit_macros::admin_command;
|
||||
use futures::StreamExt;
|
||||
use service::registration_tokens::TokenExpires;
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn issue_token(&self, expires: super::TokenExpires) -> Result {
|
||||
let expires = {
|
||||
if expires.immortal {
|
||||
None
|
||||
} else if let Some(max_uses) = expires.max_uses {
|
||||
Some(TokenExpires::AfterUses(max_uses))
|
||||
} else if expires.once {
|
||||
Some(TokenExpires::AfterUses(1))
|
||||
} else if let Some(max_age) = expires
|
||||
.max_age
|
||||
.as_deref()
|
||||
.map(|max_age| utils::time::timepoint_from_now(utils::time::parse_duration(max_age)?))
|
||||
.transpose()?
|
||||
{
|
||||
Some(TokenExpires::AfterTime(max_age))
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
};
|
||||
|
||||
let (token, info) = self
|
||||
.services
|
||||
.registration_tokens
|
||||
.issue_token(self.sender_or_service_user().into(), expires);
|
||||
|
||||
self.write_str(&format!(
|
||||
"New registration token issued: `{token}`. {}.",
|
||||
if let Some(expires) = info.expires {
|
||||
format!("{expires}")
|
||||
} else {
|
||||
"Never expires".to_owned()
|
||||
}
|
||||
))
|
||||
.await
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn revoke_token(&self, token: String) -> Result {
|
||||
let Some(token) = self
|
||||
.services
|
||||
.registration_tokens
|
||||
.validate_token(token)
|
||||
.await
|
||||
else {
|
||||
return Err!("This token does not exist or has already expired.");
|
||||
};
|
||||
|
||||
self.services.registration_tokens.revoke_token(token)?;
|
||||
|
||||
self.write_str("Token revoked successfully.").await
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn list_tokens(&self) -> Result {
|
||||
let tokens: Vec<_> = self
|
||||
.services
|
||||
.registration_tokens
|
||||
.iterate_tokens()
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
self.write_str(&format!("Found {} registration tokens:\n", tokens.len()))
|
||||
.await?;
|
||||
|
||||
for token in tokens {
|
||||
self.write_str(&format!("- {token}\n")).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
mod commands;
|
||||
|
||||
use clap::{Args, Subcommand};
|
||||
use conduwuit::Result;
|
||||
|
||||
use crate::admin_command_dispatch;
|
||||
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum TokenCommand {
|
||||
/// Issue a new registration token
|
||||
#[clap(name = "issue")]
|
||||
IssueToken {
|
||||
/// When this token will expire.
|
||||
#[command(flatten)]
|
||||
expires: TokenExpires,
|
||||
},
|
||||
|
||||
/// Revoke a registration token
|
||||
#[clap(name = "revoke")]
|
||||
RevokeToken {
|
||||
/// The token to revoke.
|
||||
token: String,
|
||||
},
|
||||
|
||||
/// List all registration tokens
|
||||
#[clap(name = "list")]
|
||||
ListTokens,
|
||||
}
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
#[group(required = true, multiple = false)]
|
||||
pub struct TokenExpires {
|
||||
/// The maximum number of times this token is allowed to be used before it
|
||||
/// expires.
|
||||
#[arg(long)]
|
||||
max_uses: Option<u64>,
|
||||
|
||||
/// The maximum age of this token (e.g. 30s, 5m, 7d). It will expire after
|
||||
/// this much time has passed.
|
||||
#[arg(long)]
|
||||
max_age: Option<String>,
|
||||
|
||||
/// This token will never expire.
|
||||
#[arg(long)]
|
||||
immortal: bool,
|
||||
|
||||
/// A shortcut for `--max-uses 1`.
|
||||
#[arg(long)]
|
||||
once: bool,
|
||||
}
|
||||
+130
-2
@@ -238,6 +238,7 @@ pub(super) async fn deactivate(&self, no_leave_rooms: bool, user_id: String) ->
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn suspend(&self, user_id: String) -> Result {
|
||||
self.bail_restricted()?;
|
||||
let user_id = parse_local_user_id(self.services, &user_id)?;
|
||||
|
||||
if user_id == self.services.globals.server_user {
|
||||
@@ -262,6 +263,7 @@ pub(super) async fn suspend(&self, user_id: String) -> Result {
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn unsuspend(&self, user_id: String) -> Result {
|
||||
self.bail_restricted()?;
|
||||
let user_id = parse_local_user_id(self.services, &user_id)?;
|
||||
|
||||
if user_id == self.services.globals.server_user {
|
||||
@@ -278,7 +280,12 @@ pub(super) async fn unsuspend(&self, user_id: String) -> Result {
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn reset_password(&self, username: String, password: Option<String>) -> Result {
|
||||
pub(super) async fn reset_password(
|
||||
&self,
|
||||
logout: bool,
|
||||
username: String,
|
||||
password: Option<String>,
|
||||
) -> Result {
|
||||
let user_id = parse_local_user_id(self.services, &username)?;
|
||||
|
||||
if user_id == self.services.globals.server_user {
|
||||
@@ -301,7 +308,18 @@ pub(super) async fn reset_password(&self, username: String, password: Option<Str
|
||||
write!(self, "Successfully reset the password for user {user_id}: `{new_password}`")
|
||||
},
|
||||
}
|
||||
.await
|
||||
.await?;
|
||||
|
||||
if logout {
|
||||
self.services
|
||||
.users
|
||||
.all_device_ids(&user_id)
|
||||
.for_each(|device_id| self.services.users.remove_device(&user_id, device_id))
|
||||
.await;
|
||||
write!(self, "\nAll existing sessions have been logged out.").await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
@@ -974,3 +992,113 @@ pub(super) async fn force_leave_remote_room(
|
||||
self.write_str(&format!("{user_id} successfully left {room_id} via remote server."))
|
||||
.await
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn lock(&self, user_id: String) -> Result {
|
||||
self.bail_restricted()?;
|
||||
let user_id = parse_local_user_id(self.services, &user_id)?;
|
||||
assert!(
|
||||
self.services.globals.user_is_local(&user_id),
|
||||
"Parsed user_id must be a local user"
|
||||
);
|
||||
if user_id == self.services.globals.server_user {
|
||||
return Err!("Not allowed to lock the server service account.",);
|
||||
}
|
||||
|
||||
if !self.services.users.exists(&user_id).await {
|
||||
return Err!("User {user_id} does not exist.");
|
||||
}
|
||||
if self.services.users.is_admin(&user_id).await {
|
||||
return Err!("Admin users cannot be locked.");
|
||||
}
|
||||
self.services
|
||||
.users
|
||||
.lock_account(&user_id, self.sender_or_service_user())
|
||||
.await;
|
||||
|
||||
self.write_str(&format!("User {user_id} has been locked."))
|
||||
.await
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn unlock(&self, user_id: String) -> Result {
|
||||
self.bail_restricted()?;
|
||||
let user_id = parse_local_user_id(self.services, &user_id)?;
|
||||
assert!(
|
||||
self.services.globals.user_is_local(&user_id),
|
||||
"Parsed user_id must be a local user"
|
||||
);
|
||||
self.services.users.unlock_account(&user_id).await;
|
||||
|
||||
self.write_str(&format!("User {user_id} has been unlocked."))
|
||||
.await
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn logout(&self, user_id: String) -> Result {
|
||||
self.bail_restricted()?;
|
||||
let user_id = parse_local_user_id(self.services, &user_id)?;
|
||||
assert!(
|
||||
self.services.globals.user_is_local(&user_id),
|
||||
"Parsed user_id must be a local user"
|
||||
);
|
||||
if user_id == self.services.globals.server_user {
|
||||
return Err!("Not allowed to log out the server service account.",);
|
||||
}
|
||||
|
||||
if !self.services.users.exists(&user_id).await {
|
||||
return Err!("User {user_id} does not exist.");
|
||||
}
|
||||
if self.services.users.is_admin(&user_id).await {
|
||||
return Err!("You cannot forcefully log out admin users.");
|
||||
}
|
||||
self.services
|
||||
.users
|
||||
.all_device_ids(&user_id)
|
||||
.for_each(|device_id| self.services.users.remove_device(&user_id, device_id))
|
||||
.await;
|
||||
self.write_str(&format!("User {user_id} has been logged out from all devices."))
|
||||
.await
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn disable_login(&self, user_id: String) -> Result {
|
||||
self.bail_restricted()?;
|
||||
let user_id = parse_local_user_id(self.services, &user_id)?;
|
||||
assert!(
|
||||
self.services.globals.user_is_local(&user_id),
|
||||
"Parsed user_id must be a local user"
|
||||
);
|
||||
if user_id == self.services.globals.server_user {
|
||||
return Err!("Not allowed to disable login for the server service account.",);
|
||||
}
|
||||
|
||||
if !self.services.users.exists(&user_id).await {
|
||||
return Err!("User {user_id} does not exist.");
|
||||
}
|
||||
if self.services.users.is_admin(&user_id).await {
|
||||
return Err!("Admin users cannot have their login disallowed.");
|
||||
}
|
||||
self.services.users.disable_login(&user_id);
|
||||
|
||||
self.write_str(&format!(
|
||||
"{user_id} can no longer log in. Their existing sessions remain unaffected."
|
||||
))
|
||||
.await
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn enable_login(&self, user_id: String) -> Result {
|
||||
self.bail_restricted()?;
|
||||
let user_id = parse_local_user_id(self.services, &user_id)?;
|
||||
assert!(
|
||||
self.services.globals.user_is_local(&user_id),
|
||||
"Parsed user_id must be a local user"
|
||||
);
|
||||
if !self.services.users.exists(&user_id).await {
|
||||
return Err!("User {user_id} does not exist.");
|
||||
}
|
||||
self.services.users.enable_login(&user_id);
|
||||
|
||||
self.write_str(&format!("{user_id} can now log in.")).await
|
||||
}
|
||||
|
||||
+70
-19
@@ -9,7 +9,7 @@
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum UserCommand {
|
||||
/// - Create a new user
|
||||
/// Create a new user
|
||||
#[clap(alias = "create")]
|
||||
CreateUser {
|
||||
/// Username of the new user
|
||||
@@ -18,15 +18,18 @@ pub enum UserCommand {
|
||||
password: Option<String>,
|
||||
},
|
||||
|
||||
/// - Reset user password
|
||||
/// Reset user password
|
||||
ResetPassword {
|
||||
/// Log out existing sessions
|
||||
#[arg(short, long)]
|
||||
logout: bool,
|
||||
/// Username of the user for whom the password should be reset
|
||||
username: String,
|
||||
/// New password for the user, if unspecified one is generated
|
||||
password: Option<String>,
|
||||
},
|
||||
|
||||
/// - Deactivate a user
|
||||
/// Deactivate a user
|
||||
///
|
||||
/// User will be removed from all rooms by default.
|
||||
/// Use --no-leave-rooms to not leave all rooms by default.
|
||||
@@ -36,7 +39,7 @@ pub enum UserCommand {
|
||||
user_id: String,
|
||||
},
|
||||
|
||||
/// - Deactivate a list of users
|
||||
/// Deactivate a list of users
|
||||
///
|
||||
/// Recommended to use in conjunction with list-local-users.
|
||||
///
|
||||
@@ -59,7 +62,19 @@ pub enum UserCommand {
|
||||
force: bool,
|
||||
},
|
||||
|
||||
/// - Suspend a user
|
||||
/// Forcefully log a user out of all of their devices.
|
||||
///
|
||||
/// This will invalidate all access tokens for the specified user,
|
||||
/// effectively logging them out from all sessions.
|
||||
/// Note that this is destructive and may result in data loss for the user,
|
||||
/// such as encryption keys. Use with caution. Can only be used in the admin
|
||||
/// room.
|
||||
Logout {
|
||||
/// Username of the user to log out
|
||||
user_id: String,
|
||||
},
|
||||
|
||||
/// Suspend a user
|
||||
///
|
||||
/// Suspended users are able to log in, sync, and read messages, but are not
|
||||
/// able to send events nor redact them, cannot change their profile, and
|
||||
@@ -72,7 +87,7 @@ pub enum UserCommand {
|
||||
user_id: String,
|
||||
},
|
||||
|
||||
/// - Unsuspend a user
|
||||
/// Unsuspend a user
|
||||
///
|
||||
/// Reverses the effects of the `suspend` command, allowing the user to send
|
||||
/// messages, change their profile, create room invites, etc.
|
||||
@@ -81,48 +96,84 @@ pub enum UserCommand {
|
||||
user_id: String,
|
||||
},
|
||||
|
||||
/// - List local users in the database
|
||||
/// Lock a user
|
||||
///
|
||||
/// Locked users are unable to use their accounts beyond logging out. This
|
||||
/// is akin to a temporary deactivation that does not change the user's
|
||||
/// password. This can be used to quickly prevent a user from accessing
|
||||
/// their account.
|
||||
Lock {
|
||||
/// Username of the user to lock
|
||||
user_id: String,
|
||||
},
|
||||
|
||||
/// Unlock a user
|
||||
///
|
||||
/// Reverses the effects of the `lock` command, allowing the user to use
|
||||
/// their account again.
|
||||
Unlock {
|
||||
/// Username of the user to unlock
|
||||
user_id: String,
|
||||
},
|
||||
|
||||
/// Enable login for a user
|
||||
EnableLogin {
|
||||
/// Username of the user to enable login for
|
||||
user_id: String,
|
||||
},
|
||||
|
||||
/// Disable login for a user
|
||||
///
|
||||
/// Disables login for the specified user without deactivating or locking
|
||||
/// their account. This prevents the user from obtaining new access tokens,
|
||||
/// but does not invalidate existing sessions.
|
||||
DisableLogin {
|
||||
/// Username of the user to disable login for
|
||||
user_id: String,
|
||||
},
|
||||
|
||||
/// List local users in the database
|
||||
#[clap(alias = "list")]
|
||||
ListUsers,
|
||||
|
||||
/// - Lists all the rooms (local and remote) that the specified user is
|
||||
/// Lists all the rooms (local and remote) that the specified user is
|
||||
/// joined in
|
||||
ListJoinedRooms {
|
||||
user_id: String,
|
||||
},
|
||||
|
||||
/// - Manually join a local user to a room.
|
||||
/// Manually join a local user to a room.
|
||||
ForceJoinRoom {
|
||||
user_id: String,
|
||||
room_id: OwnedRoomOrAliasId,
|
||||
},
|
||||
|
||||
/// - Manually leave a local user from a room.
|
||||
/// Manually leave a local user from a room.
|
||||
ForceLeaveRoom {
|
||||
user_id: String,
|
||||
room_id: OwnedRoomOrAliasId,
|
||||
},
|
||||
|
||||
/// - Manually leave a remote room for a local user.
|
||||
/// Manually leave a remote room for a local user.
|
||||
ForceLeaveRemoteRoom {
|
||||
user_id: String,
|
||||
room_id: OwnedRoomOrAliasId,
|
||||
via: Option<String>,
|
||||
},
|
||||
|
||||
/// - Forces the specified user to drop their power levels to the room
|
||||
/// Forces the specified user to drop their power levels to the room
|
||||
/// default, if their permissions allow and the auth check permits
|
||||
ForceDemote {
|
||||
user_id: String,
|
||||
room_id: OwnedRoomOrAliasId,
|
||||
},
|
||||
|
||||
/// - Grant server-admin privileges to a user.
|
||||
/// Grant server-admin privileges to a user.
|
||||
MakeUserAdmin {
|
||||
user_id: String,
|
||||
},
|
||||
|
||||
/// - Puts a room tag for the specified user and room ID.
|
||||
/// Puts a room tag for the specified user and room ID.
|
||||
///
|
||||
/// This is primarily useful if you'd like to set your admin room
|
||||
/// to the special "System Alerts" section in Element as a way to
|
||||
@@ -135,20 +186,20 @@ pub enum UserCommand {
|
||||
tag: String,
|
||||
},
|
||||
|
||||
/// - Deletes the room tag for the specified user and room ID
|
||||
/// Deletes the room tag for the specified user and room ID
|
||||
DeleteRoomTag {
|
||||
user_id: String,
|
||||
room_id: OwnedRoomId,
|
||||
tag: String,
|
||||
},
|
||||
|
||||
/// - Gets all the room tags for the specified user and room ID
|
||||
/// Gets all the room tags for the specified user and room ID
|
||||
GetRoomTags {
|
||||
user_id: String,
|
||||
room_id: OwnedRoomId,
|
||||
},
|
||||
|
||||
/// - Attempts to forcefully redact the specified event ID from the sender
|
||||
/// Attempts to forcefully redact the specified event ID from the sender
|
||||
/// user
|
||||
///
|
||||
/// This is only valid for local users
|
||||
@@ -156,7 +207,7 @@ pub enum UserCommand {
|
||||
event_id: OwnedEventId,
|
||||
},
|
||||
|
||||
/// - Force joins a specified list of local users to join the specified
|
||||
/// Force joins a specified list of local users to join the specified
|
||||
/// room.
|
||||
///
|
||||
/// Specify a codeblock of usernames.
|
||||
@@ -171,7 +222,7 @@ pub enum UserCommand {
|
||||
yes_i_want_to_do_this: bool,
|
||||
},
|
||||
|
||||
/// - Force joins all local users to the specified room.
|
||||
/// Force joins all local users to the specified room.
|
||||
///
|
||||
/// At least 1 server admin must be in the room to reduce abuse.
|
||||
///
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
[package]
|
||||
name = "conduwuit_api"
|
||||
categories.workspace = true
|
||||
description.workspace = true
|
||||
edition.workspace = true
|
||||
keywords.workspace = true
|
||||
license.workspace = true
|
||||
readme.workspace = true
|
||||
repository.workspace = true
|
||||
@@ -93,7 +91,6 @@ serde.workspace = true
|
||||
sha1.workspace = true
|
||||
tokio.workspace = true
|
||||
tracing.workspace = true
|
||||
ctor.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
pub mod rooms;
|
||||
@@ -0,0 +1,132 @@
|
||||
use axum::extract::State;
|
||||
use conduwuit::{Err, Result, info, utils::ReadyExt, warn};
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use ruma::{
|
||||
OwnedRoomAliasId, continuwuity_admin_api::rooms,
|
||||
events::room::message::RoomMessageEventContent,
|
||||
};
|
||||
|
||||
use crate::{Ruma, client::leave_room};
|
||||
|
||||
/// # `PUT /_continuwuity/admin/rooms/{roomID}/ban`
|
||||
///
|
||||
/// Bans or unbans a room.
|
||||
pub(crate) async fn ban_room(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<rooms::ban::v1::Request>,
|
||||
) -> Result<rooms::ban::v1::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
if !services.users.is_admin(sender_user).await {
|
||||
return Err!(Request(Forbidden("Only server administrators can use this endpoint")));
|
||||
}
|
||||
|
||||
if body.banned {
|
||||
// Don't ban again if already banned
|
||||
if services.rooms.metadata.is_banned(&body.room_id).await {
|
||||
return Err!(Request(InvalidParam("Room is already banned")));
|
||||
}
|
||||
info!(%sender_user, "Banning room {}", body.room_id);
|
||||
|
||||
services
|
||||
.admin
|
||||
.notice(&format!("{sender_user} banned {} (ban in progress)", body.room_id))
|
||||
.await;
|
||||
|
||||
let mut users = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_members(&body.room_id)
|
||||
.map(ToOwned::to_owned)
|
||||
.ready_filter(|user| services.globals.user_is_local(user))
|
||||
.boxed();
|
||||
let mut evicted = Vec::new();
|
||||
let mut failed_evicted = Vec::new();
|
||||
|
||||
while let Some(ref user_id) = users.next().await {
|
||||
info!("Evicting user {} from room {}", user_id, body.room_id);
|
||||
match leave_room(&services, user_id, &body.room_id, None)
|
||||
.boxed()
|
||||
.await
|
||||
{
|
||||
| Ok(()) => {
|
||||
services.rooms.state_cache.forget(&body.room_id, user_id);
|
||||
evicted.push(user_id.clone());
|
||||
},
|
||||
| Err(e) => {
|
||||
warn!("Failed to evict user {} from room {}: {}", user_id, body.room_id, e);
|
||||
failed_evicted.push(user_id.clone());
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
let aliases: Vec<OwnedRoomAliasId> = services
|
||||
.rooms
|
||||
.alias
|
||||
.local_aliases_for_room(&body.room_id)
|
||||
.map(ToOwned::to_owned)
|
||||
.collect::<Vec<_>>()
|
||||
.await;
|
||||
for alias in &aliases {
|
||||
info!("Removing alias {} for banned room {}", alias, body.room_id);
|
||||
services
|
||||
.rooms
|
||||
.alias
|
||||
.remove_alias(alias, &services.globals.server_user)
|
||||
.await?;
|
||||
}
|
||||
|
||||
services.rooms.directory.set_not_public(&body.room_id); // remove from the room directory
|
||||
services.rooms.metadata.ban_room(&body.room_id, true); // prevent further joins
|
||||
services.rooms.metadata.disable_room(&body.room_id, true); // disable federation
|
||||
|
||||
services
|
||||
.admin
|
||||
.notice(&format!(
|
||||
"Finished banning {}: Removed {} users ({} failed) and {} aliases",
|
||||
body.room_id,
|
||||
evicted.len(),
|
||||
failed_evicted.len(),
|
||||
aliases.len()
|
||||
))
|
||||
.await;
|
||||
if !evicted.is_empty() || !failed_evicted.is_empty() || !aliases.is_empty() {
|
||||
let msg = services
|
||||
.admin
|
||||
.text_or_file(RoomMessageEventContent::text_markdown(format!(
|
||||
"Removed users:\n{}\n\nFailed to remove users:\n{}\n\nRemoved aliases: {}",
|
||||
evicted
|
||||
.iter()
|
||||
.map(|u| u.as_str())
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n"),
|
||||
failed_evicted
|
||||
.iter()
|
||||
.map(|u| u.as_str())
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n"),
|
||||
aliases
|
||||
.iter()
|
||||
.map(|a| a.as_str())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", "),
|
||||
)))
|
||||
.await;
|
||||
services.admin.send_message(msg).await.ok();
|
||||
}
|
||||
|
||||
Ok(rooms::ban::v1::Response::new(evicted, failed_evicted, aliases))
|
||||
} else {
|
||||
// Don't unban if not banned
|
||||
if !services.rooms.metadata.is_banned(&body.room_id).await {
|
||||
return Err!(Request(InvalidParam("Room is not banned")));
|
||||
}
|
||||
info!(%sender_user, "Unbanning room {}", body.room_id);
|
||||
services.rooms.metadata.disable_room(&body.room_id, false);
|
||||
services.rooms.metadata.ban_room(&body.room_id, false);
|
||||
services
|
||||
.admin
|
||||
.notice(&format!("{sender_user} unbanned {}", body.room_id))
|
||||
.await;
|
||||
Ok(rooms::ban::v1::Response::new(Vec::new(), Vec::new(), Vec::new()))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
use axum::extract::State;
|
||||
use conduwuit::{Err, Result};
|
||||
use futures::StreamExt;
|
||||
use ruma::{OwnedRoomId, continuwuity_admin_api::rooms};
|
||||
|
||||
use crate::Ruma;
|
||||
|
||||
/// # `GET /_continuwuity/admin/rooms/list`
|
||||
///
|
||||
/// Lists all rooms known to this server, excluding banned ones.
|
||||
pub(crate) async fn list_rooms(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<rooms::list::v1::Request>,
|
||||
) -> Result<rooms::list::v1::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
if !services.users.is_admin(sender_user).await {
|
||||
return Err!(Request(Forbidden("Only server administrators can use this endpoint")));
|
||||
}
|
||||
|
||||
let mut rooms: Vec<OwnedRoomId> = services
|
||||
.rooms
|
||||
.metadata
|
||||
.iter_ids()
|
||||
.filter_map(|room_id| async move {
|
||||
if !services.rooms.metadata.is_banned(room_id).await {
|
||||
Some(room_id.to_owned())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
.await;
|
||||
rooms.sort();
|
||||
Ok(rooms::list::v1::Response::new(rooms))
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
pub mod ban;
|
||||
pub mod list;
|
||||
+48
-16
@@ -179,13 +179,18 @@ pub(crate) async fn register_route(
|
||||
},
|
||||
}
|
||||
|
||||
return Err!(Request(Forbidden("Registration has been disabled.")));
|
||||
return Err!(Request(Forbidden(
|
||||
"This server is not accepting registrations at this time."
|
||||
)));
|
||||
}
|
||||
|
||||
if is_guest
|
||||
&& (!services.config.allow_guest_registration
|
||||
|| (services.config.allow_registration
|
||||
&& services.globals.registration_token.is_some()))
|
||||
&& services
|
||||
.registration_tokens
|
||||
.get_config_file_token()
|
||||
.is_some()))
|
||||
{
|
||||
info!(
|
||||
"Guest registration disabled / registration enabled with token configured, \
|
||||
@@ -203,7 +208,9 @@ pub(crate) async fn register_route(
|
||||
rejecting registration. Guest's initial device name: \"{}\"",
|
||||
body.initial_device_display_name.as_deref().unwrap_or("")
|
||||
);
|
||||
return Err!(Request(Forbidden("Registration is temporarily disabled.")));
|
||||
return Err!(Request(Forbidden(
|
||||
"This server is not accepting registrations at this time."
|
||||
)));
|
||||
}
|
||||
|
||||
let user_id = match (body.username.as_ref(), is_guest) {
|
||||
@@ -301,7 +308,13 @@ pub(crate) async fn register_route(
|
||||
let skip_auth = body.appservice_info.is_some() || is_guest;
|
||||
|
||||
// Populate required UIAA flows
|
||||
if services.globals.registration_token.is_some() {
|
||||
if services
|
||||
.registration_tokens
|
||||
.iterate_tokens()
|
||||
.next()
|
||||
.await
|
||||
.is_some()
|
||||
{
|
||||
// Registration token required
|
||||
uiaainfo.flows.push(AuthFlow {
|
||||
stages: vec![AuthType::RegistrationToken],
|
||||
@@ -323,7 +336,19 @@ pub(crate) async fn register_route(
|
||||
}
|
||||
|
||||
if uiaainfo.flows.is_empty() && !skip_auth {
|
||||
// No registration token necessary, but clients must still go through the flow
|
||||
// Registration isn't _disabled_, but there's no captcha configured and no
|
||||
// registration tokens currently set. Bail out by default unless open
|
||||
// registration was explicitly enabled.
|
||||
if !services
|
||||
.config
|
||||
.yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse
|
||||
{
|
||||
return Err!(Request(Forbidden(
|
||||
"This server is not accepting registrations at this time."
|
||||
)));
|
||||
}
|
||||
|
||||
// We have open registration enabled (😧), provide a dummy stage
|
||||
uiaainfo = UiaaInfo {
|
||||
flows: vec![AuthFlow { stages: vec![AuthType::Dummy] }],
|
||||
completed: Vec::new(),
|
||||
@@ -699,7 +724,7 @@ pub(crate) async fn change_password_route(
|
||||
Ok(change_password::v3::Response {})
|
||||
}
|
||||
|
||||
/// # `GET _matrix/client/r0/account/whoami`
|
||||
/// # `GET /_matrix/client/v3/account/whoami`
|
||||
///
|
||||
/// Get `user_id` of the sender user.
|
||||
///
|
||||
@@ -708,11 +733,17 @@ pub(crate) async fn whoami_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<whoami::v3::Request>,
|
||||
) -> Result<whoami::v3::Response> {
|
||||
let is_guest = services
|
||||
.users
|
||||
.is_deactivated(body.sender_user())
|
||||
.await
|
||||
.map_err(|_| {
|
||||
err!(Request(Forbidden("Application service has not registered this user.")))
|
||||
})? && body.appservice_info.is_none();
|
||||
Ok(whoami::v3::Response {
|
||||
user_id: body.sender_user().to_owned(),
|
||||
device_id: body.sender_device.clone(),
|
||||
is_guest: services.users.is_deactivated(body.sender_user()).await?
|
||||
&& body.appservice_info.is_none(),
|
||||
is_guest,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -846,19 +877,20 @@ pub(crate) async fn request_3pid_management_token_via_msisdn_route(
|
||||
|
||||
/// # `GET /_matrix/client/v1/register/m.login.registration_token/validity`
|
||||
///
|
||||
/// Checks if the provided registration token is valid at the time of checking
|
||||
///
|
||||
/// Currently does not have any ratelimiting, and this isn't very practical as
|
||||
/// there is only one registration token allowed.
|
||||
/// Checks if the provided registration token is valid at the time of checking.
|
||||
pub(crate) async fn check_registration_token_validity(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<check_registration_token_validity::v1::Request>,
|
||||
) -> Result<check_registration_token_validity::v1::Response> {
|
||||
let Some(reg_token) = services.globals.registration_token.clone() else {
|
||||
return Err!(Request(Forbidden("Server does not allow token registration")));
|
||||
};
|
||||
// TODO: ratelimit this pretty heavily
|
||||
|
||||
Ok(check_registration_token_validity::v1::Response { valid: reg_token == body.token })
|
||||
let valid = services
|
||||
.registration_tokens
|
||||
.validate_token(body.token.clone())
|
||||
.await
|
||||
.is_some();
|
||||
|
||||
Ok(check_registration_token_validity::v1::Response { valid })
|
||||
}
|
||||
|
||||
/// Runs through all the deactivation steps:
|
||||
|
||||
@@ -91,8 +91,11 @@ pub(crate) async fn upload_keys_route(
|
||||
.users
|
||||
.get_device_keys(sender_user, sender_device)
|
||||
.await
|
||||
.and_then(|keys| keys.deserialize().map_err(Into::into))
|
||||
{
|
||||
if existing_keys.json().get() == device_keys.json().get() {
|
||||
// NOTE: also serves as a workaround for a nheko bug which omits cross-signing
|
||||
// NOTE: signatures when re-uploading the same DeviceKeys.
|
||||
if existing_keys.keys == deser_device_keys.keys {
|
||||
debug!(
|
||||
%sender_user,
|
||||
%sender_device,
|
||||
|
||||
@@ -3,10 +3,11 @@
|
||||
use conduwuit::{
|
||||
Err, Result, debug_error, err, info,
|
||||
matrix::{event::gen_event_id_canonical_json, pdu::PduBuilder},
|
||||
warn,
|
||||
};
|
||||
use futures::FutureExt;
|
||||
use ruma::{
|
||||
OwnedServerName, RoomId, UserId,
|
||||
RoomId, UserId,
|
||||
api::{client::membership::invite_user, federation::membership::create_invite},
|
||||
events::{
|
||||
invite_permission_config::FilterLevel,
|
||||
@@ -124,6 +125,18 @@ pub(crate) async fn invite_helper(
|
||||
return Err!(Request(Forbidden("Invites are not allowed on this server.")));
|
||||
}
|
||||
|
||||
if let Err(e) = services
|
||||
.antispam
|
||||
.user_may_invite(sender_user.to_owned(), recipient_user.to_owned(), room_id.to_owned())
|
||||
.await
|
||||
{
|
||||
warn!(
|
||||
"Invite from {} to {} in room {} blocked by antispam: {e:?}",
|
||||
sender_user, recipient_user, room_id
|
||||
);
|
||||
return Err!(Request(Forbidden("Invite blocked by antispam service.")));
|
||||
}
|
||||
|
||||
if !services.globals.user_is_local(recipient_user) {
|
||||
let (pdu, pdu_json, invite_room_state) = {
|
||||
let state_lock = services.rooms.state.mutex.lock(room_id).await;
|
||||
@@ -190,19 +203,10 @@ pub(crate) async fn invite_helper(
|
||||
))));
|
||||
}
|
||||
|
||||
let origin: OwnedServerName = serde_json::from_value(serde_json::to_value(
|
||||
value
|
||||
.get("origin")
|
||||
.ok_or_else(|| err!(Request(BadJson("Event missing origin field."))))?,
|
||||
)?)
|
||||
.map_err(|e| {
|
||||
err!(Request(BadJson(warn!("Origin field in event is not a valid server name: {e}"))))
|
||||
})?;
|
||||
|
||||
let pdu_id = services
|
||||
.rooms
|
||||
.event_handler
|
||||
.handle_incoming_pdu(&origin, room_id, &event_id, value, true)
|
||||
.handle_incoming_pdu(recipient_user.server_name(), room_id, &event_id, value, true)
|
||||
.boxed()
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
events::{
|
||||
StateEventType,
|
||||
room::{
|
||||
join_rules::{AllowRule, JoinRule, RoomJoinRulesEventContent},
|
||||
join_rules::{AllowRule, JoinRule},
|
||||
member::{MembershipState, RoomMemberEventContent},
|
||||
},
|
||||
},
|
||||
@@ -288,6 +288,23 @@ pub async fn join_room_by_id_helper(
|
||||
return Ok(join_room_by_id::v3::Response { room_id: room_id.into() });
|
||||
}
|
||||
|
||||
if let Err(e) = services
|
||||
.antispam
|
||||
.user_may_join_room(
|
||||
sender_user.to_owned(),
|
||||
room_id.to_owned(),
|
||||
services
|
||||
.rooms
|
||||
.state_cache
|
||||
.is_invited(sender_user, room_id)
|
||||
.await,
|
||||
)
|
||||
.await
|
||||
{
|
||||
warn!("Antispam prevented user {} from joining room {}: {}", sender_user, room_id, e);
|
||||
return Err!(Request(Forbidden("You are not allowed to join this room.")));
|
||||
}
|
||||
|
||||
let server_in_room = services
|
||||
.rooms
|
||||
.state_cache
|
||||
@@ -321,6 +338,17 @@ pub async fn join_room_by_id_helper(
|
||||
)));
|
||||
}
|
||||
|
||||
if services.antispam.check_all_joins() {
|
||||
if let Err(e) = services
|
||||
.antispam
|
||||
.meowlnir_accept_make_join(room_id.to_owned(), sender_user.to_owned())
|
||||
.await
|
||||
{
|
||||
warn!("Antispam prevented user {} from joining room {}: {}", sender_user, room_id, e);
|
||||
return Err!(Request(Forbidden("Antispam rejected join request.")));
|
||||
}
|
||||
}
|
||||
|
||||
if server_in_room {
|
||||
join_room_by_id_helper_local(
|
||||
services,
|
||||
@@ -347,7 +375,6 @@ pub async fn join_room_by_id_helper(
|
||||
.boxed()
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(join_room_by_id::v3::Response::new(room_id.to_owned()))
|
||||
}
|
||||
|
||||
@@ -720,45 +747,51 @@ async fn join_room_by_id_helper_local(
|
||||
state_lock: RoomMutexGuard,
|
||||
) -> Result {
|
||||
debug_info!("We can join locally");
|
||||
let join_rules = services.rooms.state_accessor.get_join_rules(room_id).await;
|
||||
|
||||
let join_rules_event_content = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get_content::<RoomJoinRulesEventContent>(
|
||||
room_id,
|
||||
&StateEventType::RoomJoinRules,
|
||||
"",
|
||||
)
|
||||
.await;
|
||||
|
||||
let restriction_rooms = match join_rules_event_content {
|
||||
| Ok(RoomJoinRulesEventContent {
|
||||
join_rule: JoinRule::Restricted(restricted) | JoinRule::KnockRestricted(restricted),
|
||||
}) => restricted
|
||||
.allow
|
||||
.into_iter()
|
||||
.filter_map(|a| match a {
|
||||
| AllowRule::RoomMembership(r) => Some(r.room_id),
|
||||
| _ => None,
|
||||
})
|
||||
.collect(),
|
||||
| _ => Vec::new(),
|
||||
};
|
||||
|
||||
let join_authorized_via_users_server: Option<OwnedUserId> = {
|
||||
if restriction_rooms
|
||||
.iter()
|
||||
.stream()
|
||||
.any(|restriction_room_id| {
|
||||
trace!("Checking if {sender_user} is joined to {restriction_room_id}");
|
||||
services
|
||||
.rooms
|
||||
.state_cache
|
||||
.is_joined(sender_user, restriction_room_id)
|
||||
})
|
||||
.await
|
||||
{
|
||||
services
|
||||
let mut restricted_join_authorized = None;
|
||||
match join_rules {
|
||||
| JoinRule::Restricted(restricted) | JoinRule::KnockRestricted(restricted) => {
|
||||
for restriction in restricted.allow {
|
||||
match restriction {
|
||||
| AllowRule::RoomMembership(membership) => {
|
||||
if services
|
||||
.rooms
|
||||
.state_cache
|
||||
.is_joined(sender_user, &membership.room_id)
|
||||
.await
|
||||
{
|
||||
restricted_join_authorized = Some(true);
|
||||
break;
|
||||
}
|
||||
},
|
||||
| AllowRule::UnstableSpamChecker => {
|
||||
match services
|
||||
.antispam
|
||||
.meowlnir_accept_make_join(room_id.to_owned(), sender_user.to_owned())
|
||||
.await
|
||||
{
|
||||
| Ok(()) => {
|
||||
restricted_join_authorized = Some(true);
|
||||
break;
|
||||
},
|
||||
| Err(_) =>
|
||||
return Err!(Request(Forbidden(
|
||||
"Antispam rejected join request."
|
||||
))),
|
||||
}
|
||||
},
|
||||
| _ => {},
|
||||
}
|
||||
}
|
||||
},
|
||||
| _ => {},
|
||||
}
|
||||
let join_authorized_via_users_server = if restricted_join_authorized.is_none() {
|
||||
None
|
||||
} else {
|
||||
match restricted_join_authorized.unwrap() {
|
||||
| true => services
|
||||
.rooms
|
||||
.state_cache
|
||||
.local_users_in_room(room_id)
|
||||
@@ -774,10 +807,14 @@ async fn join_room_by_id_helper_local(
|
||||
.boxed()
|
||||
.next()
|
||||
.await
|
||||
.map(ToOwned::to_owned)
|
||||
} else {
|
||||
trace!("No restriction rooms are joined by {sender_user}");
|
||||
None
|
||||
.map(ToOwned::to_owned),
|
||||
| false => {
|
||||
warn!(
|
||||
"Join authorization failed for restricted join in room {room_id} for user \
|
||||
{sender_user}"
|
||||
);
|
||||
return Err!(Request(Forbidden("You are not authorized to join this room.")));
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
@@ -805,16 +842,14 @@ async fn join_room_by_id_helper_local(
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
if restriction_rooms.is_empty()
|
||||
&& (servers.is_empty()
|
||||
|| servers.len() == 1 && services.globals.server_is_ours(&servers[0]))
|
||||
{
|
||||
if servers.is_empty() || servers.len() == 1 && services.globals.server_is_ours(&servers[0]) {
|
||||
return Err(error);
|
||||
}
|
||||
|
||||
warn!(
|
||||
"We couldn't do the join locally, maybe federation can help to satisfy the restricted \
|
||||
join requirements"
|
||||
?error,
|
||||
servers = %servers.len(),
|
||||
"Could not join restricted room locally, attempting remote join",
|
||||
);
|
||||
let Ok((make_join_response, remote_server)) =
|
||||
make_join_request(services, sender_user, room_id, servers).await
|
||||
|
||||
@@ -178,7 +178,20 @@ pub async fn leave_room(
|
||||
.rooms
|
||||
.state_cache
|
||||
.left_state(user_id, room_id)
|
||||
.await?
|
||||
.await
|
||||
.inspect_err(|err| {
|
||||
// `left_state` may return an Err if the user _is_ in the room they're
|
||||
// trying to leave, but the membership cache is incorrect and
|
||||
// they're cached as being joined. In this situation
|
||||
// we save a `None` to the `roomuserid_leftcount` table, which generates
|
||||
// and sends a dummy leave to the client.
|
||||
warn!(
|
||||
?err,
|
||||
"Trying to leave room not cached as leave, sending dummy leave \
|
||||
event to client"
|
||||
);
|
||||
})
|
||||
.unwrap_or_default()
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
@@ -371,11 +371,3 @@ pub(crate) async fn is_ignored_invite(
|
||||
.invite_filter_level(&sender_user, recipient_user)
|
||||
.await == FilterLevel::Ignore
|
||||
}
|
||||
|
||||
#[cfg_attr(debug_assertions, ctor::ctor)]
|
||||
fn _is_sorted() {
|
||||
debug_assert!(
|
||||
IGNORED_MESSAGE_TYPES.is_sorted(),
|
||||
"IGNORED_MESSAGE_TYPES must be sorted by the developer"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -238,6 +238,7 @@ pub(crate) async fn create_room_route(
|
||||
event_type: TimelineEventType::RoomCreate,
|
||||
content: to_raw_value(&create_content)?,
|
||||
state_key: Some(StateKey::new()),
|
||||
timestamp: body.origin_server_ts,
|
||||
..Default::default()
|
||||
},
|
||||
sender_user,
|
||||
@@ -256,6 +257,14 @@ pub(crate) async fn create_room_route(
|
||||
},
|
||||
};
|
||||
drop(state_lock);
|
||||
if let Some(expected_room_id) = body.room_id.as_ref() {
|
||||
if expected_room_id.as_str() != room_id.as_str() {
|
||||
return Err!(Request(InvalidParam(
|
||||
"Custom room ID {expected_room_id} does not match the generated room ID \
|
||||
{room_id}.",
|
||||
)));
|
||||
}
|
||||
}
|
||||
debug!("Room created with ID {room_id}");
|
||||
let state_lock = services.rooms.state.mutex.lock(&room_id).await;
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
use conduwuit::{
|
||||
Err, Error, Result, debug, err, info,
|
||||
utils::{self, ReadyExt, hash},
|
||||
warn,
|
||||
};
|
||||
use conduwuit_core::{debug_error, debug_warn};
|
||||
use conduwuit_service::{Services, uiaa::SESSION_ID_LENGTH};
|
||||
@@ -12,6 +13,7 @@
|
||||
use ruma::{
|
||||
OwnedUserId, UserId,
|
||||
api::client::{
|
||||
error::ErrorKind,
|
||||
session::{
|
||||
get_login_token,
|
||||
get_login_types::{
|
||||
@@ -184,6 +186,15 @@ pub(crate) async fn handle_login(
|
||||
return Err!(Request(Unknown("User ID does not belong to this homeserver")));
|
||||
}
|
||||
|
||||
if services.users.is_locked(&user_id).await? {
|
||||
return Err(Error::BadRequest(ErrorKind::UserLocked, "This account has been locked."));
|
||||
}
|
||||
|
||||
if services.users.is_login_disabled(&user_id).await {
|
||||
warn!(%user_id, "user attempted to log in with a login-disabled account");
|
||||
return Err!(Request(Forbidden("This account is not permitted to log in.")));
|
||||
}
|
||||
|
||||
if cfg!(feature = "ldap") && services.config.ldap.enable {
|
||||
match Box::pin(ldap_login(services, &user_id, &lowercased_user_id, password)).await {
|
||||
| Ok(user_id) => Ok(user_id),
|
||||
|
||||
+3
-2
@@ -1,12 +1,13 @@
|
||||
#![type_length_limit = "16384"] //TODO: reduce me
|
||||
#![allow(clippy::toplevel_ref_arg)]
|
||||
|
||||
extern crate conduwuit_core as conduwuit;
|
||||
extern crate conduwuit_service as service;
|
||||
pub mod client;
|
||||
pub mod router;
|
||||
pub mod server;
|
||||
|
||||
extern crate conduwuit_core as conduwuit;
|
||||
extern crate conduwuit_service as service;
|
||||
pub mod admin;
|
||||
|
||||
pub(crate) use self::router::{Ruma, RumaResponse, State};
|
||||
|
||||
|
||||
+4
-2
@@ -17,7 +17,7 @@
|
||||
|
||||
use self::handler::RouterExt;
|
||||
pub(super) use self::{args::Args as Ruma, response::RumaResponse};
|
||||
use crate::{client, server};
|
||||
use crate::{admin, client, server};
|
||||
|
||||
pub fn build(router: Router<State>, server: &Server) -> Router<State> {
|
||||
let config = &server.config;
|
||||
@@ -187,7 +187,9 @@ pub fn build(router: Router<State>, server: &Server) -> Router<State> {
|
||||
.route("/_conduwuit/server_version", get(client::conduwuit_server_version))
|
||||
.route("/_continuwuity/server_version", get(client::conduwuit_server_version))
|
||||
.ruma_route(&client::room_initial_sync_route)
|
||||
.route("/client/server.json", get(client::syncv3_client_server_json));
|
||||
.route("/client/server.json", get(client::syncv3_client_server_json))
|
||||
.ruma_route(&admin::rooms::ban::ban_room)
|
||||
.ruma_route(&admin::rooms::list::list_rooms);
|
||||
|
||||
if config.allow_federation {
|
||||
router = router
|
||||
|
||||
+24
-6
@@ -137,12 +137,30 @@ pub(super) async fn auth(
|
||||
| (
|
||||
AuthScheme::AccessToken | AuthScheme::AccessTokenOptional | AuthScheme::None,
|
||||
Token::User((user_id, device_id)),
|
||||
) => Ok(Auth {
|
||||
origin: None,
|
||||
sender_user: Some(user_id),
|
||||
sender_device: Some(device_id),
|
||||
appservice_info: None,
|
||||
}),
|
||||
) => {
|
||||
let is_locked = services.users.is_locked(&user_id).await.map_err(|e| {
|
||||
err!(Request(Forbidden(warn!("Failed to check user lock status: {e}"))))
|
||||
})?;
|
||||
if is_locked {
|
||||
// Only /logout and /logout/all are allowed for locked users
|
||||
if !matches!(
|
||||
metadata,
|
||||
&ruma::api::client::session::logout::v3::Request::METADATA
|
||||
| &ruma::api::client::session::logout_all::v3::Request::METADATA
|
||||
) {
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::UserLocked,
|
||||
"This account has been locked.",
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(Auth {
|
||||
origin: None,
|
||||
sender_user: Some(user_id),
|
||||
sender_device: Some(device_id),
|
||||
appservice_info: None,
|
||||
})
|
||||
},
|
||||
| (AuthScheme::ServerSignatures, Token::None) =>
|
||||
Ok(auth_server(services, request, json_body).await?),
|
||||
| (
|
||||
|
||||
@@ -148,6 +148,15 @@ pub(crate) async fn create_invite_route(
|
||||
return Err!(Request(Forbidden("This server does not allow room invites.")));
|
||||
}
|
||||
|
||||
if let Err(e) = services
|
||||
.antispam
|
||||
.user_may_invite(sender_user.to_owned(), recipient_user.clone(), body.room_id.clone())
|
||||
.await
|
||||
{
|
||||
warn!("Antispam rejected invite: {e:?}");
|
||||
return Err!(Request(Forbidden("Invite rejected by antispam service.")));
|
||||
}
|
||||
|
||||
let mut invite_state = body.invite_room_state.clone();
|
||||
|
||||
let mut event: JsonObject = serde_json::from_str(body.event.get())
|
||||
|
||||
+50
-22
@@ -1,7 +1,7 @@
|
||||
use std::borrow::ToOwned;
|
||||
|
||||
use axum::extract::State;
|
||||
use conduwuit::{
|
||||
Err, Error, Result, debug_info, info, matrix::pdu::PduBuilder, utils::IterStream, warn,
|
||||
};
|
||||
use conduwuit::{Err, Error, Result, debug, debug_info, info, matrix::pdu::PduBuilder, warn};
|
||||
use conduwuit_service::Services;
|
||||
use futures::StreamExt;
|
||||
use ruma::{
|
||||
@@ -122,6 +122,16 @@ pub(crate) async fn create_join_event_template_route(
|
||||
None
|
||||
}
|
||||
};
|
||||
if services.antispam.check_all_joins() && join_authorized_via_users_server.is_none() {
|
||||
if services
|
||||
.antispam
|
||||
.meowlnir_accept_make_join(body.room_id.clone(), body.user_id.clone())
|
||||
.await
|
||||
.is_err()
|
||||
{
|
||||
return Err!(Request(Forbidden("Antispam rejected join request.")));
|
||||
}
|
||||
}
|
||||
|
||||
let (_pdu, mut pdu_json) = services
|
||||
.rooms
|
||||
@@ -136,7 +146,6 @@ pub(crate) async fn create_join_event_template_route(
|
||||
&state_lock,
|
||||
)
|
||||
.await?;
|
||||
|
||||
drop(state_lock);
|
||||
|
||||
// room v3 and above removed the "event_id" field from remote PDU format
|
||||
@@ -192,25 +201,44 @@ pub(crate) async fn user_can_perform_restricted_join(
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
if r.allow
|
||||
.iter()
|
||||
.filter_map(|rule| {
|
||||
if let AllowRule::RoomMembership(membership) = rule {
|
||||
Some(membership)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.stream()
|
||||
.any(|m| services.rooms.state_cache.is_joined(user_id, &m.room_id))
|
||||
.await
|
||||
{
|
||||
Ok(true)
|
||||
} else {
|
||||
Err!(Request(UnableToAuthorizeJoin(
|
||||
"Joining user is not known to be in any required room."
|
||||
)))
|
||||
for allow_rule in &r.allow {
|
||||
match allow_rule {
|
||||
| AllowRule::RoomMembership(membership) => {
|
||||
if services
|
||||
.rooms
|
||||
.state_cache
|
||||
.is_joined(user_id, &membership.room_id)
|
||||
.await
|
||||
{
|
||||
debug!(
|
||||
"User {} is allowed to join room {} via membership in room {}",
|
||||
user_id, room_id, membership.room_id
|
||||
);
|
||||
return Ok(true);
|
||||
}
|
||||
},
|
||||
| AllowRule::UnstableSpamChecker =>
|
||||
return match services
|
||||
.antispam
|
||||
.meowlnir_accept_make_join(room_id.to_owned(), user_id.to_owned())
|
||||
.await
|
||||
{
|
||||
| Ok(()) => Ok(true),
|
||||
| Err(_) => Err!(Request(Forbidden("Antispam rejected join request."))),
|
||||
},
|
||||
| _ => {
|
||||
debug_info!(
|
||||
"Unsupported allow rule in restricted join for room {}: {:?}",
|
||||
room_id,
|
||||
allow_rule
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Err!(Request(UnableToAuthorizeJoin(
|
||||
"Joining user is not known to be in any required room."
|
||||
)))
|
||||
}
|
||||
|
||||
pub(crate) fn maybe_strip_event_id(
|
||||
|
||||
@@ -13,8 +13,7 @@
|
||||
use conduwuit_service::Services;
|
||||
use futures::{FutureExt, StreamExt, TryStreamExt};
|
||||
use ruma::{
|
||||
CanonicalJsonValue, OwnedEventId, OwnedRoomId, OwnedServerName, OwnedUserId, RoomId,
|
||||
ServerName,
|
||||
CanonicalJsonValue, OwnedEventId, OwnedRoomId, OwnedUserId, RoomId, ServerName,
|
||||
api::federation::membership::create_join_event,
|
||||
events::{
|
||||
StateEventType,
|
||||
@@ -178,15 +177,6 @@ async fn create_join_event(
|
||||
}
|
||||
}
|
||||
|
||||
let origin: OwnedServerName = serde_json::from_value(
|
||||
value
|
||||
.get("origin")
|
||||
.ok_or_else(|| err!(Request(BadJson("Event does not have an origin server name."))))?
|
||||
.clone()
|
||||
.into(),
|
||||
)
|
||||
.map_err(|e| err!(Request(BadJson("Event has an invalid origin server name: {e}"))))?;
|
||||
|
||||
trace!("Signing send_join event");
|
||||
services
|
||||
.server_keys
|
||||
@@ -204,7 +194,7 @@ async fn create_join_event(
|
||||
let pdu_id = services
|
||||
.rooms
|
||||
.event_handler
|
||||
.handle_incoming_pdu(&origin, room_id, &event_id, value.clone(), true)
|
||||
.handle_incoming_pdu(sender.server_name(), room_id, &event_id, value.clone(), true)
|
||||
.boxed()
|
||||
.await?
|
||||
.ok_or_else(|| err!(Request(InvalidParam("Could not accept as timeline event."))))?;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
};
|
||||
use futures::FutureExt;
|
||||
use ruma::{
|
||||
OwnedServerName, OwnedUserId,
|
||||
OwnedUserId,
|
||||
RoomVersionId::*,
|
||||
api::federation::knock::send_knock,
|
||||
events::{
|
||||
@@ -136,15 +136,6 @@ pub(crate) async fn create_knock_event_v1_route(
|
||||
return Err!(Request(InvalidParam("state_key does not match sender user of event.")));
|
||||
}
|
||||
|
||||
let origin: OwnedServerName = serde_json::from_value(
|
||||
value
|
||||
.get("origin")
|
||||
.ok_or_else(|| err!(Request(BadJson("Event does not have an origin server name."))))?
|
||||
.clone()
|
||||
.into(),
|
||||
)
|
||||
.map_err(|e| err!(Request(BadJson("Event has an invalid origin server name: {e}"))))?;
|
||||
|
||||
let mut event: JsonObject = serde_json::from_str(body.pdu.get())
|
||||
.map_err(|e| err!(Request(InvalidParam("Invalid knock event PDU: {e}"))))?;
|
||||
|
||||
@@ -163,7 +154,7 @@ pub(crate) async fn create_knock_event_v1_route(
|
||||
let pdu_id = services
|
||||
.rooms
|
||||
.event_handler
|
||||
.handle_incoming_pdu(&origin, &body.room_id, &event_id, value.clone(), true)
|
||||
.handle_incoming_pdu(sender.server_name(), &body.room_id, &event_id, value.clone(), true)
|
||||
.boxed()
|
||||
.await?
|
||||
.ok_or_else(|| err!(Request(InvalidParam("Could not accept as timeline event."))))?;
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
[package]
|
||||
name = "conduwuit_build_metadata"
|
||||
categories.workspace = true
|
||||
description.workspace = true
|
||||
edition.workspace = true
|
||||
keywords.workspace = true
|
||||
license.workspace = true
|
||||
readme.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
[package]
|
||||
name = "conduwuit_core"
|
||||
categories.workspace = true
|
||||
description.workspace = true
|
||||
edition.workspace = true
|
||||
keywords.workspace = true
|
||||
license.workspace = true
|
||||
readme.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
const NAME_MAX: usize = 128;
|
||||
const KEY_SEGS: usize = 8;
|
||||
|
||||
#[crate::ctor]
|
||||
#[ctor::ctor]
|
||||
fn _static_initialization() {
|
||||
acq_epoch().expect("pre-initialization of jemalloc failed");
|
||||
acq_epoch().expect("pre-initialization of jemalloc failed");
|
||||
|
||||
@@ -146,22 +146,6 @@ pub fn check(config: &Config) -> Result {
|
||||
));
|
||||
}
|
||||
|
||||
// check if we can read the token file path, and check if the file is empty
|
||||
if config.registration_token_file.as_ref().is_some_and(|path| {
|
||||
let Ok(token) = std::fs::read_to_string(path).inspect_err(|e| {
|
||||
error!("Failed to read the registration token file: {e}");
|
||||
}) else {
|
||||
return true;
|
||||
};
|
||||
|
||||
token == String::new()
|
||||
}) {
|
||||
return Err!(Config(
|
||||
"registration_token_file",
|
||||
"Registration token file was specified but is empty or failed to be read"
|
||||
));
|
||||
}
|
||||
|
||||
if config.max_request_size < 10_000_000 {
|
||||
return Err!(Config(
|
||||
"max_request_size",
|
||||
@@ -187,29 +171,9 @@ pub fn check(config: &Config) -> Result {
|
||||
));
|
||||
}
|
||||
|
||||
if config.allow_registration
|
||||
&& !config.yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse
|
||||
&& config.registration_token.is_none()
|
||||
&& config.registration_token_file.is_none()
|
||||
&& config.recaptcha_site_key.is_none()
|
||||
{
|
||||
return Err!(Config(
|
||||
"registration_token",
|
||||
"!! You have `allow_registration` enabled without a token or captcha configured \
|
||||
which means you are allowing ANYONE to register on your continuwuity instance \
|
||||
without any 2nd-step (e.g. registration token, captcha), which is FREQUENTLY \
|
||||
abused by malicious actors. If this is not the intended behaviour, please set a \
|
||||
registration token. For security and safety reasons, continuwuity will shut down. \
|
||||
If you are extra sure this is the desired behaviour you want, please set the \
|
||||
following config option to true:
|
||||
`yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse`"
|
||||
));
|
||||
}
|
||||
|
||||
if config.allow_registration
|
||||
&& config.yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse
|
||||
&& config.registration_token.is_none()
|
||||
&& config.registration_token_file.is_none()
|
||||
{
|
||||
warn!(
|
||||
"Open registration is enabled via setting \
|
||||
|
||||
+109
-22
@@ -18,7 +18,7 @@
|
||||
pub use figment::{Figment, value::Value as FigmentValue};
|
||||
use regex::RegexSet;
|
||||
use ruma::{
|
||||
OwnedRoomOrAliasId, OwnedServerName, OwnedUserId, RoomVersionId,
|
||||
OwnedRoomId, OwnedRoomOrAliasId, OwnedServerName, OwnedUserId, RoomVersionId,
|
||||
api::client::discovery::discover_support::ContactRole,
|
||||
};
|
||||
use serde::{Deserialize, de::IgnoredAny};
|
||||
@@ -53,7 +53,7 @@
|
||||
### For more information, see:
|
||||
### https://continuwuity.org/configuration.html
|
||||
"#,
|
||||
ignore = "config_paths catchall well_known tls blurhashing allow_invalid_tls_certificates_yes_i_know_what_the_fuck_i_am_doing_with_this_and_i_know_this_is_insecure"
|
||||
ignore = "config_paths catchall"
|
||||
)]
|
||||
pub struct Config {
|
||||
// Paths to config file(s). Not supposed to be set manually in the config file,
|
||||
@@ -104,7 +104,7 @@ pub struct Config {
|
||||
#[serde(default = "default_port")]
|
||||
port: ListeningPort,
|
||||
|
||||
// external structure; separate section
|
||||
/// display: nested
|
||||
#[serde(default)]
|
||||
pub tls: TlsConfig,
|
||||
|
||||
@@ -434,7 +434,9 @@ pub struct Config {
|
||||
/// this to be high to account for extremely large room joins, slow
|
||||
/// homeservers, your own resources etc.
|
||||
///
|
||||
/// default: 300
|
||||
/// Joins have 6x the timeout.
|
||||
///
|
||||
/// default: 60
|
||||
#[serde(default = "default_federation_timeout")]
|
||||
pub federation_timeout: u64,
|
||||
|
||||
@@ -500,7 +502,19 @@ pub struct Config {
|
||||
#[serde(default = "default_appservice_idle_timeout")]
|
||||
pub appservice_idle_timeout: u64,
|
||||
|
||||
/// Notification gateway pusher idle connection pool timeout.
|
||||
/// Notification gateway pusher request connection timeout (seconds).
|
||||
///
|
||||
/// default: 15
|
||||
#[serde(default = "default_pusher_conn_timeout")]
|
||||
pub pusher_conn_timeout: u64,
|
||||
|
||||
/// Notification gateway pusher total request timeout (seconds).
|
||||
///
|
||||
/// default: 60
|
||||
#[serde(default = "default_pusher_timeout")]
|
||||
pub pusher_timeout: u64,
|
||||
|
||||
/// Notification gateway pusher idle connection pool timeout (seconds).
|
||||
///
|
||||
/// default: 15
|
||||
#[serde(default = "default_pusher_idle_timeout")]
|
||||
@@ -544,7 +558,7 @@ pub struct Config {
|
||||
/// `yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse`
|
||||
///
|
||||
/// If you would like registration only via token reg, please configure
|
||||
/// `registration_token` or `registration_token_file`.
|
||||
/// `registration_token`.
|
||||
#[serde(default)]
|
||||
pub allow_registration: bool,
|
||||
|
||||
@@ -575,22 +589,14 @@ pub struct Config {
|
||||
/// `yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse`
|
||||
/// to true to allow open registration without any conditions.
|
||||
///
|
||||
/// YOU NEED TO EDIT THIS OR USE registration_token_file.
|
||||
/// If you do not want to set a static token, the `!admin token` commands
|
||||
/// may also be used to manage registration tokens.
|
||||
///
|
||||
/// example: "o&^uCtes4HPf0Vu@F20jQeeWE7"
|
||||
///
|
||||
/// display: sensitive
|
||||
pub registration_token: Option<String>,
|
||||
|
||||
/// Path to a file on the system that gets read for additional registration
|
||||
/// tokens. Multiple tokens can be added if you separate them with
|
||||
/// whitespace
|
||||
///
|
||||
/// continuwuity must be able to access the file, and it must not be empty
|
||||
///
|
||||
/// example: "/etc/continuwuity/.reg_token"
|
||||
pub registration_token_file: Option<PathBuf>,
|
||||
|
||||
/// The public site key for reCaptcha. If this is provided, reCaptcha
|
||||
/// becomes required during registration. If both captcha *and*
|
||||
/// registration token are enabled, both will be required during
|
||||
@@ -717,7 +723,7 @@ pub struct Config {
|
||||
#[serde(default = "default_default_room_version")]
|
||||
pub default_room_version: RoomVersionId,
|
||||
|
||||
// external structure; separate section
|
||||
/// display: nested
|
||||
#[serde(default)]
|
||||
pub well_known: WellKnownConfig,
|
||||
|
||||
@@ -1670,6 +1676,13 @@ pub struct Config {
|
||||
#[serde(default = "default_url_preview_max_spider_size")]
|
||||
pub url_preview_max_spider_size: usize,
|
||||
|
||||
/// Total request timeout for URL previews (seconds). This includes
|
||||
/// connection, request, and response body reading time.
|
||||
///
|
||||
/// default: 120
|
||||
#[serde(default = "default_url_preview_timeout")]
|
||||
pub url_preview_timeout: u64,
|
||||
|
||||
/// Option to decide whether you would like to run the domain allowlist
|
||||
/// checks (contains and explicit) on the root domain or not. Does not apply
|
||||
/// to URL contains allowlist. Defaults to false.
|
||||
@@ -1887,7 +1900,7 @@ pub struct Config {
|
||||
|
||||
/// Enable the tokio-console. This option is only relevant to developers.
|
||||
///
|
||||
/// For more information, see:
|
||||
/// For more information, see:
|
||||
/// https://continuwuity.org/development.html#debugging-with-tokio-console
|
||||
#[serde(default)]
|
||||
pub tokio_console: bool,
|
||||
@@ -2016,15 +2029,22 @@ pub struct Config {
|
||||
/// etc. This is a hidden argument that should NOT be used in production as
|
||||
/// it is highly insecure and I will personally yell at you if I catch you
|
||||
/// using this.
|
||||
///
|
||||
/// display: hidden
|
||||
#[serde(default)]
|
||||
pub allow_invalid_tls_certificates_yes_i_know_what_the_fuck_i_am_doing_with_this_and_i_know_this_is_insecure:
|
||||
bool,
|
||||
|
||||
// external structure; separate section
|
||||
/// display: nested
|
||||
#[serde(default)]
|
||||
pub ldap: LdapConfig,
|
||||
|
||||
// external structure; separate section
|
||||
/// Configuration for antispam support
|
||||
/// display: nested
|
||||
#[serde(default)]
|
||||
pub antispam: Option<Antispam>,
|
||||
|
||||
/// display: nested
|
||||
#[serde(default)]
|
||||
pub blurhashing: BlurhashConfig,
|
||||
#[serde(flatten)]
|
||||
@@ -2240,7 +2260,67 @@ struct ListeningAddr {
|
||||
addrs: Either<IpAddr, Vec<IpAddr>>,
|
||||
}
|
||||
|
||||
const DEPRECATED_KEYS: &[&str; 9] = &[
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
#[config_example_generator(
|
||||
filename = "conduwuit-example.toml",
|
||||
section = "global.antispam",
|
||||
optional = "true"
|
||||
)]
|
||||
pub struct Antispam {
|
||||
/// display: nested
|
||||
pub meowlnir: Option<MeowlnirConfig>,
|
||||
/// display: nested
|
||||
pub draupnir: Option<DraupnirConfig>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
#[config_example_generator(
|
||||
filename = "conduwuit-example.toml",
|
||||
section = "global.antispam.meowlnir",
|
||||
optional = "true"
|
||||
)]
|
||||
pub struct MeowlnirConfig {
|
||||
/// The base URL on which to contact Meowlnir (before /_meowlnir/antispam).
|
||||
///
|
||||
/// Example: "http://127.0.0.1:29339"
|
||||
pub base_url: Url,
|
||||
|
||||
/// The authentication secret defined in antispam->secret. Required for
|
||||
/// continuwuity to talk to Meowlnir.
|
||||
pub secret: String,
|
||||
|
||||
/// The management room for which to send requests
|
||||
pub management_room: OwnedRoomId,
|
||||
|
||||
/// If enabled run all federated join attempts (both federated and local)
|
||||
/// through the Meowlnir anti-spam checks.
|
||||
///
|
||||
/// By default, only join attempts for rooms with the `fi.mau.spam_checker`
|
||||
/// restricted join rule are checked.
|
||||
#[serde(default)]
|
||||
pub check_all_joins: bool,
|
||||
}
|
||||
|
||||
// TODO: the DraupnirConfig and MeowlnirConfig are basically identical.
|
||||
// Maybe management_room could just become an Option<> and these structs merged?
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
#[config_example_generator(
|
||||
filename = "conduwuit-example.toml",
|
||||
section = "global.antispam.draupnir",
|
||||
optional = "true"
|
||||
)]
|
||||
pub struct DraupnirConfig {
|
||||
/// The base URL on which to contact Draupnir (before /api/).
|
||||
///
|
||||
/// Example: "http://127.0.0.1:29339"
|
||||
pub base_url: Url,
|
||||
|
||||
/// The authentication secret defined in
|
||||
/// web->synapseHTTPAntispam->authorization
|
||||
pub secret: String,
|
||||
}
|
||||
|
||||
const DEPRECATED_KEYS: &[&str] = &[
|
||||
"cache_capacity",
|
||||
"conduit_cache_capacity_modifier",
|
||||
"max_concurrent_requests",
|
||||
@@ -2250,6 +2330,7 @@ struct ListeningAddr {
|
||||
"well_known_support_role",
|
||||
"well_known_support_email",
|
||||
"well_known_support_mxid",
|
||||
"registration_token_file",
|
||||
];
|
||||
|
||||
impl Config {
|
||||
@@ -2406,7 +2487,7 @@ fn default_well_known_timeout() -> u64 { 10 }
|
||||
|
||||
fn default_federation_conn_timeout() -> u64 { 10 }
|
||||
|
||||
fn default_federation_timeout() -> u64 { 25 }
|
||||
fn default_federation_timeout() -> u64 { 60 }
|
||||
|
||||
fn default_policy_server_request_timeout() -> u64 { 10 }
|
||||
|
||||
@@ -2424,6 +2505,10 @@ fn default_appservice_timeout() -> u64 { 35 }
|
||||
|
||||
fn default_appservice_idle_timeout() -> u64 { 300 }
|
||||
|
||||
fn default_pusher_conn_timeout() -> u64 { 15 }
|
||||
|
||||
fn default_pusher_timeout() -> u64 { 60 }
|
||||
|
||||
fn default_pusher_idle_timeout() -> u64 { 15 }
|
||||
|
||||
fn default_max_fetch_prev_events() -> u16 { 192_u16 }
|
||||
@@ -2551,6 +2636,8 @@ fn default_url_preview_max_spider_size() -> usize {
|
||||
256_000 // 256KB
|
||||
}
|
||||
|
||||
fn default_url_preview_timeout() -> u64 { 120 }
|
||||
|
||||
fn default_new_user_displayname_suffix() -> String { "🏳️⚧️".to_owned() }
|
||||
|
||||
fn default_sentry_endpoint() -> Option<Url> { None }
|
||||
|
||||
+1
-1
@@ -62,7 +62,7 @@ macro_rules! debug_info {
|
||||
pub static DEBUGGER: LazyLock<bool> =
|
||||
LazyLock::new(|| env::var("_").unwrap_or_default().ends_with("gdb"));
|
||||
|
||||
#[cfg_attr(debug_assertions, crate::ctor)]
|
||||
#[cfg_attr(debug_assertions, ctor::ctor)]
|
||||
#[cfg_attr(not(debug_assertions), allow(dead_code))]
|
||||
fn set_panic_trap() {
|
||||
if !*DEBUGGER {
|
||||
|
||||
@@ -115,7 +115,7 @@ macro_rules! err {
|
||||
macro_rules! err_log {
|
||||
($out:ident, $level:ident, $($fields:tt)+) => {{
|
||||
use $crate::tracing::{
|
||||
callsite, callsite2, metadata, valueset, Callsite,
|
||||
callsite, callsite2, metadata, valueset_all, Callsite,
|
||||
Level,
|
||||
};
|
||||
|
||||
@@ -133,7 +133,7 @@ macro_rules! err_log {
|
||||
fields: $($fields)+,
|
||||
};
|
||||
|
||||
($crate::error::visit)(&mut $out, LEVEL, &__CALLSITE, &mut valueset!(__CALLSITE.metadata().fields(), $($fields)+));
|
||||
($crate::error::visit)(&mut $out, LEVEL, &__CALLSITE, &mut valueset_all!(__CALLSITE.metadata().fields(), $($fields)+));
|
||||
($out).into()
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -75,10 +75,12 @@ pub(super) fn bad_request_code(kind: &ErrorKind) -> StatusCode {
|
||||
| ThreepidDenied
|
||||
| InviteBlocked
|
||||
| WrongRoomKeysVersion { .. }
|
||||
| UserSuspended
|
||||
| Forbidden { .. } => StatusCode::FORBIDDEN,
|
||||
|
||||
// 401
|
||||
| UnknownToken { .. } | MissingToken | Unauthorized => StatusCode::UNAUTHORIZED,
|
||||
| UnknownToken { .. } | MissingToken | Unauthorized | UserLocked =>
|
||||
StatusCode::UNAUTHORIZED,
|
||||
|
||||
// 400
|
||||
| _ => StatusCode::BAD_REQUEST,
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
//! Information about the build related to Cargo. This is a frontend interface
|
||||
//! informed by proc-macros that capture raw information at build time which is
|
||||
//! further processed at runtime either during static initialization or as
|
||||
//! necessary.
|
||||
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use cargo_toml::{DepsSet, Manifest};
|
||||
use conduwuit_macros::cargo_manifest;
|
||||
|
||||
use crate::Result;
|
||||
|
||||
// Raw captures of the cargo manifest for each crate. This is provided by a
|
||||
// proc-macro at build time since the source directory and the cargo toml's may
|
||||
// not be present during execution.
|
||||
|
||||
#[cargo_manifest]
|
||||
const WORKSPACE_MANIFEST: &'static str = ();
|
||||
#[cargo_manifest(crate = "macros")]
|
||||
const MACROS_MANIFEST: &'static str = ();
|
||||
#[cargo_manifest(crate = "core")]
|
||||
const CORE_MANIFEST: &'static str = ();
|
||||
#[cargo_manifest(crate = "database")]
|
||||
const DATABASE_MANIFEST: &'static str = ();
|
||||
#[cargo_manifest(crate = "service")]
|
||||
const SERVICE_MANIFEST: &'static str = ();
|
||||
#[cargo_manifest(crate = "admin")]
|
||||
const ADMIN_MANIFEST: &'static str = ();
|
||||
#[cargo_manifest(crate = "router")]
|
||||
const ROUTER_MANIFEST: &'static str = ();
|
||||
#[cargo_manifest(crate = "main")]
|
||||
const MAIN_MANIFEST: &'static str = ();
|
||||
|
||||
/// Processed list of features across all project crates. This is generated from
|
||||
/// the data in the MANIFEST strings and contains all possible project features.
|
||||
/// For *enabled* features see the info::rustc module instead.
|
||||
static FEATURES: OnceLock<Vec<String>> = OnceLock::new();
|
||||
|
||||
/// Processed list of dependencies. This is generated from the data captured in
|
||||
/// the MANIFEST.
|
||||
static DEPENDENCIES: OnceLock<DepsSet> = OnceLock::new();
|
||||
|
||||
#[must_use]
|
||||
pub fn dependencies_names() -> Vec<&'static str> {
|
||||
dependencies().keys().map(String::as_str).collect()
|
||||
}
|
||||
|
||||
pub fn dependencies() -> &'static DepsSet {
|
||||
DEPENDENCIES.get_or_init(|| {
|
||||
init_dependencies().unwrap_or_else(|e| panic!("Failed to initialize dependencies: {e}"))
|
||||
})
|
||||
}
|
||||
|
||||
/// List of all possible features for the project. For *enabled* features in
|
||||
/// this build see the companion function in info::rustc.
|
||||
pub fn features() -> &'static Vec<String> {
|
||||
FEATURES.get_or_init(|| {
|
||||
init_features().unwrap_or_else(|e| panic!("Failed initialize features: {e}"))
|
||||
})
|
||||
}
|
||||
|
||||
fn init_features() -> Result<Vec<String>> {
|
||||
let mut features = Vec::new();
|
||||
append_features(&mut features, WORKSPACE_MANIFEST)?;
|
||||
append_features(&mut features, MACROS_MANIFEST)?;
|
||||
append_features(&mut features, CORE_MANIFEST)?;
|
||||
append_features(&mut features, DATABASE_MANIFEST)?;
|
||||
append_features(&mut features, SERVICE_MANIFEST)?;
|
||||
append_features(&mut features, ADMIN_MANIFEST)?;
|
||||
append_features(&mut features, ROUTER_MANIFEST)?;
|
||||
append_features(&mut features, MAIN_MANIFEST)?;
|
||||
features.sort();
|
||||
features.dedup();
|
||||
|
||||
Ok(features)
|
||||
}
|
||||
|
||||
fn append_features(features: &mut Vec<String>, manifest: &str) -> Result<()> {
|
||||
let manifest = Manifest::from_str(manifest)?;
|
||||
features.extend(manifest.features.keys().cloned());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn init_dependencies() -> Result<DepsSet> {
|
||||
let manifest = Manifest::from_str(WORKSPACE_MANIFEST)?;
|
||||
let deps_set = manifest
|
||||
.workspace
|
||||
.as_ref()
|
||||
.expect("manifest has workspace section")
|
||||
.dependencies
|
||||
.clone();
|
||||
|
||||
Ok(deps_set)
|
||||
}
|
||||
@@ -1,12 +1,5 @@
|
||||
//! Information about the project. This module contains version, build, system,
|
||||
//! etc information which can be queried by admins or used by developers.
|
||||
|
||||
pub mod cargo;
|
||||
pub mod room_version;
|
||||
pub mod rustc;
|
||||
pub mod version;
|
||||
|
||||
pub use conduwuit_macros::rustc_flags_capture;
|
||||
|
||||
pub const MODULE_ROOT: &str = const_str::split!(std::module_path!(), "::")[0];
|
||||
pub const CRATE_PREFIX: &str = const_str::split!(MODULE_ROOT, '_')[0];
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user