Compare commits

...

31 Commits

Author SHA1 Message Date
Jade Ellis
587abe9d14 chore: Release 2026-01-12 23:47:37 +00:00
Jade Ellis
c499042a76 docs: Changelog 2026-01-12 23:45:42 +00:00
timedout
86e450a835 fix: M_BAD_JSON in send_join and send_knock 2026-01-12 17:53:37 +00:00
Jade Ellis
4c796029bb chore: Add correct configuration for cargo release 2026-01-12 16:20:38 +00:00
Jade Ellis
fc3615c46b docs: Changelog 2026-01-12 16:20:38 +00:00
Jade Ellis
7375f7a68e feat: Improve the display of the configuration in the admin room 2026-01-12 16:20:38 +00:00
Ginger
ae28fe92d2 feat: Exclude undocumented commands 2026-01-12 10:51:17 -05:00
Ginger
00eeeb78de fix: Remove extraneous dashes from command help 2026-01-12 10:47:19 -05:00
Ginger
a028049e6f feat: Add copy to admin command reference index 2026-01-12 10:36:37 -05:00
Ginger
7b159bc8c8 feat: Add comments to generated files 2026-01-12 10:36:37 -05:00
Ginger
66fcedf08b fix: Update documentation TOC 2026-01-12 10:36:37 -05:00
Ginger
3f790844f3 chore: Clippy fixes 2026-01-12 10:36:37 -05:00
Ginger
89be9d1efc feat: Improve admin command reference generation
- Change xtasks to use `clap` for argument parsing
- Generate admin command reference manually instead of with `clap_markdown`
- Split admin command reference into multiple files
2026-01-12 10:36:37 -05:00
Astralchroma
60dd6baffd Link to documentation clarifying what exactly "Performance optimised version." means 2026-01-11 16:54:33 +00:00
timedout
99a10998b4 style: Remove unused import 2026-01-11 15:42:06 +00:00
nex
05c6b5df75 fix: M_BAD_JSON in c2s invite 2026-01-11 15:37:59 +00:00
Jade Ellis
74db426c6b fix: Correct federation timeouts 2026-01-09 19:51:29 +00:00
Jade Ellis
344d68dabc fix: Use correct token handlers for Ruma 2026-01-09 19:42:14 +00:00
Jade Ellis
d3ee9c407a fix: Apply timeouts in more places 2026-01-09 19:42:13 +00:00
Ginger
da956b1a2a chore: Release 2026-01-09 09:28:54 -05:00
Ginger
244bf6ed2f chore: Update changelog for 0.5.2 2026-01-09 09:28:37 -05:00
timedout
52a51f1de0 fix: Remove useless timestamp check in outlier upgrade process 2026-01-09 02:50:04 +00:00
timedout
6e118f445b chore: Add news fragment 2026-01-09 02:50:04 +00:00
timedout
e3cf288f39 feat: Support creating custom v12 room IDs 2026-01-09 02:50:04 +00:00
Ginger
6e6c9ae4cb chore: Update news fragments 2026-01-08 20:14:46 -05:00
timedout
5a2a1b6240 style: Clean up whoami code 2026-01-09 01:12:38 +00:00
timedout
fb536ca1ce chore: Add news fragment 2026-01-09 00:47:09 +00:00
timedout
d22d47954f fix: Return 403 instead of 404 at /_matrix/client/v3/account/whoami 2026-01-09 00:44:38 +00:00
Ginger
d48cc46643 fix: Allow cargo_common_metadata clippy lint 2026-01-08 19:28:27 +00:00
Ginger
8cf2d175d6 fix: Update package and crate metadata 2026-01-08 19:28:27 +00:00
Ginger
205ac22008 chore: Update admin command documentation 2026-01-08 14:27:50 -05:00
94 changed files with 1586 additions and 3399 deletions

View File

@@ -7,6 +7,5 @@
"continuwuity",
"homeserver",
"homeservers"
],
"rust-analyzer.cargo.features": ["full"]
]
}

View File

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

257
Cargo.lock generated
View File

