Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 44b3e37faa |
Generated
+2
-10
@@ -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"
|
||||
@@ -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",
|
||||
@@ -2752,9 +2744,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.10.5"
|
||||
version = "1.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f"
|
||||
checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
|
||||
@@ -25,9 +25,6 @@ version = "0.4.6"
|
||||
[workspace.metadata.crane]
|
||||
name = "conduit"
|
||||
|
||||
[workspace.dependencies.arrayvec]
|
||||
version = "0.7.4"
|
||||
|
||||
[workspace.dependencies.const-str]
|
||||
version = "0.5.7"
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
Vendored
+1
@@ -16,6 +16,7 @@ case "$1" in
|
||||
--home "$CONDUWUIT_DATABASE_PATH" \
|
||||
--disabled-login \
|
||||
--shell "/usr/sbin/nologin" \
|
||||
--verbose \
|
||||
conduwuit
|
||||
fi
|
||||
|
||||
|
||||
@@ -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
@@ -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`).
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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`
|
||||
@@ -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
@@ -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
@@ -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": {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 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");
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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}")]
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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
@@ -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 }
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
//
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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."),
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user