Compare commits

..

1 Commits

Author SHA1 Message Date
renovate[bot] af80565e6a chore(deps): update rust crate bytes to v1.7.1 2024-08-03 07:46:30 +00:00
38 changed files with 164 additions and 408 deletions
Generated
+2 -10
View File
@@ -71,12 +71,6 @@ dependencies = [
"password-hash",
]
[[package]]
name = "arrayvec"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
[[package]]
name = "as_variant"
version = "1.2.0"
@@ -418,9 +412,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
[[package]]
name = "bytes"
version = "1.7.0"
version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fca2be1d5c43812bae364ee3f30b3afcb7877cf59f4aeb94c66f313a41d2fac9"
checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50"
[[package]]
name = "bzip2-sys"
@@ -633,13 +627,11 @@ name = "conduit_core"
version = "0.4.6"
dependencies = [
"argon2",
"arrayvec",
"axum",
"bytes",
"cargo_toml",
"checked_ops",
"chrono",
"clap",
"conduit_macros",
"const-str",
"ctor",
-25
View File
@@ -514,31 +514,6 @@ allow_profile_lookup_federation_requests = true
# Defaults to false as this uses more CPU when compressing.
#rocksdb_bottommost_compression = false
# Level of statistics collection. Some admin commands to display database statistics may require
# this option to be set. Database performance may be impacted by higher settings.
#
# Option is a number ranging from 0 to 6:
# 0 = No statistics.
# 1 = No statistics in release mode (default).
# 2 to 3 = Statistics with no performance impact.
# 3 to 5 = Statistics with possible performance impact.
# 6 = All statistics.
#
# Defaults to 1 (No statistics, except in debug-mode)
#rocksdb_stats_level = 1
# Database repair mode (for RocksDB SST corruption)
#
# Use this option when the server reports corruption while running or panics. If the server refuses
# to start use the recovery mode options first. Corruption errors containing the acronym 'SST' which
# occur after startup will likely require this option.
#
# - Backing up your database directory is recommended prior to running the repair.
# - Disabling repair mode and restarting the server is recommended after running the repair.
#
# Defaults to false
#rocksdb_repair = false
# Database recovery mode (for RocksDB WAL corruption)
#
# Use this option when the server reports corruption and refuses to start. Set mode 2 (PointInTime)
+1
View File
@@ -16,6 +16,7 @@ case "$1" in
--home "$CONDUWUIT_DATABASE_PATH" \
--disabled-login \
--shell "/usr/sbin/nologin" \
--verbose \
conduwuit
fi
-1
View File
@@ -10,7 +10,6 @@
- [Docker](deploying/docker.md)
- [Arch Linux](deploying/arch-linux.md)
- [Debian](deploying/debian.md)
- [FreeBSD](deploying/freebsd.md)
- [TURN](turn.md)
- [Appservices](appservices.md)
- [Maintenance](maintenance.md)
+4 -24
View File
@@ -4,35 +4,15 @@ This chapter describes various ways to configure conduwuit.
## Basics
conduwuit uses a config file for the majority of the settings, but also supports setting individual config options via commandline.
Please refer to the [example config file](./configuration/examples.md#example-configuration) for all of those settings.
The config file to use can be specified on the commandline when running conduwuit by specifying the
Conduwuit uses a config file for the majority of the settings. Please refer to the
[example config file](./configuration/examples.md#example-configuration) for all of those settings.
The config file to use can either be specified on the command line when running conduwuit by specifying the
`-c`, `--config` flag. Alternatively, you can use the environment variable `CONDUWUIT_CONFIG` to specify the config
file to used. Conduit's environment variables are supported for backwards compatibility.
## Option commandline flag
conduwuit supports setting individual config options in TOML format from the `-O` / `--option` flag. For example, you can set your server name via `-O server_name=\"example.com\"`.
Note that the config is parsed as TOML, and shells like bash will remove quotes. So unfortunately it is required to escape quotes if the config option takes a string.
This does not apply to options that take booleans or numbers:
- `--option allow_registration=true` works ✅
- `-O max_request_size=99999999` works ✅
- `-O server_name=example.com` does not work ❌
- `--option log=\"debug\"` works ✅
- `--option server_name='"example.com'"` works ✅
file to used.
## Environment variables
All of the settings that are found in the config file can be specified by using environment variables.
The environment variable names should be all caps and prefixed with `CONDUWUIT_`.
For example, if the setting you are changing is `max_request_size`, then the environment variable to set is
`CONDUWUIT_MAX_REQUEST_SIZE`.
To modify config options not in the `[global]` context such as `[global.well_known]`, use the `__` suffix split: `CONDUWUIT_WELL_KNOWN__SERVER`
Conduit's environment variables are supported for backwards compatibility (e.g. `CONDUIT_SERVER_NAME`).
+2 -2
View File
@@ -17,9 +17,9 @@ OCI images for conduwuit are available in the registries listed below.
| GitLab Registry | [registry.gitlab.com/conduwuit/conduwuit:main][gl] | ![Image Size][shield-main] | Stable main branch. |
| Docker Hub | [docker.io/girlbossceo/conduwuit:main][dh] | ![Image Size][shield-main] | Stable main branch. |
[dh]: https://hub.docker.com/r/girlbossceo/conduwuit
[dh]: https://hub.docker.com/repository/docker/girlbossceo/conduwuit
[gh]: https://github.com/girlbossceo/conduwuit/pkgs/container/conduwuit
[gl]: https://gitlab.com/conduwuit/conduwuit/container_registry/6369729
[gl]: https://gitlab.com/conduwuit/conduwuit/container_registry/6351657
[shield-latest]: https://img.shields.io/docker/image-size/girlbossceo/conduwuit/latest
[shield-main]: https://img.shields.io/docker/image-size/girlbossceo/conduwuit/main
-8
View File
@@ -1,8 +0,0 @@
# conduwuit for FreeBSD
conduwuit at the moment does not provide FreeBSD builds. Building conduwuit on FreeBSD requires a specific environment variable to use the
system prebuilt RocksDB library instead of rust-rocksdb / rust-librocksdb-sys which does *not* work and will cause a build error or coredump.
Use the following environment variable: `ROCKSDB_LIB_DIR=/usr/local/lib`
Such example commandline with it can be: `ROCKSDB_LIB_DIR=/usr/local/lib cargo build --release`
+4 -2
View File
@@ -9,12 +9,14 @@
You may simply download the binary that fits your machine. Run `uname -m` to see what you need.
Prebuilt fully static musl binaries can be downloaded from the latest tagged release [here](https://github.com/girlbossceo/conduwuit/releases/latest) or `main` CI branch workflow artifact output. These also include Debian packages. These binaries have jemalloc and io_uring statically linked and included with them.
Prebuilt binaries can be downloaded from the latest tagged release [here](https://github.com/girlbossceo/conduwuit/releases/latest).
The latest tagged release also includes the Debian packages.
Alternatively, you may compile the binary yourself. We recommend using [Lix](https://lix.systems) to build conduwuit as this has the most guaranteed
reproducibiltiy and easiest to get a build environment and output going.
Otherwise, follow standard Rust project build guides (installing git and cloning the repo, getting the Rust toolchain via rustup, installing LLVM toolchain + libclang for RocksDB, installing liburing for io_uring and RocksDB, etc).
Otherwise, follow standard Rust project build guides (installing git and cloning the repo, getting the Rust toolchain via rustup, installing LLVM toolchain + libclang, installing liburing for io_uring and RocksDB, etc).
## Adding a conduwuit user
+2 -11
View File
@@ -8,17 +8,6 @@
>
> If there are things like Compose file issues or Dockerhub image issues, those can still be mentioned as long as they're something we can fix.
## General potential issues
#### Potential DNS issues when using Docker
Docker has issues with its default DNS setup that may cause DNS to not be properly functional when running conduwuit, resulting in federation issues.
The symptoms of this have shown in excessively long room joins (30+ minutes) from very long DNS timeouts, log entries of "mismatching responding nameservers", and/or partial or non-functional inbound/outbound federation.
This is **not** a conduwuit issue, and is purely a Docker issue. It is not sustainable for heavy DNS activity which is normal for Matrix federation. The workarounds for this are:
- Use DNS over TCP via the config option `query_over_tcp_only = true`
- Don't use Docker's default DNS setup and instead allow the container to use and communicate with your host's DNS servers (host's `/etc/resolv.conf`)
## Rocksdb / database issues
#### Direct IO
@@ -49,6 +38,8 @@ With this in mind:
- Leave your conduwuit running in `PointInTime` for at least 30-60 minutes so as much possible corruption is restored
- If all goes will, you should be able to restore back to using `TolerateCorruptedTailRecords` and you have successfully recovered your database
## Media
## Debugging
Note that users should not really be debugging things. If you find yourself debugging and find the issue, please let us know and/or how we can fix it. Various debug commands can be found in `!admin debug`.
Generated
+3 -3
View File
@@ -81,11 +81,11 @@
"complement": {
"flake": false,
"locked": {
"lastModified": 1722323564,
"narHash": "sha256-6w6/N8walz4Ayc9zu7iySqJRmGFukhkaICLn4dweAcA=",
"lastModified": 1720637557,
"narHash": "sha256-oZz6nCmFmdJZpC+K1iOG2KkzTI6rlAmndxANPDVU7X0=",
"owner": "matrix-org",
"repo": "complement",
"rev": "6e4426a9e63233f9821a4d2382bfed145244183f",
"rev": "0d14432e010482ea9e13a6f7c47c1533c0c9d62f",
"type": "github"
},
"original": {
-4
View File
@@ -137,11 +137,7 @@
# Useful for editing the book locally
mdbook
# used for rust caching in CI to speed it up
sccache
# needed so we can get rid of gcc and other unused deps that bloat OCI images
removeReferencesTo
])
++ scope.main.buildInputs
++ scope.main.propagatedBuildInputs
+3 -15
View File
@@ -7,7 +7,6 @@
, liburing
, pkgsBuildHost
, rocksdb
, removeReferencesTo
, rust
, rust-jemalloc-sys
, stdenv
@@ -95,8 +94,8 @@ buildDepsOnlyEnv =
else if stdenv.targetPlatform.isAarch64
then lib.subtractLists [ "-DPORTABLE=1" ] old.cmakeFlags
++ lib.optionals stdenv.targetPlatform.isAarch64 [
# cortex-a73 == ARMv8-A
"-DPORTABLE=armv8-a"
# cortex-a55 == ARMv8.2-a
"-DPORTABLE=armv8.2-a"
]
else old.cmakeFlags;
});
@@ -129,7 +128,7 @@ buildPackageEnv = {
+ lib.optionalString stdenv.targetPlatform.isx86_64
" -Ctarget-cpu=x86-64-v2"
+ lib.optionalString stdenv.targetPlatform.isAarch64
" -Ctarget-cpu=cortex-a73"; # cortex-a73 == ARMv8-A
" -Ctarget-cpu=cortex-a55"; # cortex-a55 == ARMv8.2-a
};
@@ -155,7 +154,6 @@ commonAttrs = {
};
dontStrip = profile == "dev" || profile == "test";
dontPatchELF = profile == "dev" || profile == "test";
buildInputs = lib.optional (featureEnabled "jemalloc") rust-jemalloc-sys';
@@ -170,9 +168,6 @@ commonAttrs = {
# differing values for `NIX_CFLAGS_COMPILE`, which contributes to spurious
# rebuilds of bindgen and its depedents.
jq
# needed so we can get rid of gcc and other unused deps that bloat OCI images
removeReferencesTo
]
++ lib.optionals stdenv.isDarwin [
# https://github.com/NixOS/nixpkgs/issues/206242
@@ -182,13 +177,6 @@ commonAttrs = {
# https://discourse.nixos.org/t/compile-a-rust-binary-on-macos-dbcrossbar/8612
pkgsBuildHost.darwin.apple_sdk.frameworks.Security
];
# for some reason gcc and other weird deps are added to OCI images and bloats it up
#
# <https://github.com/input-output-hk/haskell.nix/issues/829>
postInstall = with pkgsBuildHost; ''
find "$out" -type f -exec remove-references-to -t ${stdenv.cc} -t ${gcc} -t ${libgcc} -t ${linuxHeaders} -t ${libidn2} -t ${libunistring} '{}' +
'';
};
in
+1 -4
View File
@@ -1,9 +1,6 @@
use std::time::SystemTime;
use conduit_service::Services;
use service::Services;
pub(crate) struct Command<'a> {
pub(crate) services: &'a Services,
pub(crate) body: &'a [&'a str],
pub(crate) timer: SystemTime,
}
+27 -37
View File
@@ -1,12 +1,16 @@
use std::{
collections::{BTreeMap, HashMap},
fmt::Write,
sync::Arc,
sync::{Arc, Mutex},
time::{Instant, SystemTime},
};
use api::client::validate_and_add_event_id;
use conduit::{debug, debug_error, err, info, trace, utils, warn, Error, PduEvent, Result};
use conduit::{
debug, debug_error, err, info, log,
log::{capture, Capture},
utils, warn, Error, PduEvent, Result,
};
use ruma::{
api::{client::error::ErrorKind, federation::event::get_room_state},
events::room::message::RoomMessageEventContent,
@@ -145,32 +149,23 @@ pub(super) async fn get_remote_pdu_list(
.filter_map(|pdu| EventId::parse(pdu).ok())
.collect::<Vec<_>>();
let mut failed_count: usize = 0;
let mut success_count: usize = 0;
for pdu in list {
if force {
if let Err(e) = self.get_remote_pdu(Box::from(pdu), server.clone()).await {
failed_count = failed_count.saturating_add(1);
self.services
.admin
.send_message(RoomMessageEventContent::text_plain(format!(
"Failed to get remote PDU, ignoring error: {e}"
)))
.await;
warn!("Failed to get remote PDU, ignoring error: {e}");
} else {
success_count = success_count.saturating_add(1);
warn!(%e, "Failed to get remote PDU, ignoring error");
}
} else {
self.get_remote_pdu(Box::from(pdu), server.clone()).await?;
success_count = success_count.saturating_add(1);
}
}
Ok(RoomMessageEventContent::text_plain(format!(
"Fetched {success_count} remote PDUs successfully with {failed_count} failures"
)))
Ok(RoomMessageEventContent::text_plain("Fetched list of remote PDUs."))
}
#[admin_command]
@@ -209,7 +204,7 @@ pub(super) async fn get_remote_pdu(
Error::BadRequest(ErrorKind::Unknown, "Received response from server but failed to parse PDU")
})?;
trace!("Attempting to parse PDU: {:?}", &response.pdu);
debug!("Attempting to parse PDU: {:?}", &response.pdu);
let parsed_pdu = {
let parsed_result = self
.services
@@ -713,14 +708,30 @@ pub(super) async fn resolve_true_destination(
));
}
let filter: &capture::Filter = &|data| {
data.level() <= log::Level::DEBUG
&& data.mod_name().starts_with("conduit")
&& matches!(data.span_name(), "actual" | "well-known" | "srv")
};
let state = &self.services.server.log.capture;
let logs = Arc::new(Mutex::new(String::new()));
let capture = Capture::new(state, Some(filter), capture::fmt_markdown(logs.clone()));
let capture_scope = capture.start();
let actual = self
.services
.resolver
.resolve_actual_dest(&server_name, !no_cache)
.await?;
drop(capture_scope);
let msg = format!("Destination: {}\nHostname URI: {}", actual.dest, actual.host,);
let msg = format!(
"{}\nDestination: {}\nHostname URI: {}",
logs.lock().expect("locked"),
actual.dest,
actual.host,
);
Ok(RoomMessageEventContent::text_markdown(msg))
}
@@ -808,24 +819,3 @@ pub(super) async fn list_dependencies(&self, names: bool) -> Result<RoomMessageE
Ok(RoomMessageEventContent::notice_markdown(out))
}
#[admin_command]
pub(super) async fn database_stats(
&self, property: Option<String>, map: Option<String>,
) -> Result<RoomMessageEventContent> {
let property = property.unwrap_or_else(|| "rocksdb.stats".to_owned());
let map_name = map.as_ref().map_or(utils::string::EMPTY, String::as_str);
let mut out = String::new();
for (name, map) in self.services.db.iter_maps() {
if !map_name.is_empty() && *map_name != *name {
continue;
}
let res = map.property(&property)?;
let res = res.trim();
writeln!(out, "##### {name}:\n```\n{res}\n```")?;
}
Ok(RoomMessageEventContent::notice_markdown(out))
}
-8
View File
@@ -184,14 +184,6 @@ pub(super) enum DebugCommand {
names: bool,
},
/// - Get database statistics
DatabaseStats {
property: Option<String>,
#[arg(short, long, alias("column"))]
map: Option<String>,
},
/// - Developer test stubs
#[command(subcommand)]
#[allow(non_snake_case)]
+67 -85
View File
@@ -1,21 +1,7 @@
use std::{
panic::AssertUnwindSafe,
sync::{Arc, Mutex},
time::SystemTime,
};
use std::{panic::AssertUnwindSafe, sync::Arc, time::Instant};
use clap::{CommandFactory, Parser};
use conduit::{
debug, error,
log::{
capture,
capture::Capture,
fmt::{markdown_table, markdown_table_head},
},
trace,
utils::string::{collect_stream, common_prefix},
Error, Result,
};
use conduit::{checked, error, trace, utils::string::common_prefix, Error, Result};
use futures_util::future::FutureExt;
use ruma::{
events::{
@@ -25,10 +11,9 @@ use ruma::{
OwnedEventId,
};
use service::{
admin::{CommandInput, CommandOutput, HandlerFuture, HandlerResult},
admin::{CommandInput, CommandOutput, CommandResult, HandlerResult},
Services,
};
use tracing::Level;
use crate::{admin, admin::AdminCommand, Command};
@@ -36,12 +21,12 @@ use crate::{admin, admin::AdminCommand, Command};
pub(super) fn complete(line: &str) -> String { complete_command(AdminCommand::command(), line) }
#[must_use]
pub(super) fn handle(services: Arc<Services>, command: CommandInput) -> HandlerFuture {
pub(super) fn handle(services: Arc<Services>, command: CommandInput) -> HandlerResult {
Box::pin(handle_command(services, command))
}
#[tracing::instrument(skip_all, name = "admin")]
async fn handle_command(services: Arc<Services>, command: CommandInput) -> HandlerResult {
async fn handle_command(services: Arc<Services>, command: CommandInput) -> CommandResult {
AssertUnwindSafe(Box::pin(process_command(services, &command)))
.catch_unwind()
.await
@@ -49,24 +34,13 @@ async fn handle_command(services: Arc<Services>, command: CommandInput) -> Handl
.or_else(|error| handle_panic(&error, command))
}
async fn process_command(services: Arc<Services>, input: &CommandInput) -> CommandOutput {
let (command, args, body) = match parse(&services, input) {
Err(error) => return error,
Ok(parsed) => parsed,
};
let context = Command {
services: &services,
body: &body,
timer: SystemTime::now(),
};
process(&context, command, &args)
async fn process_command(services: Arc<Services>, command: &CommandInput) -> CommandOutput {
process(services, &command.command)
.await
.and_then(|content| reply(content, input.reply_id.clone()))
.and_then(|content| reply(content, command.reply_id.clone()))
}
fn handle_panic(error: &Error, command: CommandInput) -> HandlerResult {
fn handle_panic(error: &Error, command: CommandInput) -> CommandResult {
let link = "Please submit a [bug report](https://github.com/girlbossceo/conduwuit/issues/new). 🥺";
let msg = format!("Panic occurred while processing command:\n```\n{error:#?}\n```\n{link}");
let content = RoomMessageEventContent::notice_markdown(msg);
@@ -85,61 +59,69 @@ fn reply(mut content: RoomMessageEventContent, reply_id: Option<OwnedEventId>) -
}
// Parse and process a message from the admin room
async fn process(context: &Command<'_>, command: AdminCommand, args: &[String]) -> CommandOutput {
let filter: &capture::Filter =
&|data| data.level() <= Level::DEBUG && data.our_modules() && data.scope.contains(&"admin");
let logs = Arc::new(Mutex::new(
collect_stream(|s| markdown_table_head(s)).expect("markdown table header"),
));
let capture = Capture::new(
&context.services.server.log.capture,
Some(filter),
capture::fmt(markdown_table, logs.clone()),
);
let capture_scope = capture.start();
let result = Box::pin(admin::process(command, context)).await;
drop(capture_scope);
debug!(
ok = result.is_ok(),
elapsed = ?context.timer.elapsed(),
command = ?args,
"command processed"
);
let logs = logs.lock().expect("locked");
let output = match result {
Err(error) => format!("{logs}\nEncountered an error while handling the command:\n```\n{error:#?}\n```"),
Ok(reply) => format!("{logs}\n{}", reply.body()), //TODO: content is recreated to add logs
async fn process(services: Arc<Services>, msg: &str) -> CommandOutput {
let lines = msg.lines().filter(|l| !l.trim().is_empty());
let command = lines
.clone()
.next()
.expect("each string has at least one line");
let (parsed, body) = match parse_command(command) {
Ok(parsed) => parsed,
Err(error) => {
let server_name = services.globals.server_name();
let message = error.replace("server.name", server_name.as_str());
return Some(RoomMessageEventContent::notice_markdown(message));
},
};
Some(RoomMessageEventContent::notice_markdown(output))
}
// Parse chat messages from the admin room into an AdminCommand object
fn parse<'a>(
services: &Arc<Services>, input: &'a CommandInput,
) -> Result<(AdminCommand, Vec<String>, Vec<&'a str>), CommandOutput> {
let lines = input.command.lines().filter(|line| !line.trim().is_empty());
let command_line = lines.clone().next().expect("command missing first line");
let body = lines.skip(1).collect();
match parse_command(command_line) {
Ok((command, args)) => Ok((command, args, body)),
Err(error) => {
let message = error
.to_string()
.replace("server.name", services.globals.server_name().as_str());
Err(Some(RoomMessageEventContent::notice_markdown(message)))
},
let body = parse_body(AdminCommand::command(), &body, lines.skip(1).collect()).expect("trailing body parsed");
let context = Command {
services: &services,
body: &body,
};
let timer = Instant::now();
let result = Box::pin(admin::process(parsed, &context)).await;
let elapsed = timer.elapsed();
conduit::debug!(?command, ok = result.is_ok(), "command processed in {elapsed:?}");
match result {
Ok(reply) => Some(reply),
Err(error) => Some(RoomMessageEventContent::notice_markdown(format!(
"Encountered an error while handling the command:\n```\n{error:#?}\n```"
))),
}
}
fn parse_command(line: &str) -> Result<(AdminCommand, Vec<String>)> {
let argv = parse_line(line);
let command = AdminCommand::try_parse_from(&argv)?;
Ok((command, argv))
// Parse chat messages from the admin room into an AdminCommand object
fn parse_command(command_line: &str) -> Result<(AdminCommand, Vec<String>), String> {
let argv = parse_line(command_line);
let com = AdminCommand::try_parse_from(&argv).map_err(|error| error.to_string())?;
Ok((com, argv))
}
fn parse_body<'a>(mut cmd: clap::Command, body: &'a [String], lines: Vec<&'a str>) -> Result<Vec<&'a str>> {
let mut start = 1;
'token: for token in body.iter().skip(1) {
let cmd_ = cmd.clone();
for sub in cmd_.get_subcommands() {
if sub.get_name() == *token {
start = checked!(start + 1)?;
cmd = sub.clone();
continue 'token;
}
}
// positional arguments have to be skipped too
let num_posargs = cmd_.get_positionals().count();
start = checked!(start + num_posargs)?;
break;
}
Ok(body
.iter()
.skip(start)
.map(String::as_str)
.chain(lines)
.collect::<Vec<&'a str>>())
}
fn complete_command(mut cmd: clap::Command, line: &str) -> String {
+1 -1
View File
@@ -547,7 +547,7 @@ async fn list_banned_rooms(&self) -> Result<RoomMessageEventContent> {
rooms.reverse();
let output_plain = format!(
"Rooms Banned ({}):\n```\n{}\n```",
"Rooms Banned ({}):\n```\n{}```",
rooms.len(),
rooms
.iter()
+3 -7
View File
@@ -286,13 +286,9 @@ pub(crate) async fn register_route(
let token = utils::random_string(TOKEN_LENGTH);
// Create device for this account
services.users.create_device(
&user_id,
&device_id,
&token,
body.initial_device_display_name.clone(),
Some(client.to_string()),
)?;
services
.users
.create_device(&user_id, &device_id, &token, body.initial_device_display_name.clone())?;
debug_info!(%user_id, %device_id, "User account was created");
+1 -3
View File
@@ -21,13 +21,11 @@ pub(crate) async fn get_context_route(
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
// some clients, at least element, seem to require knowledge of redundant
// members for "inline" profiles on the timeline to work properly
let (lazy_load_enabled, lazy_load_send_redundant) = match &body.filter.lazy_load_options {
LazyLoadOptions::Enabled {
include_redundant_members,
} => (true, *include_redundant_members),
LazyLoadOptions::Disabled => (false, cfg!(feature = "element_hacks")),
LazyLoadOptions::Disabled => (false, false),
};
let mut lazy_loaded = HashSet::new();
+7 -17
View File
@@ -1,5 +1,4 @@
use axum::extract::State;
use axum_client_ip::InsecureClientIp;
use ruma::{
api::client::{
error::ErrorKind,
@@ -34,9 +33,8 @@ struct Claims {
///
/// Get the supported login types of this server. One of these should be used as
/// the `type` field when logging in.
#[tracing::instrument(skip_all, fields(%client), name = "register")]
pub(crate) async fn get_login_types_route(
InsecureClientIp(client): InsecureClientIp, _body: Ruma<get_login_types::v3::Request>,
_body: Ruma<get_login_types::v3::Request>,
) -> Result<get_login_types::v3::Response> {
Ok(get_login_types::v3::Response::new(vec![
get_login_types::v3::LoginType::Password(PasswordLoginType::default()),
@@ -58,9 +56,8 @@ pub(crate) async fn get_login_types_route(
/// Note: You can use [`GET
/// /_matrix/client/r0/login`](fn.get_supported_versions_route.html) to see
/// supported login types.
#[tracing::instrument(skip_all, fields(%client), name = "register")]
pub(crate) async fn login_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp, body: Ruma<login::v3::Request>,
State(services): State<crate::State>, body: Ruma<login::v3::Request>,
) -> Result<login::v3::Response> {
// Validate login method
// TODO: Other login methods
@@ -179,13 +176,9 @@ pub(crate) async fn login_route(
if device_exists {
services.users.set_token(&user_id, &device_id, &token)?;
} else {
services.users.create_device(
&user_id,
&device_id,
&token,
body.initial_device_display_name.clone(),
Some(client.to_string()),
)?;
services
.users
.create_device(&user_id, &device_id, &token, body.initial_device_display_name.clone())?;
}
// send client well-known if specified so the client knows to reconfigure itself
@@ -221,9 +214,8 @@ pub(crate) async fn login_route(
/// last seen ts)
/// - Forgets to-device events
/// - Triggers device list updates
#[tracing::instrument(skip_all, fields(%client), name = "register")]
pub(crate) async fn logout_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp, body: Ruma<logout::v3::Request>,
State(services): State<crate::State>, body: Ruma<logout::v3::Request>,
) -> Result<logout::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let sender_device = body.sender_device.as_ref().expect("user is authenticated");
@@ -249,10 +241,8 @@ pub(crate) async fn logout_route(
/// Note: This is equivalent to calling [`GET
/// /_matrix/client/r0/logout`](fn.logout_route.html) from each device of this
/// user.
#[tracing::instrument(skip_all, fields(%client), name = "register")]
pub(crate) async fn logout_all_route(
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
body: Ruma<logout_all::v3::Request>,
State(services): State<crate::State>, body: Ruma<logout_all::v3::Request>,
) -> Result<logout_all::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
+3 -5
View File
@@ -106,13 +106,11 @@ pub(crate) async fn sync_events_route(
.unwrap_or_default(),
};
// some clients, at least element, seem to require knowledge of redundant
// members for "inline" profiles on the timeline to work properly
let (lazy_load_enabled, lazy_load_send_redundant) = match filter.room.state.lazy_load_options {
LazyLoadOptions::Enabled {
include_redundant_members,
} => (true, include_redundant_members),
LazyLoadOptions::Disabled => (false, cfg!(feature = "element_hacks")),
include_redundant_members: redundant,
} => (true, redundant),
LazyLoadOptions::Disabled => (false, false),
};
let full_state = body.full_state;
-2
View File
@@ -51,13 +51,11 @@ sha256_media = []
[dependencies]
argon2.workspace = true
arrayvec.workspace = true
axum.workspace = true
bytes.workspace = true
cargo_toml.workspace = true
checked_ops.workspace = true
chrono.workspace = true
clap.workspace = true
conduit-macros.workspace = true
const-str.workspace = true
ctor.workspace = true
-5
View File
@@ -236,8 +236,6 @@ pub struct Config {
pub rocksdb_compaction_ioprio_idle: bool,
#[serde(default = "true_fn")]
pub rocksdb_compaction: bool,
#[serde(default = "default_rocksdb_stats_level")]
pub rocksdb_stats_level: u8,
pub emergency_password: Option<String>,
@@ -720,7 +718,6 @@ impl fmt::Display for Config {
&self.rocksdb_compaction_ioprio_idle.to_string(),
);
line("RocksDB Compaction enabled", &self.rocksdb_compaction.to_string());
line("RocksDB Statistics level", &self.rocksdb_stats_level.to_string());
line("Media integrity checks on startup", &self.media_startup_check.to_string());
line("Media compatibility filesystem links", &self.media_compat_file_link.to_string());
line("Prevent Media Downloads From", {
@@ -1005,8 +1002,6 @@ fn default_rocksdb_compression_level() -> i32 { 32767 }
#[allow(clippy::doc_markdown)]
fn default_rocksdb_bottommost_compression_level() -> i32 { 32767 }
fn default_rocksdb_stats_level() -> u8 { 1 }
// I know, it's a great name
#[must_use]
pub fn default_default_room_version() -> RoomVersionId { RoomVersionId::V10 }
-2
View File
@@ -62,8 +62,6 @@ pub enum Error {
TomlSerError(#[from] toml::ser::Error),
#[error("{0}")]
TomlDeError(#[from] toml::de::Error),
#[error("{0}")]
Clap(#[from] clap::error::Error),
// ruma
#[error("{0}")]
-3
View File
@@ -6,6 +6,3 @@ pub mod rustc;
pub mod version;
pub use conduit_macros::rustc_flags_capture;
pub const MODULE_ROOT: &str = const_str::split!(std::module_path!(), "::")[0];
pub const CRATE_PREFIX: &str = const_str::split!(MODULE_ROOT, '_')[0];
+5 -8
View File
@@ -2,20 +2,15 @@ use tracing::Level;
use tracing_core::{span::Current, Event};
use super::{layer::Value, Layer};
use crate::{info, utils::string::EMPTY};
pub struct Data<'a> {
pub layer: &'a Layer,
pub event: &'a Event<'a>,
pub current: &'a Current,
pub values: &'a [Value],
pub scope: &'a [&'static str],
pub values: Option<&'a mut [Value]>,
}
impl Data<'_> {
#[must_use]
pub fn our_modules(&self) -> bool { self.mod_name().starts_with(info::CRATE_PREFIX) }
#[must_use]
pub fn level(&self) -> Level { *self.event.metadata().level() }
@@ -23,13 +18,15 @@ impl Data<'_> {
pub fn mod_name(&self) -> &str { self.event.metadata().module_path().unwrap_or_default() }
#[must_use]
pub fn span_name(&self) -> &str { self.current.metadata().map_or(EMPTY, |s| s.name()) }
pub fn span_name(&self) -> &str { self.current.metadata().map_or("", |s| s.name()) }
#[must_use]
pub fn message(&self) -> &str {
self.values
.as_ref()
.expect("values are not composed for a filter")
.iter()
.find(|(k, _)| *k == "message")
.map_or(EMPTY, |(_, v)| v.as_str())
.map_or("", |(_, v)| v.as_str())
}
}
+6 -21
View File
@@ -1,25 +1,21 @@
use std::{fmt, sync::Arc};
use arrayvec::ArrayVec;
use tracing::field::{Field, Visit};
use tracing_core::{Event, Subscriber};
use tracing_subscriber::{layer::Context, registry::LookupSpan};
use super::{Capture, Data, State};
pub type Value = (&'static str, String);
pub struct Layer {
state: Arc<State>,
}
struct Visitor {
values: Values,
values: Vec<Value>,
}
type Values = ArrayVec<Value, 32>;
pub type Value = (&'static str, String);
type ScopeNames = ArrayVec<&'static str, 32>;
impl Layer {
#[inline]
pub fn new(state: &Arc<State>) -> Self {
@@ -55,9 +51,8 @@ fn handle<S>(layer: &Layer, capture: &Capture, event: &Event<'_>, ctx: &Context<
where
S: Subscriber + for<'a> LookupSpan<'a>,
{
let names = ScopeNames::new();
let mut visitor = Visitor {
values: Values::new(),
values: Vec::new(),
};
event.record(&mut visitor);
@@ -66,8 +61,7 @@ where
layer,
event,
current: &ctx.current_span(),
values: &visitor.values,
scope: &names,
values: Some(&mut visitor.values),
});
}
@@ -75,21 +69,12 @@ fn filter<S>(layer: &Layer, capture: &Capture, event: &Event<'_>, ctx: &Context<
where
S: Subscriber + for<'a> LookupSpan<'a>,
{
let values = Values::new();
let mut names = ScopeNames::new();
if let Some(scope) = ctx.event_scope(event) {
for span in scope {
names.push(span.name());
}
}
capture.filter.as_ref().map_or(true, |filter| {
filter(Data {
layer,
event,
current: &ctx.current_span(),
values: &values,
scope: &names,
values: None,
})
})
}
+6 -6
View File
@@ -5,7 +5,7 @@ use crate::Result;
pub fn html<S>(out: &mut S, level: &Level, span: &str, msg: &str) -> Result<()>
where
S: Write + ?Sized,
S: Write,
{
let color = color::code_tag(level);
let level = level.as_str().to_uppercase();
@@ -19,7 +19,7 @@ where
pub fn markdown<S>(out: &mut S, level: &Level, span: &str, msg: &str) -> Result<()>
where
S: Write + ?Sized,
S: Write,
{
let level = level.as_str().to_uppercase();
writeln!(out, "`{level:>5}` `{span:^12}` `{msg}`")?;
@@ -29,19 +29,19 @@ where
pub fn markdown_table<S>(out: &mut S, level: &Level, span: &str, msg: &str) -> Result<()>
where
S: Write + ?Sized,
S: Write,
{
let level = level.as_str().to_uppercase();
writeln!(out, "| {level:>5} | {span:^12} | {msg} |")?;
writeln!(out, "| `{level:>5}` | `{span:^12}` | `{msg} |")?;
Ok(())
}
pub fn markdown_table_head<S>(out: &mut S) -> Result<()>
where
S: Write + ?Sized,
S: Write,
{
write!(out, "| level | span | message |\n| ------: | :-----: | :------- |\n")?;
write!(out, "| level | span | message |\n|------:|:----:|:--------|\n")?;
Ok(())
}
-10
View File
@@ -30,16 +30,6 @@ macro_rules! is_format {
};
}
#[inline]
pub fn collect_stream<F>(func: F) -> Result<String>
where
F: FnOnce(&mut dyn std::fmt::Write) -> Result<()>,
{
let mut out = String::new();
func(&mut out)?;
Ok(out)
}
#[inline]
#[must_use]
pub fn camel_to_snake_string(s: &str) -> String {
+1 -9
View File
@@ -2,12 +2,7 @@ use std::{ops::Index, sync::Arc};
use conduit::{Result, Server};
use crate::{
cork::Cork,
maps,
maps::{Maps, MapsKey, MapsVal},
Engine, Map,
};
use crate::{cork::Cork, maps, maps::Maps, Engine, Map};
pub struct Database {
pub db: Arc<Engine>,
@@ -35,9 +30,6 @@ impl Database {
#[inline]
#[must_use]
pub fn cork_and_sync(&self) -> Cork { Cork::new(&self.db, true, true) }
#[inline]
pub fn iter_maps(&self) -> impl Iterator<Item = (&MapsKey, &MapsVal)> + '_ { self.map.iter() }
}
impl Index<&str> for Database {
+1 -17
View File
@@ -1,6 +1,5 @@
use std::{
collections::{BTreeSet, HashMap},
ffi::CStr,
fmt::Write,
path::PathBuf,
sync::{atomic::AtomicU32, Arc, Mutex, RwLock},
@@ -10,8 +9,7 @@ use conduit::{debug, error, info, utils::time::rfc2822_from_seconds, warn, Err,
use rocksdb::{
backup::{BackupEngine, BackupEngineOptions},
perf::get_memory_usage_stats,
AsColumnFamilyRef, BoundColumnFamily, Cache, ColumnFamilyDescriptor, DBCommon, DBWithThreadMode, Env,
MultiThreaded, Options,
BoundColumnFamily, Cache, ColumnFamilyDescriptor, DBCommon, DBWithThreadMode, Env, MultiThreaded, Options,
};
use crate::{
@@ -242,20 +240,6 @@ impl Engine {
},
}
}
/// Query for database property by null-terminated name which is expected to
/// have a result with an integer representation. This is intended for
/// low-overhead programmatic use.
pub(crate) fn property_integer(&self, cf: &impl AsColumnFamilyRef, name: &CStr) -> Result<u64> {
result(self.db.property_int_value_cf(cf, name))
.and_then(|val| val.map_or_else(|| Err!("Property {name:?} not found."), Ok))
}
/// Query for database property by name receiving the result in a string.
pub(crate) fn property(&self, cf: &impl AsColumnFamilyRef, name: &str) -> Result<String> {
result(self.db.property_value_cf(cf, name))
.and_then(|val| val.map_or_else(|| Err!("Property {name:?} not found."), Ok))
}
}
pub(crate) fn repair(db_opts: &Options, path: &PathBuf) -> Result<()> {
+1 -5
View File
@@ -1,4 +1,4 @@
use std::{ffi::CStr, future::Future, mem::size_of, pin::Pin, sync::Arc};
use std::{future::Future, mem::size_of, pin::Pin, sync::Arc};
use conduit::{utils, Result};
use rocksdb::{
@@ -189,10 +189,6 @@ impl Map {
self.watchers.watch(prefix)
}
pub fn property_integer(&self, name: &CStr) -> Result<u64> { self.db.property_integer(&self.cf(), name) }
pub fn property(&self, name: &str) -> Result<String> { self.db.property(&self.cf(), name) }
#[inline]
pub fn name(&self) -> &str { &self.name }
+1 -3
View File
@@ -4,9 +4,7 @@ use conduit::Result;
use crate::{Engine, Map};
pub type Maps = BTreeMap<MapsKey, MapsVal>;
pub(crate) type MapsVal = Arc<Map>;
pub(crate) type MapsKey = String;
pub type Maps = BTreeMap<String, Arc<Map>>;
pub(crate) fn open(db: &Arc<Engine>) -> Result<Maps> { open_list(db, MAPS) }
+3 -18
View File
@@ -2,8 +2,8 @@ use std::{cmp, collections::HashMap};
use conduit::{utils, Config};
use rocksdb::{
statistics::StatsLevel, BlockBasedOptions, Cache, DBCompactionStyle, DBCompressionType, DBRecoveryMode, Env,
LogLevel, Options, UniversalCompactOptions, UniversalCompactionStopStyle,
BlockBasedOptions, Cache, DBCompactionStyle, DBCompressionType, DBRecoveryMode, Env, LogLevel, Options,
UniversalCompactOptions, UniversalCompactionStopStyle,
};
/// Create database-wide options suitable for opening the database. This also
@@ -13,11 +13,6 @@ use rocksdb::{
/// through cf_options().
pub(crate) fn db_options(config: &Config, env: &mut Env, row_cache: &Cache, col_cache: &Cache) -> Options {
const MIN_PARALLELISM: usize = 2;
const DEFAULT_STATS_LEVEL: StatsLevel = if cfg!(debug_assertions) {
StatsLevel::ExceptDetailedTimers
} else {
StatsLevel::DisableAll
};
let mut opts = Options::default();
@@ -73,18 +68,8 @@ pub(crate) fn db_options(config: &Config, env: &mut Env, row_cache: &Cache, col_
set_compression_defaults(&mut opts, config);
// Misc
opts.create_if_missing(true);
opts.set_disable_auto_compactions(!config.rocksdb_compaction);
opts.set_statistics_level(match config.rocksdb_stats_level {
0 => StatsLevel::DisableAll,
1 => DEFAULT_STATS_LEVEL,
2 => StatsLevel::ExceptHistogramOrTimers,
3 => StatsLevel::ExceptTimers,
4 => StatsLevel::ExceptDetailedTimers,
5 => StatsLevel::ExceptTimeForMutex,
6_u8..=u8::MAX => StatsLevel::All,
});
opts.create_if_missing(true);
// Default: https://github.com/facebook/rocksdb/wiki/WAL-Recovery-Modes#ktoleratecorruptedtailrecords
//
+4 -4
View File
@@ -51,9 +51,9 @@ pub struct CommandInput {
}
pub type Completer = fn(&str) -> String;
pub type Handler = fn(Arc<crate::Services>, CommandInput) -> HandlerFuture;
pub type HandlerFuture = Pin<Box<dyn Future<Output = HandlerResult> + Send>>;
pub type HandlerResult = Result<CommandOutput>;
pub type Handler = fn(Arc<crate::Services>, CommandInput) -> HandlerResult;
pub type HandlerResult = Pin<Box<dyn Future<Output = CommandResult> + Send>>;
pub type CommandResult = Result<CommandOutput, Error>;
pub type CommandOutput = Option<RoomMessageEventContent>;
const COMMAND_QUEUE_LIMIT: usize = 512;
@@ -173,7 +173,7 @@ impl Service {
}
}
async fn process_command(&self, command: CommandInput) -> HandlerResult {
async fn process_command(&self, command: CommandInput) -> CommandResult {
let Some(services) = self
.services
.services
+3 -19
View File
@@ -61,22 +61,6 @@ impl Data {
Some(ref presence) => presence.1.content.presence != *presence_state,
};
let status_msg_changed = match last_presence {
None => true,
Some(ref last_presence) => {
let old_msg = last_presence
.1
.content
.status_msg
.clone()
.unwrap_or_default();
let new_msg = status_msg.clone().unwrap_or_default();
new_msg != old_msg
},
};
let now = utils::millis_since_unix_epoch();
let last_last_active_ts = match last_presence {
None => 0,
@@ -88,10 +72,10 @@ impl Data {
Some(last_active_ago) => now.saturating_sub(last_active_ago.into()),
};
// TODO: tighten for state flicker?
if !status_msg_changed && !state_changed && last_active_ts < last_last_active_ts {
// tighten for state flicker?
if !state_changed && last_active_ts <= last_last_active_ts {
debug_warn!(
"presence spam {:?} last_active_ts:{:?} < {:?}",
"presence spam {:?} last_active_ts:{:?} <= {:?}",
user_id,
last_active_ts,
last_last_active_ts
+1 -2
View File
@@ -246,7 +246,6 @@ impl Data {
/// Adds a new device to a user.
pub(super) fn create_device(
&self, user_id: &UserId, device_id: &DeviceId, token: &str, initial_device_display_name: Option<String>,
client_ip: Option<String>,
) -> Result<()> {
// This method should never be called for nonexistent users. We shouldn't assert
// though...
@@ -267,7 +266,7 @@ impl Data {
&serde_json::to_vec(&Device {
device_id: device_id.into(),
display_name: initial_device_display_name,
last_seen_ip: client_ip,
last_seen_ip: None, // TODO
last_seen_ts: Some(MilliSecondsSinceUnixEpoch::now()),
})
.expect("Device::to_string never fails."),
+1 -2
View File
@@ -328,10 +328,9 @@ impl Service {
/// Adds a new device to a user.
pub fn create_device(
&self, user_id: &UserId, device_id: &DeviceId, token: &str, initial_device_display_name: Option<String>,
client_ip: Option<String>,
) -> Result<()> {
self.db
.create_device(user_id, device_id, token, initial_device_display_name, client_ip)
.create_device(user_id, device_id, token, initial_device_display_name)
}
/// Removes a device from a user.