@@ -72,56 +72,12 @@ dependencies = [
"alloc-no-stdlib",
]
[[package]]
name = "anstream"
version = "0.6.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
[[package]]
name = "anstyle-parse"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2"
dependencies = [
"windows-sys 0.60.2",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a"
dependencies = [
"anstyle",
"once_cell_polyfill",
"windows-sys 0.60.2",
]
[[package]]
name = "anyhow"
version = "1.0.100"
@@ -199,7 +155,20 @@ version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f75363874b771be265f4ffe307ca705ef6f3baa19011c149da8674a87f1b75c4"
dependencies = [
"askama_derive",
"askama_derive 0.14.0",
"itoa",
"percent-encoding",
"serde",
"serde_json",
]
[[package]]
name = "askama"
version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb7125972258312e79827b60c9eb93938334100245081cf701a2dee981b17427"
dependencies = [
"askama_macros",
"itoa",
"percent-encoding",
"serde",
@@ -212,7 +181,7 @@ version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "129397200fe83088e8a68407a8e2b1f826cf0086b21ccdb866a722c8bcd3a94f"
dependencies = [
"askama_parser",
"askama_parser 0.14.0",
"basic-toml",
"memchr",
"proc-macro2",
@@ -223,6 +192,32 @@ dependencies = [
"syn",
]
[[package]]
name = "askama_derive"
version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ba5e7259a1580c61571e3116ebaaa01e3c001b2132b17c4cc5c70780ca3e994"
dependencies = [
"askama_parser 0.15.1",
"basic-toml",
"memchr",
"proc-macro2",
"quote",
"rustc-hash",
"serde",
"serde_derive",
"syn",
]
[[package]]
name = "askama_macros"
version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "236ce20b77cb13506eaf5024899f4af6e12e8825f390bd943c4c37fd8f322e46"
dependencies = [
"askama_derive 0.15.1",
]
[[package]]
name = "askama_parser"
version = "0.14.0"
@@ -235,6 +230,19 @@ dependencies = [
"winnow",
]
[[package]]
name = "askama_parser"
version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3c63392767bb2df6aa65a6e1e3b80fd89bb7af6d58359b924c0695620f1512e"
dependencies = [
"rustc-hash",
"serde",
"serde_derive",
"unicode-ident",
"winnow",
]
[[package]]
name = "asn1-rs"
version = "0.7.1"
@@ -757,6 +765,39 @@ dependencies = [
"pkg-config",
]
[[package]]
name = "camino"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48"
dependencies = [
"serde_core",
]
[[package]]
name = "cargo-platform"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87a0c0e6148f11f01f32650a2ea02d532b2ad4e81d8bd41e6e565b5adc5e6082"
dependencies = [
"serde",
"serde_core",
]
[[package]]
name = "cargo_metadata"
version = "0.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef987d17b0a113becdd19d3d0022d04d7ef41f9efe4f3fb63ac44ba61df3ade9"
dependencies = [
"camino",
"cargo-platform",
"semver",
"serde",
"serde_json",
"thiserror 2.0.17",
]
[[package]]
name = "cargo_toml"
version = "0.22.3"
@@ -839,25 +880,14 @@ dependencies = [
"clap_derive",
]
[[package]]
name = "clap-markdown"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2a2617956a06d4885b490697b5307ebb09fec10b088afc18c81762d848c2339"
dependencies = [
"clap",
]
[[package]]
name = "clap_builder"
version = "4.5.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
]
[[package]]
@@ -878,16 +908,6 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d"
[[package]]
name = "clap_mangen"
version = "0.2.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ea63a92086df93893164221ad4f24142086d535b3a0957b9b9bea2dc86301"
dependencies = [
"clap",
"roff",
]
[[package]]
name = "cmake"
version = "0.1.54"
@@ -903,12 +923,6 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
[[package]]
name = "colorchoice"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
[[package]]
name = "compression-codecs"
version = "0.4.31"
@@ -940,7 +954,7 @@ dependencies = [
[[package]]
name = "conduwuit"
version = "0.5.1"
version = "0.5.3"
dependencies = [
"clap",
"conduwuit_admin",
@@ -972,7 +986,7 @@ dependencies = [
[[package]]
name = "conduwuit_admin"
version = "0.5.1"
version = "0.5.3"
dependencies = [
"clap",
"conduwuit_api",
@@ -994,7 +1008,7 @@ dependencies = [
[[package]]
name = "conduwuit_api"
version = "0.5.1"
version = "0.5.3"
dependencies = [
"async-trait",
"axum 0.7.9",
@@ -1027,14 +1041,14 @@ dependencies = [
[[package]]
name = "conduwuit_build_metadata"
version = "0.5.1"
version = "0.5.3"
dependencies = [
"built",
]
[[package]]
name = "conduwuit_core"
version = "0.5.1"
version = "0.5.3"
dependencies = [
"argon2",
"arrayvec",
@@ -1095,7 +1109,7 @@ dependencies = [
[[package]]
name = "conduwuit_database"
version = "0.5.1"
version = "0.5.3"
dependencies = [
"async-channel",
"conduwuit_core",
@@ -1114,7 +1128,7 @@ dependencies = [
[[package]]
name = "conduwuit_macros"
version = "0.5.1"
version = "0.5.3"
dependencies = [
"itertools 0.14.0",
"proc-macro2",
@@ -1124,7 +1138,7 @@ dependencies = [
[[package]]
name = "conduwuit_router"
version = "0.5.1"
version = "0.5.3"
dependencies = [
"axum 0.7.9",
"axum-client-ip",
@@ -1159,7 +1173,7 @@ dependencies = [
[[package]]
name = "conduwuit_service"
version = "0.5.1"
version = "0.5.3"
dependencies = [
"async-trait",
"base64 0.22.1",
@@ -1200,9 +1214,9 @@ dependencies = [
[[package]]
name = "conduwuit_web"
version = "0.5.1"
version = "0.5.3"
dependencies = [
"askama",
"askama 0.14.0",
"axum 0.7.9",
"conduwuit_build_metadata",
"conduwuit_service",
@@ -1635,7 +1649,7 @@ dependencies = [
[[package]]
name = "draupnir-antispam"
version = "0.1.0"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=79abd5d331bca596b7f37e367a9f2cebccd9f64d#79abd5d331bca596b7f37e367a9f2cebccd9f64d"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=f9e74cb206cfa45cf5f17d39282253b43a15fcd5#f9e74cb206cfa45cf5f17d39282253b43a15fcd5"
dependencies = [
"ruma-common",
"serde",
@@ -2639,12 +2653,6 @@ dependencies = [
"serde",
]
[[package]]
name = "is_terminal_polyfill"
version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]]
name = "itertools"
version = "0.12.1"
@@ -2995,7 +3003,7 @@ checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
[[package]]
name = "meowlnir-antispam"
version = "0.1.0"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=79abd5d331bca596b7f37e367a9f2cebccd9f64d#79abd5d331bca596b7f37e367a9f2cebccd9f64d"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=f9e74cb206cfa45cf5f17d39282253b43a15fcd5#f9e74cb206cfa45cf5f17d39282253b43a15fcd5"
dependencies = [
"ruma-common",
"serde",
@@ -3272,12 +3280,6 @@ dependencies = [
"portable-atomic",
]
[[package]]
name = "once_cell_polyfill"
version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
[[package]]
name = "openssl-probe"
version = "0.1.6"
@@ -4076,16 +4078,10 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "roff"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88f8660c1ff60292143c98d08fc6e2f654d722db50410e3f3797d40baaf9d8f3"
[[package]]
name = "ruma"
version = "0.10.1"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=79abd5d331bca596b7f37e367a9f2cebccd9f64d#79abd5d331bca596b7f37e367a9f2cebccd9f64d"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=f9e74cb206cfa45cf5f17d39282253b43a15fcd5#f9e74cb206cfa45cf5f17d39282253b43a15fcd5"
dependencies = [
"assign",
"draupnir-antispam",
@@ -4107,7 +4103,7 @@ dependencies = [
[[package]]
name = "ruma-appservice-api"
version = "0.10.0"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=79abd5d331bca596b7f37e367a9f2cebccd9f64d#79abd5d331bca596b7f37e367a9f2cebccd9f64d"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=f9e74cb206cfa45cf5f17d39282253b43a15fcd5#f9e74cb206cfa45cf5f17d39282253b43a15fcd5"
dependencies = [
"js_int",
"ruma-common",
@@ -4119,7 +4115,7 @@ dependencies = [
[[package]]
name = "ruma-client-api"
version = "0.18.0"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=79abd5d331bca596b7f37e367a9f2cebccd9f64d#79abd5d331bca596b7f37e367a9f2cebccd9f64d"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=f9e74cb206cfa45cf5f17d39282253b43a15fcd5#f9e74cb206cfa45cf5f17d39282253b43a15fcd5"
dependencies = [
"as_variant",
"assign",
@@ -4142,7 +4138,7 @@ dependencies = [
[[package]]
name = "ruma-common"
version = "0.13.0"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=79abd5d331bca596b7f37e367a9f2cebccd9f64d#79abd5d331bca596b7f37e367a9f2cebccd9f64d"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=f9e74cb206cfa45cf5f17d39282253b43a15fcd5#f9e74cb206cfa45cf5f17d39282253b43a15fcd5"
dependencies = [
"as_variant",
"base64 0.22.1",
@@ -4174,7 +4170,7 @@ dependencies = [
[[package]]
name = "ruma-events"
version = "0.28.1"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=79abd5d331bca596b7f37e367a9f2cebccd9f64d#79abd5d331bca596b7f37e367a9f2cebccd9f64d"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=f9e74cb206cfa45cf5f17d39282253b43a15fcd5#f9e74cb206cfa45cf5f17d39282253b43a15fcd5"
dependencies = [
"as_variant",
"indexmap",
@@ -4199,7 +4195,7 @@ dependencies = [
[[package]]
name = "ruma-federation-api"
version = "0.9.0"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=79abd5d331bca596b7f37e367a9f2cebccd9f64d#79abd5d331bca596b7f37e367a9f2cebccd9f64d"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=f9e74cb206cfa45cf5f17d39282253b43a15fcd5#f9e74cb206cfa45cf5f17d39282253b43a15fcd5"
dependencies = [
"bytes",
"headers",
@@ -4221,7 +4217,7 @@ dependencies = [
[[package]]
name = "ruma-identifiers-validation"
version = "0.9.5"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=79abd5d331bca596b7f37e367a9f2cebccd9f64d#79abd5d331bca596b7f37e367a9f2cebccd9f64d"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=f9e74cb206cfa45cf5f17d39282253b43a15fcd5#f9e74cb206cfa45cf5f17d39282253b43a15fcd5"
dependencies = [
"js_int",
"thiserror 2.0.17",
@@ -4230,7 +4226,7 @@ dependencies = [
[[package]]
name = "ruma-identity-service-api"
version = "0.9.0"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=79abd5d331bca596b7f37e367a9f2cebccd9f64d#79abd5d331bca596b7f37e367a9f2cebccd9f64d"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=f9e74cb206cfa45cf5f17d39282253b43a15fcd5#f9e74cb206cfa45cf5f17d39282253b43a15fcd5"
dependencies = [
"js_int",
"ruma-common",
@@ -4240,7 +4236,7 @@ dependencies = [
[[package]]
name = "ruma-macros"
version = "0.13.0"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=79abd5d331bca596b7f37e367a9f2cebccd9f64d#79abd5d331bca596b7f37e367a9f2cebccd9f64d"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=f9e74cb206cfa45cf5f17d39282253b43a15fcd5#f9e74cb206cfa45cf5f17d39282253b43a15fcd5"
dependencies = [
"cfg-if",
"proc-macro-crate",
@@ -4255,7 +4251,7 @@ dependencies = [
[[package]]
name = "ruma-push-gateway-api"
version = "0.9.0"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=79abd5d331bca596b7f37e367a9f2cebccd9f64d#79abd5d331bca596b7f37e367a9f2cebccd9f64d"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=f9e74cb206cfa45cf5f17d39282253b43a15fcd5#f9e74cb206cfa45cf5f17d39282253b43a15fcd5"
dependencies = [
"js_int",
"ruma-common",
@@ -4267,7 +4263,7 @@ dependencies = [
[[package]]
name = "ruma-signatures"
version = "0.15.0"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=79abd5d331bca596b7f37e367a9f2cebccd9f64d#79abd5d331bca596b7f37e367a9f2cebccd9f64d"
source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=f9e74cb206cfa45cf5f17d39282253b43a15fcd5#f9e74cb206cfa45cf5f17d39282253b43a15fcd5"
dependencies = [
"base64 0.22.1",
"ed25519-dalek",
@@ -4506,6 +4502,10 @@ name = "semver"
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
dependencies = [
"serde",
"serde_core",
]
[[package]]
name = "sentry"
@@ -4961,12 +4961,6 @@ dependencies = [
"quote",
]
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "subslice"
version = "0.2.3"
@@ -5700,12 +5694,6 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "uuid"
version = "1.18.1"
@@ -6228,20 +6216,11 @@ dependencies = [
[[package]]
name = "xtask"
version = "0.5.1"
version = "0.5.3"
dependencies = [
"askama 0.15.1",
"cargo_metadata",
"clap",
"serde",
"serde_json",
]
[[package]]
name = "xtask-generate-commands"
version = "0.5.1"
dependencies = [
"clap-markdown",
"clap_builder",
"clap_mangen",
"conduwuit",
"conduwuit_admin",
]

View File

@@ -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"
@@ -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 = "79abd5d331bca596b7f37e367a9f2cebccd9f64d"
rev = "f9e74cb206cfa45cf5f17d39282253b43a15fcd5"
features = [
"compat",
"rand",
@@ -848,6 +839,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 }

View File

@@ -1 +0,0 @@
The `console` feature is now enabled by default, allowing the server console to be used for running admin commands directly.

View File

@@ -1 +0,0 @@
Certain potentially dangerous admin commands are now restricted to only be usable in the admin room and server console.

View File

@@ -1 +0,0 @@
Implemented a configuration defined admin list independent of the admin room. (@Terryiscool160).

View File

@@ -1 +0,0 @@
Fixed unreliable room summary fetching and improved error messages. Contributed by @nex.

View File

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

View File

@@ -1,2 +0,0 @@
Added support for invite and join anti-spam via Draupnir and Meowlnir, similar to that of synapse-http-antispam.
Contributed by @nex.

View File

@@ -1 +0,0 @@
Implemented account locking functionality, to complement user suspension. Contributed by @nex.

View File

@@ -1 +0,0 @@
Added admin command to forcefully log out all of a user's existing sessions. Contributed by @nex.

View File

@@ -1 +0,0 @@
Implemented toggling the ability for an account to log in without mutating any of its data. Contributed by @nex.

View File

@@ -1 +0,0 @@
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**.

View File

@@ -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
@@ -1446,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.
@@ -1744,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.
@@ -1915,6 +1926,8 @@
#
#admin_filter = ""
[global.antispam]
[global.antispam.meowlnir]
# The base URL on which to contact Meowlnir (before /_meowlnir/antispam).

View File

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

View File

@@ -18,7 +18,7 @@
},
{
"text": "Admin Command Reference",
"link": "/reference/admin"
"link": "/reference/admin/"
},
{
"text": "Server Reference",

View File

@@ -13,8 +13,8 @@ ### Use a registry
| --------------- | --------------------------------------------------------------- | -----------------------|
| 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. |
| Forgejo Registry| [forgejo.ellis.link/continuwuation/continuwuity:main-maxperf](https://forgejo.ellis.link/continuwuation/-/packages/container/continuwuity/main-maxperf) | Performance optimised version. |
| 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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,139 @@
<!-- 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 list-dependencies`
List dependencies
## `!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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,53 @@
<!-- 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 list-features`
List the features built into the server
## `!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

View File

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

View File

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

View File

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

26
package-lock.json generated
View File

@@ -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",
@@ -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",
@@ -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,7 +4280,6 @@
"integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -4325,7 +4306,6 @@
"integrity": "sha512-l2OwHn3UUnEVUqc6/1VMmR1cvZryZ3j3NzapC2eUXO1dB0sYp5mvwdjiXhpUbRb21eFow3qSxpP8Yv6oAU824Q==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@remix-run/router": "1.23.1",
"react-router": "6.30.2"
@@ -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"
},

View File

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

View File

@@ -1 +1,8 @@
tag-message = "chore: Release v{{version}}"
tag-prefix = ""
shared-version = true
publish = false
sign-commit = true
sign-tag = true

View File

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

View File

@@ -19,43 +19,43 @@
#[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 registration tokens
/// Commands for managing registration tokens
Token(TokenCommand),
#[command(subcommand)]
/// - Commands for managing rooms
/// 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),
}

View File

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

View File

@@ -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();

View File

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

View File

@@ -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,23 @@ 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
/// List dependencies
ListDependencies {
#[arg(short, long)]
names: bool,
},
/// - Get database statistics
/// Get database statistics
DatabaseStats {
property: Option<String>,
@@ -220,10 +220,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 +231,7 @@ pub enum DebugCommand {
level: Option<i32>,
},
/// - Developer test stubs
/// Developer test stubs
#[command(subcommand)]
#[allow(non_snake_case)]
#[clap(hide(true))]

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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),
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -10,18 +10,18 @@
#[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
/// List the features built into the server
ListFeatures {
#[arg(short, long)]
available: bool,
@@ -33,35 +33,35 @@ pub enum ServerCommand {
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,
}

View File

@@ -8,7 +8,7 @@
#[admin_command_dispatch]
#[derive(Debug, Subcommand)]
pub enum TokenCommand {
/// - Issue a new registration token
/// Issue a new registration token
#[clap(name = "issue")]
IssueToken {
/// When this token will expire.
@@ -16,14 +16,14 @@ pub enum TokenCommand {
expires: TokenExpires,
},
/// - Revoke a registration token
/// Revoke a registration token
#[clap(name = "revoke")]
RevokeToken {
/// The token to revoke.
token: String,
},
/// - List all registration tokens
/// List all registration tokens
#[clap(name = "list")]
ListTokens,
}

View File

@@ -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,7 +18,7 @@ pub enum UserCommand {
password: Option<String>,
},
/// - Reset user password
/// Reset user password
ResetPassword {
/// Log out existing sessions
#[arg(short, long)]
@@ -29,7 +29,7 @@ pub enum UserCommand {
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.
@@ -39,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.
///
@@ -62,7 +62,7 @@ pub enum UserCommand {
force: bool,
},
/// - Forcefully log a user out of all of their devices.
/// 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.
@@ -74,7 +74,7 @@ pub enum UserCommand {
user_id: String,
},
/// - Suspend a user
/// 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
@@ -87,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.
@@ -96,7 +96,7 @@ pub enum UserCommand {
user_id: String,
},
/// - Lock a user
/// 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
@@ -107,7 +107,7 @@ pub enum UserCommand {
user_id: String,
},
/// - Unlock a user
/// Unlock a user
///
/// Reverses the effects of the `lock` command, allowing the user to use
/// their account again.
@@ -116,13 +116,13 @@ pub enum UserCommand {
user_id: String,
},
/// - Enable login for a user
/// Enable login for a user
EnableLogin {
/// Username of the user to enable login for
user_id: String,
},
/// - Disable login for a user
/// 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,
@@ -132,48 +132,48 @@ pub enum UserCommand {
user_id: String,
},
/// - List local users in the database
/// 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
@@ -186,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
@@ -207,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.
@@ -222,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.
///

View File

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

View File

@@ -724,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.
///
@@ -733,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,
})
}

View File

@@ -7,7 +7,7 @@
};
use futures::FutureExt;
use ruma::{
OwnedServerName, RoomId, UserId,
RoomId, UserId,
api::{client::membership::invite_user, federation::membership::create_invite},
events::{
invite_permission_config::FilterLevel,
@@ -203,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(|| {

View File

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

View File

@@ -178,15 +178,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 +195,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."))))?;

View File

@@ -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."))))?;

View File

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

View File

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

View File

@@ -53,8 +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 antispam"
ignore = "config_paths catchall"
)]
pub struct Config {
// Paths to config file(s). Not supposed to be set manually in the config file,
@@ -105,7 +104,7 @@ pub struct Config {
#[serde(default = "default_port")]
port: ListeningPort,
// external structure; separate section
/// display: nested
#[serde(default)]
pub tls: TlsConfig,
@@ -435,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,
@@ -501,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")]
@@ -710,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,
@@ -1663,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.
@@ -2009,19 +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,
/// Configuration for antispam support
/// display: nested
#[serde(default)]
pub antispam: Option<Antispam>,
// external structure; separate section
/// display: nested
#[serde(default)]
pub blurhashing: BlurhashConfig,
#[serde(flatten)]
@@ -2238,8 +2261,11 @@ struct ListeningAddr {
}
#[derive(Clone, Debug, Deserialize)]
#[config_example_generator(filename = "conduwuit-example.toml", section = "global.antispam")]
pub struct Antispam {
/// display: nested
pub meowlnir: Option<MeowlnirConfig>,
/// display: nested
pub draupnir: Option<DraupnirConfig>,
}
@@ -2455,7 +2481,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 }
@@ -2473,6 +2499,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 }
@@ -2600,6 +2630,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 }

View File

@@ -1,9 +1,7 @@
[package]
name = "conduwuit_database"
categories.workspace = true
description.workspace = true
edition.workspace = true
keywords.workspace = true
license.workspace = true
readme.workspace = true
repository.workspace = true

View File

@@ -1,9 +1,7 @@
[package]
name = "conduwuit_macros"
categories.workspace = true
description.workspace = true
edition.workspace = true
keywords.workspace = true
license.workspace = true
readme.workspace = true
repository.workspace = true

View File

@@ -78,6 +78,8 @@ fn generate_example(input: &ItemStruct, args: &[Meta], write: bool) -> Result<To
}
let mut summary: Vec<TokenStream2> = Vec::new();
let mut nested_displays: Vec<TokenStream2> = Vec::new();
if let Fields::Named(FieldsNamed { named, .. }) = &input.fields {
for field in named {
let Some(ident) = &field.ident else {
@@ -92,35 +94,6 @@ fn generate_example(input: &ItemStruct, args: &[Meta], write: bool) -> Result<To
continue;
};
let doc = get_doc_comment(field)
.unwrap_or_else(|| undocumented.into())
.trim_end()
.to_owned();
let doc = if doc.ends_with('#') {
format!("{doc}\n")
} else {
format!("{doc}\n#\n")
};
let default = get_doc_comment_line(field, "default")
.or_else(|| get_default(field))
.unwrap_or_default();
let default = if !default.is_empty() {
format!(" {default}")
} else {
default
};
if let Some(file) = file.as_mut() {
file.write_fmt(format_args!("\n{doc}"))
.expect("written to config file");
file.write_fmt(format_args!("#{ident} ={default}\n"))
.expect("written to config file");
}
let display = get_doc_comment_line(field, "display");
let display_directive = |key| {
display
@@ -129,17 +102,77 @@ fn generate_example(input: &ItemStruct, args: &[Meta], write: bool) -> Result<To
.flat_map(|display| display.split(' '))
.any(|directive| directive == key)
};
let is_nested = display_directive("nested");
let is_hidden = display_directive("hidden");
if !display_directive("hidden") {
let value = if display_directive("sensitive") {
quote! { "***********" }
// Only generate config file entries for non-nested, visible types
if !is_nested && !is_hidden {
let doc = get_doc_comment(field)
.unwrap_or_else(|| undocumented.into())
.trim_end()
.to_owned();
let doc = if doc.ends_with('#') {
format!("{doc}\n")
} else {
quote! { format_args!("{:?}", self.#ident) }
format!("{doc}\n#\n")
};
let name = ident.to_string();
let default = get_doc_comment_line(field, "default")
.or_else(|| get_default(field))
.unwrap_or_default();
let default = if !default.is_empty() {
format!(" {default}")
} else {
default
};
if let Some(file) = file.as_mut() {
file.write_fmt(format_args!("\n{doc}"))
.expect("written to config file");
file.write_fmt(format_args!("#{ident} ={default}\n"))
.expect("written to config file");
}
}
// Generate Display implementation for all fields
let name = ident.to_string();
if display_directive("sensitive") {
summary.push(quote! {
writeln!(out, "| {} | {} |", #name, #value)?;
writeln!(out, "| {} | {} |", #name, "***********")?;
});
} else if is_nested {
let is_option = matches!(type_name.as_str(), "Option");
if is_option {
summary.push(quote! {
writeln!(out, "| {} | {} |", #name,
if self.#ident.is_some() { "[configured]" } else { "None" })?;
});
nested_displays.push(quote! {
if let Some(nested) = &self.#ident {
writeln!(out)?;
writeln!(out, "## {}", #name)?;
write!(out, "{}", nested)?;
}
});
} else {
summary.push(quote! {
writeln!(out, "| {} | [configured] |", #name)?;
});
nested_displays.push(quote! {
writeln!(out)?;
writeln!(out, "## {}", #name)?;
write!(out, "{}", &self.#ident)?;
});
}
} else {
summary.push(quote! {
writeln!(out, "| {} | {:?} |", #name, self.#ident)?;
});
}
}
@@ -159,6 +192,7 @@ fn fmt(&self, out: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(out, "| name | value |")?;
writeln!(out, "| :--- | :--- |")?;
#( #summary )*
#( #nested_displays )*
Ok(())
}
}

View File

@@ -2,15 +2,12 @@
name = "conduwuit"
default-run = "conduwuit"
authors.workspace = true
categories.workspace = true
description.workspace = true
edition.workspace = true
homepage.workspace = true
keywords.workspace = true
license.workspace = true
readme.workspace = true
repository.workspace = true
rust-version.workspace = true
version.workspace = true
metadata.crane.workspace = true
@@ -23,14 +20,13 @@ crate-type = [
[package.metadata.deb]
name = "continuwuity"
maintainer = "continuwuity developers <contact@continuwuity.org>"
copyright = "2024, continuwuity developers"
maintainer = "Continuwuity Team and contributors <team@continuwuity.org>"
license-file = ["../../LICENSE", "3"]
depends = "$auto, ca-certificates"
breaks = ["conduwuit (<<0.5.0)"]
replaces = ["conduwuit (<<0.5.0)"]
extended-description = """\
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."""
section = "net"
priority = "optional"
conf-files = ["/etc/conduwuit/conduwuit.toml"]

View File

@@ -1,9 +1,7 @@
[package]
name = "conduwuit_router"
categories.workspace = true
description.workspace = true
edition.workspace = true
keywords.workspace = true
license.workspace = true
readme.workspace = true
repository.workspace = true

View File

@@ -1,9 +1,7 @@
[package]
name = "conduwuit_service"
categories.workspace = true
description.workspace = true
edition.workspace = true
keywords.workspace = true
license.workspace = true
readme.workspace = true
repository.workspace = true

View File

@@ -47,6 +47,7 @@ fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
})?
.local_address(url_preview_bind_addr)
.dns_resolver(resolver.resolver.clone())
.timeout(Duration::from_secs(config.url_preview_timeout))
.redirect(redirect::Policy::limited(3))
.build()?,
@@ -68,6 +69,11 @@ fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
.dns_resolver(resolver.resolver.hooked.clone())
.connect_timeout(Duration::from_secs(config.federation_conn_timeout))
.read_timeout(Duration::from_secs(config.federation_timeout))
.timeout(Duration::from_secs(
config
.federation_timeout
.saturating_add(config.federation_conn_timeout),
))
.pool_max_idle_per_host(config.federation_idle_per_host.into())
.pool_idle_timeout(Duration::from_secs(config.federation_idle_timeout))
.redirect(redirect::Policy::limited(3))
@@ -76,7 +82,13 @@ fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
synapse: base(config)?
.dns_resolver(resolver.resolver.hooked.clone())
.connect_timeout(Duration::from_secs(config.federation_conn_timeout))
.read_timeout(Duration::from_secs(305))
.read_timeout(Duration::from_secs(config.federation_timeout.saturating_mul(6)))
.timeout(Duration::from_secs(
config
.federation_timeout
.saturating_mul(6)
.saturating_add(config.federation_conn_timeout),
))
.pool_max_idle_per_host(0)
.redirect(redirect::Policy::limited(3))
.build()?,
@@ -103,6 +115,8 @@ fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
pusher: base(config)?
.dns_resolver(resolver.resolver.clone())
.connect_timeout(Duration::from_secs(config.pusher_conn_timeout))
.timeout(Duration::from_secs(config.pusher_timeout))
.pool_max_idle_per_host(1)
.pool_idle_timeout(Duration::from_secs(config.pusher_idle_timeout))
.redirect(redirect::Policy::limited(2))

View File

@@ -3,7 +3,7 @@
use bytes::Bytes;
use conduwuit::{
Err, Error, Result, debug, debug::INFO_SPAN_LEVEL, debug_error, debug_warn, err,
error::inspect_debug_log, implement, trace, utils::string::EMPTY,
error::inspect_debug_log, implement, trace,
};
use http::{HeaderValue, header::AUTHORIZATION};
use ipaddress::IPAddress;
@@ -90,7 +90,9 @@ async fn perform<T>(
debug!(%method, %url, "Sending request");
match client.execute(request).await {
| Ok(response) => handle_response::<T>(dest, actual, &method, &url, response).await,
| Ok(response) =>
self.handle_response::<T>(dest, actual, &method, &url, response)
.await,
| Err(error) =>
Err(handle_error(actual, &method, &url, error).expect_err("always returns error")),
}
@@ -119,7 +121,9 @@ fn validate_url(&self, url: &Url) -> Result<()> {
Ok(())
}
#[implement(super::Service)]
async fn handle_response<T>(
&self,
dest: &ServerName,
actual: &ActualDest,
method: &Method,
@@ -162,7 +166,6 @@ async fn into_http_response(
.expect("http::response::Builder is usable"),
);
// TODO: handle timeout
trace!("Waiting for response body...");
let body = response
.bytes()
@@ -286,10 +289,13 @@ fn into_http_request<T>(actual: &ActualDest, request: T) -> Result<http::Request
T: OutgoingRequest + Send,
{
const VERSIONS: [MatrixVersion; 1] = [MatrixVersion::V1_11];
const SATIR: SendAccessToken<'_> = SendAccessToken::IfRequired(EMPTY);
let http_request = request
.try_into_http_request::<Vec<u8>>(actual.string().as_str(), SATIR, &VERSIONS)
.try_into_http_request::<Vec<u8>>(
actual.string().as_str(),
SendAccessToken::None,
&VERSIONS,
)
.map_err(|e| err!(BadServerResponse("Invalid destination: {e:?}")))?;
Ok(http_request)

View File

@@ -198,7 +198,7 @@ pub async fn send_request<T>(&self, dest: &str, request: T) -> Result<T::Incomin
trace!("Push gateway destination: {dest}");
let http_request = request
.try_into_http_request::<BytesMut>(&dest, SendAccessToken::IfRequired(""), &VERSIONS)
.try_into_http_request::<BytesMut>(&dest, SendAccessToken::None, &VERSIONS)
.map_err(|e| {
err!(BadServerResponse(warn!(
"Failed to find destination {dest} for push gateway: {e}"
@@ -245,7 +245,7 @@ pub async fn send_request<T>(&self, dest: &str, request: T) -> Result<T::Incomin
.expect("http::response::Builder is usable"),
);
let body = response.bytes().await?; // TODO: handle timeout
let body = response.bytes().await?;
if !status.is_success() {
debug_warn!("Push gateway response body: {:?}", string_from_bytes(&body));
@@ -288,7 +288,7 @@ pub async fn send_push_notice<E>(
let mut notify = None;
let mut tweaks = Vec::new();
if event.room_id().is_none() {
// TODO(hydra): does this matter?
// This only affects v12+ create events
return Ok(());
}

View File

@@ -131,10 +131,6 @@ pub async fn handle_incoming_pdu<'a>(
.await?
.origin_server_ts();
if incoming_pdu.origin_server_ts() < first_ts_in_room {
return Ok(None);
}
// 9. Fetch any missing prev events doing all checks listed here starting at 1.
// These are timeline events
let (sorted_prev_events, mut eventid_info) = self

View File

@@ -15,7 +15,6 @@
use ruma::{
CanonicalJsonObject, CanonicalJsonValue, OwnedEventId, OwnedRoomId, RoomId, RoomVersionId,
UserId,
canonical_json::to_canonical_value,
events::{StateEventType, TimelineEventType, room::create::RoomCreateEventContent},
uint,
};
@@ -210,7 +209,7 @@ fn from_evt(
} else {
Some(to_raw_value(&unsigned)?)
},
hashes: EventHash { sha256: "aaa".to_owned() },
hashes: EventHash { sha256: String::new() },
signatures: None,
};
@@ -269,12 +268,6 @@ fn from_evt(
},
}
pdu_json.insert(
"origin".to_owned(),
to_canonical_value(self.services.globals.server_name())
.expect("server name is a valid CanonicalJsonValue"),
);
trace!("hashing and signing event {}", pdu.event_id);
if let Err(e) = self
.services

View File

@@ -1,8 +1,7 @@
use std::{fmt::Debug, mem};
use bytes::BytesMut;
use conduwuit::{Err, Result, debug_error, err, trace, utils, warn};
use reqwest::Client;
use conduwuit::{Err, Result, debug_error, err, implement, trace, utils, warn};
use ruma::api::{
IncomingResponse, MatrixVersion, OutgoingRequest, SendAccessToken, appservice::Registration,
};
@@ -11,8 +10,9 @@
///
/// Only returns Ok(None) if there is no url specified in the appservice
/// registration file
pub(crate) async fn send_request<T>(
client: &Client,
#[implement(super::Service)]
pub async fn send_appservice_request<T>(
&self,
registration: Registration,
request: T,
) -> Result<Option<T::IncomingResponse>>
@@ -35,7 +35,7 @@ pub(crate) async fn send_request<T>(
let mut http_request = request
.try_into_http_request::<BytesMut>(
&dest,
SendAccessToken::IfRequired(hs_token),
SendAccessToken::Appservice(hs_token),
&VERSIONS,
)
.map_err(|e| {
@@ -58,6 +58,8 @@ pub(crate) async fn send_request<T>(
let reqwest_request = reqwest::Request::try_from(http_request)?;
let client = &self.services.client.appservice;
let mut response = client.execute(reqwest_request).await.map_err(|e| {
warn!("Could not send request to appservice \"{}\" at {dest}: {e:?}", registration.id);
e
@@ -75,7 +77,7 @@ pub(crate) async fn send_request<T>(
.expect("http::response::Builder is usable"),
);
let body = response.bytes().await?; // TODO: handle timeout
let body = response.bytes().await?;
if !status.is_success() {
debug_error!("Appservice response bytes: {:?}", utils::string_from_bytes(&body));

View File

@@ -19,10 +19,7 @@
warn,
};
use futures::{FutureExt, Stream, StreamExt};
use ruma::{
RoomId, ServerName, UserId,
api::{OutgoingRequest, appservice::Registration},
};
use ruma::{RoomId, ServerName, UserId, api::OutgoingRequest};
use tokio::{task, task::JoinSet};
use self::data::Data;
@@ -319,22 +316,6 @@ pub async fn send_synapse_request<T>(
.await
}
/// Sends a request to an appservice
///
/// Only returns None if there is no url specified in the appservice
/// registration file
pub async fn send_appservice_request<T>(
&self,
registration: Registration,
request: T,
) -> Result<Option<T::IncomingResponse>>
where
T: OutgoingRequest + Debug + Send,
{
let client = &self.services.client.appservice;
appservice::send_request(client, registration, request).await
}
/// Clean up queued sending event data
///
/// Used after we remove an appservice registration or a user deletes a push

View File

@@ -50,9 +50,7 @@
};
use serde_json::value::{RawValue as RawJsonValue, to_raw_value};
use super::{
Destination, EduBuf, EduVec, Msg, SendingEvent, Service, appservice, data::QueueItem,
};
use super::{Destination, EduBuf, EduVec, Msg, SendingEvent, Service, data::QueueItem};
#[derive(Debug)]
enum TransactionStatus {
@@ -720,18 +718,18 @@ async fn send_events_dest_appservice(
//debug_assert!(pdu_jsons.len() + edu_jsons.len() > 0, "sending empty
// transaction");
let client = &self.services.client.appservice;
match appservice::send_request(
client,
appservice,
ruma::api::appservice::event::push_events::v1::Request {
events: pdu_jsons,
txn_id: txn_id.into(),
ephemeral: edu_jsons,
to_device: Vec::new(), // TODO
},
)
.await
match self
.send_appservice_request(
appservice,
ruma::api::appservice::event::push_events::v1::Request {
events: pdu_jsons,
txn_id: txn_id.into(),
ephemeral: edu_jsons,
to_device: Vec::new(), // TODO
},
)
.await
{
| Ok(_) => Ok(Destination::Appservice(id)),
| Err(e) => Err((Destination::Appservice(id), e)),

View File

@@ -1,9 +1,7 @@
[package]
name = "conduwuit_web"
categories.workspace = true
description.workspace = true
edition.workspace = true
keywords.workspace = true
license.workspace = true
readme.workspace = true
repository.workspace = true

View File

@@ -1,22 +1,21 @@
[package]
name = "xtask"
authors.workspace = true
categories.workspace = true
description.workspace = true
edition.workspace = true
homepage.workspace = true
keywords.workspace = true
license.workspace = true
readme.workspace = true
repository.workspace = true
rust-version.workspace = true
version.workspace = true
[dependencies]
conduwuit-admin.workspace = true
conduwuit.workspace = true
clap.workspace = true
# Required for working with JSON output from cargo metadata
serde.workspace = true
serde_json = "1.0"
askama = "0.15.1"
cargo_metadata = "0.23.1"
[lints]
workspace = true

View File

@@ -1,26 +0,0 @@
[package]
name = "xtask-generate-commands"
authors.workspace = true
categories.workspace = true
description.workspace = true
edition.workspace = true
homepage.workspace = true
keywords.workspace = true
license.workspace = true
readme.workspace = true
repository.workspace = true
rust-version.workspace = true
version.workspace = true
[dependencies]
clap-markdown = "0.1.5"
clap_builder = { version = "4.5.38", default-features = false }
clap_mangen = "0.2"
conduwuit-admin.workspace = true
# Hack to prevent rebuilds
conduwuit.workspace = true
[lints]
workspace = true

View File

@@ -1,113 +0,0 @@
use std::{
fs::{self, File},
io::{self, Write},
path::Path,
};
use clap_builder::{Command, CommandFactory};
use conduwuit_admin::AdminCommand;
enum CommandType {
Admin,
Server,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut args = std::env::args().skip(1);
let command_type = args.next();
let task = args.next();
match (command_type, task) {
| (None, _) => {
return Err("Missing command type (admin or server)".into());
},
| (Some(cmd_type), None) => {
return Err(format!("Missing task for {cmd_type} command").into());
},
| (Some(cmd_type), Some(task)) => {
let command_type = match cmd_type.as_str() {
| "admin" => CommandType::Admin,
| "server" => CommandType::Server,
| _ => return Err(format!("Invalid command type: {cmd_type}").into()),
};
match task.as_str() {
| "man" => match command_type {
| CommandType::Admin => {
let dir = Path::new("./admin-man");
gen_admin_manpages(dir)?;
},
| CommandType::Server => {
let dir = Path::new("./server-man");
gen_server_manpages(dir)?;
},
},
| "md" => {
match command_type {
| CommandType::Admin => {
let command = AdminCommand::command().name("admin");
let res = clap_markdown::help_markdown_command_custom(
&command,
&clap_markdown::MarkdownOptions::default().show_footer(false),
)
.replace("\n\r", "\n")
.replace("\r\n", "\n")
.replace(" \n", "\n");
let mut file = File::create(Path::new("./docs/admin_reference.md"))?;
file.write_all(res.trim_end().as_bytes())?;
file.write_all(b"\n")?;
},
| CommandType::Server => {
// Get the server command from the conduwuit crate
let command = conduwuit::Args::command();
let res = clap_markdown::help_markdown_command_custom(
&command,
&clap_markdown::MarkdownOptions::default().show_footer(false),
)
.replace("\n\r", "\n")
.replace("\r\n", "\n")
.replace(" \n", "\n");
let mut file = File::create(Path::new("./docs/server_reference.md"))?;
file.write_all(res.trim_end().as_bytes())?;
file.write_all(b"\n")?;
},
}
},
| invalid => return Err(format!("Invalid task name: {invalid}").into()),
}
},
}
Ok(())
}
fn gen_manpage_common(dir: &Path, c: &Command, prefix: Option<&str>) -> Result<(), io::Error> {
fs::create_dir_all(dir)?;
let sub_name = c.get_display_name().unwrap_or_else(|| c.get_name());
let name = if let Some(prefix) = prefix {
format!("{prefix}-{sub_name}")
} else {
sub_name.to_owned()
};
let mut out = File::create(dir.join(format!("{name}.1")))?;
let clap_mangen = clap_mangen::Man::new(c.to_owned().disable_help_flag(true));
clap_mangen.render(&mut out)?;
for sub in c.get_subcommands() {
gen_manpage_common(&dir.join(sub_name), sub, Some(&name))?;
}
Ok(())
}
fn gen_admin_manpages(dir: &Path) -> Result<(), io::Error> {
gen_manpage_common(dir, &AdminCommand::command().name("admin"), None)
}
fn gen_server_manpages(dir: &Path) -> Result<(), io::Error> {
gen_manpage_common(dir, &conduwuit::Args::command(), None)
}

View File

@@ -1,11 +0,0 @@
use std::{env, process::Command};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut child = Command::new("cargo").args(["run", "--package", "xtask-generate-commands", "--"].into_iter().map(ToOwned::to_owned).chain(env::args().skip(2)))
// .stdout(Stdio::piped())
// .stderr(Stdio::piped())
.spawn()
.expect("failed to execute child");
child.wait()?;
Ok(())
}

26
xtask/src/main.rs Normal file
View File

@@ -0,0 +1,26 @@
mod tasks;
use clap::Parser;
use crate::tasks::Task;
#[derive(clap::Parser)]
struct BaseArgs {
#[command(subcommand)]
task: Task,
#[command(flatten)]
args: Args,
}
#[derive(clap::Args)]
struct Args {
/// Simulate without actually touching the filesystem
#[arg(long)]
dry_run: bool,
}
fn main() -> impl std::process::Termination {
let BaseArgs { task, args } = BaseArgs::parse();
task.invoke(args)
}

View File

@@ -0,0 +1,114 @@
//! Generates documentation for the various commands that may be used in the admin room and server console.
//!
//! This generates one index page and several category pages, one for each of the direct subcommands of the top-level
//! `!admin` command. Those category pages then list all of the sub-subcommands.
use std::path::Path;
use askama::Template;
use clap::{Command, CommandFactory};
use conduwuit_admin::AdminCommand;
use crate::tasks::{TaskResult, generate_docs::FileOutput};
#[derive(askama::Template)]
#[template(path = "admin/index.md")]
/// The template for the index page, which links to all of the category pages.
struct Index {
categories: Vec<Category>
}
/// A direct subcommand of the top-level `!admin` command.
#[derive(askama::Template)]
#[template(path = "admin/category.md")]
struct Category {
name: String,
description: String,
commands: Vec<Subcommand>,
}
/// A second-or-deeper level subcommand of the `!admin` command.
struct Subcommand {
name: String,
description: String,
/// How deeply nested this command was in the original command tree.
/// This determines the header size used for it in the documentation.
depth: usize,
}
fn flatten_subcommands(command: &Command) -> Vec<Subcommand> {
fn flatten(
subcommands: &mut Vec<Subcommand>,
name_stack: &mut Vec<String>,
command: &Command
) {
let depth = name_stack.len();
name_stack.push(command.get_name().to_owned());
// do not include the root command
if depth > 0 {
let name = name_stack.join(" ");
let description = command
.get_long_about()
.or_else(|| command.get_about())
.map(ToString::to_string);
if let Some(description) = description {
subcommands.push(
Subcommand {
name,
description,
depth,
}
);
}
}
for command in command.get_subcommands() {
flatten(subcommands, name_stack, command);
}
name_stack.pop();
}
let mut subcommands = Vec::new();
let mut name_stack = Vec::new();
flatten(&mut subcommands, &mut name_stack, command);
subcommands
}
pub(super) fn generate(out: &mut impl FileOutput) -> TaskResult<()> {
let admin_commands = AdminCommand::command();
let categories: Vec<_> = admin_commands
.get_subcommands()
.map(|command| {
Category {
name: command.get_name().to_owned(),
description: command.get_about().expect("categories should have a docstring").to_string(),
commands: flatten_subcommands(command),
}
})
.collect();
let root = Path::new("reference/admin/");
for category in &categories {
out.create_file(
root.join(&category.name).with_extension("md"),
category.render()?
);
}
out.create_file(
root.join("index.md"),
Index { categories }.render()?,
);
Ok(())
}

View File

@@ -0,0 +1,86 @@
mod admin_commands;
use std::{collections::HashMap, fs::File, io::Write, path::{Path, PathBuf}};
use cargo_metadata::MetadataCommand;
use crate::tasks::TaskResult;
trait FileOutput {
fn create_file(&mut self, path: PathBuf, contents: String);
}
#[derive(Default)]
struct FileQueue {
queue: HashMap<PathBuf, String>,
}
impl FileQueue {
const GENERATED_HEADER: &'static str = "This file is generated by `cargo xtask generate-docs`. Do not edit.";
/// Generate the header for the file at `path`.
fn header_for(path: &Path) -> Option<String> {
Some(match path.extension()?.as_encoded_bytes() {
b"md" => format!("<!-- {} -->", Self::GENERATED_HEADER),
_ => return None
})
}
/// Write all queued files into the file tree rooted at `root`.
fn write(self, root: &Path, dry_run: bool) -> std::io::Result<()> {
for (path, contents) in self.queue {
let path = root.join(&path);
eprintln!("Writing {}", path.display());
if !dry_run {
let mut file = File::create(&path)?;
if let Some(header) = Self::header_for(&path) {
writeln!(file, "{header}")?;
}
write!(file, "{contents}")?;
if !contents.ends_with('\n') {
writeln!(file)?;
}
}
}
Ok(())
}
}
impl FileOutput for FileQueue {
fn create_file(&mut self, path: PathBuf, contents: String) {
assert!(path.is_relative(), "path must be relative");
assert!(path.extension().is_some(), "path must not point to a directory");
assert!(!self.queue.contains_key(&path), "attempted to create an already created file {}", path.display());
self.queue.insert(path, contents);
}
}
#[derive(clap::Args)]
pub(crate) struct Args {
/// The base path of the documentation. Defaults to `docs/` in the crate root.
root: Option<PathBuf>,
}
#[expect(clippy::needless_pass_by_value)]
pub(super) fn run(common_args: crate::Args, task_args: Args) -> TaskResult<()> {
let mut queue = FileQueue::default();
let metadata = MetadataCommand::new()
.no_deps()
.exec()
.expect("should have been able to run cargo");
let root = task_args.root.unwrap_or_else(|| metadata.workspace_root.join_os("docs/"));
admin_commands::generate(&mut queue)?;
queue.write(&root, common_args.dry_run)?;
Ok(())
}

37
xtask/src/tasks/mod.rs Normal file
View File

@@ -0,0 +1,37 @@
type TaskResult<T> = Result<T, Box<dyn std::error::Error>>;
#[macro_export]
macro_rules! tasks {
(
$(
$module:ident: $desc:literal
),*
) => {
$(pub(super) mod $module;)*
#[derive(clap::Subcommand)]
#[allow(non_camel_case_types)]
pub(super) enum Task {
$(
#[clap(about = $desc, long_about = None)]
$module($module::Args),
)*
}
impl Task {
pub(super) fn invoke(self, common_args: $crate::Args) -> TaskResult<impl std::process::Termination> {
match self {
$(
Self::$module(task_args) => {
$module::run(common_args, task_args)
},
)*
}
}
}
};
}
tasks! {
generate_docs: "Generate various documentation files. This is run automatically when compiling the website."
}

View File

@@ -0,0 +1,10 @@
# `!admin {{ name }}`
{{ description }}
{% for command in commands %}
{% let header = "#".repeat((command.depth + 1).min(3)) -%}
{{ header }} `!admin {{ command.name }}`
{{ command.description }}
{% endfor %}

View File

@@ -0,0 +1,15 @@
# 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
{%~ for category in categories %}
- [`!admin {{ category.name }}`]({{ category.name }}/): {{ category.description }}
{%- endfor %}