Compare commits
96 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c02d77207c | |||
| 5edd391e83 | |||
| 7658387a74 | |||
| be2d1c722b | |||
| 8cf55c702f | |||
| 3b9fba233c | |||
| af81baae44 | |||
| 52d470058a | |||
| df0328f43f | |||
| 473b29d524 | |||
| e1d1dac95e | |||
| a2d25215a3 | |||
| 46423cab4f | |||
| 2e2cf08bb2 | |||
| ee64fb149c | |||
| 0522fe7d92 | |||
| 6dd6e4bfaf | |||
| a1ced0a56f | |||
| 0171262581 | |||
| 972037dcd9 | |||
| 0613140130 | |||
| f43c09b05d | |||
| b035cd7cbe | |||
| b7aa79884d | |||
| 9c6cc44abb | |||
| 9c0d7b1da0 | |||
| ed3a464722 | |||
| 28ac3790c2 | |||
| 9115901c66 | |||
| 652faafe6e | |||
| 8deba94a12 | |||
| bb27f21ac1 | |||
| 339a1fc4e8 | |||
| 75731671d1 | |||
| 20aab1792b | |||
| e9f4cd58a9 | |||
| 6bae136720 | |||
| 39a1e994eb | |||
| c2c36197e9 | |||
| c952db72e7 | |||
| 1ca484f90d | |||
| b1150cded9 | |||
| 2b35f83089 | |||
| 792d4aa6ce | |||
| 6c461025e5 | |||
| 9d1db7d171 | |||
| 2387f7f955 | |||
| 5805394ca5 | |||
| 3301cde8e7 | |||
| b28d216e00 | |||
| f127987c7a | |||
| fa02d7b7e3 | |||
| 96a16190c5 | |||
| d9054f5364 | |||
| db288b169e | |||
| bc50034173 | |||
| 20fe84379c | |||
| 06f01d384f | |||
| bd19fac4c5 | |||
| 703c275266 | |||
| c05f00661b | |||
| 0e46b750a0 | |||
| 3a51e18ce6 | |||
| 7638bbc49c | |||
| 35049c94de | |||
| 7db8478dc0 | |||
| df7a1dee15 | |||
| dc633100da | |||
| 0f1432f448 | |||
| 30b5ad3870 | |||
| cb48e25783 | |||
| 57acc4f655 | |||
| 875d9e8b07 | |||
| de21f7442a | |||
| 7ad7badd60 | |||
| a6edaad6fc | |||
| fbcdb3860a | |||
| 01f4455ceb | |||
| c1712d4d8b | |||
| 3480074f61 | |||
| efbdced535 | |||
| 19d8f0b27e | |||
| 8d5305c769 | |||
| 73b85372d6 | |||
| 5e6b9ec1cf | |||
| e24b7d2f3e | |||
| c61258fa0d | |||
| 65177d237a | |||
| 325f1c0bd2 | |||
| 68f1b4789a | |||
| 624cd2acfa | |||
| fb758f5f4b | |||
| 5d138a7e5e | |||
| 563618cc9e | |||
| 0a28d80cf2 | |||
| 6f9d7b5667 |
+49
-28
@@ -16,6 +16,7 @@ on:
|
||||
- 'docker/**'
|
||||
branches:
|
||||
- main
|
||||
- change-ci-cache
|
||||
tags:
|
||||
- '*'
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
@@ -64,13 +65,7 @@ jobs:
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Install Nix
|
||||
uses: DeterminateSystems/nix-installer-action@main
|
||||
with:
|
||||
diagnostic-endpoint: ""
|
||||
extra-conf: |
|
||||
experimental-features = nix-command flakes
|
||||
accept-flake-config = true
|
||||
- uses: nixbuild/nix-quick-install-action@v28
|
||||
|
||||
- name: Enable Cachix binary cache
|
||||
run: |
|
||||
@@ -78,15 +73,30 @@ jobs:
|
||||
cachix use crane
|
||||
cachix use nix-community
|
||||
|
||||
- name: Configure Magic Nix Cache
|
||||
uses: DeterminateSystems/magic-nix-cache-action@main
|
||||
- name: Restore and cache Nix store
|
||||
uses: nix-community/cache-nix-action@v5.2.1
|
||||
with:
|
||||
diagnostic-endpoint: ""
|
||||
upstream-cache: "https://attic.kennel.juneis.dog/conduwuit"
|
||||
# restore and save a cache using this key
|
||||
primary-key: nix-${{ runner.os }}-${{ hashFiles('**/*.nix', '**/.lock') }}
|
||||
# if there's no cache hit, restore a cache by this prefix
|
||||
restore-prefixes-first-match: nix-${{ runner.os }}-
|
||||
# collect garbage until Nix store size (in bytes) is at most this number
|
||||
# before trying to save a new cache
|
||||
gc-max-store-size-linux: 2073741824
|
||||
# do purge caches
|
||||
purge: true
|
||||
# purge all versions of the cache
|
||||
purge-prefixes: nix-${{ runner.os }}-
|
||||
# created more than this number of seconds ago relative to the start of the `Post Restore` phase
|
||||
purge-last-accessed: 86400
|
||||
# except the version with the `primary-key`, if it exists
|
||||
purge-primary-key: never
|
||||
# always save the cache
|
||||
save-always: true
|
||||
|
||||
- name: Apply Nix binary cache configuration
|
||||
run: |
|
||||
sudo tee -a /etc/nix/nix.conf > /dev/null <<EOF
|
||||
sudo tee -a "${XDG_CONFIG_HOME:-$HOME/.config}/nix/nix.conf" > /dev/null <<EOF
|
||||
extra-substituters = https://attic.kennel.juneis.dog/conduit https://attic.kennel.juneis.dog/conduwuit https://cache.lix.systems https://conduwuit.cachix.org
|
||||
extra-trusted-public-keys = conduit:eEKoUwlQGDdYmAI/Q/0slVlegqh/QmAvQd7HBSm21Wk= conduwuit:BbycGUgTISsltcmH0qNjFR9dbrQNYgdIAcmViSGoVTE= cache.lix.systems:aBnZUw8zA7H35Cz2RyKFVs3H4PlGTLawyY5KRbvJR8o= conduwuit.cachix.org-1:MFRm6jcnfTf0jSAbmvLfhO3KBMt4px+1xaereWXp8Xg=
|
||||
EOF
|
||||
@@ -94,7 +104,7 @@ jobs:
|
||||
- name: Use alternative Nix binary caches if specified
|
||||
if: ${{ (env.ATTIC_ENDPOINT != '') && (env.ATTIC_PUBLIC_KEY != '') }}
|
||||
run: |
|
||||
sudo tee -a /etc/nix/nix.conf > /dev/null <<EOF
|
||||
sudo tee -a "${XDG_CONFIG_HOME:-$HOME/.config}/nix/nix.conf" > /dev/null <<EOF
|
||||
extra-substituters = ${{ env.ATTIC_ENDPOINT }}
|
||||
extra-trusted-public-keys = ${{ env.ATTIC_PUBLIC_KEY }}
|
||||
EOF
|
||||
@@ -109,6 +119,8 @@ jobs:
|
||||
- name: Cache CI dependencies
|
||||
run: |
|
||||
bin/nix-build-and-cache ci
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
- name: Run CI tests
|
||||
run: |
|
||||
@@ -178,29 +190,38 @@ jobs:
|
||||
- name: Sync repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Nix
|
||||
uses: DeterminateSystems/nix-installer-action@main
|
||||
with:
|
||||
diagnostic-endpoint: ""
|
||||
extra-conf: |
|
||||
experimental-features = nix-command flakes
|
||||
accept-flake-config = true
|
||||
- uses: nixbuild/nix-quick-install-action@v28
|
||||
|
||||
- name: Install and enable Cachix binary cache
|
||||
- name: Enable Cachix binary cache
|
||||
run: |
|
||||
nix profile install nixpkgs#cachix
|
||||
cachix use crane
|
||||
cachix use nix-community
|
||||
|
||||
- name: Configure Magic Nix Cache
|
||||
uses: DeterminateSystems/magic-nix-cache-action@main
|
||||
- name: Restore and cache Nix store
|
||||
uses: nix-community/cache-nix-action@v5.2.1
|
||||
with:
|
||||
diagnostic-endpoint: ""
|
||||
upstream-cache: "https://attic.kennel.juneis.dog/conduwuit"
|
||||
# restore and save a cache using this key
|
||||
primary-key: nix-${{ runner.os }}-${{ matrix.target }}-${{ hashFiles('**/*.nix', '**/.lock') }}
|
||||
# if there's no cache hit, restore a cache by this prefix
|
||||
restore-prefixes-first-match: nix-${{ runner.os }}-
|
||||
# collect garbage until Nix store size (in bytes) is at most this number
|
||||
# before trying to save a new cache
|
||||
gc-max-store-size-linux: 2073741824
|
||||
# do purge caches
|
||||
purge: true
|
||||
# purge all versions of the cache
|
||||
purge-prefixes: nix-${{ runner.os }}-
|
||||
# created more than this number of seconds ago relative to the start of the `Post Restore` phase
|
||||
purge-last-accessed: 86400
|
||||
# except the version with the `primary-key`, if it exists
|
||||
purge-primary-key: never
|
||||
# always save the cache
|
||||
save-always: true
|
||||
|
||||
- name: Apply Nix binary cache configuration
|
||||
run: |
|
||||
sudo tee -a /etc/nix/nix.conf > /dev/null <<EOF
|
||||
sudo tee -a "${XDG_CONFIG_HOME:-$HOME/.config}/nix/nix.conf" > /dev/null <<EOF
|
||||
extra-substituters = https://attic.kennel.juneis.dog/conduit https://attic.kennel.juneis.dog/conduwuit https://cache.lix.systems https://conduwuit.cachix.org
|
||||
extra-trusted-public-keys = conduit:eEKoUwlQGDdYmAI/Q/0slVlegqh/QmAvQd7HBSm21Wk= conduwuit:BbycGUgTISsltcmH0qNjFR9dbrQNYgdIAcmViSGoVTE= cache.lix.systems:aBnZUw8zA7H35Cz2RyKFVs3H4PlGTLawyY5KRbvJR8o= conduwuit.cachix.org-1:MFRm6jcnfTf0jSAbmvLfhO3KBMt4px+1xaereWXp8Xg=
|
||||
EOF
|
||||
@@ -208,7 +229,7 @@ jobs:
|
||||
- name: Use alternative Nix binary caches if specified
|
||||
if: ${{ (env.ATTIC_ENDPOINT != '') && (env.ATTIC_PUBLIC_KEY != '') }}
|
||||
run: |
|
||||
sudo tee -a /etc/nix/nix.conf > /dev/null <<EOF
|
||||
sudo tee -a "${XDG_CONFIG_HOME:-$HOME/.config}/nix/nix.conf" > /dev/null <<EOF
|
||||
extra-substituters = ${{ env.ATTIC_ENDPOINT }}
|
||||
extra-trusted-public-keys = ${{ env.ATTIC_PUBLIC_KEY }}
|
||||
EOF
|
||||
@@ -267,7 +288,7 @@ jobs:
|
||||
name: Docker publish
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
if: (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main' || (github.event.pull_request.draft != true)) && (vars.DOCKER_USERNAME != '') && (vars.GITLAB_USERNAME != '') && github.event.pull_request.user.login != 'renovate'
|
||||
if: (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main' || (github.event.pull_request.draft != true)) && (vars.DOCKER_USERNAME != '') && (vars.GITLAB_USERNAME != '') && github.event.pull_request.user.login != 'renovate[bot]'
|
||||
env:
|
||||
DOCKER_ARM64: docker.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-arm64v8
|
||||
DOCKER_AMD64: docker.io/${{ github.repository }}:${{ (github.head_ref != '' && format('merge-{0}-{1}', github.event.number, github.event.pull_request.user.login)) || github.ref_name }}-${{ github.sha }}-amd64
|
||||
|
||||
@@ -51,13 +51,7 @@ jobs:
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: actions/configure-pages@v5
|
||||
|
||||
- name: Install Nix
|
||||
uses: DeterminateSystems/nix-installer-action@main
|
||||
with:
|
||||
diagnostic-endpoint: ""
|
||||
extra-conf: |
|
||||
experimental-features = nix-command flakes
|
||||
accept-flake-config = true
|
||||
- uses: nixbuild/nix-quick-install-action@v28
|
||||
|
||||
- name: Enable Cachix binary cache
|
||||
run: |
|
||||
@@ -65,23 +59,38 @@ jobs:
|
||||
cachix use crane
|
||||
cachix use nix-community
|
||||
|
||||
- name: Configure Magic Nix Cache
|
||||
uses: DeterminateSystems/magic-nix-cache-action@main
|
||||
- name: Restore and cache Nix store
|
||||
uses: nix-community/cache-nix-action@v5.2.1
|
||||
with:
|
||||
diagnostic-endpoint: ""
|
||||
upstream-cache: "https://attic.kennel.juneis.dog/conduwuit"
|
||||
# restore and save a cache using this key
|
||||
primary-key: nix-${{ runner.os }}-${{ hashFiles('**/*.nix', '**/.lock') }}
|
||||
# if there's no cache hit, restore a cache by this prefix
|
||||
restore-prefixes-first-match: nix-${{ runner.os }}-
|
||||
# collect garbage until Nix store size (in bytes) is at most this number
|
||||
# before trying to save a new cache
|
||||
gc-max-store-size-linux: 2073741824
|
||||
# do purge caches
|
||||
purge: true
|
||||
# purge all versions of the cache
|
||||
purge-prefixes: nix-${{ runner.os }}-
|
||||
# created more than this number of seconds ago relative to the start of the `Post Restore` phase
|
||||
purge-last-accessed: 86400
|
||||
# except the version with the `primary-key`, if it exists
|
||||
purge-primary-key: never
|
||||
# always save the cache
|
||||
save-always: true
|
||||
|
||||
- name: Apply Nix binary cache configuration
|
||||
run: |
|
||||
sudo tee -a /etc/nix/nix.conf > /dev/null <<EOF
|
||||
sudo tee -a "${XDG_CONFIG_HOME:-$HOME/.config}/nix/nix.conf" > /dev/null <<EOF
|
||||
extra-substituters = https://attic.kennel.juneis.dog/conduit https://attic.kennel.juneis.dog/conduwuit https://cache.lix.systems https://conduwuit.cachix.org
|
||||
extra-trusted-public-keys = conduit:eEKoUwlQGDdYmAI/Q/0slVlegqh/QmAvQd7HBSm21Wkconduwuit:BbycGUgTISsltcmH0qNjFR9dbrQNYgdIAcmViSGoVTEcache.lix.systems:aBnZUw8zA7H35Cz2RyKFVs3H4PlGTLawyY5KRbvJR8o= conduwuit.cachix.org-1:MFRm6jcnfTf0jSAbmvLfhO3KBMt4px+1xaereWXp8Xg=
|
||||
extra-trusted-public-keys = conduit:eEKoUwlQGDdYmAI/Q/0slVlegqh/QmAvQd7HBSm21Wk= conduwuit:BbycGUgTISsltcmH0qNjFR9dbrQNYgdIAcmViSGoVTE= cache.lix.systems:aBnZUw8zA7H35Cz2RyKFVs3H4PlGTLawyY5KRbvJR8o= conduwuit.cachix.org-1:MFRm6jcnfTf0jSAbmvLfhO3KBMt4px+1xaereWXp8Xg=
|
||||
EOF
|
||||
|
||||
- name: Use alternative Nix binary caches if specified
|
||||
if: ${{ (env.ATTIC_ENDPOINT != '') && (env.ATTIC_PUBLIC_KEY != '') }}
|
||||
run: |
|
||||
sudo tee -a /etc/nix/nix.conf > /dev/null <<EOF
|
||||
sudo tee -a "${XDG_CONFIG_HOME:-$HOME/.config}/nix/nix.conf" > /dev/null <<EOF
|
||||
extra-substituters = ${{ env.ATTIC_ENDPOINT }}
|
||||
extra-trusted-public-keys = ${{ env.ATTIC_PUBLIC_KEY }}
|
||||
EOF
|
||||
|
||||
+2
-2
@@ -58,7 +58,7 @@ before_script:
|
||||
|
||||
ci:
|
||||
stage: ci
|
||||
image: nixos/nix:2.23.0
|
||||
image: nixos/nix:2.23.1
|
||||
script:
|
||||
# Cache CI dependencies
|
||||
- ./bin/nix-build-and-cache ci
|
||||
@@ -83,7 +83,7 @@ ci:
|
||||
|
||||
artifacts:
|
||||
stage: artifacts
|
||||
image: nixos/nix:2.23.0
|
||||
image: nixos/nix:2.23.1
|
||||
script:
|
||||
- ./bin/nix-build-and-cache just .#static-x86_64-unknown-linux-musl
|
||||
- cp result/bin/conduit x86_64-unknown-linux-musl
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
# Contributing guide
|
||||
|
||||
This page is for about contributing to conduwuit. The [development](docs/development.md) page may be of interest for you as well.
|
||||
This page is for about contributing to conduwuit. The [development](development.md) page may be of interest for you as well.
|
||||
|
||||
If you would like to work on an [issue][issues] that is not assigned, preferably ask in the Matrix room first at [#conduwuit:puppygock.gay][conduwuit-matrix], and comment on it.
|
||||
|
||||
|
||||
Generated
+104
-183
@@ -17,18 +17,6 @@ version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.3"
|
||||
@@ -130,7 +118,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.67",
|
||||
"syn 2.0.68",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -141,7 +129,7 @@ checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.67",
|
||||
"syn 2.0.68",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -206,7 +194,7 @@ dependencies = [
|
||||
"http 1.1.0",
|
||||
"http-body 1.0.0",
|
||||
"http-body-util",
|
||||
"hyper 1.3.1",
|
||||
"hyper 1.4.0",
|
||||
"hyper-util",
|
||||
"itoa",
|
||||
"matchit",
|
||||
@@ -310,7 +298,7 @@ dependencies = [
|
||||
"http 1.1.0",
|
||||
"http-body 1.0.0",
|
||||
"http-body-util",
|
||||
"hyper 1.3.1",
|
||||
"hyper 1.4.0",
|
||||
"hyper-util",
|
||||
"pin-project-lite",
|
||||
"rustls 0.21.12",
|
||||
@@ -378,7 +366,7 @@ version = "0.69.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"bitflags 2.6.0",
|
||||
"cexpr",
|
||||
"clang-sys",
|
||||
"itertools 0.12.1",
|
||||
@@ -389,7 +377,7 @@ dependencies = [
|
||||
"regex",
|
||||
"rustc-hash",
|
||||
"shlex",
|
||||
"syn 2.0.67",
|
||||
"syn 2.0.68",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -400,9 +388,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.5.0"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
|
||||
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||
|
||||
[[package]]
|
||||
name = "blake2"
|
||||
@@ -486,9 +474,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.99"
|
||||
version = "1.0.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695"
|
||||
checksum = "74b6a57f98764a267ff415d50a25e6e166f3831a5071af4995296ea97d210490"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
@@ -538,9 +526,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.7"
|
||||
version = "4.5.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f"
|
||||
checksum = "84b3edb18336f4df585bc9aa31dd99c036dfa5dc5e9a2939a722a188f3a8970d"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@@ -548,9 +536,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.7"
|
||||
version = "4.5.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f"
|
||||
checksum = "c1c09dd5ada6c6c78075d6fd0da3f90d8080651e2d6cc8eb2f1aaa4034ced708"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
@@ -558,14 +546,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.5"
|
||||
version = "4.5.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6"
|
||||
checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085"
|
||||
dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.67",
|
||||
"syn 2.0.68",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -622,7 +610,6 @@ dependencies = [
|
||||
"loole",
|
||||
"regex",
|
||||
"ruma",
|
||||
"rust-rocksdb-uwu",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_yaml",
|
||||
@@ -646,7 +633,7 @@ dependencies = [
|
||||
"futures-util",
|
||||
"hmac",
|
||||
"http 1.1.0",
|
||||
"hyper 1.3.1",
|
||||
"hyper 1.4.0",
|
||||
"image",
|
||||
"ipaddress",
|
||||
"jsonwebtoken",
|
||||
@@ -671,6 +658,7 @@ dependencies = [
|
||||
"argon2",
|
||||
"axum 0.7.5",
|
||||
"bytes",
|
||||
"chrono",
|
||||
"either",
|
||||
"figment",
|
||||
"hardened_malloc-rs",
|
||||
@@ -682,20 +670,16 @@ dependencies = [
|
||||
"libloading",
|
||||
"log",
|
||||
"nix",
|
||||
"parking_lot",
|
||||
"rand",
|
||||
"regex",
|
||||
"reqwest",
|
||||
"ring",
|
||||
"ruma",
|
||||
"rusqlite",
|
||||
"rust-rocksdb-uwu",
|
||||
"sanitize-filename",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_regex",
|
||||
"thiserror",
|
||||
"thread_local",
|
||||
"tikv-jemalloc-ctl",
|
||||
"tikv-jemalloc-sys",
|
||||
"tikv-jemallocator",
|
||||
@@ -710,15 +694,10 @@ dependencies = [
|
||||
name = "conduit_database"
|
||||
version = "0.4.3"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"conduit_core",
|
||||
"log",
|
||||
"lru-cache",
|
||||
"parking_lot",
|
||||
"ruma",
|
||||
"rusqlite",
|
||||
"rust-rocksdb-uwu",
|
||||
"thread_local",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
@@ -740,7 +719,7 @@ dependencies = [
|
||||
"conduit_service",
|
||||
"http 1.1.0",
|
||||
"http-body-util",
|
||||
"hyper 1.3.1",
|
||||
"hyper 1.4.0",
|
||||
"hyper-util",
|
||||
"log",
|
||||
"regex",
|
||||
@@ -780,7 +759,7 @@ dependencies = [
|
||||
"regex",
|
||||
"reqwest",
|
||||
"ruma",
|
||||
"ruma-identifiers-validation",
|
||||
"ruma-identifiers-validation 0.9.5 (git+https://github.com/girlbossceo/ruwuma?rev=fd686e77950680462377c9105dfb4136dd49c7a0)",
|
||||
"rustyline-async",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -973,7 +952,7 @@ version = "0.27.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"bitflags 2.6.0",
|
||||
"crossterm_winapi",
|
||||
"futures-core",
|
||||
"libc",
|
||||
@@ -1027,7 +1006,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.67",
|
||||
"syn 2.0.68",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1115,9 +1094,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.12.0"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b"
|
||||
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
@@ -1131,7 +1110,7 @@ dependencies = [
|
||||
"heck 0.4.1",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.67",
|
||||
"syn 2.0.68",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1140,18 +1119,6 @@ version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
|
||||
[[package]]
|
||||
name = "fallible-iterator"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
|
||||
|
||||
[[package]]
|
||||
name = "fallible-streaming-iterator"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
|
||||
|
||||
[[package]]
|
||||
name = "fdeflate"
|
||||
version = "0.3.4"
|
||||
@@ -1279,7 +1246,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.67",
|
||||
"syn 2.0.68",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1411,18 +1378,6 @@ name = "hashbrown"
|
||||
version = "0.14.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashlink"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af"
|
||||
dependencies = [
|
||||
"hashbrown 0.14.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hdrhistogram"
|
||||
@@ -1572,7 +1527,7 @@ dependencies = [
|
||||
"markup5ever",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.67",
|
||||
"syn 2.0.68",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1675,9 +1630,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "1.3.1"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d"
|
||||
checksum = "c4fe55fb7a772d59a5ff1dfbff4fe0258d19b89fec4b233e75d35d5d2316badc"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
@@ -1702,7 +1657,7 @@ checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"http 1.1.0",
|
||||
"hyper 1.3.1",
|
||||
"hyper 1.4.0",
|
||||
"hyper-util",
|
||||
"rustls 0.23.10",
|
||||
"rustls-native-certs",
|
||||
@@ -1727,16 +1682,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hyper-util"
|
||||
version = "0.1.5"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b875924a60b96e5d7b9ae7b066540b1dd1cbd90d1828f54c92e02a283351c56"
|
||||
checksum = "3ab92f4f49ee4fb4f997c784b7a2e0fa70050211e0b6a287f898c3c9785ca956"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
"http 1.1.0",
|
||||
"http-body 1.0.0",
|
||||
"hyper 1.3.1",
|
||||
"hyper 1.4.0",
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
"tokio",
|
||||
@@ -1972,7 +1927,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"syn 2.0.67",
|
||||
"syn 2.0.68",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1995,24 +1950,14 @@ checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.8.3"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19"
|
||||
checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-targets 0.52.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libsqlite3-sys"
|
||||
version = "0.28.0"
|
||||
source = "git+https://github.com/rusqlite/rusqlite?rev=e00b626e2b1c67347d789fb7f600281705c89381#e00b626e2b1c67347d789fb7f600281705c89381"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libz-sys"
|
||||
version = "1.1.18"
|
||||
@@ -2042,9 +1987,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.21"
|
||||
version = "0.4.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
|
||||
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||
|
||||
[[package]]
|
||||
name = "loole"
|
||||
@@ -2191,7 +2136,7 @@ version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"bitflags 2.6.0",
|
||||
"cfg-if",
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
@@ -2239,9 +2184,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint"
|
||||
version = "0.4.5"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7"
|
||||
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
|
||||
dependencies = [
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
@@ -2314,9 +2259,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.36.0"
|
||||
version = "0.36.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "576dfe1fc8f9df304abb159d767a29d0476f7750fbf8aa7ad07816004a207434"
|
||||
checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@@ -2388,7 +2333,7 @@ dependencies = [
|
||||
"glob",
|
||||
"once_cell",
|
||||
"opentelemetry",
|
||||
"ordered-float 4.2.0",
|
||||
"ordered-float 4.2.1",
|
||||
"percent-encoding",
|
||||
"rand",
|
||||
"thiserror",
|
||||
@@ -2407,9 +2352,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ordered-float"
|
||||
version = "4.2.0"
|
||||
version = "4.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a76df7075c7d4d01fdcb46c912dd17fba5b60c78ea480b475f2b6ab6f666584e"
|
||||
checksum = "19ff2cf528c6c03d9ed653d6c4ce1dc0582dc4af309790ad92f07c1cd551b0be"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
@@ -2491,7 +2436,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"proc-macro2-diagnostics",
|
||||
"quote",
|
||||
"syn 2.0.67",
|
||||
"syn 2.0.68",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2584,7 +2529,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.67",
|
||||
"syn 2.0.68",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2672,7 +2617,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.67",
|
||||
"syn 2.0.68",
|
||||
"version_check",
|
||||
"yansi",
|
||||
]
|
||||
@@ -2697,7 +2642,7 @@ dependencies = [
|
||||
"itertools 0.12.1",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.67",
|
||||
"syn 2.0.68",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2711,11 +2656,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pulldown-cmark"
|
||||
version = "0.10.3"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76979bea66e7875e7509c4ec5300112b316af87fa7a252ca91c448b32dfe3993"
|
||||
checksum = "8746739f11d39ce5ad5c2520a9b75285310dbfe78c541ccf832d38615765aec0"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"bitflags 2.6.0",
|
||||
"memchr",
|
||||
"pulldown-cmark-escape",
|
||||
"unicase",
|
||||
@@ -2723,9 +2668,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pulldown-cmark-escape"
|
||||
version = "0.10.1"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd348ff538bc9caeda7ee8cad2d1d48236a1f443c1fa3913c6a02fe0043b1dd3"
|
||||
checksum = "007d8adb5ddab6f8e3f491ac63566a7d5002cc7ed73901f72057943fa71ae1ae"
|
||||
|
||||
[[package]]
|
||||
name = "quick-error"
|
||||
@@ -2825,7 +2770,7 @@ version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"bitflags 2.6.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2889,7 +2834,7 @@ dependencies = [
|
||||
"http 1.1.0",
|
||||
"http-body 1.0.0",
|
||||
"http-body-util",
|
||||
"hyper 1.3.1",
|
||||
"hyper 1.4.0",
|
||||
"hyper-rustls",
|
||||
"hyper-util",
|
||||
"ipnet",
|
||||
@@ -2949,7 +2894,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ruma"
|
||||
version = "0.10.1"
|
||||
source = "git+https://github.com/girlbossceo/ruwuma?rev=fd686e77950680462377c9105dfb4136dd49c7a0#fd686e77950680462377c9105dfb4136dd49c7a0"
|
||||
source = "git+https://github.com/girlbossceo/ruwuma?rev=9a5bfad8494b7a4e6c40421c3d0675db4b434ec0#9a5bfad8494b7a4e6c40421c3d0675db4b434ec0"
|
||||
dependencies = [
|
||||
"assign",
|
||||
"js_int",
|
||||
@@ -2969,7 +2914,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ruma-appservice-api"
|
||||
version = "0.10.0"
|
||||
source = "git+https://github.com/girlbossceo/ruwuma?rev=fd686e77950680462377c9105dfb4136dd49c7a0#fd686e77950680462377c9105dfb4136dd49c7a0"
|
||||
source = "git+https://github.com/girlbossceo/ruwuma?rev=9a5bfad8494b7a4e6c40421c3d0675db4b434ec0#9a5bfad8494b7a4e6c40421c3d0675db4b434ec0"
|
||||
dependencies = [
|
||||
"js_int",
|
||||
"ruma-common",
|
||||
@@ -2981,7 +2926,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ruma-client-api"
|
||||
version = "0.18.0"
|
||||
source = "git+https://github.com/girlbossceo/ruwuma?rev=fd686e77950680462377c9105dfb4136dd49c7a0#fd686e77950680462377c9105dfb4136dd49c7a0"
|
||||
source = "git+https://github.com/girlbossceo/ruwuma?rev=9a5bfad8494b7a4e6c40421c3d0675db4b434ec0#9a5bfad8494b7a4e6c40421c3d0675db4b434ec0"
|
||||
dependencies = [
|
||||
"as_variant",
|
||||
"assign",
|
||||
@@ -3004,7 +2949,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ruma-common"
|
||||
version = "0.13.0"
|
||||
source = "git+https://github.com/girlbossceo/ruwuma?rev=fd686e77950680462377c9105dfb4136dd49c7a0#fd686e77950680462377c9105dfb4136dd49c7a0"
|
||||
source = "git+https://github.com/girlbossceo/ruwuma?rev=9a5bfad8494b7a4e6c40421c3d0675db4b434ec0#9a5bfad8494b7a4e6c40421c3d0675db4b434ec0"
|
||||
dependencies = [
|
||||
"as_variant",
|
||||
"base64 0.22.1",
|
||||
@@ -3017,7 +2962,7 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
"rand",
|
||||
"regex",
|
||||
"ruma-identifiers-validation",
|
||||
"ruma-identifiers-validation 0.9.5 (git+https://github.com/girlbossceo/ruwuma?rev=9a5bfad8494b7a4e6c40421c3d0675db4b434ec0)",
|
||||
"ruma-macros",
|
||||
"serde",
|
||||
"serde_html_form",
|
||||
@@ -3034,7 +2979,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ruma-events"
|
||||
version = "0.28.1"
|
||||
source = "git+https://github.com/girlbossceo/ruwuma?rev=fd686e77950680462377c9105dfb4136dd49c7a0#fd686e77950680462377c9105dfb4136dd49c7a0"
|
||||
source = "git+https://github.com/girlbossceo/ruwuma?rev=9a5bfad8494b7a4e6c40421c3d0675db4b434ec0#9a5bfad8494b7a4e6c40421c3d0675db4b434ec0"
|
||||
dependencies = [
|
||||
"as_variant",
|
||||
"indexmap 2.2.6",
|
||||
@@ -3044,7 +2989,7 @@ dependencies = [
|
||||
"pulldown-cmark",
|
||||
"regex",
|
||||
"ruma-common",
|
||||
"ruma-identifiers-validation",
|
||||
"ruma-identifiers-validation 0.9.5 (git+https://github.com/girlbossceo/ruwuma?rev=9a5bfad8494b7a4e6c40421c3d0675db4b434ec0)",
|
||||
"ruma-macros",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -3057,7 +3002,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ruma-federation-api"
|
||||
version = "0.9.0"
|
||||
source = "git+https://github.com/girlbossceo/ruwuma?rev=fd686e77950680462377c9105dfb4136dd49c7a0#fd686e77950680462377c9105dfb4136dd49c7a0"
|
||||
source = "git+https://github.com/girlbossceo/ruwuma?rev=9a5bfad8494b7a4e6c40421c3d0675db4b434ec0#9a5bfad8494b7a4e6c40421c3d0675db4b434ec0"
|
||||
dependencies = [
|
||||
"js_int",
|
||||
"ruma-common",
|
||||
@@ -3066,6 +3011,15 @@ dependencies = [
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruma-identifiers-validation"
|
||||
version = "0.9.5"
|
||||
source = "git+https://github.com/girlbossceo/ruwuma?rev=9a5bfad8494b7a4e6c40421c3d0675db4b434ec0#9a5bfad8494b7a4e6c40421c3d0675db4b434ec0"
|
||||
dependencies = [
|
||||
"js_int",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruma-identifiers-validation"
|
||||
version = "0.9.5"
|
||||
@@ -3078,7 +3032,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ruma-identity-service-api"
|
||||
version = "0.9.0"
|
||||
source = "git+https://github.com/girlbossceo/ruwuma?rev=fd686e77950680462377c9105dfb4136dd49c7a0#fd686e77950680462377c9105dfb4136dd49c7a0"
|
||||
source = "git+https://github.com/girlbossceo/ruwuma?rev=9a5bfad8494b7a4e6c40421c3d0675db4b434ec0#9a5bfad8494b7a4e6c40421c3d0675db4b434ec0"
|
||||
dependencies = [
|
||||
"js_int",
|
||||
"ruma-common",
|
||||
@@ -3088,22 +3042,22 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ruma-macros"
|
||||
version = "0.13.0"
|
||||
source = "git+https://github.com/girlbossceo/ruwuma?rev=fd686e77950680462377c9105dfb4136dd49c7a0#fd686e77950680462377c9105dfb4136dd49c7a0"
|
||||
source = "git+https://github.com/girlbossceo/ruwuma?rev=9a5bfad8494b7a4e6c40421c3d0675db4b434ec0#9a5bfad8494b7a4e6c40421c3d0675db4b434ec0"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"ruma-identifiers-validation",
|
||||
"ruma-identifiers-validation 0.9.5 (git+https://github.com/girlbossceo/ruwuma?rev=9a5bfad8494b7a4e6c40421c3d0675db4b434ec0)",
|
||||
"serde",
|
||||
"syn 2.0.67",
|
||||
"syn 2.0.68",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruma-push-gateway-api"
|
||||
version = "0.9.0"
|
||||
source = "git+https://github.com/girlbossceo/ruwuma?rev=fd686e77950680462377c9105dfb4136dd49c7a0#fd686e77950680462377c9105dfb4136dd49c7a0"
|
||||
source = "git+https://github.com/girlbossceo/ruwuma?rev=9a5bfad8494b7a4e6c40421c3d0675db4b434ec0#9a5bfad8494b7a4e6c40421c3d0675db4b434ec0"
|
||||
dependencies = [
|
||||
"js_int",
|
||||
"ruma-common",
|
||||
@@ -3115,7 +3069,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ruma-signatures"
|
||||
version = "0.15.0"
|
||||
source = "git+https://github.com/girlbossceo/ruwuma?rev=fd686e77950680462377c9105dfb4136dd49c7a0#fd686e77950680462377c9105dfb4136dd49c7a0"
|
||||
source = "git+https://github.com/girlbossceo/ruwuma?rev=9a5bfad8494b7a4e6c40421c3d0675db4b434ec0#9a5bfad8494b7a4e6c40421c3d0675db4b434ec0"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"ed25519-dalek",
|
||||
@@ -3131,7 +3085,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ruma-state-res"
|
||||
version = "0.11.0"
|
||||
source = "git+https://github.com/girlbossceo/ruwuma?rev=fd686e77950680462377c9105dfb4136dd49c7a0#fd686e77950680462377c9105dfb4136dd49c7a0"
|
||||
source = "git+https://github.com/girlbossceo/ruwuma?rev=9a5bfad8494b7a4e6c40421c3d0675db4b434ec0#9a5bfad8494b7a4e6c40421c3d0675db4b434ec0"
|
||||
dependencies = [
|
||||
"itertools 0.12.1",
|
||||
"js_int",
|
||||
@@ -3143,23 +3097,10 @@ dependencies = [
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rusqlite"
|
||||
version = "0.31.0"
|
||||
source = "git+https://github.com/rusqlite/rusqlite?rev=e00b626e2b1c67347d789fb7f600281705c89381#e00b626e2b1c67347d789fb7f600281705c89381"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"fallible-iterator",
|
||||
"fallible-streaming-iterator",
|
||||
"hashlink",
|
||||
"libsqlite3-sys",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-librocksdb-sys"
|
||||
version = "0.22.0+9.2.1"
|
||||
source = "git+https://github.com/zaidoon1/rust-rocksdb?rev=e9e1cb5ba92a44ea225fe8d13b31aa23621b9035#e9e1cb5ba92a44ea225fe8d13b31aa23621b9035"
|
||||
version = "0.23.0+9.3.1"
|
||||
source = "git+https://github.com/zaidoon1/rust-rocksdb?rev=b4887edfb84771336930855727390edec07d63fa#b4887edfb84771336930855727390edec07d63fa"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
"bzip2-sys",
|
||||
@@ -3175,8 +3116,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rust-rocksdb"
|
||||
version = "0.26.0"
|
||||
source = "git+https://github.com/zaidoon1/rust-rocksdb?rev=e9e1cb5ba92a44ea225fe8d13b31aa23621b9035#e9e1cb5ba92a44ea225fe8d13b31aa23621b9035"
|
||||
version = "0.27.0"
|
||||
source = "git+https://github.com/zaidoon1/rust-rocksdb?rev=b4887edfb84771336930855727390edec07d63fa#b4887edfb84771336930855727390edec07d63fa"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rust-librocksdb-sys",
|
||||
@@ -3376,7 +3317,7 @@ version = "2.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"bitflags 2.6.0",
|
||||
"core-foundation",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
@@ -3551,7 +3492,7 @@ checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.67",
|
||||
"syn 2.0.68",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3569,9 +3510,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.117"
|
||||
version = "1.0.120"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
|
||||
checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
@@ -3827,9 +3768,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.6.0"
|
||||
version = "2.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d0208408ba0c3df17ed26eb06992cb1a1268d41b2c0e12e65203fbe3972cee5"
|
||||
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
@@ -3844,9 +3785,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.67"
|
||||
version = "2.0.68"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff8655ed1d86f3af4ee3fd3263786bc14245ad17c4c7e85ba7187fb3ae028c90"
|
||||
checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -3919,7 +3860,7 @@ checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.67",
|
||||
"syn 2.0.68",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4018,9 +3959,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.6.0"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
|
||||
checksum = "c55115c6fbe2d2bef26eb09ad74bde02d8255476fc0c7b515ef09fbb35742d82"
|
||||
dependencies = [
|
||||
"tinyvec_macros",
|
||||
]
|
||||
@@ -4068,7 +4009,7 @@ checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.67",
|
||||
"syn 2.0.68",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4238,7 +4179,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5"
|
||||
dependencies = [
|
||||
"async-compression",
|
||||
"bitflags 2.5.0",
|
||||
"bitflags 2.6.0",
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
@@ -4284,7 +4225,7 @@ source = "git+https://github.com/girlbossceo/tracing?rev=b348dca742af641c47bc390
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.67",
|
||||
"syn 2.0.68",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4493,9 +4434,9 @@ checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.8.0"
|
||||
version = "1.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0"
|
||||
checksum = "5de17fd2f7da591098415cff336e12965a28061ddace43b59cb3c430179c9439"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"serde",
|
||||
@@ -4555,7 +4496,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.67",
|
||||
"syn 2.0.68",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@@ -4589,7 +4530,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.67",
|
||||
"syn 2.0.68",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
@@ -4904,26 +4845,6 @@ version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.7.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.67",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.8.1"
|
||||
|
||||
+4
-18
@@ -117,7 +117,7 @@ version = "1.0.203"
|
||||
features = ["rc"]
|
||||
|
||||
[workspace.dependencies.serde_json]
|
||||
version = "1.0.117"
|
||||
version = "1.0.120"
|
||||
features = ["raw_value"]
|
||||
|
||||
# Used for appservice registration files
|
||||
@@ -212,7 +212,7 @@ features = ["alloc"]
|
||||
default-features = false
|
||||
|
||||
[workspace.dependencies.hyper]
|
||||
version = "1.3.1"
|
||||
version = "1.4.0"
|
||||
features = [
|
||||
"server",
|
||||
"http1",
|
||||
@@ -220,7 +220,7 @@ features = [
|
||||
]
|
||||
|
||||
[workspace.dependencies.hyper-util]
|
||||
version = "0.1.5"
|
||||
version = "0.1.6"
|
||||
features = [
|
||||
"client",
|
||||
"server-auto",
|
||||
@@ -274,7 +274,7 @@ version = "0.1.2"
|
||||
[workspace.dependencies.ruma]
|
||||
git = "https://github.com/girlbossceo/ruwuma"
|
||||
#branch = "conduwuit-changes"
|
||||
rev = "fd686e77950680462377c9105dfb4136dd49c7a0"
|
||||
rev = "9a5bfad8494b7a4e6c40421c3d0675db4b434ec0"
|
||||
features = [
|
||||
"compat",
|
||||
"rand",
|
||||
@@ -378,20 +378,6 @@ version = "0.5.4"
|
||||
default-features = false
|
||||
features = ["use_std"]
|
||||
|
||||
[workspace.dependencies.rusqlite]
|
||||
git = "https://github.com/rusqlite/rusqlite"
|
||||
#branch = "master"
|
||||
rev = "e00b626e2b1c67347d789fb7f600281705c89381"
|
||||
features = ["bundled"]
|
||||
|
||||
# used only by rusqlite
|
||||
[workspace.dependencies.parking_lot]
|
||||
version = "0.12.3"
|
||||
|
||||
# used only by rusqlite
|
||||
[workspace.dependencies.thread_local]
|
||||
version = "1.1.8"
|
||||
|
||||
[workspace.dependencies.tokio-metrics]
|
||||
version = "0.3.1"
|
||||
default-features = false
|
||||
|
||||
@@ -45,6 +45,8 @@ RuntimeDirectory=conduwuit
|
||||
RuntimeDirectoryMode=0750
|
||||
|
||||
Environment="CONDUWUIT_CONFIG=/etc/conduwuit/conduwuit.toml"
|
||||
BindPaths=/var/lib/private/conduwuit:/var/lib/matrix-conduit
|
||||
BindPaths=/var/lib/private/conduwuit:/var/lib/private/matrix-conduit
|
||||
|
||||
ExecStart=/usr/bin/conduwuit
|
||||
Restart=on-failure
|
||||
|
||||
+20
-3
@@ -64,9 +64,7 @@
|
||||
# Note: this was previously "/var/lib/matrix-conduit"
|
||||
database_path = "/var/lib/conduwuit"
|
||||
|
||||
# Database backend: Only rocksdb and sqlite are supported. Please note that sqlite
|
||||
# will perform significantly worse than rocksdb as it is not intended to be used the
|
||||
# way it is by conduwuit. sqlite only exists for historical reasons.
|
||||
# Database backend: Only rocksdb is supported.
|
||||
database_backend = "rocksdb"
|
||||
|
||||
|
||||
@@ -373,6 +371,25 @@ allow_profile_lookup_federation_requests = true
|
||||
# If 'tracing_flame' is enabled, set the path to write the generated profile.
|
||||
# tracing_flame_output_path = "./tracing.folded"
|
||||
|
||||
# Enable the tokio-console. This option is only relevant to developers.
|
||||
# See: docs/development.md#debugging-with-tokio-console for more information.
|
||||
#tokio_console = false
|
||||
|
||||
# Enable backward-compatibility with Conduit's media directory by creating symlinks of media. This
|
||||
# option is only necessary if you plan on using Conduit again. Otherwise setting this to false
|
||||
# reduces filesystem clutter and overhead for managing these symlinks in the directory.
|
||||
#media_compat_file_link = true
|
||||
|
||||
# Checks consistency of the media directory at startup:
|
||||
# 1. When `media_compat_file_link` is enbled, this check will upgrade media when switching back
|
||||
# and forth between Conduit and Conduwuit. Both options must be enabled to handle this.
|
||||
# 2. When media is deleted from the directory, this check will also delete its database entry.
|
||||
#
|
||||
# If none of these checks apply to your use cases, and your media directory is significantly large
|
||||
# setting this to false may reduce startup time.
|
||||
#media_statup_check = true
|
||||
|
||||
|
||||
### Generic database options
|
||||
|
||||
# Set this to any float value to multiply conduwuit's in-memory LRU caches with.
|
||||
|
||||
Vendored
+1
-1
@@ -15,7 +15,7 @@ in `/etc/conduwuit/conduwuit.toml`.
|
||||
|
||||
### Running
|
||||
|
||||
The package uses the [`conduwuit.service`](../configuration.md#example-systemd-unit-file) systemd unit file to start and stop conduwuit. The binary is installed at `/usr/sbin/conduwuit`.
|
||||
The package uses the [`conduwuit.service`](../configuration/examples.md#example-systemd-unit-file) systemd unit file to start and stop conduwuit. The binary is installed at `/usr/sbin/conduwuit`.
|
||||
|
||||
This package assumes by default that conduwuit will be placed behind a reverse proxy. The default config options apply (listening on `localhost` and TCP port `6167`). Matrix federation requires a valid domain name and TLS, so you will need to set up TLS certificates and renewal for it to work properly if you intend to federate.
|
||||
|
||||
|
||||
Vendored
-1
@@ -6,7 +6,6 @@ set -e
|
||||
|
||||
CONDUWUIT_DATABASE_PATH=/var/lib/conduwuit
|
||||
CONDUWUIT_CONFIG_PATH=/etc/conduwuit
|
||||
CONDUWUIT_CONFIG_FILE="${CONDUWUIT_CONFIG_PATH}/conduwuit.toml"
|
||||
|
||||
case "$1" in
|
||||
configure)
|
||||
|
||||
Vendored
+1
-1
@@ -27,7 +27,7 @@ malloc-usable-size = ["rust-rocksdb/malloc-usable-size"]
|
||||
|
||||
[dependencies.rust-rocksdb]
|
||||
git = "https://github.com/zaidoon1/rust-rocksdb"
|
||||
rev = "e9e1cb5ba92a44ea225fe8d13b31aa23621b9035"
|
||||
rev = "b4887edfb84771336930855727390edec07d63fa"
|
||||
#branch = "master"
|
||||
default-features = false
|
||||
|
||||
|
||||
+2
-1
@@ -2,7 +2,8 @@
|
||||
|
||||
- [Introduction](introduction.md)
|
||||
- [Differences from upstream Conduit](differences.md)
|
||||
- [Example configuration](configuration.md)
|
||||
- [Configuration](configuration.md)
|
||||
- [Examples](configuration/examples.md)
|
||||
- [Deploying](deploying.md)
|
||||
- [Generic](deploying/generic.md)
|
||||
- [NixOS](deploying/nixos.md)
|
||||
|
||||
+13
-27
@@ -1,32 +1,18 @@
|
||||
## Example configuration
|
||||
# Configuration
|
||||
|
||||
<details>
|
||||
<summary>Example configuration</summary>
|
||||
This chapter describes various ways to configure conduwuit.
|
||||
|
||||
```toml
|
||||
{{#include ../conduwuit-example.toml}}
|
||||
```
|
||||
## Basics
|
||||
|
||||
</details>
|
||||
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.
|
||||
|
||||
## Debian systemd unit file
|
||||
## Environment variables
|
||||
|
||||
<details>
|
||||
<summary>Debian systemd unit file</summary>
|
||||
|
||||
```
|
||||
{{#include ../debian/conduwuit.service}}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## Arch Linux systemd unit file
|
||||
|
||||
<details>
|
||||
<summary>Arch Linux systemd unit file</summary>
|
||||
|
||||
```
|
||||
{{#include ../arch/conduwuit.service}}
|
||||
```
|
||||
|
||||
</details>
|
||||
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`.
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
## Example configuration
|
||||
|
||||
<details>
|
||||
<summary>Example configuration</summary>
|
||||
|
||||
```toml
|
||||
{{#include ../../conduwuit-example.toml}}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## Debian systemd unit file
|
||||
|
||||
<details>
|
||||
<summary>Debian systemd unit file</summary>
|
||||
|
||||
```
|
||||
{{#include ../../debian/conduwuit.service}}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## Arch Linux systemd unit file
|
||||
|
||||
<details>
|
||||
<summary>Arch Linux systemd unit file</summary>
|
||||
|
||||
```
|
||||
{{#include ../../arch/conduwuit.service}}
|
||||
```
|
||||
|
||||
</details>
|
||||
@@ -44,7 +44,7 @@ docker run -d -p 8448:6167 \
|
||||
|
||||
or you can use [docker compose](#docker-compose).
|
||||
|
||||
The `-d` flag lets the container run in detached mode. You may supply an optional `conduwuit.toml` config file, the example config can be found [here](../configuration.md).
|
||||
The `-d` flag lets the container run in detached mode. You may supply an optional `conduwuit.toml` config file, the example config can be found [here](../configuration/examples.md).
|
||||
You can pass in different env vars to change config values on the fly. You can even configure conduwuit completely by using env vars. For an overview of possible
|
||||
values, please take a look at the [`docker-compose.yml`](docker-compose.yml) file.
|
||||
|
||||
|
||||
@@ -43,13 +43,13 @@ If conduwuit runs behind a router or in a container and has a different public I
|
||||
|
||||
## Setting up a systemd service
|
||||
|
||||
The systemd unit for conduwuit can be found [here](../configuration.md#example-systemd-unit-file). You may need to change the `ExecStart=` path to where you placed the conduwuit binary.
|
||||
The systemd unit for conduwuit can be found [here](../configuration/examples.md#example-systemd-unit-file). You may need to change the `ExecStart=` path to where you placed the conduwuit binary.
|
||||
|
||||
## Creating the conduwuit configuration file
|
||||
|
||||
Now we need to create the conduwuit's config file in `/etc/conduwuit/conduwuit.toml`. The example config can be found at [conduwuit-example.toml](../configuration.md).**Please take a moment to read it. You need to change at least the server name.**
|
||||
Now we need to create the conduwuit's config file in `/etc/conduwuit/conduwuit.toml`. The example config can be found at [conduwuit-example.toml](../configuration/examples.md).**Please take a moment to read it. You need to change at least the server name.**
|
||||
|
||||
RocksDB is the only supported database backend. SQLite only exists for historical reasons, is not recommended, and will be removed soon (likely in v0.5.0). Any performance issues, storage issues, database issues, etc will not be assisted if using SQLite and you will be asked to migrate to RocksDB first.
|
||||
RocksDB is the only supported database backend.
|
||||
|
||||
## Setting the correct file permissions
|
||||
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ Any commands with `-list` in them will require a codeblock in the message with e
|
||||
|
||||
If using RocksDB, there's very little you need to do. Compaction is ran automatically based on various defined thresholds tuned for conduwuit to be high performance with the least I/O amplifcation or overhead. Manually running compaction is not recommended, or compaction via a timer. RocksDB is built with io_uring support via liburing for async read I/O.
|
||||
|
||||
Some RocksDB settings can be adjusted such as the compression method chosen. See the RocksDB section in the [example config](configuration.md). btrfs users may benefit from disabling compression on RocksDB if CoW is in use.
|
||||
Some RocksDB settings can be adjusted such as the compression method chosen. See the RocksDB section in the [example config](configuration/examples.md). btrfs users may benefit from disabling compression on RocksDB if CoW is in use.
|
||||
|
||||
RocksDB troubleshooting can be found [in the RocksDB section of troubleshooting](troubleshooting.md).
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
#### Direct IO
|
||||
|
||||
Some filesystems may not like RocksDB using [Direct IO](https://github.com/facebook/rocksdb/wiki/Direct-IO). Direct IO is for non-buffered I/O which improves conduwuit performance, but at least FUSE is a filesystem potentially known to not like this. See the [example config](configuration.md) for disabling it if needed. Issues from Direct IO on unsupported filesystems are usually shown as startup errors.
|
||||
Some filesystems may not like RocksDB using [Direct IO](https://github.com/facebook/rocksdb/wiki/Direct-IO). Direct IO is for non-buffered I/O which improves conduwuit performance, but at least FUSE is a filesystem potentially known to not like this. See the [example config](configuration/examples.md) for disabling it if needed. Issues from Direct IO on unsupported filesystems are usually shown as startup errors.
|
||||
|
||||
#### Database corruption
|
||||
|
||||
@@ -32,7 +32,8 @@ By default, conduwuit uses `TolerateCorruptedTailRecords` as generally these may
|
||||
`AbsoluteConsistency` will fail to start the database if any sign of corruption is detected. `SkipAnyCorruptedRecord` will skip all forms of corruption unless it forbids the database from opening (e.g. too severe). Usage of `SkipAnyCorruptedRecord` voids any support as this may cause more damage and/or leave your database in a permanently inconsistent state, but it may do something if `PointInTime` does not work as a last ditch effort.
|
||||
|
||||
With this in mind:
|
||||
- First start conduwuit with the `PointInTime` recovery method. See the [example config](configuration.md) for how to do this using `rocksdb_recovery_mode`
|
||||
|
||||
- First start conduwuit with the `PointInTime` recovery method. See the [example config](configuration/examples.md) for how to do this using `rocksdb_recovery_mode`
|
||||
- If your database successfully opens, clients are recommended to clear their client cache to account for the rollback
|
||||
- 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
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ realm=<your server domain>
|
||||
```
|
||||
A common way to generate a suitable alphanumeric secret key is by using `pwgen -s 64 1`.
|
||||
|
||||
These same values need to be set in conduwuit. See the [example config](configuration.md) in the TURN section for configuring these and restart conduwuit after.
|
||||
These same values need to be set in conduwuit. See the [example config](configuration/examples.md) in the TURN section for configuring these and restart conduwuit after.
|
||||
|
||||
### Run
|
||||
Run the [Coturn](https://hub.docker.com/r/coturn/coturn) image using
|
||||
|
||||
+1
-1
@@ -136,7 +136,7 @@ cargo clippy \
|
||||
[[task]]
|
||||
name = "lychee"
|
||||
group = "lints"
|
||||
script = "lychee --verbose --offline docs *.md"
|
||||
script = "lychee --verbose --offline docs *.md --exclude development.md"
|
||||
|
||||
[[task]]
|
||||
name = "cargo/all"
|
||||
|
||||
Generated
+151
-66
@@ -9,11 +9,11 @@
|
||||
"nixpkgs-stable": "nixpkgs-stable"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1711742460,
|
||||
"narHash": "sha256-0O4v6e4a1toxXZ2gf5INhg4WPE5C5T+SVvsBt+45Mcc=",
|
||||
"lastModified": 1717279440,
|
||||
"narHash": "sha256-kH04ReTjxOpQumgWnqy40vvQLSnLGxWP6RF3nq5Esrk=",
|
||||
"owner": "zhaofengli",
|
||||
"repo": "attic",
|
||||
"rev": "4dbdbee45728d8ce5788db6461aaaa89d98081f0",
|
||||
"rev": "717cc95983cdc357bc347d70be20ced21f935843",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -31,11 +31,11 @@
|
||||
"pre-commit-hooks": "pre-commit-hooks"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1717420532,
|
||||
"narHash": "sha256-OCCmI69EMaA4BcxRKrXJsx5Ozua2f/PKEy4aJbE7ziM=",
|
||||
"lastModified": 1719923519,
|
||||
"narHash": "sha256-7Rhljj2fsklFRsu+eq7N683Z9qukmreMEj5C1GqCrSA=",
|
||||
"owner": "cachix",
|
||||
"repo": "cachix",
|
||||
"rev": "5727f0676f08a4b41ed13d403ec64dcce989f6e5",
|
||||
"rev": "4e9e71f78b9500fa6210cf1eaa4d75bdbab777c3",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -45,14 +45,47 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"cachix_2": {
|
||||
"inputs": {
|
||||
"devenv": "devenv_2",
|
||||
"flake-compat": [
|
||||
"cachix",
|
||||
"devenv",
|
||||
"flake-compat"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"cachix",
|
||||
"devenv",
|
||||
"nixpkgs"
|
||||
],
|
||||
"pre-commit-hooks": [
|
||||
"cachix",
|
||||
"devenv",
|
||||
"pre-commit-hooks"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1712055811,
|
||||
"narHash": "sha256-7FcfMm5A/f02yyzuavJe06zLa9hcMHsagE28ADcmQvk=",
|
||||
"owner": "cachix",
|
||||
"repo": "cachix",
|
||||
"rev": "02e38da89851ec7fec3356a5c04bc8349cae0e30",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"repo": "cachix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"complement": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1715700731,
|
||||
"narHash": "sha256-cie+b5N/TQAFD8vF/XbqfyFJkFU0qUPDbtJQDm/TfQc=",
|
||||
"lastModified": 1719903368,
|
||||
"narHash": "sha256-PPzgxM4Bir+Zh9FUV/v+RBxEYeJxYVmi/BYo3uqt268=",
|
||||
"owner": "matrix-org",
|
||||
"repo": "complement",
|
||||
"rev": "8587fb3cbe746754b2c883ff6c818ca4d987d0a5",
|
||||
"rev": "bc97f1ddc1cd7485faf80c8935ee2641f3e1b57c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -70,11 +103,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1702918879,
|
||||
"narHash": "sha256-tWJqzajIvYcaRWxn+cLUB9L9Pv4dQ3Bfit/YjU5ze3g=",
|
||||
"lastModified": 1717025063,
|
||||
"narHash": "sha256-dIubLa56W9sNNz0e8jGxrX3CAkPXsq7snuFA/Ie6dn8=",
|
||||
"owner": "ipetkov",
|
||||
"repo": "crane",
|
||||
"rev": "7195c00c272fdd92fc74e7d5a0a2844b9fadb2fb",
|
||||
"rev": "480dff0be03dac0e51a8dfc26e882b0d123a450e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -106,14 +139,49 @@
|
||||
},
|
||||
"devenv": {
|
||||
"inputs": {
|
||||
"cachix": "cachix_2",
|
||||
"flake-compat": [
|
||||
"cachix",
|
||||
"flake-compat"
|
||||
],
|
||||
"nix": "nix_2",
|
||||
"nixpkgs": [
|
||||
"cachix",
|
||||
"nixpkgs"
|
||||
],
|
||||
"pre-commit-hooks": [
|
||||
"cachix",
|
||||
"pre-commit-hooks"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1719759336,
|
||||
"narHash": "sha256-3a34VL/QnHprl5gMy9xlx6d8J+iNp+W88Ex8smkgH9M=",
|
||||
"owner": "cachix",
|
||||
"repo": "devenv",
|
||||
"rev": "bb32aa986f2f695385e54428d0eaf7d05b31466e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"repo": "devenv",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"devenv_2": {
|
||||
"inputs": {
|
||||
"flake-compat": [
|
||||
"cachix",
|
||||
"devenv",
|
||||
"cachix",
|
||||
"flake-compat"
|
||||
],
|
||||
"nix": "nix",
|
||||
"nixpkgs": "nixpkgs_2",
|
||||
"poetry2nix": "poetry2nix",
|
||||
"pre-commit-hooks": [
|
||||
"cachix",
|
||||
"devenv",
|
||||
"cachix",
|
||||
"pre-commit-hooks"
|
||||
]
|
||||
@@ -281,24 +349,6 @@
|
||||
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils_4": {
|
||||
"inputs": {
|
||||
"systems": "systems_3"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1710146030,
|
||||
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"ref": "main",
|
||||
@@ -331,11 +381,11 @@
|
||||
"liburing": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1716565485,
|
||||
"narHash": "sha256-4R19aJNQYs6vb0/Hz4bWT56YN1P1DkFL/sxdE4Yj0CE=",
|
||||
"lastModified": 1719025212,
|
||||
"narHash": "sha256-kD0yhjNStqC6uFqC1AxBwUpc/HlSFtiKrV+gwDyroDc=",
|
||||
"owner": "axboe",
|
||||
"repo": "liburing",
|
||||
"rev": "b90c0e670a93caabbebe2d9e24ff85cece4cfe0e",
|
||||
"rev": "7b3245583069bd481190c9da18f22e9fc8c3a805",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -349,6 +399,8 @@
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat_2",
|
||||
"nixpkgs": [
|
||||
"cachix",
|
||||
"devenv",
|
||||
"cachix",
|
||||
"devenv",
|
||||
"nixpkgs"
|
||||
@@ -356,11 +408,11 @@
|
||||
"nixpkgs-regression": "nixpkgs-regression"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1708577783,
|
||||
"narHash": "sha256-92xq7eXlxIT5zFNccLpjiP7sdQqQI30Gyui2p/PfKZM=",
|
||||
"lastModified": 1712911606,
|
||||
"narHash": "sha256-BGvBhepCufsjcUkXnEEXhEVjwdJAwPglCC2+bInc794=",
|
||||
"owner": "domenkozar",
|
||||
"repo": "nix",
|
||||
"rev": "ecd0af0c1f56de32cbad14daa1d82a132bf298f8",
|
||||
"rev": "b24a9318ea3f3600c1e24b4a00691ee912d4de12",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -389,6 +441,8 @@
|
||||
"nix-github-actions": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"cachix",
|
||||
"devenv",
|
||||
"cachix",
|
||||
"devenv",
|
||||
"poetry2nix",
|
||||
@@ -409,6 +463,35 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix_2": {
|
||||
"inputs": {
|
||||
"flake-compat": [
|
||||
"cachix",
|
||||
"devenv",
|
||||
"flake-compat"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"cachix",
|
||||
"devenv",
|
||||
"nixpkgs"
|
||||
],
|
||||
"nixpkgs-regression": "nixpkgs-regression_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1712911606,
|
||||
"narHash": "sha256-BGvBhepCufsjcUkXnEEXhEVjwdJAwPglCC2+bInc794=",
|
||||
"owner": "domenkozar",
|
||||
"repo": "nix",
|
||||
"rev": "b24a9318ea3f3600c1e24b4a00691ee912d4de12",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "domenkozar",
|
||||
"ref": "devenv-2.21",
|
||||
"repo": "nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1711401922,
|
||||
@@ -441,6 +524,22 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-regression_2": {
|
||||
"locked": {
|
||||
"lastModified": 1643052045,
|
||||
"narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-stable": {
|
||||
"locked": {
|
||||
"lastModified": 1711460390,
|
||||
@@ -459,11 +558,11 @@
|
||||
},
|
||||
"nixpkgs-stable_2": {
|
||||
"locked": {
|
||||
"lastModified": 1710695816,
|
||||
"narHash": "sha256-3Eh7fhEID17pv9ZxrPwCLfqXnYP006RKzSs0JptsN84=",
|
||||
"lastModified": 1718811006,
|
||||
"narHash": "sha256-0Y8IrGhRmBmT7HHXlxxepg2t8j1X90++qRN3lukGaIk=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "614b4613980a522ba49f0d194531beddbb7220d3",
|
||||
"rev": "03d771e513ce90147b65fe922d87d3a0356fc125",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -491,11 +590,11 @@
|
||||
},
|
||||
"nixpkgs_3": {
|
||||
"locked": {
|
||||
"lastModified": 1715534503,
|
||||
"narHash": "sha256-5ZSVkFadZbFP1THataCaSf0JH2cAH3S29hU9rrxTEqk=",
|
||||
"lastModified": 1719848872,
|
||||
"narHash": "sha256-H3+EC5cYuq+gQW8y0lSrrDZfH71LB4DAf+TDFyvwCNA=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "2057814051972fa1453ddfb0d98badbea9b83c06",
|
||||
"rev": "00d80d13810dbfea8ab4ed1009b09100cca86ba8",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -526,6 +625,8 @@
|
||||
"flake-utils": "flake-utils_2",
|
||||
"nix-github-actions": "nix-github-actions",
|
||||
"nixpkgs": [
|
||||
"cachix",
|
||||
"devenv",
|
||||
"cachix",
|
||||
"devenv",
|
||||
"nixpkgs"
|
||||
@@ -548,7 +649,6 @@
|
||||
"pre-commit-hooks": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat_4",
|
||||
"flake-utils": "flake-utils_3",
|
||||
"gitignore": "gitignore",
|
||||
"nixpkgs": [
|
||||
"cachix",
|
||||
@@ -557,11 +657,11 @@
|
||||
"nixpkgs-stable": "nixpkgs-stable_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1715609711,
|
||||
"narHash": "sha256-/5u29K0c+4jyQ8x7dUIEUWlz2BoTSZWUP2quPwFCE7M=",
|
||||
"lastModified": 1719259945,
|
||||
"narHash": "sha256-F1h+XIsGKT9TkGO3omxDLEb/9jOOsI6NnzsXFsZhry4=",
|
||||
"owner": "cachix",
|
||||
"repo": "pre-commit-hooks.nix",
|
||||
"rev": "c182c876690380f8d3b9557c4609472ebfa1b141",
|
||||
"rev": "0ff4381bbb8f7a52ca4a851660fc7a437a4c6e07",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -573,16 +673,16 @@
|
||||
"rocksdb": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1716773462,
|
||||
"narHash": "sha256-5kUH+XK+2lbFfUgbxuNy3YMLHbp6scfWPdtc8za1wDM=",
|
||||
"lastModified": 1719949653,
|
||||
"narHash": "sha256-DYx7XHH2GEh17GukKhXs6laM6l+eugCmRkF0adpi9wk=",
|
||||
"owner": "girlbossceo",
|
||||
"repo": "rocksdb",
|
||||
"rev": "c8a1450231e9c608edf535538dbe8ca1a8d2f3bc",
|
||||
"rev": "a935c0273e1ba44eacf88ce3685a9b9831486155",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "girlbossceo",
|
||||
"ref": "v9.2.1",
|
||||
"ref": "v9.3.1",
|
||||
"repo": "rocksdb",
|
||||
"type": "github"
|
||||
}
|
||||
@@ -595,7 +695,7 @@
|
||||
"crane": "crane_2",
|
||||
"fenix": "fenix",
|
||||
"flake-compat": "flake-compat_5",
|
||||
"flake-utils": "flake-utils_4",
|
||||
"flake-utils": "flake-utils_3",
|
||||
"liburing": "liburing",
|
||||
"nix-filter": "nix-filter",
|
||||
"nixpkgs": "nixpkgs_4",
|
||||
@@ -648,21 +748,6 @@
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems_3": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
nix-filter.url = "github:numtide/nix-filter?ref=main";
|
||||
nixpkgs.url = "github:NixOS/nixpkgs?ref=nixos-unstable";
|
||||
# https://github.com/girlbossceo/rocksdb/commit/db6df0b185774778457dabfcbd822cb81760cade
|
||||
rocksdb = { url = "github:girlbossceo/rocksdb?ref=v9.2.1"; flake = false; };
|
||||
rocksdb = { url = "github:girlbossceo/rocksdb?ref=v9.3.1"; flake = false; };
|
||||
liburing = { url = "github:axboe/liburing?ref=master"; flake = false; };
|
||||
};
|
||||
|
||||
|
||||
+1
-15
@@ -17,25 +17,13 @@ crate-type = [
|
||||
]
|
||||
|
||||
[features]
|
||||
dev_release_log_level = []
|
||||
#dev_release_log_level = []
|
||||
release_max_log_level = [
|
||||
"tracing/max_level_trace",
|
||||
"tracing/release_max_level_info",
|
||||
"log/max_level_trace",
|
||||
"log/release_max_level_info",
|
||||
]
|
||||
rocksdb = [
|
||||
"dep:rust-rocksdb",
|
||||
]
|
||||
jemalloc = [
|
||||
"rust-rocksdb/jemalloc",
|
||||
]
|
||||
io_uring = [
|
||||
"rust-rocksdb/io-uring",
|
||||
]
|
||||
zstd_compression = [
|
||||
"rust-rocksdb/zstd",
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
clap.workspace = true
|
||||
@@ -48,8 +36,6 @@ log.workspace = true
|
||||
loole.workspace = true
|
||||
regex.workspace = true
|
||||
ruma.workspace = true
|
||||
rust-rocksdb.optional = true
|
||||
rust-rocksdb.workspace = true
|
||||
serde_json.workspace = true
|
||||
serde.workspace = true
|
||||
serde_yaml.workspace = true
|
||||
|
||||
@@ -19,7 +19,7 @@ pub(super) async fn check_all_users(_body: Vec<&str>) -> Result<RoomMessageEvent
|
||||
|
||||
let message = format!(
|
||||
"Database query completed in {query_time:?}:\n\n```\nTotal entries: {total:?}\nFailure/Invalid user count: \
|
||||
{err_count:?}\nSuccess/Valid user count: {ok_count:?}```"
|
||||
{err_count:?}\nSuccess/Valid user count: {ok_count:?}\n```"
|
||||
);
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(message))
|
||||
@@ -8,12 +8,12 @@ use self::commands::*;
|
||||
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
#[derive(Subcommand)]
|
||||
pub(super) enum FsckCommand {
|
||||
CheckAllUsers,
|
||||
pub(super) enum CheckCommand {
|
||||
AllUsers,
|
||||
}
|
||||
|
||||
pub(super) async fn process(command: FsckCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn process(command: CheckCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
Ok(match command {
|
||||
FsckCommand::CheckAllUsers => check_all_users(body).await?,
|
||||
CheckCommand::AllUsers => check_all_users(body).await?,
|
||||
})
|
||||
}
|
||||
@@ -13,7 +13,7 @@ use conduit::{
|
||||
use ruma::{
|
||||
api::{client::error::ErrorKind, federation::event::get_room_state},
|
||||
events::room::message::RoomMessageEventContent,
|
||||
CanonicalJsonObject, EventId, RoomId, RoomVersionId, ServerName,
|
||||
CanonicalJsonObject, EventId, OwnedRoomOrAliasId, RoomId, RoomVersionId, ServerName,
|
||||
};
|
||||
use service::{rooms::event_handler::parse_incoming_pdu, sending::resolve::resolve_actual_dest, services, PduEvent};
|
||||
use tokio::sync::RwLock;
|
||||
@@ -236,7 +236,8 @@ pub(super) async fn get_remote_pdu(
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) async fn get_room_state(_body: Vec<&str>, room_id: Box<RoomId>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn get_room_state(_body: Vec<&str>, room: OwnedRoomOrAliasId) -> Result<RoomMessageEventContent> {
|
||||
let room_id = services().rooms.alias.resolve(&room).await?;
|
||||
let room_state = services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
@@ -252,17 +253,14 @@ pub(super) async fn get_room_state(_body: Vec<&str>, room_id: Box<RoomId>) -> Re
|
||||
));
|
||||
}
|
||||
|
||||
let json_text = serde_json::to_string_pretty(&room_state).map_err(|e| {
|
||||
let json = serde_json::to_string_pretty(&room_state).map_err(|e| {
|
||||
warn!("Failed converting room state vector in our database to pretty JSON: {e}");
|
||||
Error::bad_database(
|
||||
"Failed to convert room state events to pretty JSON, possible invalid room state events in our database",
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"{}\n```json\n{}\n```",
|
||||
"Found full room state", json_text
|
||||
)))
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!("```json\n{json}\n```")))
|
||||
}
|
||||
|
||||
pub(super) async fn ping(_body: Vec<&str>, server: Box<ServerName>) -> Result<RoomMessageEventContent> {
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
mod commands;
|
||||
pub(crate) mod tester;
|
||||
|
||||
use clap::Subcommand;
|
||||
use conduit::Result;
|
||||
use ruma::{events::room::message::RoomMessageEventContent, EventId, RoomId, ServerName};
|
||||
use ruma::{events::room::message::RoomMessageEventContent, EventId, OwnedRoomOrAliasId, RoomId, ServerName};
|
||||
use tester::TesterCommand;
|
||||
|
||||
use self::commands::*;
|
||||
|
||||
@@ -71,7 +73,7 @@ pub(super) enum DebugCommand {
|
||||
/// Of course the check is still done on the actual client API.
|
||||
GetRoomState {
|
||||
/// Room ID
|
||||
room_id: Box<RoomId>,
|
||||
room_id: OwnedRoomOrAliasId,
|
||||
},
|
||||
|
||||
/// - Sends a federation request to the remote server's
|
||||
@@ -157,6 +159,10 @@ pub(super) enum DebugCommand {
|
||||
|
||||
/// - Print extended memory usage
|
||||
MemoryStats,
|
||||
|
||||
/// - Developer test stubs
|
||||
#[command(subcommand)]
|
||||
Tester(TesterCommand),
|
||||
}
|
||||
|
||||
pub(super) async fn process(command: DebugCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
@@ -207,5 +213,6 @@ pub(super) async fn process(command: DebugCommand, body: Vec<&str>) -> Result<Ro
|
||||
no_cache,
|
||||
} => resolve_true_destination(body, server_name, no_cache).await?,
|
||||
DebugCommand::MemoryStats => memory_stats(),
|
||||
DebugCommand::Tester(command) => tester::process(command, body).await?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
use ruma::events::room::message::RoomMessageEventContent;
|
||||
|
||||
use crate::Result;
|
||||
|
||||
#[derive(clap::Subcommand)]
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
pub(crate) enum TesterCommand {
|
||||
Tester,
|
||||
Timer,
|
||||
}
|
||||
|
||||
pub(super) async fn process(command: TesterCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
match command {
|
||||
TesterCommand::Tester => tester(body).await,
|
||||
TesterCommand::Timer => timer(body).await,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
#[rustfmt::skip]
|
||||
#[allow(unused_variables)]
|
||||
async fn tester(body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
|
||||
Ok(RoomMessageEventContent::notice_plain("completed"))
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
#[rustfmt::skip]
|
||||
async fn timer(body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
let started = std::time::Instant::now();
|
||||
timed(&body);
|
||||
|
||||
let elapsed = started.elapsed();
|
||||
Ok(RoomMessageEventContent::notice_plain(format!("completed in {elapsed:#?}")))
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
#[rustfmt::skip]
|
||||
#[allow(unused_variables)]
|
||||
fn timed(body: &[&str]) {
|
||||
|
||||
}
|
||||
+23
-24
@@ -1,3 +1,5 @@
|
||||
use std::time::Instant;
|
||||
|
||||
use clap::Parser;
|
||||
use conduit::trace;
|
||||
use ruma::events::{
|
||||
@@ -11,11 +13,10 @@ use conduit::Result;
|
||||
pub(crate) use service::admin::{Command, Service};
|
||||
use service::admin::{CommandOutput, CommandResult, HandlerResult};
|
||||
|
||||
use self::{fsck::FsckCommand, tester::TesterCommands};
|
||||
use crate::{
|
||||
appservice, appservice::AppserviceCommand, debug, debug::DebugCommand, federation, federation::FederationCommand,
|
||||
fsck, media, media::MediaCommand, query, query::QueryCommand, room, room::RoomCommand, server,
|
||||
server::ServerCommand, services, tester, user, user::UserCommand,
|
||||
appservice, appservice::AppserviceCommand, check, check::CheckCommand, debug, debug::DebugCommand, federation,
|
||||
federation::FederationCommand, media, media::MediaCommand, query, query::QueryCommand, room, room::RoomCommand,
|
||||
server, server::ServerCommand, services, user, user::UserCommand,
|
||||
};
|
||||
pub(crate) const PAGE_SIZE: usize = 100;
|
||||
|
||||
@@ -47,20 +48,17 @@ pub(crate) enum AdminCommand {
|
||||
/// - Commands for managing media
|
||||
Media(MediaCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - Commands for checking integrity
|
||||
Check(CheckCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - Commands for debugging things
|
||||
Debug(DebugCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - Query all the database getters and iterators
|
||||
/// - Low-level queries for database getters and iterators
|
||||
Query(QueryCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
/// - Query all the database getters and iterators
|
||||
Fsck(FsckCommand),
|
||||
|
||||
#[command(subcommand)]
|
||||
Tester(TesterCommands),
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
@@ -84,11 +82,10 @@ async fn handle_command(command: Command) -> CommandResult {
|
||||
// Parse and process a message from the admin room
|
||||
async fn process_admin_message(msg: String) -> CommandOutput {
|
||||
let mut lines = msg.lines().filter(|l| !l.trim().is_empty());
|
||||
let command_line = lines.next().expect("each string has at least one line");
|
||||
let command = lines.next().expect("each string has at least one line");
|
||||
let body = lines.collect::<Vec<_>>();
|
||||
|
||||
let admin_command = match parse_admin_command(command_line) {
|
||||
Ok(command) => command,
|
||||
let parsed = match parse_admin_command(command) {
|
||||
Ok(parsed) => parsed,
|
||||
Err(error) => {
|
||||
let server_name = services().globals.server_name();
|
||||
let message = error.replace("server.name", server_name.as_str());
|
||||
@@ -96,12 +93,15 @@ async fn process_admin_message(msg: String) -> CommandOutput {
|
||||
},
|
||||
};
|
||||
|
||||
match process_admin_command(admin_command, body).await {
|
||||
Ok(reply_message) => Some(reply_message),
|
||||
Err(error) => {
|
||||
let markdown_message = format!("Encountered an error while handling the command:\n```\n{error}\n```",);
|
||||
Some(RoomMessageEventContent::notice_markdown(markdown_message))
|
||||
},
|
||||
let timer = Instant::now();
|
||||
let result = process_admin_command(parsed, body).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```"
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,8 +164,7 @@ async fn process_admin_command(command: AdminCommand, body: Vec<&str>) -> Result
|
||||
AdminCommand::Server(command) => server::process(command, body).await?,
|
||||
AdminCommand::Debug(command) => debug::process(command, body).await?,
|
||||
AdminCommand::Query(command) => query::process(command, body).await?,
|
||||
AdminCommand::Fsck(command) => fsck::process(command, body).await?,
|
||||
AdminCommand::Tester(command) => tester::process(command, body).await?,
|
||||
AdminCommand::Check(command) => check::process(command, body).await?,
|
||||
};
|
||||
|
||||
Ok(reply_message_content)
|
||||
|
||||
@@ -15,7 +15,7 @@ pub(super) async fn delete(
|
||||
|
||||
if let Some(mxc) = mxc {
|
||||
debug!("Got MXC URL: {mxc}");
|
||||
services().media.delete(mxc.to_string()).await?;
|
||||
services().media.delete(mxc.as_ref()).await?;
|
||||
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
"Deleted the MXC from our database and on our filesystem.",
|
||||
@@ -123,7 +123,7 @@ pub(super) async fn delete(
|
||||
}
|
||||
|
||||
for mxc_url in mxc_urls {
|
||||
services().media.delete(mxc_url).await?;
|
||||
services().media.delete(&mxc_url).await?;
|
||||
mxc_deletion_count = mxc_deletion_count.saturating_add(1);
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@ pub(super) async fn delete_list(body: Vec<&str>) -> Result<RoomMessageEventConte
|
||||
|
||||
for mxc in mxc_list {
|
||||
debug!("Deleting MXC {mxc} in bulk");
|
||||
services().media.delete(mxc.to_owned()).await?;
|
||||
services().media.delete(mxc).await?;
|
||||
mxc_deletion_count = mxc_deletion_count
|
||||
.checked_add(1)
|
||||
.expect("mxc_deletion_count should not get this high");
|
||||
|
||||
+1
-2
@@ -1,15 +1,14 @@
|
||||
#![allow(clippy::wildcard_imports)]
|
||||
|
||||
pub(crate) mod appservice;
|
||||
pub(crate) mod check;
|
||||
pub(crate) mod debug;
|
||||
pub(crate) mod federation;
|
||||
pub(crate) mod fsck;
|
||||
pub(crate) mod handler;
|
||||
pub(crate) mod media;
|
||||
pub(crate) mod query;
|
||||
pub(crate) mod room;
|
||||
pub(crate) mod server;
|
||||
pub(crate) mod tester;
|
||||
pub(crate) mod user;
|
||||
pub(crate) mod utils;
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@ pub(super) async fn account_data(subcommand: AccountData) -> Result<RoomMessageE
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = services()
|
||||
.account_data
|
||||
.db
|
||||
.changes_since(room_id.as_deref(), &user_id, since)?;
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
@@ -30,7 +29,6 @@ pub(super) async fn account_data(subcommand: AccountData) -> Result<RoomMessageE
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = services()
|
||||
.account_data
|
||||
.db
|
||||
.get(room_id.as_deref(), &user_id, kind)?;
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ pub(super) async fn appservice(subcommand: Appservice) -> Result<RoomMessageEven
|
||||
},
|
||||
Appservice::All => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = services().appservice.db.all();
|
||||
let results = services().appservice.all();
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
|
||||
@@ -22,9 +22,8 @@ pub(super) async fn presence(subcommand: Presence) -> Result<RoomMessageEventCon
|
||||
} => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = services().presence.db.presence_since(since);
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
let presence_since: Vec<(_, _, _)> = results.collect();
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{presence_since:#?}\n```"
|
||||
|
||||
@@ -10,7 +10,7 @@ pub(super) async fn room_alias(subcommand: RoomAlias) -> Result<RoomMessageEvent
|
||||
alias,
|
||||
} => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = services().rooms.alias.db.resolve_local_alias(&alias);
|
||||
let results = services().rooms.alias.resolve_local_alias(&alias);
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
@@ -21,10 +21,9 @@ pub(super) async fn room_alias(subcommand: RoomAlias) -> Result<RoomMessageEvent
|
||||
room_id,
|
||||
} => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = services().rooms.alias.db.local_aliases_for_room(&room_id);
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
let results = services().rooms.alias.local_aliases_for_room(&room_id);
|
||||
let aliases: Vec<_> = results.collect();
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{aliases:#?}\n```"
|
||||
@@ -32,10 +31,9 @@ pub(super) async fn room_alias(subcommand: RoomAlias) -> Result<RoomMessageEvent
|
||||
},
|
||||
RoomAlias::AllLocalAliases => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = services().rooms.alias.db.all_local_aliases();
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
let results = services().rooms.alias.all_local_aliases();
|
||||
let aliases: Vec<_> = results.collect();
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{aliases:#?}\n```"
|
||||
|
||||
+23
-49
@@ -9,9 +9,8 @@ pub(super) async fn sending(subcommand: Sending) -> Result<RoomMessageEventConte
|
||||
Sending::ActiveRequests => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = services().sending.db.active_requests();
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
let active_requests: Result<Vec<(_, _, _)>> = results.collect();
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{active_requests:#?}\n```"
|
||||
@@ -29,8 +28,8 @@ pub(super) async fn sending(subcommand: Sending) -> Result<RoomMessageEventConte
|
||||
--help for more details.",
|
||||
));
|
||||
}
|
||||
|
||||
let (results, query_time) = match (appservice_id, server_name, user_id, push_key) {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = match (appservice_id, server_name, user_id, push_key) {
|
||||
(Some(appservice_id), None, None, None) => {
|
||||
if appservice_id.is_empty() {
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
@@ -39,25 +38,15 @@ pub(super) async fn sending(subcommand: Sending) -> Result<RoomMessageEventConte
|
||||
));
|
||||
}
|
||||
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = services()
|
||||
services()
|
||||
.sending
|
||||
.db
|
||||
.queued_requests(&Destination::Appservice(appservice_id));
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
(results, query_time)
|
||||
},
|
||||
(None, Some(server_name), None, None) => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = services()
|
||||
.sending
|
||||
.db
|
||||
.queued_requests(&Destination::Normal(server_name.into()));
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
(results, query_time)
|
||||
.queued_requests(&Destination::Appservice(appservice_id))
|
||||
},
|
||||
(None, Some(server_name), None, None) => services()
|
||||
.sending
|
||||
.db
|
||||
.queued_requests(&Destination::Normal(server_name.into())),
|
||||
(None, None, Some(user_id), Some(push_key)) => {
|
||||
if push_key.is_empty() {
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
@@ -66,14 +55,10 @@ pub(super) async fn sending(subcommand: Sending) -> Result<RoomMessageEventConte
|
||||
));
|
||||
}
|
||||
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = services()
|
||||
services()
|
||||
.sending
|
||||
.db
|
||||
.queued_requests(&Destination::Push(user_id.into(), push_key));
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
(results, query_time)
|
||||
.queued_requests(&Destination::Push(user_id.into(), push_key))
|
||||
},
|
||||
(Some(_), Some(_), Some(_), Some(_)) => {
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
@@ -90,6 +75,7 @@ pub(super) async fn sending(subcommand: Sending) -> Result<RoomMessageEventConte
|
||||
};
|
||||
|
||||
let queued_requests = results.collect::<Result<Vec<(_, _)>>>();
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{queued_requests:#?}\n```"
|
||||
@@ -108,7 +94,8 @@ pub(super) async fn sending(subcommand: Sending) -> Result<RoomMessageEventConte
|
||||
));
|
||||
}
|
||||
|
||||
let (results, query_time) = match (appservice_id, server_name, user_id, push_key) {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = match (appservice_id, server_name, user_id, push_key) {
|
||||
(Some(appservice_id), None, None, None) => {
|
||||
if appservice_id.is_empty() {
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
@@ -117,25 +104,15 @@ pub(super) async fn sending(subcommand: Sending) -> Result<RoomMessageEventConte
|
||||
));
|
||||
}
|
||||
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = services()
|
||||
services()
|
||||
.sending
|
||||
.db
|
||||
.active_requests_for(&Destination::Appservice(appservice_id));
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
(results, query_time)
|
||||
},
|
||||
(None, Some(server_name), None, None) => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = services()
|
||||
.sending
|
||||
.db
|
||||
.active_requests_for(&Destination::Normal(server_name.into()));
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
(results, query_time)
|
||||
.active_requests_for(&Destination::Appservice(appservice_id))
|
||||
},
|
||||
(None, Some(server_name), None, None) => services()
|
||||
.sending
|
||||
.db
|
||||
.active_requests_for(&Destination::Normal(server_name.into())),
|
||||
(None, None, Some(user_id), Some(push_key)) => {
|
||||
if push_key.is_empty() {
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
@@ -144,14 +121,10 @@ pub(super) async fn sending(subcommand: Sending) -> Result<RoomMessageEventConte
|
||||
));
|
||||
}
|
||||
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = services()
|
||||
services()
|
||||
.sending
|
||||
.db
|
||||
.active_requests_for(&Destination::Push(user_id.into(), push_key));
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
(results, query_time)
|
||||
.active_requests_for(&Destination::Push(user_id.into(), push_key))
|
||||
},
|
||||
(Some(_), Some(_), Some(_), Some(_)) => {
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
@@ -168,6 +141,7 @@ pub(super) async fn sending(subcommand: Sending) -> Result<RoomMessageEventConte
|
||||
};
|
||||
|
||||
let active_requests = results.collect::<Result<Vec<(_, _)>>>();
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{active_requests:#?}\n```"
|
||||
|
||||
@@ -9,9 +9,8 @@ pub(super) async fn users(subcommand: Users) -> Result<RoomMessageEventContent>
|
||||
Users::Iter => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = services().users.db.iter();
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
let users = results.collect::<Vec<_>>();
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{users:#?}\n```"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use api::client::{get_alias_helper, leave_room};
|
||||
use api::client::leave_room;
|
||||
use ruma::{
|
||||
events::room::message::RoomMessageEventContent, OwnedRoomId, OwnedUserId, RoomAliasId, RoomId, RoomOrAliasId,
|
||||
};
|
||||
@@ -76,13 +76,18 @@ async fn ban_room(
|
||||
} else {
|
||||
debug!("We don't have this room alias to a room ID locally, attempting to fetch room ID over federation");
|
||||
|
||||
match get_alias_helper(room_alias, None).await {
|
||||
Ok(response) => {
|
||||
debug!("Got federation response fetching room ID for room {room}: {:?}", response);
|
||||
response.room_id
|
||||
match services()
|
||||
.rooms
|
||||
.alias
|
||||
.resolve_alias(&room_alias, None)
|
||||
.await
|
||||
{
|
||||
Ok((room_id, servers)) => {
|
||||
debug!(?room_id, ?servers, "Got federation response fetching room ID for {room}");
|
||||
room_id
|
||||
},
|
||||
Err(e) => {
|
||||
return Ok(RoomMessageEventContent::text_plain(format!(
|
||||
return Ok(RoomMessageEventContent::notice_plain(format!(
|
||||
"Failed to resolve room alias {room} to a room ID: {e}"
|
||||
)));
|
||||
},
|
||||
@@ -237,13 +242,19 @@ async fn ban_list_of_rooms(body: Vec<&str>, force: bool, disable_federation: boo
|
||||
ID over federation"
|
||||
);
|
||||
|
||||
match get_alias_helper(room_alias, None).await {
|
||||
Ok(response) => {
|
||||
match services()
|
||||
.rooms
|
||||
.alias
|
||||
.resolve_alias(&room_alias, None)
|
||||
.await
|
||||
{
|
||||
Ok((room_id, servers)) => {
|
||||
debug!(
|
||||
"Got federation response fetching room ID for room {room}: {:?}",
|
||||
response
|
||||
?room_id,
|
||||
?servers,
|
||||
"Got federation response fetching room ID for {room}",
|
||||
);
|
||||
response.room_id
|
||||
room_id
|
||||
},
|
||||
Err(e) => {
|
||||
// don't fail if force blocking
|
||||
@@ -426,10 +437,15 @@ async fn unban_room(
|
||||
} else {
|
||||
debug!("We don't have this room alias to a room ID locally, attempting to fetch room ID over federation");
|
||||
|
||||
match get_alias_helper(room_alias, None).await {
|
||||
Ok(response) => {
|
||||
debug!("Got federation response fetching room ID for room {room}: {:?}", response);
|
||||
response.room_id
|
||||
match services()
|
||||
.rooms
|
||||
.alias
|
||||
.resolve_alias(&room_alias, None)
|
||||
.await
|
||||
{
|
||||
Ok((room_id, servers)) => {
|
||||
debug!(?room_id, ?servers, "Got federation response fetching room ID for room {room}");
|
||||
room_id
|
||||
},
|
||||
Err(e) => {
|
||||
return Ok(RoomMessageEventContent::text_plain(format!(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use conduit::{warn, Result};
|
||||
use conduit::{warn, Error, Result};
|
||||
use ruma::events::room::message::RoomMessageEventContent;
|
||||
|
||||
use crate::services;
|
||||
@@ -64,12 +64,6 @@ pub(super) async fn list_backups(_body: Vec<&str>) -> Result<RoomMessageEventCon
|
||||
}
|
||||
|
||||
pub(super) async fn backup_database(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
if !cfg!(feature = "rocksdb") {
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
"Only RocksDB supports online backups in conduwuit.",
|
||||
));
|
||||
}
|
||||
|
||||
let mut result = services()
|
||||
.server
|
||||
.runtime()
|
||||
@@ -88,12 +82,6 @@ pub(super) async fn backup_database(_body: Vec<&str>) -> Result<RoomMessageEvent
|
||||
}
|
||||
|
||||
pub(super) async fn list_database_files(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
if !cfg!(feature = "rocksdb") {
|
||||
return Ok(RoomMessageEventContent::text_plain(
|
||||
"Only RocksDB supports listing files in conduwuit.",
|
||||
));
|
||||
}
|
||||
|
||||
let result = services().globals.db.file_list()?;
|
||||
|
||||
Ok(RoomMessageEventContent::notice_markdown(result))
|
||||
@@ -114,7 +102,17 @@ pub(super) async fn reload(_body: Vec<&str>) -> Result<RoomMessageEventContent>
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
pub(super) async fn restart(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
pub(super) async fn restart(_body: Vec<&str>, force: bool) -> Result<RoomMessageEventContent> {
|
||||
use conduit::utils::sys::current_exe_deleted;
|
||||
|
||||
if !force && current_exe_deleted() {
|
||||
return Err(Error::Err(
|
||||
"The server cannot be restarted because the executable was tampered with. If this is expected use --force \
|
||||
to override."
|
||||
.to_owned(),
|
||||
));
|
||||
}
|
||||
|
||||
services().server.restart()?;
|
||||
|
||||
Ok(RoomMessageEventContent::notice_plain("Restarting server..."))
|
||||
|
||||
@@ -51,7 +51,10 @@ pub(super) enum ServerCommand {
|
||||
|
||||
#[cfg(unix)]
|
||||
/// - Restart the server
|
||||
Restart,
|
||||
Restart {
|
||||
#[arg(short, long)]
|
||||
force: bool,
|
||||
},
|
||||
|
||||
/// - Shutdown the server
|
||||
Shutdown,
|
||||
@@ -77,7 +80,9 @@ pub(super) async fn process(command: ServerCommand, body: Vec<&str>) -> Result<R
|
||||
#[cfg(conduit_mods)]
|
||||
ServerCommand::Reload => reload(body).await?,
|
||||
#[cfg(unix)]
|
||||
ServerCommand::Restart => restart(body).await?,
|
||||
ServerCommand::Restart {
|
||||
force,
|
||||
} => restart(body, force).await?,
|
||||
ServerCommand::Shutdown => shutdown(body).await?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
use ruma::events::room::message::RoomMessageEventContent;
|
||||
|
||||
use crate::Result;
|
||||
|
||||
#[cfg_attr(test, derive(Debug))]
|
||||
#[derive(clap::Subcommand)]
|
||||
pub(super) enum TesterCommands {
|
||||
Tester,
|
||||
}
|
||||
pub(super) async fn process(command: TesterCommands, _body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||
Ok(match command {
|
||||
TesterCommands::Tester => RoomMessageEventContent::notice_plain(String::from("completed")),
|
||||
})
|
||||
}
|
||||
+1
-1
@@ -18,7 +18,7 @@ crate-type = [
|
||||
|
||||
[features]
|
||||
element_hacks = []
|
||||
dev_release_log_level = []
|
||||
#dev_release_log_level = []
|
||||
release_max_log_level = [
|
||||
"tracing/max_level_trace",
|
||||
"tracing/release_max_level_info",
|
||||
|
||||
+17
-145
@@ -1,22 +1,14 @@
|
||||
use rand::seq::SliceRandom;
|
||||
use ruma::{
|
||||
api::{
|
||||
appservice,
|
||||
client::{
|
||||
alias::{create_alias, delete_alias, get_alias},
|
||||
error::ErrorKind,
|
||||
},
|
||||
federation,
|
||||
api::client::{
|
||||
alias::{create_alias, delete_alias, get_alias},
|
||||
error::ErrorKind,
|
||||
},
|
||||
OwnedRoomAliasId, OwnedServerName, RoomAliasId, RoomId,
|
||||
OwnedServerName, RoomAliasId, RoomId,
|
||||
};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::{
|
||||
debug_info, debug_warn,
|
||||
service::{appservice::RegistrationInfo, server_is_ours},
|
||||
services, Error, Result, Ruma,
|
||||
};
|
||||
use crate::{service::server_is_ours, services, Error, Result, Ruma};
|
||||
|
||||
/// # `PUT /_matrix/client/v3/directory/room/{roomAlias}`
|
||||
///
|
||||
@@ -24,7 +16,7 @@ use crate::{
|
||||
pub(crate) async fn create_alias_route(body: Ruma<create_alias::v3::Request>) -> Result<create_alias::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
alias_checks(&body.room_alias, &body.appservice_info).await?;
|
||||
service::rooms::alias::appservice_checks(&body.room_alias, &body.appservice_info).await?;
|
||||
|
||||
// this isn't apart of alias_checks or delete alias route because we should
|
||||
// allow removing forbidden room aliases
|
||||
@@ -61,7 +53,7 @@ pub(crate) async fn create_alias_route(body: Ruma<create_alias::v3::Request>) ->
|
||||
pub(crate) async fn delete_alias_route(body: Ruma<delete_alias::v3::Request>) -> Result<delete_alias::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
alias_checks(&body.room_alias, &body.appservice_info).await?;
|
||||
service::rooms::alias::appservice_checks(&body.room_alias, &body.appservice_info).await?;
|
||||
|
||||
if services()
|
||||
.rooms
|
||||
@@ -87,124 +79,20 @@ pub(crate) async fn delete_alias_route(body: Ruma<delete_alias::v3::Request>) ->
|
||||
///
|
||||
/// Resolve an alias locally or over federation.
|
||||
pub(crate) async fn get_alias_route(body: Ruma<get_alias::v3::Request>) -> Result<get_alias::v3::Response> {
|
||||
get_alias_helper(body.body.room_alias, None).await
|
||||
}
|
||||
let room_alias = body.body.room_alias;
|
||||
let servers = None;
|
||||
|
||||
pub async fn get_alias_helper(
|
||||
room_alias: OwnedRoomAliasId, servers: Option<Vec<OwnedServerName>>,
|
||||
) -> Result<get_alias::v3::Response> {
|
||||
debug!("get_alias_helper servers: {servers:?}");
|
||||
if !server_is_ours(room_alias.server_name())
|
||||
&& (!servers
|
||||
.as_ref()
|
||||
.is_some_and(|servers| servers.contains(&services().globals.server_name().to_owned()))
|
||||
|| servers.as_ref().is_none())
|
||||
{
|
||||
let mut response = services()
|
||||
.sending
|
||||
.send_federation_request(
|
||||
room_alias.server_name(),
|
||||
federation::query::get_room_information::v1::Request {
|
||||
room_alias: room_alias.clone(),
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
debug!("room alias server_name get_alias_helper response: {response:?}");
|
||||
|
||||
if let Err(ref e) = response {
|
||||
debug_info!(
|
||||
"Server {} of the original room alias failed to assist in resolving room alias: {e}",
|
||||
room_alias.server_name()
|
||||
);
|
||||
}
|
||||
|
||||
if response.as_ref().is_ok_and(|resp| resp.servers.is_empty()) || response.as_ref().is_err() {
|
||||
if let Some(servers) = servers {
|
||||
for server in servers {
|
||||
response = services()
|
||||
.sending
|
||||
.send_federation_request(
|
||||
&server,
|
||||
federation::query::get_room_information::v1::Request {
|
||||
room_alias: room_alias.clone(),
|
||||
},
|
||||
)
|
||||
.await;
|
||||
debug!("Got response from server {server} for room aliases: {response:?}");
|
||||
|
||||
if let Ok(ref response) = response {
|
||||
if !response.servers.is_empty() {
|
||||
break;
|
||||
}
|
||||
debug_warn!(
|
||||
"Server {server} responded with room aliases, but was empty? Response: {response:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(response) = response {
|
||||
let room_id = response.room_id;
|
||||
|
||||
let mut pre_servers = response.servers;
|
||||
// since the room alis server responded, insert it into the list
|
||||
pre_servers.push(room_alias.server_name().into());
|
||||
|
||||
let servers = room_available_servers(&room_id, &room_alias, &Some(pre_servers));
|
||||
debug!(
|
||||
"room alias servers from federation response for room ID {room_id} and room alias {room_alias}: \
|
||||
{servers:?}"
|
||||
);
|
||||
|
||||
return Ok(get_alias::v3::Response::new(room_id, servers));
|
||||
}
|
||||
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::NotFound,
|
||||
"No servers could assist in resolving the room alias",
|
||||
));
|
||||
}
|
||||
|
||||
let mut room_id = None;
|
||||
match services().rooms.alias.resolve_local_alias(&room_alias)? {
|
||||
Some(r) => room_id = Some(r),
|
||||
None => {
|
||||
for appservice in services().appservice.read().await.values() {
|
||||
if appservice.aliases.is_match(room_alias.as_str())
|
||||
&& matches!(
|
||||
services()
|
||||
.sending
|
||||
.send_appservice_request(
|
||||
appservice.registration.clone(),
|
||||
appservice::query::query_room_alias::v1::Request {
|
||||
room_alias: room_alias.clone(),
|
||||
},
|
||||
)
|
||||
.await,
|
||||
Ok(Some(_opt_result))
|
||||
) {
|
||||
room_id = Some(
|
||||
services()
|
||||
.rooms
|
||||
.alias
|
||||
.resolve_local_alias(&room_alias)?
|
||||
.ok_or_else(|| Error::bad_config("Room does not exist."))?,
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
let Some(room_id) = room_id else {
|
||||
let Ok((room_id, pre_servers)) = services()
|
||||
.rooms
|
||||
.alias
|
||||
.resolve_alias(&room_alias, servers.as_ref())
|
||||
.await
|
||||
else {
|
||||
return Err(Error::BadRequest(ErrorKind::NotFound, "Room with alias not found."));
|
||||
};
|
||||
|
||||
let servers = room_available_servers(&room_id, &room_alias, &None);
|
||||
|
||||
debug!("room alias servers for room ID {room_id} and room alias {room_alias}");
|
||||
let servers = room_available_servers(&room_id, &room_alias, &pre_servers);
|
||||
debug!(?room_alias, ?room_id, "available servers: {servers:?}");
|
||||
|
||||
Ok(get_alias::v3::Response::new(room_id, servers))
|
||||
}
|
||||
@@ -250,19 +138,3 @@ fn room_available_servers(
|
||||
|
||||
servers
|
||||
}
|
||||
|
||||
async fn alias_checks(room_alias: &RoomAliasId, appservice_info: &Option<RegistrationInfo>) -> Result<()> {
|
||||
if !server_is_ours(room_alias.server_name()) {
|
||||
return Err(Error::BadRequest(ErrorKind::InvalidParam, "Alias is from another server."));
|
||||
}
|
||||
|
||||
if let Some(ref info) = appservice_info {
|
||||
if !info.aliases.is_match(room_alias.as_str()) {
|
||||
return Err(Error::BadRequest(ErrorKind::Exclusive, "Room alias is not in namespace."));
|
||||
}
|
||||
} else if services().appservice.is_exclusive_alias(room_alias).await {
|
||||
return Err(Error::BadRequest(ErrorKind::Exclusive, "Room alias reserved by appservice."));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+10
-7
@@ -1,3 +1,5 @@
|
||||
#![allow(deprecated)]
|
||||
|
||||
use std::{io::Cursor, sync::Arc, time::Duration};
|
||||
|
||||
use image::io::Reader as ImgReader;
|
||||
@@ -133,7 +135,7 @@ pub(crate) async fn create_content_route(
|
||||
.media
|
||||
.create(
|
||||
Some(sender_user.clone()),
|
||||
mxc.clone(),
|
||||
&mxc,
|
||||
body.filename
|
||||
.as_ref()
|
||||
.map(|filename| {
|
||||
@@ -186,7 +188,7 @@ pub(crate) async fn get_content_route(body: Ruma<get_content::v3::Request>) -> R
|
||||
content_type,
|
||||
file,
|
||||
content_disposition,
|
||||
}) = services().media.get(mxc.clone()).await?
|
||||
}) = services().media.get(&mxc).await?
|
||||
{
|
||||
let content_disposition = Some(make_content_disposition(&content_type, content_disposition, None));
|
||||
|
||||
@@ -264,7 +266,7 @@ pub(crate) async fn get_content_as_filename_route(
|
||||
content_type,
|
||||
file,
|
||||
content_disposition,
|
||||
}) = services().media.get(mxc.clone()).await?
|
||||
}) = services().media.get(&mxc).await?
|
||||
{
|
||||
let content_disposition = Some(make_content_disposition(
|
||||
&content_type,
|
||||
@@ -352,7 +354,7 @@ pub(crate) async fn get_content_thumbnail_route(
|
||||
}) = services()
|
||||
.media
|
||||
.get_thumbnail(
|
||||
mxc.clone(),
|
||||
&mxc,
|
||||
body.width
|
||||
.try_into()
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Width is invalid."))?,
|
||||
@@ -396,6 +398,7 @@ pub(crate) async fn get_content_thumbnail_route(
|
||||
media_id: body.media_id.clone(),
|
||||
timeout_ms: body.timeout_ms,
|
||||
allow_redirect: body.allow_redirect,
|
||||
animated: body.animated,
|
||||
},
|
||||
)
|
||||
.await
|
||||
@@ -405,7 +408,7 @@ pub(crate) async fn get_content_thumbnail_route(
|
||||
.media
|
||||
.upload_thumbnail(
|
||||
None,
|
||||
mxc,
|
||||
&mxc,
|
||||
None,
|
||||
get_thumbnail_response.content_type.as_deref(),
|
||||
body.width.try_into().expect("all UInts are valid u32s"),
|
||||
@@ -494,7 +497,7 @@ async fn get_remote_content(
|
||||
.media
|
||||
.create(
|
||||
None,
|
||||
mxc.to_owned(),
|
||||
mxc,
|
||||
content_disposition.as_deref(),
|
||||
content_response.content_type.as_deref(),
|
||||
&content_response.file,
|
||||
@@ -520,7 +523,7 @@ async fn download_image(client: &reqwest::Client, url: &str) -> Result<UrlPrevie
|
||||
|
||||
services()
|
||||
.media
|
||||
.create(None, mxc.clone(), None, None, &image)
|
||||
.create(None, &mxc, None, None, &image)
|
||||
.await?;
|
||||
|
||||
let (width, height) = match ImgReader::new(Cursor::new(&image)).with_guessed_format() {
|
||||
|
||||
@@ -36,7 +36,6 @@ use serde_json::value::{to_raw_value, RawValue as RawJsonValue};
|
||||
use tokio::sync::RwLock;
|
||||
use tracing::{debug, error, info, trace, warn};
|
||||
|
||||
use super::get_alias_helper;
|
||||
use crate::{
|
||||
client::{update_avatar_url, update_displayname},
|
||||
service::{
|
||||
@@ -259,17 +258,24 @@ pub(crate) async fn join_room_by_id_or_alias_route(
|
||||
(servers, room_id)
|
||||
},
|
||||
Err(room_alias) => {
|
||||
let response = get_alias_helper(room_alias.clone(), Some(body.server_name.clone())).await?;
|
||||
let response = services()
|
||||
.rooms
|
||||
.alias
|
||||
.resolve_alias(&room_alias, Some(&body.server_name.clone()))
|
||||
.await?;
|
||||
let (room_id, mut pre_servers) = response;
|
||||
|
||||
banned_room_check(sender_user, Some(&response.room_id), Some(room_alias.server_name()), client).await?;
|
||||
banned_room_check(sender_user, Some(&room_id), Some(room_alias.server_name()), client).await?;
|
||||
|
||||
let mut servers = body.server_name;
|
||||
servers.extend(response.servers);
|
||||
if let Some(pre_servers) = &mut pre_servers {
|
||||
servers.append(pre_servers);
|
||||
}
|
||||
servers.extend(
|
||||
services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.servers_invite_via(&response.room_id)
|
||||
.servers_invite_via(&room_id)
|
||||
.filter_map(Result::ok),
|
||||
);
|
||||
|
||||
@@ -277,7 +283,7 @@ pub(crate) async fn join_room_by_id_or_alias_route(
|
||||
services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.invite_state(sender_user, &response.room_id)?
|
||||
.invite_state(sender_user, &room_id)?
|
||||
.unwrap_or_default()
|
||||
.iter()
|
||||
.filter_map(|event| serde_json::from_str(event.json().get()).ok())
|
||||
@@ -287,7 +293,7 @@ pub(crate) async fn join_room_by_id_or_alias_route(
|
||||
.map(|user| user.server_name().to_owned()),
|
||||
);
|
||||
|
||||
(servers, response.room_id)
|
||||
(servers, room_id)
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1049,7 +1055,7 @@ async fn join_room_by_id_helper_local(
|
||||
.filter(|user| user_is_local(user))
|
||||
.collect::<Vec<OwnedUserId>>();
|
||||
|
||||
let mut authorized_user: Option<OwnedUserId> = None;
|
||||
let mut join_authorized_via_users_server: Option<OwnedUserId> = None;
|
||||
|
||||
if restriction_rooms.iter().any(|restriction_room_id| {
|
||||
services()
|
||||
@@ -1063,10 +1069,9 @@ async fn join_room_by_id_helper_local(
|
||||
.rooms
|
||||
.state_accessor
|
||||
.user_can_invite(room_id, &user, sender_user, &state_lock)
|
||||
.await
|
||||
.unwrap_or(false)
|
||||
{
|
||||
authorized_user = Some(user);
|
||||
join_authorized_via_users_server = Some(user);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1080,7 +1085,7 @@ async fn join_room_by_id_helper_local(
|
||||
third_party_invite: None,
|
||||
blurhash: services().users.blurhash(sender_user)?,
|
||||
reason: reason.clone(),
|
||||
join_authorized_via_users_server: authorized_user,
|
||||
join_authorized_via_users_server,
|
||||
};
|
||||
|
||||
// Try normal join first
|
||||
|
||||
@@ -35,7 +35,6 @@ pub(super) mod user_directory;
|
||||
pub(super) mod voip;
|
||||
|
||||
pub(super) use account::*;
|
||||
pub use alias::get_alias_helper;
|
||||
pub(super) use alias::*;
|
||||
pub(super) use backup::*;
|
||||
pub(super) use capabilities::*;
|
||||
|
||||
@@ -365,9 +365,7 @@ pub(crate) async fn get_pushers_route(body: Ruma<get_pushers::v3::Request>) -> R
|
||||
pub(crate) async fn set_pushers_route(body: Ruma<set_pusher::v3::Request>) -> Result<set_pusher::v3::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
services()
|
||||
.pusher
|
||||
.set_pusher(sender_user, body.action.clone())?;
|
||||
services().pusher.set_pusher(sender_user, &body.action)?;
|
||||
|
||||
Ok(set_pusher::v3::Response::default())
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ pub(crate) async fn set_read_marker_route(
|
||||
services().rooms.read_receipt.readreceipt_update(
|
||||
sender_user,
|
||||
&body.room_id,
|
||||
ruma::events::receipt::ReceiptEvent {
|
||||
&ruma::events::receipt::ReceiptEvent {
|
||||
content: ruma::events::receipt::ReceiptEventContent(receipt_content),
|
||||
room_id: body.room_id.clone(),
|
||||
},
|
||||
@@ -145,7 +145,7 @@ pub(crate) async fn create_receipt_route(
|
||||
services().rooms.read_receipt.readreceipt_update(
|
||||
sender_user,
|
||||
&body.room_id,
|
||||
ruma::events::receipt::ReceiptEvent {
|
||||
&ruma::events::receipt::ReceiptEvent {
|
||||
content: ruma::events::receipt::ReceiptEventContent(receipt_content),
|
||||
room_id: body.room_id.clone(),
|
||||
},
|
||||
|
||||
@@ -118,7 +118,7 @@ pub(crate) async fn search_events_route(body: Ruma<search_events::v3::Request>)
|
||||
};
|
||||
|
||||
let mut results = Vec::new();
|
||||
let next_batch: usize = skip.saturating_add(limit);
|
||||
let next_batch = skip.saturating_add(limit);
|
||||
|
||||
for _ in 0..next_batch {
|
||||
if let Some(s) = searches
|
||||
|
||||
+8
-10
@@ -2,10 +2,10 @@ use std::str::FromStr;
|
||||
|
||||
use ruma::{
|
||||
api::client::{error::ErrorKind, space::get_hierarchy},
|
||||
uint, UInt,
|
||||
UInt,
|
||||
};
|
||||
|
||||
use crate::{service::rooms::spaces::PagnationToken, services, Error, Result, Ruma};
|
||||
use crate::{service::rooms::spaces::PaginationToken, services, Error, Result, Ruma};
|
||||
|
||||
/// # `GET /_matrix/client/v1/rooms/{room_id}/hierarchy`
|
||||
///
|
||||
@@ -14,12 +14,10 @@ use crate::{service::rooms::spaces::PagnationToken, services, Error, Result, Rum
|
||||
pub(crate) async fn get_hierarchy_route(body: Ruma<get_hierarchy::v1::Request>) -> Result<get_hierarchy::v1::Response> {
|
||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||
|
||||
let limit: usize = body
|
||||
let limit = body
|
||||
.limit
|
||||
.unwrap_or_else(|| uint!(10))
|
||||
.try_into()
|
||||
.unwrap_or(10)
|
||||
.min(100);
|
||||
.unwrap_or_else(|| UInt::from(10_u32))
|
||||
.min(UInt::from(100_u32));
|
||||
|
||||
let max_depth = body
|
||||
.max_depth
|
||||
@@ -29,7 +27,7 @@ pub(crate) async fn get_hierarchy_route(body: Ruma<get_hierarchy::v1::Request>)
|
||||
let key = body
|
||||
.from
|
||||
.as_ref()
|
||||
.and_then(|s| PagnationToken::from_str(s).ok());
|
||||
.and_then(|s| PaginationToken::from_str(s).ok());
|
||||
|
||||
// Should prevent unexpeded behaviour in (bad) clients
|
||||
if let Some(ref token) = key {
|
||||
@@ -47,8 +45,8 @@ pub(crate) async fn get_hierarchy_route(body: Ruma<get_hierarchy::v1::Request>)
|
||||
.get_client_hierarchy(
|
||||
sender_user,
|
||||
&body.room_id,
|
||||
limit,
|
||||
key.map_or(0, |token| token.skip.try_into().unwrap_or(0)),
|
||||
limit.try_into().unwrap_or(10),
|
||||
key.map_or(vec![], |token| token.short_room_ids),
|
||||
max_depth.try_into().unwrap_or(3),
|
||||
body.suggested_only,
|
||||
)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use conduit::{error, warn};
|
||||
use ruma::{
|
||||
api::client::{
|
||||
error::ErrorKind,
|
||||
@@ -16,7 +17,6 @@ use ruma::{
|
||||
serde::Raw,
|
||||
EventId, RoomId, UserId,
|
||||
};
|
||||
use tracing::{error, log::warn};
|
||||
|
||||
use crate::{
|
||||
service::{pdu::PduBuilder, server_is_ours},
|
||||
|
||||
@@ -138,7 +138,7 @@ pub(crate) async fn sync_events_route(
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Coalesce database writes for the remainder of this scope.
|
||||
let _cork = services().globals.db.cork_and_flush();
|
||||
let _cork = services().db.cork_and_flush();
|
||||
|
||||
for room_id in all_joined_rooms {
|
||||
let room_id = room_id?;
|
||||
|
||||
@@ -145,7 +145,7 @@ pub(crate) async fn syncv3_client_server_json() -> Result<impl IntoResponse> {
|
||||
|
||||
Ok(Json(serde_json::json!({
|
||||
"server": server_url,
|
||||
"version": conduit::version::conduwuit(),
|
||||
"version": conduit::version(),
|
||||
})))
|
||||
}
|
||||
|
||||
@@ -155,8 +155,8 @@ pub(crate) async fn syncv3_client_server_json() -> Result<impl IntoResponse> {
|
||||
/// `/_matrix/federation/v1/version`
|
||||
pub(crate) async fn conduwuit_server_version() -> Result<impl IntoResponse> {
|
||||
Ok(Json(serde_json::json!({
|
||||
"name": "conduwuit",
|
||||
"version": conduit::version::conduwuit(),
|
||||
"name": conduit::version::name(),
|
||||
"version": conduit::version::version(),
|
||||
})))
|
||||
}
|
||||
|
||||
|
||||
+97
-85
@@ -7,15 +7,12 @@ use ruma::{
|
||||
},
|
||||
StateEventType, TimelineEventType,
|
||||
},
|
||||
RoomVersionId,
|
||||
RoomId, RoomVersionId, UserId,
|
||||
};
|
||||
use serde_json::value::to_raw_value;
|
||||
use tracing::warn;
|
||||
|
||||
use crate::{
|
||||
service::{pdu::PduBuilder, user_is_local},
|
||||
services, Error, Result, Ruma,
|
||||
};
|
||||
use crate::{service::pdu::PduBuilder, services, Error, Result, Ruma};
|
||||
|
||||
/// # `GET /_matrix/federation/v1/make_join/{roomId}/{userId}`
|
||||
///
|
||||
@@ -72,95 +69,42 @@ pub(crate) async fn create_join_event_template_route(
|
||||
}
|
||||
}
|
||||
|
||||
let room_version_id = services().rooms.state.get_room_version(&body.room_id)?;
|
||||
|
||||
let state_lock = services()
|
||||
.globals
|
||||
.roomid_mutex_state
|
||||
.lock(&body.room_id)
|
||||
.await;
|
||||
let join_rules_event =
|
||||
services()
|
||||
|
||||
let join_authorized_via_users_server = if (services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.is_left(&body.user_id, &body.room_id)
|
||||
.unwrap_or(true))
|
||||
&& user_can_perform_restricted_join(&body.user_id, &body.room_id, &room_version_id)?
|
||||
{
|
||||
let auth_user = services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get(&body.room_id, &StateEventType::RoomJoinRules, "")?;
|
||||
|
||||
let join_rules_event_content: Option<RoomJoinRulesEventContent> = join_rules_event
|
||||
.as_ref()
|
||||
.map(|join_rules_event| {
|
||||
serde_json::from_str(join_rules_event.content.get())
|
||||
.map_err(|_| Error::bad_database("Invalid join rules event in db."))
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
let join_authorized_via_users_server = if let Some(join_rules_event_content) = join_rules_event_content {
|
||||
if let JoinRule::Restricted(r) | JoinRule::KnockRestricted(r) = join_rules_event_content.join_rule {
|
||||
if r.allow
|
||||
.iter()
|
||||
.filter_map(|rule| {
|
||||
if let AllowRule::RoomMembership(membership) = rule {
|
||||
Some(membership)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.any(|m| {
|
||||
services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.is_joined(&body.user_id, &m.room_id)
|
||||
.unwrap_or(false)
|
||||
}) {
|
||||
if services()
|
||||
.state_cache
|
||||
.room_members(&body.room_id)
|
||||
.filter_map(Result::ok)
|
||||
.filter(|user| user.server_name() == services().globals.server_name())
|
||||
.find(|user| {
|
||||
services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.is_left(&body.user_id, &body.room_id)
|
||||
.unwrap_or(true)
|
||||
{
|
||||
let members: Vec<_> = services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_members(&body.room_id)
|
||||
.filter_map(Result::ok)
|
||||
.filter(|user| user_is_local(user))
|
||||
.collect();
|
||||
.state_accessor
|
||||
.user_can_invite(&body.room_id, user, &body.user_id, &state_lock)
|
||||
.unwrap_or(false)
|
||||
});
|
||||
|
||||
let mut auth_user = None;
|
||||
|
||||
for user in members {
|
||||
if services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.user_can_invite(&body.room_id, &user, &body.user_id, &state_lock)
|
||||
.await
|
||||
.unwrap_or(false)
|
||||
{
|
||||
auth_user = Some(user);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if auth_user.is_some() {
|
||||
auth_user
|
||||
} else {
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::UnableToGrantJoin,
|
||||
"No user on this server is able to assist in joining.",
|
||||
));
|
||||
}
|
||||
} else {
|
||||
// If the user has any state other than leave, either:
|
||||
// - the auth_check will deny them (ban, knock - (until/unless MSC4123 is
|
||||
// merged))
|
||||
// - they are able to join via other methods (invite)
|
||||
// - they are already in the room (join)
|
||||
None
|
||||
}
|
||||
} else {
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::UnableToAuthorizeJoin,
|
||||
"User is not known to be in any required room.",
|
||||
));
|
||||
}
|
||||
if auth_user.is_some() {
|
||||
auth_user
|
||||
} else {
|
||||
None
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::UnableToGrantJoin,
|
||||
"No user on this server is able to assist in joining.",
|
||||
));
|
||||
}
|
||||
} else {
|
||||
None
|
||||
@@ -231,3 +175,71 @@ pub(crate) async fn create_join_event_template_route(
|
||||
event: to_raw_value(&pdu_json).expect("CanonicalJson can be serialized to JSON"),
|
||||
})
|
||||
}
|
||||
|
||||
/// Checks whether the given user can join the given room via a restricted join.
|
||||
/// This doesn't check the current user's membership. This should be done
|
||||
/// externally, either by using the state cache or attempting to authorize the
|
||||
/// event.
|
||||
pub(crate) fn user_can_perform_restricted_join(
|
||||
user_id: &UserId, room_id: &RoomId, room_version_id: &RoomVersionId,
|
||||
) -> Result<bool> {
|
||||
let join_rules_event =
|
||||
services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get(room_id, &StateEventType::RoomJoinRules, "")?;
|
||||
|
||||
let Some(join_rules_event_content) = join_rules_event
|
||||
.as_ref()
|
||||
.map(|join_rules_event| {
|
||||
serde_json::from_str::<RoomJoinRulesEventContent>(join_rules_event.content.get()).map_err(|e| {
|
||||
warn!("Invalid join rules event in database: {e}");
|
||||
Error::bad_database("Invalid join rules event in database")
|
||||
})
|
||||
})
|
||||
.transpose()?
|
||||
else {
|
||||
return Ok(false);
|
||||
};
|
||||
|
||||
if matches!(
|
||||
room_version_id,
|
||||
RoomVersionId::V1
|
||||
| RoomVersionId::V2
|
||||
| RoomVersionId::V3
|
||||
| RoomVersionId::V4
|
||||
| RoomVersionId::V5
|
||||
| RoomVersionId::V6
|
||||
| RoomVersionId::V7
|
||||
) {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let (JoinRule::Restricted(r) | JoinRule::KnockRestricted(r)) = join_rules_event_content.join_rule else {
|
||||
return Ok(false);
|
||||
};
|
||||
|
||||
if r.allow
|
||||
.iter()
|
||||
.filter_map(|rule| {
|
||||
if let AllowRule::RoomMembership(membership) = rule {
|
||||
Some(membership)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.any(|m| {
|
||||
services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.is_joined(user_id, &m.room_id)
|
||||
.unwrap_or(false)
|
||||
}) {
|
||||
Ok(true)
|
||||
} else {
|
||||
Err(Error::BadRequest(
|
||||
ErrorKind::UnableToAuthorizeJoin,
|
||||
"User is not known to be in any required room.",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
+321
-247
@@ -1,4 +1,4 @@
|
||||
use std::{collections::BTreeMap, time::Instant};
|
||||
use std::{collections::BTreeMap, net::IpAddr, time::Instant};
|
||||
|
||||
use axum_client_ip::InsecureClientIp;
|
||||
use conduit::debug_warn;
|
||||
@@ -6,12 +6,16 @@ use ruma::{
|
||||
api::{
|
||||
client::error::ErrorKind,
|
||||
federation::transactions::{
|
||||
edu::{DeviceListUpdateContent, DirectDeviceContent, Edu, SigningKeyUpdateContent},
|
||||
edu::{
|
||||
DeviceListUpdateContent, DirectDeviceContent, Edu, PresenceContent, ReceiptContent,
|
||||
SigningKeyUpdateContent, TypingContent,
|
||||
},
|
||||
send_transaction_message,
|
||||
},
|
||||
},
|
||||
events::receipt::{ReceiptEvent, ReceiptEventContent, ReceiptType},
|
||||
to_device::DeviceIdOrAllDevices,
|
||||
OwnedEventId, ServerName,
|
||||
};
|
||||
use tokio::sync::RwLock;
|
||||
use tracing::{debug, error, trace, warn};
|
||||
@@ -23,6 +27,8 @@ use crate::{
|
||||
Error, Result, Ruma,
|
||||
};
|
||||
|
||||
type ResolvedMap = BTreeMap<OwnedEventId, Result<(), Error>>;
|
||||
|
||||
/// # `PUT /_matrix/federation/v1/send/{txnId}`
|
||||
///
|
||||
/// Push EDUs and PDUs to this server.
|
||||
@@ -53,15 +59,39 @@ pub(crate) async fn send_transaction_message_route(
|
||||
));
|
||||
}
|
||||
|
||||
// This is all the auth_events that have been recursively fetched so they don't
|
||||
// have to be deserialized over and over again.
|
||||
// TODO: make this persist across requests but not in a DB Tree (in globals?)
|
||||
// TODO: This could potentially also be some sort of trie (suffix tree) like
|
||||
// structure so that once an auth event is known it would know (using indexes
|
||||
// maybe) all of the auth events that it references.
|
||||
// let mut auth_cache = EventMap::new();
|
||||
|
||||
let txn_start_time = Instant::now();
|
||||
trace!(
|
||||
pdus = ?body.pdus.len(),
|
||||
edus = ?body.edus.len(),
|
||||
elapsed = ?txn_start_time.elapsed(),
|
||||
id = ?body.transaction_id,
|
||||
origin =?body.origin,
|
||||
"Starting txn",
|
||||
);
|
||||
|
||||
let resolved_map = handle_pdus(&client, &body, origin, &txn_start_time).await?;
|
||||
handle_edus(&client, &body, origin).await?;
|
||||
|
||||
debug!(
|
||||
pdus = ?body.pdus.len(),
|
||||
edus = ?body.edus.len(),
|
||||
elapsed = ?txn_start_time.elapsed(),
|
||||
id = ?body.transaction_id,
|
||||
origin =?body.origin,
|
||||
"Finished txn",
|
||||
);
|
||||
|
||||
Ok(send_transaction_message::v1::Response {
|
||||
pdus: resolved_map
|
||||
.into_iter()
|
||||
.map(|(e, r)| (e, r.map_err(|e| e.sanitized_error())))
|
||||
.collect(),
|
||||
})
|
||||
}
|
||||
|
||||
async fn handle_pdus(
|
||||
_client: &IpAddr, body: &Ruma<send_transaction_message::v1::Request>, origin: &ServerName, txn_start_time: &Instant,
|
||||
) -> Result<ResolvedMap> {
|
||||
let mut parsed_pdus = Vec::with_capacity(body.pdus.len());
|
||||
for pdu in &body.pdus {
|
||||
parsed_pdus.push(match parse_incoming_pdu(pdu) {
|
||||
@@ -76,15 +106,6 @@ pub(crate) async fn send_transaction_message_route(
|
||||
// and hashes checks
|
||||
}
|
||||
|
||||
trace!(
|
||||
pdus = ?parsed_pdus.len(),
|
||||
edus = ?body.edus.len(),
|
||||
elapsed = ?txn_start_time.elapsed(),
|
||||
id = ?body.transaction_id,
|
||||
origin =?body.origin,
|
||||
"Starting txn",
|
||||
);
|
||||
|
||||
// We go through all the signatures we see on the PDUs and fetch the
|
||||
// corresponding signing keys
|
||||
let pub_key_map = RwLock::new(BTreeMap::new());
|
||||
@@ -94,9 +115,7 @@ pub(crate) async fn send_transaction_message_route(
|
||||
.event_handler
|
||||
.fetch_required_signing_keys(parsed_pdus.iter().map(|(_event_id, event, _room_id)| event), &pub_key_map)
|
||||
.await
|
||||
.unwrap_or_else(|e| {
|
||||
warn!("Could not fetch all signatures for PDUs from {origin}: {:?}", e);
|
||||
});
|
||||
.unwrap_or_else(|e| warn!("Could not fetch all signatures for PDUs from {origin}: {e:?}"));
|
||||
|
||||
debug!(
|
||||
elapsed = ?txn_start_time.elapsed(),
|
||||
@@ -133,244 +152,299 @@ pub(crate) async fn send_transaction_message_route(
|
||||
for pdu in &resolved_map {
|
||||
if let Err(e) = pdu.1 {
|
||||
if matches!(e, Error::BadRequest(ErrorKind::NotFound, _)) {
|
||||
warn!("Incoming PDU failed {:?}", pdu);
|
||||
warn!("Incoming PDU failed {pdu:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(resolved_map)
|
||||
}
|
||||
|
||||
async fn handle_edus(
|
||||
client: &IpAddr, body: &Ruma<send_transaction_message::v1::Request>, origin: &ServerName,
|
||||
) -> Result<()> {
|
||||
for edu in body
|
||||
.edus
|
||||
.iter()
|
||||
.filter_map(|edu| serde_json::from_str::<Edu>(edu.json().get()).ok())
|
||||
{
|
||||
match edu {
|
||||
Edu::Presence(presence) => {
|
||||
if !services().globals.allow_incoming_presence() {
|
||||
continue;
|
||||
}
|
||||
|
||||
for update in presence.push {
|
||||
if update.user_id.server_name() != origin {
|
||||
debug_warn!(%update.user_id, %origin, "received presence EDU for user not belonging to origin");
|
||||
continue;
|
||||
}
|
||||
|
||||
services().presence.set_presence(
|
||||
&update.user_id,
|
||||
&update.presence,
|
||||
Some(update.currently_active),
|
||||
Some(update.last_active_ago),
|
||||
update.status_msg.clone(),
|
||||
)?;
|
||||
}
|
||||
},
|
||||
Edu::Receipt(receipt) => {
|
||||
if !services().globals.allow_incoming_read_receipts() {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (room_id, room_updates) in receipt.receipts {
|
||||
if services()
|
||||
.rooms
|
||||
.event_handler
|
||||
.acl_check(origin, &room_id)
|
||||
.is_err()
|
||||
{
|
||||
debug_warn!(%origin, %room_id, "received read receipt EDU from ACL'd server");
|
||||
continue;
|
||||
}
|
||||
|
||||
for (user_id, user_updates) in room_updates.read {
|
||||
if user_id.server_name() != origin {
|
||||
debug_warn!(%user_id, %origin, "received read receipt EDU for user not belonging to origin");
|
||||
continue;
|
||||
}
|
||||
|
||||
if services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_members(&room_id)
|
||||
.filter_map(Result::ok)
|
||||
.any(|member| member.server_name() == user_id.server_name())
|
||||
{
|
||||
for event_id in &user_updates.event_ids {
|
||||
let user_receipts = BTreeMap::from([(user_id.clone(), user_updates.data.clone())]);
|
||||
|
||||
let receipts = BTreeMap::from([(ReceiptType::Read, user_receipts)]);
|
||||
|
||||
let receipt_content = BTreeMap::from([(event_id.to_owned(), receipts)]);
|
||||
|
||||
let event = ReceiptEvent {
|
||||
content: ReceiptEventContent(receipt_content),
|
||||
room_id: room_id.clone(),
|
||||
};
|
||||
|
||||
services()
|
||||
.rooms
|
||||
.read_receipt
|
||||
.readreceipt_update(&user_id, &room_id, event)?;
|
||||
}
|
||||
} else {
|
||||
debug_warn!(%user_id, %room_id, %origin, "received read receipt EDU from server who does not have a single member from their server in the room");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Edu::Typing(typing) => {
|
||||
if !services().globals.config.allow_incoming_typing {
|
||||
continue;
|
||||
}
|
||||
|
||||
if typing.user_id.server_name() != origin {
|
||||
debug_warn!(%typing.user_id, %origin, "received typing EDU for user not belonging to origin");
|
||||
continue;
|
||||
}
|
||||
|
||||
if services()
|
||||
.rooms
|
||||
.event_handler
|
||||
.acl_check(typing.user_id.server_name(), &typing.room_id)
|
||||
.is_err()
|
||||
{
|
||||
debug_warn!(%typing.user_id, %typing.room_id, %origin, "received typing EDU for ACL'd user's server");
|
||||
continue;
|
||||
}
|
||||
|
||||
if services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.is_joined(&typing.user_id, &typing.room_id)?
|
||||
{
|
||||
if typing.typing {
|
||||
let timeout = utils::millis_since_unix_epoch().saturating_add(
|
||||
services()
|
||||
.globals
|
||||
.config
|
||||
.typing_federation_timeout_s
|
||||
.saturating_mul(1000),
|
||||
);
|
||||
services()
|
||||
.rooms
|
||||
.typing
|
||||
.typing_add(&typing.user_id, &typing.room_id, timeout)
|
||||
.await?;
|
||||
} else {
|
||||
services()
|
||||
.rooms
|
||||
.typing
|
||||
.typing_remove(&typing.user_id, &typing.room_id)
|
||||
.await?;
|
||||
}
|
||||
} else {
|
||||
debug_warn!(%typing.user_id, %typing.room_id, %origin, "received typing EDU for user not in room");
|
||||
continue;
|
||||
}
|
||||
},
|
||||
Edu::DeviceListUpdate(DeviceListUpdateContent {
|
||||
user_id,
|
||||
..
|
||||
}) => {
|
||||
if user_id.server_name() != origin {
|
||||
debug_warn!(%user_id, %origin, "received device list update EDU for user not belonging to origin");
|
||||
continue;
|
||||
}
|
||||
|
||||
services().users.mark_device_key_update(&user_id)?;
|
||||
},
|
||||
Edu::DirectToDevice(DirectDeviceContent {
|
||||
sender,
|
||||
ev_type,
|
||||
message_id,
|
||||
messages,
|
||||
}) => {
|
||||
if sender.server_name() != origin {
|
||||
debug_warn!(%sender, %origin, "received direct to device EDU for user not belonging to origin");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if this is a new transaction id
|
||||
if services()
|
||||
.transaction_ids
|
||||
.existing_txnid(&sender, None, &message_id)?
|
||||
.is_some()
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (target_user_id, map) in &messages {
|
||||
for (target_device_id_maybe, event) in map {
|
||||
match target_device_id_maybe {
|
||||
DeviceIdOrAllDevices::DeviceId(target_device_id) => {
|
||||
services().users.add_to_device_event(
|
||||
&sender,
|
||||
target_user_id,
|
||||
target_device_id,
|
||||
&ev_type.to_string(),
|
||||
event.deserialize_as().map_err(|e| {
|
||||
error!("To-Device event is invalid: {event:?} {e}");
|
||||
Error::BadRequest(ErrorKind::InvalidParam, "Event is invalid")
|
||||
})?,
|
||||
)?;
|
||||
},
|
||||
|
||||
DeviceIdOrAllDevices::AllDevices => {
|
||||
for target_device_id in services().users.all_device_ids(target_user_id) {
|
||||
services().users.add_to_device_event(
|
||||
&sender,
|
||||
target_user_id,
|
||||
&target_device_id?,
|
||||
&ev_type.to_string(),
|
||||
event.deserialize_as().map_err(|_| {
|
||||
Error::BadRequest(ErrorKind::InvalidParam, "Event is invalid")
|
||||
})?,
|
||||
)?;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Save transaction id with empty data
|
||||
services()
|
||||
.transaction_ids
|
||||
.add_txnid(&sender, None, &message_id, &[])?;
|
||||
},
|
||||
Edu::SigningKeyUpdate(SigningKeyUpdateContent {
|
||||
user_id,
|
||||
master_key,
|
||||
self_signing_key,
|
||||
}) => {
|
||||
if user_id.server_name() != origin {
|
||||
debug_warn!(%user_id, %origin, "received signing key update EDU from server that does not belong to user's server");
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(master_key) = master_key {
|
||||
services()
|
||||
.users
|
||||
.add_cross_signing_keys(&user_id, &master_key, &self_signing_key, &None, true)?;
|
||||
}
|
||||
},
|
||||
Edu::Presence(presence) => handle_edu_presence(client, origin, presence).await?,
|
||||
Edu::Receipt(receipt) => handle_edu_receipt(client, origin, receipt).await?,
|
||||
Edu::Typing(typing) => handle_edu_typing(client, origin, typing).await?,
|
||||
Edu::DeviceListUpdate(content) => handle_edu_device_list_update(client, origin, content).await?,
|
||||
Edu::DirectToDevice(content) => handle_edu_direct_to_device(client, origin, content).await?,
|
||||
Edu::SigningKeyUpdate(content) => handle_edu_signing_key_update(client, origin, content).await?,
|
||||
Edu::_Custom(ref _custom) => {
|
||||
debug_warn!(?edu, "received custom/unknown EDU");
|
||||
debug_warn!(?body.edus, "received custom/unknown EDU");
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
debug!(
|
||||
pdus = ?body.pdus.len(),
|
||||
edus = ?body.edus.len(),
|
||||
elapsed = ?txn_start_time.elapsed(),
|
||||
id = ?body.transaction_id,
|
||||
origin =?body.origin,
|
||||
"Finished txn",
|
||||
);
|
||||
|
||||
Ok(send_transaction_message::v1::Response {
|
||||
pdus: resolved_map
|
||||
.into_iter()
|
||||
.map(|(e, r)| (e, r.map_err(|e| e.sanitized_error())))
|
||||
.collect(),
|
||||
})
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_edu_presence(_client: &IpAddr, origin: &ServerName, presence: PresenceContent) -> Result<()> {
|
||||
if !services().globals.allow_incoming_presence() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
for update in presence.push {
|
||||
if update.user_id.server_name() != origin {
|
||||
debug_warn!(
|
||||
%update.user_id, %origin,
|
||||
"received presence EDU for user not belonging to origin"
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
services().presence.set_presence(
|
||||
&update.user_id,
|
||||
&update.presence,
|
||||
Some(update.currently_active),
|
||||
Some(update.last_active_ago),
|
||||
update.status_msg.clone(),
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_edu_receipt(_client: &IpAddr, origin: &ServerName, receipt: ReceiptContent) -> Result<()> {
|
||||
if !services().globals.allow_incoming_read_receipts() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
for (room_id, room_updates) in receipt.receipts {
|
||||
if services()
|
||||
.rooms
|
||||
.event_handler
|
||||
.acl_check(origin, &room_id)
|
||||
.is_err()
|
||||
{
|
||||
debug_warn!(
|
||||
%origin, %room_id,
|
||||
"received read receipt EDU from ACL'd server"
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (user_id, user_updates) in room_updates.read {
|
||||
if user_id.server_name() != origin {
|
||||
debug_warn!(
|
||||
%user_id, %origin,
|
||||
"received read receipt EDU for user not belonging to origin"
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
if services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_members(&room_id)
|
||||
.filter_map(Result::ok)
|
||||
.any(|member| member.server_name() == user_id.server_name())
|
||||
{
|
||||
for event_id in &user_updates.event_ids {
|
||||
let user_receipts = BTreeMap::from([(user_id.clone(), user_updates.data.clone())]);
|
||||
let receipts = BTreeMap::from([(ReceiptType::Read, user_receipts)]);
|
||||
let receipt_content = BTreeMap::from([(event_id.to_owned(), receipts)]);
|
||||
let event = ReceiptEvent {
|
||||
content: ReceiptEventContent(receipt_content),
|
||||
room_id: room_id.clone(),
|
||||
};
|
||||
|
||||
services()
|
||||
.rooms
|
||||
.read_receipt
|
||||
.readreceipt_update(&user_id, &room_id, &event)?;
|
||||
}
|
||||
} else {
|
||||
debug_warn!(
|
||||
%user_id, %room_id, %origin,
|
||||
"received read receipt EDU from server who does not have a member in the room",
|
||||
);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_edu_typing(_client: &IpAddr, origin: &ServerName, typing: TypingContent) -> Result<()> {
|
||||
if !services().globals.config.allow_incoming_typing {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if typing.user_id.server_name() != origin {
|
||||
debug_warn!(
|
||||
%typing.user_id, %origin,
|
||||
"received typing EDU for user not belonging to origin"
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if services()
|
||||
.rooms
|
||||
.event_handler
|
||||
.acl_check(typing.user_id.server_name(), &typing.room_id)
|
||||
.is_err()
|
||||
{
|
||||
debug_warn!(
|
||||
%typing.user_id, %typing.room_id, %origin,
|
||||
"received typing EDU for ACL'd user's server"
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.is_joined(&typing.user_id, &typing.room_id)?
|
||||
{
|
||||
if typing.typing {
|
||||
let timeout = utils::millis_since_unix_epoch().saturating_add(
|
||||
services()
|
||||
.globals
|
||||
.config
|
||||
.typing_federation_timeout_s
|
||||
.saturating_mul(1000),
|
||||
);
|
||||
services()
|
||||
.rooms
|
||||
.typing
|
||||
.typing_add(&typing.user_id, &typing.room_id, timeout)
|
||||
.await?;
|
||||
} else {
|
||||
services()
|
||||
.rooms
|
||||
.typing
|
||||
.typing_remove(&typing.user_id, &typing.room_id)
|
||||
.await?;
|
||||
}
|
||||
} else {
|
||||
debug_warn!(
|
||||
%typing.user_id, %typing.room_id, %origin,
|
||||
"received typing EDU for user not in room"
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_edu_device_list_update(
|
||||
_client: &IpAddr, origin: &ServerName, content: DeviceListUpdateContent,
|
||||
) -> Result<()> {
|
||||
let DeviceListUpdateContent {
|
||||
user_id,
|
||||
..
|
||||
} = content;
|
||||
|
||||
if user_id.server_name() != origin {
|
||||
debug_warn!(
|
||||
%user_id, %origin,
|
||||
"received device list update EDU for user not belonging to origin"
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
services().users.mark_device_key_update(&user_id)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_edu_direct_to_device(
|
||||
_client: &IpAddr, origin: &ServerName, content: DirectDeviceContent,
|
||||
) -> Result<()> {
|
||||
let DirectDeviceContent {
|
||||
sender,
|
||||
ev_type,
|
||||
message_id,
|
||||
messages,
|
||||
} = content;
|
||||
|
||||
if sender.server_name() != origin {
|
||||
debug_warn!(
|
||||
%sender, %origin,
|
||||
"received direct to device EDU for user not belonging to origin"
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Check if this is a new transaction id
|
||||
if services()
|
||||
.transaction_ids
|
||||
.existing_txnid(&sender, None, &message_id)?
|
||||
.is_some()
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
for (target_user_id, map) in &messages {
|
||||
for (target_device_id_maybe, event) in map {
|
||||
match target_device_id_maybe {
|
||||
DeviceIdOrAllDevices::DeviceId(target_device_id) => {
|
||||
services().users.add_to_device_event(
|
||||
&sender,
|
||||
target_user_id,
|
||||
target_device_id,
|
||||
&ev_type.to_string(),
|
||||
event.deserialize_as().map_err(|e| {
|
||||
error!("To-Device event is invalid: {event:?} {e}");
|
||||
Error::BadRequest(ErrorKind::InvalidParam, "Event is invalid")
|
||||
})?,
|
||||
)?;
|
||||
},
|
||||
|
||||
DeviceIdOrAllDevices::AllDevices => {
|
||||
for target_device_id in services().users.all_device_ids(target_user_id) {
|
||||
services().users.add_to_device_event(
|
||||
&sender,
|
||||
target_user_id,
|
||||
&target_device_id?,
|
||||
&ev_type.to_string(),
|
||||
event
|
||||
.deserialize_as()
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Event is invalid"))?,
|
||||
)?;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Save transaction id with empty data
|
||||
services()
|
||||
.transaction_ids
|
||||
.add_txnid(&sender, None, &message_id, &[])?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_edu_signing_key_update(
|
||||
_client: &IpAddr, origin: &ServerName, content: SigningKeyUpdateContent,
|
||||
) -> Result<()> {
|
||||
let SigningKeyUpdateContent {
|
||||
user_id,
|
||||
master_key,
|
||||
self_signing_key,
|
||||
} = content;
|
||||
|
||||
if user_id.server_name() != origin {
|
||||
debug_warn!(
|
||||
%user_id, %origin,
|
||||
"received signing key update EDU from server that does not belong to user's server"
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let Some(master_key) = master_key {
|
||||
services()
|
||||
.users
|
||||
.add_cross_signing_keys(&user_id, &master_key, &self_signing_key, &None, true)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+31
-27
@@ -4,10 +4,14 @@ use std::collections::BTreeMap;
|
||||
|
||||
use ruma::{
|
||||
api::{client::error::ErrorKind, federation::membership::create_join_event},
|
||||
events::{room::member::MembershipState, StateEventType},
|
||||
events::{
|
||||
room::member::{MembershipState, RoomMemberEventContent},
|
||||
StateEventType,
|
||||
},
|
||||
CanonicalJsonValue, OwnedServerName, OwnedUserId, RoomId, ServerName,
|
||||
};
|
||||
use serde_json::value::{to_raw_value, RawValue as RawJsonValue};
|
||||
use service::user_is_local;
|
||||
use tokio::sync::RwLock;
|
||||
use tracing::warn;
|
||||
|
||||
@@ -63,22 +67,16 @@ async fn create_join_event(
|
||||
));
|
||||
}
|
||||
|
||||
let content = value
|
||||
.get("content")
|
||||
.ok_or_else(|| Error::BadRequest(ErrorKind::InvalidParam, "Event missing content property."))?
|
||||
.as_object()
|
||||
.ok_or_else(|| Error::BadRequest(ErrorKind::InvalidParam, "Event content is empty or invalid."))?;
|
||||
|
||||
let membership: MembershipState = serde_json::from_value(
|
||||
content
|
||||
.get("membership")
|
||||
.ok_or_else(|| Error::BadRequest(ErrorKind::InvalidParam, "Event content missing membership property."))?
|
||||
let content: RoomMemberEventContent = serde_json::from_value(
|
||||
value
|
||||
.get("content")
|
||||
.ok_or_else(|| Error::BadRequest(ErrorKind::InvalidParam, "Event missing content property"))?
|
||||
.clone()
|
||||
.into(),
|
||||
)
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Event has an invalid membership state."))?;
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Event content is empty or invalid"))?;
|
||||
|
||||
if membership != MembershipState::Join {
|
||||
if content.membership != MembershipState::Join {
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::InvalidParam,
|
||||
"Not allowed to send a non-join membership event to join endpoint.",
|
||||
@@ -122,15 +120,27 @@ async fn create_join_event(
|
||||
ErrorKind::InvalidParam,
|
||||
"State key does not match sender user",
|
||||
));
|
||||
};
|
||||
|
||||
if content
|
||||
.join_authorized_via_users_server
|
||||
.is_some_and(|user| user_is_local(&user))
|
||||
&& super::user_can_perform_restricted_join(&sender, room_id, &room_version_id).unwrap_or_default()
|
||||
{
|
||||
ruma::signatures::hash_and_sign_event(
|
||||
services().globals.server_name().as_str(),
|
||||
services().globals.keypair(),
|
||||
&mut value,
|
||||
&room_version_id,
|
||||
)
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Failed to sign event."))?;
|
||||
}
|
||||
|
||||
ruma::signatures::hash_and_sign_event(
|
||||
services().globals.server_name().as_str(),
|
||||
services().globals.keypair(),
|
||||
&mut value,
|
||||
&room_version_id,
|
||||
)
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Failed to sign event."))?;
|
||||
services()
|
||||
.rooms
|
||||
.event_handler
|
||||
.fetch_required_signing_keys([&value], &pub_key_map)
|
||||
.await?;
|
||||
|
||||
let origin: OwnedServerName = serde_json::from_value(
|
||||
serde_json::to_value(
|
||||
@@ -142,12 +152,6 @@ async fn create_join_event(
|
||||
)
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "origin is not a server name."))?;
|
||||
|
||||
services()
|
||||
.rooms
|
||||
.event_handler
|
||||
.fetch_required_signing_keys([&value], &pub_key_map)
|
||||
.await?;
|
||||
|
||||
let mutex_lock = services()
|
||||
.globals
|
||||
.roomid_mutex_federation
|
||||
@@ -186,7 +190,7 @@ async fn create_join_event(
|
||||
.collect(),
|
||||
// Event field is required if the room version supports restricted join rules.
|
||||
event: Some(
|
||||
to_raw_value(&CanonicalJsonValue::Object(value.clone()))
|
||||
to_raw_value(&CanonicalJsonValue::Object(value))
|
||||
.expect("To raw json should not fail since only change was adding signature"),
|
||||
),
|
||||
})
|
||||
|
||||
@@ -4,7 +4,10 @@ use std::collections::BTreeMap;
|
||||
|
||||
use ruma::{
|
||||
api::{client::error::ErrorKind, federation::membership::create_leave_event},
|
||||
events::{room::member::MembershipState, StateEventType},
|
||||
events::{
|
||||
room::member::{MembershipState, RoomMemberEventContent},
|
||||
StateEventType,
|
||||
},
|
||||
OwnedServerName, OwnedUserId, RoomId, ServerName,
|
||||
};
|
||||
use serde_json::value::RawValue as RawJsonValue;
|
||||
@@ -62,22 +65,16 @@ async fn create_leave_event(origin: &ServerName, room_id: &RoomId, pdu: &RawJson
|
||||
));
|
||||
};
|
||||
|
||||
let content = value
|
||||
.get("content")
|
||||
.ok_or_else(|| Error::BadRequest(ErrorKind::InvalidParam, "Event missing content property."))?
|
||||
.as_object()
|
||||
.ok_or_else(|| Error::BadRequest(ErrorKind::InvalidParam, "Event content not an object."))?;
|
||||
|
||||
let membership: MembershipState = serde_json::from_value(
|
||||
content
|
||||
.get("membership")
|
||||
.ok_or_else(|| Error::BadRequest(ErrorKind::InvalidParam, "Event membership is missing."))?
|
||||
let content: RoomMemberEventContent = serde_json::from_value(
|
||||
value
|
||||
.get("content")
|
||||
.ok_or_else(|| Error::BadRequest(ErrorKind::InvalidParam, "Event missing content property"))?
|
||||
.clone()
|
||||
.into(),
|
||||
)
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Event membership state is not valid."))?;
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Event content is empty or invalid"))?;
|
||||
|
||||
if membership != MembershipState::Leave {
|
||||
if content.membership != MembershipState::Leave {
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::InvalidParam,
|
||||
"Not allowed to send a non-leave membership event to leave endpoint.",
|
||||
|
||||
@@ -10,8 +10,8 @@ pub(crate) async fn get_server_version_route(
|
||||
) -> Result<get_server_version::v1::Response> {
|
||||
Ok(get_server_version::v1::Response {
|
||||
server: Some(get_server_version::v1::Server {
|
||||
name: Some("Conduwuit".to_owned()),
|
||||
version: Some(conduit::version::conduwuit()),
|
||||
name: Some(conduit::version::name().into()),
|
||||
version: Some(conduit::version::version().into()),
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
+3
-24
@@ -17,26 +17,17 @@ crate-type = [
|
||||
]
|
||||
|
||||
[features]
|
||||
dev_release_log_level = []
|
||||
#dev_release_log_level = []
|
||||
release_max_log_level = [
|
||||
"tracing/max_level_trace",
|
||||
"tracing/release_max_level_info",
|
||||
"log/max_level_trace",
|
||||
"log/release_max_level_info",
|
||||
]
|
||||
sqlite = [
|
||||
"dep:rusqlite",
|
||||
"dep:parking_lot",
|
||||
"dep:thread_local",
|
||||
]
|
||||
rocksdb = [
|
||||
"dep:rust-rocksdb",
|
||||
]
|
||||
jemalloc = [
|
||||
"dep:tikv-jemalloc-sys",
|
||||
"dep:tikv-jemalloc-ctl",
|
||||
"dep:tikv-jemallocator",
|
||||
"rust-rocksdb/jemalloc",
|
||||
]
|
||||
jemalloc_prof = [
|
||||
"tikv-jemalloc-sys/profiling",
|
||||
@@ -44,18 +35,13 @@ jemalloc_prof = [
|
||||
hardened_malloc = [
|
||||
"dep:hardened_malloc-rs"
|
||||
]
|
||||
io_uring = [
|
||||
"rust-rocksdb/io-uring",
|
||||
]
|
||||
zstd_compression = [
|
||||
"rust-rocksdb/zstd",
|
||||
]
|
||||
gzip_compression = [
|
||||
"reqwest/gzip",
|
||||
]
|
||||
brotli_compression = [
|
||||
"reqwest/brotli",
|
||||
]
|
||||
zstd_compression =[]
|
||||
perf_measurements = []
|
||||
sentry_telemetry = []
|
||||
|
||||
@@ -63,6 +49,7 @@ sentry_telemetry = []
|
||||
argon2.workspace = true
|
||||
axum.workspace = true
|
||||
bytes.workspace = true
|
||||
chrono.workspace = true
|
||||
either.workspace = true
|
||||
figment.workspace = true
|
||||
http-body-util.workspace = true
|
||||
@@ -72,24 +59,16 @@ ipaddress.workspace = true
|
||||
itertools.workspace = true
|
||||
libloading.workspace = true
|
||||
log.workspace = true
|
||||
parking_lot.optional = true
|
||||
parking_lot.workspace = true
|
||||
rand.workspace = true
|
||||
regex.workspace = true
|
||||
reqwest.workspace = true
|
||||
ring.workspace = true
|
||||
ruma.workspace = true
|
||||
rusqlite.optional = true
|
||||
rusqlite.workspace = true
|
||||
rust-rocksdb.optional = true
|
||||
rust-rocksdb.workspace = true
|
||||
sanitize-filename.workspace = true
|
||||
serde_json.workspace = true
|
||||
serde_regex.workspace = true
|
||||
serde.workspace = true
|
||||
thiserror.workspace = true
|
||||
thread_local.optional = true
|
||||
thread_local.workspace = true
|
||||
tikv-jemallocator.optional = true
|
||||
tikv-jemallocator.workspace = true
|
||||
tikv-jemalloc-ctl.optional = true
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! hmalloc allocator
|
||||
|
||||
#[global_allocator]
|
||||
static HMALLOC: hardened_malloc_rs::HardenedMalloc = hardened_malloc_rs::HardenedMalloc;
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! jemalloc allocator
|
||||
|
||||
use std::ffi::{c_char, c_void};
|
||||
|
||||
use tikv_jemalloc_ctl as mallctl;
|
||||
|
||||
+8
-18
@@ -1,27 +1,17 @@
|
||||
//! Integration with allocators
|
||||
|
||||
// jemalloc
|
||||
#[cfg(all(not(target_env = "msvc"), feature = "jemalloc", not(feature = "hardened_malloc")))]
|
||||
#[cfg(all(not(target_env = "msvc"), feature = "jemalloc"))]
|
||||
pub mod je;
|
||||
#[cfg(all(not(target_env = "msvc"), feature = "jemalloc", not(feature = "hardened_malloc")))]
|
||||
#[cfg(all(not(target_env = "msvc"), feature = "jemalloc"))]
|
||||
pub use je::{memory_stats, memory_usage};
|
||||
|
||||
// hardened_malloc
|
||||
//#[cfg(all(not(target_env = "msvc"), feature = "hardened_malloc", target_os =
|
||||
//#[cfg(all(not(target_env "linux", not(feature = "jemalloc")))]
|
||||
//pub mod hardened;
|
||||
//#[cfg(all(not(target_env = "msvc"), feature = "hardened_malloc", target_os =
|
||||
//#[cfg(all(not(target_env "linux", not(feature = "jemalloc")))]
|
||||
//pub use hardened::{memory_stats, memory_usage};
|
||||
#[cfg(all(not(target_env = "msvc"), feature = "hardened_malloc", not(feature = "jemalloc")))]
|
||||
pub mod hardened;
|
||||
#[cfg(all(not(target_env = "msvc"), feature = "hardened_malloc", not(feature = "jemalloc")))]
|
||||
pub use hardened::{memory_stats, memory_usage};
|
||||
|
||||
// default, enabled when none or multiple of the above are enabled
|
||||
#[cfg(any(
|
||||
not(any(feature = "jemalloc", feature = "hardened_malloc")),
|
||||
all(feature = "jemalloc", feature = "hardened_malloc"),
|
||||
))]
|
||||
#[cfg(any(target_env = "msvc", all(not(feature = "hardened_malloc"), not(feature = "jemalloc"))))]
|
||||
pub mod default;
|
||||
#[cfg(any(
|
||||
not(any(feature = "jemalloc", feature = "hardened_malloc")),
|
||||
all(feature = "jemalloc", feature = "hardened_malloc"),
|
||||
))]
|
||||
#[cfg(any(target_env = "msvc", all(not(feature = "hardened_malloc"), not(feature = "jemalloc"))))]
|
||||
pub use default::{memory_stats, memory_usage};
|
||||
|
||||
@@ -14,10 +14,7 @@ pub fn check(config: &Config) -> Result<(), Error> {
|
||||
}
|
||||
|
||||
if cfg!(feature = "hardened_malloc") && cfg!(feature = "jemalloc") {
|
||||
warn!(
|
||||
"hardened_malloc and jemalloc were built together, this causes neither to be used. Conduwuit will still \
|
||||
function, but consider rebuilding and pick one as this is now no-op."
|
||||
);
|
||||
warn!("hardened_malloc and jemalloc are both enabled, this causes jemalloc to be used.");
|
||||
}
|
||||
|
||||
if config.unix_socket_path.is_some() && !cfg!(unix) {
|
||||
@@ -66,7 +63,7 @@ pub fn check(config: &Config) -> Result<(), Error> {
|
||||
});
|
||||
|
||||
// rocksdb does not allow max_log_files to be 0
|
||||
if config.rocksdb_max_log_files == 0 && cfg!(feature = "rocksdb") {
|
||||
if config.rocksdb_max_log_files == 0 {
|
||||
return Err(Error::bad_config(
|
||||
"When using RocksDB, rocksdb_max_log_files cannot be 0. Please set a value at least 1.",
|
||||
));
|
||||
|
||||
+56
-70
@@ -300,8 +300,13 @@ pub struct Config {
|
||||
#[serde(default)]
|
||||
pub allow_guests_auto_join_rooms: bool,
|
||||
|
||||
#[serde(default = "true_fn")]
|
||||
pub media_startup_check: bool,
|
||||
#[serde(default = "true_fn")]
|
||||
pub media_compat_file_link: bool,
|
||||
#[serde(default = "Vec::new")]
|
||||
pub prevent_media_downloads_from: Vec<OwnedServerName>,
|
||||
|
||||
#[serde(default = "Vec::new")]
|
||||
pub forbidden_remote_server_names: Vec<OwnedServerName>,
|
||||
#[serde(default = "Vec::new")]
|
||||
@@ -350,6 +355,9 @@ pub struct Config {
|
||||
#[serde(default = "default_sentry_traces_sample_rate")]
|
||||
pub sentry_traces_sample_rate: f32,
|
||||
|
||||
#[serde(default)]
|
||||
pub tokio_console: bool,
|
||||
|
||||
#[serde(flatten)]
|
||||
#[allow(clippy::zero_sized_map_values)] // this is a catchall, the map shouldn't be zero at runtime
|
||||
catchall: BTreeMap<String, IgnoredAny>,
|
||||
@@ -513,10 +521,9 @@ impl fmt::Display for Config {
|
||||
("Database path", &self.database_path.to_string_lossy()),
|
||||
(
|
||||
"Database backup path",
|
||||
match &self.database_backup_path {
|
||||
Some(path) => path.to_str().unwrap(),
|
||||
None => "",
|
||||
},
|
||||
self.database_backup_path
|
||||
.as_ref()
|
||||
.map_or("", |path| path.to_str().unwrap_or("")),
|
||||
),
|
||||
("Database backups to keep", &self.database_backups_to_keep.to_string()),
|
||||
("Database cache capacity (MB)", &self.db_cache_capacity_mb.to_string()),
|
||||
@@ -568,9 +575,10 @@ impl fmt::Display for Config {
|
||||
("Allow registration", &self.allow_registration.to_string()),
|
||||
(
|
||||
"Registration token",
|
||||
match self.registration_token {
|
||||
Some(_) => "set",
|
||||
None => "not set (open registration!)",
|
||||
if self.registration_token.is_some() {
|
||||
"set"
|
||||
} else {
|
||||
"not set (open registration!)"
|
||||
},
|
||||
),
|
||||
(
|
||||
@@ -651,13 +659,14 @@ impl fmt::Display for Config {
|
||||
None => "not set",
|
||||
},
|
||||
),
|
||||
("Trusted key servers", {
|
||||
let mut lst = vec![];
|
||||
for server in &self.trusted_servers {
|
||||
lst.push(server.host());
|
||||
}
|
||||
&lst.join(", ")
|
||||
}),
|
||||
(
|
||||
"Trusted key servers",
|
||||
&self
|
||||
.trusted_servers
|
||||
.iter()
|
||||
.map(|server| server.host())
|
||||
.join(", "),
|
||||
),
|
||||
(
|
||||
"Query Trusted Key Servers First",
|
||||
&self.query_trusted_key_servers_first.to_string(),
|
||||
@@ -706,58 +715,43 @@ impl fmt::Display for Config {
|
||||
("Gzip HTTP Compression", &self.gzip_compression.to_string()),
|
||||
#[cfg(feature = "brotli_compression")]
|
||||
("Brotli HTTP Compression", &self.brotli_compression.to_string()),
|
||||
#[cfg(feature = "rocksdb")]
|
||||
("RocksDB database LOG level", &self.rocksdb_log_level),
|
||||
#[cfg(feature = "rocksdb")]
|
||||
("RocksDB database LOG to stderr", &self.rocksdb_log_stderr.to_string()),
|
||||
#[cfg(feature = "rocksdb")]
|
||||
("RocksDB database LOG time-to-roll", &self.rocksdb_log_time_to_roll.to_string()),
|
||||
#[cfg(feature = "rocksdb")]
|
||||
("RocksDB Max LOG Files", &self.rocksdb_max_log_files.to_string()),
|
||||
#[cfg(feature = "rocksdb")]
|
||||
(
|
||||
"RocksDB database max LOG file size",
|
||||
&self.rocksdb_max_log_file_size.to_string(),
|
||||
),
|
||||
#[cfg(feature = "rocksdb")]
|
||||
(
|
||||
"RocksDB database optimize for spinning disks",
|
||||
&self.rocksdb_optimize_for_spinning_disks.to_string(),
|
||||
),
|
||||
#[cfg(feature = "rocksdb")]
|
||||
("RocksDB Direct-IO", &self.rocksdb_direct_io.to_string()),
|
||||
#[cfg(feature = "rocksdb")]
|
||||
("RocksDB Parallelism Threads", &self.rocksdb_parallelism_threads.to_string()),
|
||||
#[cfg(feature = "rocksdb")]
|
||||
("RocksDB Compression Algorithm", &self.rocksdb_compression_algo),
|
||||
#[cfg(feature = "rocksdb")]
|
||||
("RocksDB Compression Level", &self.rocksdb_compression_level.to_string()),
|
||||
#[cfg(feature = "rocksdb")]
|
||||
(
|
||||
"RocksDB Bottommost Compression Level",
|
||||
&self.rocksdb_bottommost_compression_level.to_string(),
|
||||
),
|
||||
#[cfg(feature = "rocksdb")]
|
||||
(
|
||||
"RocksDB Bottommost Level Compression",
|
||||
&self.rocksdb_bottommost_compression.to_string(),
|
||||
),
|
||||
#[cfg(feature = "rocksdb")]
|
||||
("RocksDB Recovery Mode", &self.rocksdb_recovery_mode.to_string()),
|
||||
#[cfg(feature = "rocksdb")]
|
||||
("RocksDB Repair Mode", &self.rocksdb_repair.to_string()),
|
||||
#[cfg(feature = "rocksdb")]
|
||||
("RocksDB Read-only Mode", &self.rocksdb_read_only.to_string()),
|
||||
#[cfg(feature = "rocksdb")]
|
||||
(
|
||||
"RocksDB Compaction Idle Priority",
|
||||
&self.rocksdb_compaction_prio_idle.to_string(),
|
||||
),
|
||||
#[cfg(feature = "rocksdb")]
|
||||
(
|
||||
"RocksDB Compaction Idle IOPriority",
|
||||
&self.rocksdb_compaction_ioprio_idle.to_string(),
|
||||
),
|
||||
("Media integrity checks on startup", &self.media_startup_check.to_string()),
|
||||
("Media compatibility filesystem links", &self.media_compat_file_link.to_string()),
|
||||
("Prevent Media Downloads From", {
|
||||
let mut lst = vec![];
|
||||
for domain in &self.prevent_media_downloads_from {
|
||||
@@ -824,52 +818,47 @@ impl fmt::Display for Config {
|
||||
("Sentry.io tracing sample rate", &self.sentry_traces_sample_rate.to_string()),
|
||||
(
|
||||
"Well-known server name",
|
||||
&if let Some(server) = &self.well_known.server {
|
||||
server.to_string()
|
||||
} else {
|
||||
String::new()
|
||||
},
|
||||
self.well_known
|
||||
.server
|
||||
.as_ref()
|
||||
.map_or("", |server| server.as_str()),
|
||||
),
|
||||
(
|
||||
"Well-known client URL",
|
||||
&if let Some(server) = &self.well_known.client {
|
||||
server.to_string()
|
||||
} else {
|
||||
String::new()
|
||||
},
|
||||
self.well_known
|
||||
.client
|
||||
.as_ref()
|
||||
.map_or("", |url| url.as_str()),
|
||||
),
|
||||
(
|
||||
"Well-known support email",
|
||||
&if let Some(support_email) = &self.well_known.support_email {
|
||||
support_email.to_string()
|
||||
} else {
|
||||
String::new()
|
||||
},
|
||||
self.well_known
|
||||
.support_email
|
||||
.as_ref()
|
||||
.map_or("", |str| str.as_ref()),
|
||||
),
|
||||
(
|
||||
"Well-known support Matrix ID",
|
||||
&if let Some(support_mxid) = &self.well_known.support_mxid {
|
||||
support_mxid.to_string()
|
||||
} else {
|
||||
String::new()
|
||||
},
|
||||
self.well_known
|
||||
.support_mxid
|
||||
.as_ref()
|
||||
.map_or("", |mxid| mxid.as_str()),
|
||||
),
|
||||
(
|
||||
"Well-known support role",
|
||||
&if let Some(support_role) = &self.well_known.support_role {
|
||||
support_role.to_string()
|
||||
} else {
|
||||
String::new()
|
||||
},
|
||||
self.well_known
|
||||
.support_role
|
||||
.as_ref()
|
||||
.map_or("", |role| role.as_str()),
|
||||
),
|
||||
(
|
||||
"Well-known support page/URL",
|
||||
&if let Some(support_page) = &self.well_known.support_page {
|
||||
support_page.to_string()
|
||||
} else {
|
||||
String::new()
|
||||
},
|
||||
self.well_known
|
||||
.support_page
|
||||
.as_ref()
|
||||
.map_or("", |url| url.as_str()),
|
||||
),
|
||||
("Enable the tokio-console", &self.tokio_console.to_string()),
|
||||
];
|
||||
|
||||
let mut msg: String = "Active config values:\n\n".to_owned();
|
||||
@@ -984,14 +973,13 @@ fn default_tracing_flame_output_path() -> String { "./tracing.folded".to_owned()
|
||||
|
||||
fn default_trusted_servers() -> Vec<OwnedServerName> { vec![OwnedServerName::try_from("matrix.org").unwrap()] }
|
||||
|
||||
/// do debug logging by default for debug builds
|
||||
#[must_use]
|
||||
pub fn default_log() -> String {
|
||||
// do debug logging by default for debug builds
|
||||
if cfg!(debug_assertions) {
|
||||
"debug".to_owned()
|
||||
} else {
|
||||
"info".to_owned()
|
||||
}
|
||||
cfg!(debug_assertions)
|
||||
.then_some("debug")
|
||||
.unwrap_or("info")
|
||||
.to_owned()
|
||||
}
|
||||
|
||||
fn default_notification_push_path() -> String { "/_matrix/push/v1/notify".to_owned() }
|
||||
@@ -1072,9 +1060,7 @@ fn default_url_preview_max_spider_size() -> usize {
|
||||
fn default_new_user_displayname_suffix() -> String { "🏳️⚧️".to_owned() }
|
||||
|
||||
fn default_sentry_endpoint() -> Option<Url> {
|
||||
Url::parse("https://fe2eb4536aa04949e28eff3128d64757@o4506996327251968.ingest.us.sentry.io/4506996334657536")
|
||||
.unwrap()
|
||||
.into()
|
||||
Url::parse("https://fe2eb4536aa04949e28eff3128d64757@o4506996327251968.ingest.us.sentry.io/4506996334657536").ok()
|
||||
}
|
||||
|
||||
fn default_sentry_traces_sample_rate() -> f32 { 0.15 }
|
||||
|
||||
+5
-5
@@ -14,9 +14,9 @@ pub use crate::utils::debug::*;
|
||||
macro_rules! debug_event {
|
||||
( $level:expr, $($x:tt)+ ) => {
|
||||
if cfg!(debug_assertions) && cfg!(not(feature = "dev_release_log_level")) {
|
||||
tracing::event!( $level, $($x)+ );
|
||||
::tracing::event!( $level, $($x)+ );
|
||||
} else {
|
||||
tracing::debug!( $($x)+ );
|
||||
::tracing::debug!( $($x)+ );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,7 @@ macro_rules! debug_event {
|
||||
#[macro_export]
|
||||
macro_rules! debug_error {
|
||||
( $($x:tt)+ ) => {
|
||||
$crate::debug_event!(tracing::Level::ERROR, $($x)+ );
|
||||
$crate::debug_event!(::tracing::Level::ERROR, $($x)+ );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ macro_rules! debug_error {
|
||||
#[macro_export]
|
||||
macro_rules! debug_warn {
|
||||
( $($x:tt)+ ) => {
|
||||
$crate::debug_event!(tracing::Level::WARN, $($x)+ );
|
||||
$crate::debug_event!(::tracing::Level::WARN, $($x)+ );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ macro_rules! debug_warn {
|
||||
#[macro_export]
|
||||
macro_rules! debug_info {
|
||||
( $($x:tt)+ ) => {
|
||||
$crate::debug_event!(tracing::Level::INFO, $($x)+ );
|
||||
$crate::debug_event!(::tracing::Level::INFO, $($x)+ );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+55
-72
@@ -7,7 +7,11 @@ use http_body_util::Full;
|
||||
use ruma::{
|
||||
api::{
|
||||
client::{
|
||||
error::{Error as RumaError, ErrorBody, ErrorKind},
|
||||
error::ErrorKind::{
|
||||
Forbidden, GuestAccessForbidden, LimitExceeded, MissingToken, NotFound, ThreepidAuthFailed,
|
||||
ThreepidDenied, TooLarge, Unauthorized, Unknown, UnknownToken, Unrecognized, UserDeactivated,
|
||||
WrongRoomKeysVersion,
|
||||
},
|
||||
uiaa::{UiaaInfo, UiaaResponse},
|
||||
},
|
||||
OutgoingResponse,
|
||||
@@ -16,78 +20,62 @@ use ruma::{
|
||||
};
|
||||
use thiserror::Error;
|
||||
use tracing::error;
|
||||
use ErrorKind::{
|
||||
Forbidden, GuestAccessForbidden, LimitExceeded, MissingToken, NotFound, ThreepidAuthFailed, ThreepidDenied,
|
||||
TooLarge, Unauthorized, Unknown, UnknownToken, Unrecognized, UserDeactivated, WrongRoomKeysVersion,
|
||||
};
|
||||
|
||||
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
||||
|
||||
#[derive(Error)]
|
||||
pub enum Error {
|
||||
#[cfg(feature = "sqlite")]
|
||||
#[error("There was a problem with the connection to the sqlite database: {source}")]
|
||||
Sqlite {
|
||||
#[from]
|
||||
source: rusqlite::Error,
|
||||
},
|
||||
#[cfg(feature = "rocksdb")]
|
||||
#[error("There was a problem with the connection to the rocksdb database: {source}")]
|
||||
RocksDb {
|
||||
#[from]
|
||||
source: rust_rocksdb::Error,
|
||||
},
|
||||
#[error("Could not generate an image: {source}")]
|
||||
Image {
|
||||
#[from]
|
||||
source: image::error::ImageError,
|
||||
},
|
||||
#[error("Could not connect to server: {source}")]
|
||||
Reqwest {
|
||||
#[from]
|
||||
source: reqwest::Error,
|
||||
},
|
||||
#[error("Could build regular expression: {source}")]
|
||||
Regex {
|
||||
#[from]
|
||||
source: regex::Error,
|
||||
},
|
||||
#[error("Remote server {0} responded with: {1}")]
|
||||
Federation(OwnedServerName, RumaError),
|
||||
#[error("Could not do this io: {source}")]
|
||||
Io {
|
||||
#[from]
|
||||
source: std::io::Error,
|
||||
},
|
||||
#[error("There was a problem with your configuration: {0}")]
|
||||
BadConfig(String),
|
||||
// std
|
||||
#[error("{0}")]
|
||||
BadServerResponse(&'static str),
|
||||
Fmt(#[from] fmt::Error),
|
||||
#[error("I/O error: {0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
#[error("{0}")]
|
||||
/// Don't create this directly. Use Error::bad_database instead.
|
||||
BadDatabase(&'static str),
|
||||
#[error("uiaa")]
|
||||
Uiaa(UiaaInfo),
|
||||
#[error("{0}: {1}")]
|
||||
BadRequest(ErrorKind, &'static str),
|
||||
Utf8Error(#[from] std::str::Utf8Error),
|
||||
#[error("{0}")]
|
||||
Conflict(&'static str), // This is only needed for when a room alias already exists
|
||||
FromUtf8Error(#[from] std::string::FromUtf8Error),
|
||||
#[error("{0}")]
|
||||
TryFromSliceError(#[from] std::array::TryFromSliceError),
|
||||
|
||||
// third-party
|
||||
#[error("Regex error: {0}")]
|
||||
Regex(#[from] regex::Error),
|
||||
#[error("Tracing filter error: {0}")]
|
||||
TracingFilter(#[from] tracing_subscriber::filter::ParseError),
|
||||
#[error("Image error: {0}")]
|
||||
Image(#[from] image::error::ImageError),
|
||||
#[error("Request error: {0}")]
|
||||
Reqwest(#[from] reqwest::Error),
|
||||
#[error("{0}")]
|
||||
Extension(#[from] axum::extract::rejection::ExtensionRejection),
|
||||
#[error("{0}")]
|
||||
Path(#[from] axum::extract::rejection::PathRejection),
|
||||
#[error("from {0}: {1}")]
|
||||
Redaction(OwnedServerName, ruma::canonical_json::RedactionError),
|
||||
#[error("{0} in {1}")]
|
||||
InconsistentRoomState(&'static str, ruma::OwnedRoomId),
|
||||
#[error("{0}")]
|
||||
TracingFilter(#[from] tracing_subscriber::filter::ParseError),
|
||||
#[error("{0}")]
|
||||
AdminCommand(&'static str),
|
||||
#[error("{0}")]
|
||||
Fmt(#[from] fmt::Error),
|
||||
|
||||
// ruma
|
||||
#[error("{0}")]
|
||||
Mxid(#[from] ruma::IdParseError),
|
||||
#[error("{0}: {1}")]
|
||||
BadRequest(ruma::api::client::error::ErrorKind, &'static str),
|
||||
#[error("from {0}: {1}")]
|
||||
Redaction(OwnedServerName, ruma::canonical_json::RedactionError),
|
||||
#[error("Remote server {0} responded with: {1}")]
|
||||
Federation(OwnedServerName, ruma::api::client::error::Error),
|
||||
#[error("{0} in {1}")]
|
||||
InconsistentRoomState(&'static str, ruma::OwnedRoomId),
|
||||
|
||||
// conduwuit
|
||||
#[error("There was a problem with your configuration: {0}")]
|
||||
BadConfig(String),
|
||||
#[error("{0}")]
|
||||
BadDatabase(&'static str),
|
||||
#[error("{0}")]
|
||||
Database(String),
|
||||
#[error("{0}")]
|
||||
BadServerResponse(&'static str),
|
||||
#[error("{0}")]
|
||||
Conflict(&'static str), // This is only needed for when a room alias already exists
|
||||
#[error("uiaa")]
|
||||
Uiaa(UiaaInfo),
|
||||
|
||||
// unique / untyped
|
||||
#[error("{0}")]
|
||||
Err(String),
|
||||
}
|
||||
@@ -104,7 +92,7 @@ impl Error {
|
||||
}
|
||||
|
||||
/// Returns the Matrix error code / error kind
|
||||
pub fn error_code(&self) -> ErrorKind {
|
||||
pub fn error_code(&self) -> ruma::api::client::error::ErrorKind {
|
||||
if let Self::Federation(_, error) = self {
|
||||
return error.error_kind().unwrap_or_else(|| &Unknown).clone();
|
||||
}
|
||||
@@ -117,20 +105,13 @@ impl Error {
|
||||
|
||||
/// Sanitizes public-facing errors that can leak sensitive information.
|
||||
pub fn sanitized_error(&self) -> String {
|
||||
let db_error = String::from("Database or I/O error occurred.");
|
||||
|
||||
match self {
|
||||
#[cfg(feature = "sqlite")]
|
||||
Self::Sqlite {
|
||||
Self::Database {
|
||||
..
|
||||
} => db_error,
|
||||
#[cfg(feature = "rocksdb")]
|
||||
Self::RocksDb {
|
||||
..
|
||||
} => db_error,
|
||||
} => String::from("Database error occurred."),
|
||||
Self::Io {
|
||||
..
|
||||
} => db_error,
|
||||
} => String::from("I/O error occurred."),
|
||||
_ => self.to_string(),
|
||||
}
|
||||
}
|
||||
@@ -157,6 +138,8 @@ impl From<Error> for RumaResponse<UiaaResponse> {
|
||||
|
||||
impl Error {
|
||||
pub fn to_response(&self) -> RumaResponse<UiaaResponse> {
|
||||
use ruma::api::client::error::{Error as RumaError, ErrorBody};
|
||||
|
||||
if let Self::Uiaa(uiaainfo) = self {
|
||||
return RumaResponse(UiaaResponse::AuthResponse(uiaainfo.clone()));
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
use tracing::Level;
|
||||
use tracing_core::{span::Current, Event};
|
||||
|
||||
use super::layer::Value;
|
||||
use super::{layer::Value, Layer};
|
||||
|
||||
pub struct Data<'a> {
|
||||
pub layer: &'a Layer,
|
||||
pub event: &'a Event<'a>,
|
||||
pub current: &'a Current,
|
||||
pub values: Option<&'a mut [Value]>,
|
||||
|
||||
@@ -40,12 +40,12 @@ where
|
||||
.read()
|
||||
.expect("shared lock")
|
||||
.iter()
|
||||
.filter(|capture| filter(capture, event, &ctx))
|
||||
.for_each(|capture| handle(capture, event, &ctx));
|
||||
.filter(|capture| filter(self, capture, event, &ctx))
|
||||
.for_each(|capture| handle(self, capture, event, &ctx));
|
||||
}
|
||||
}
|
||||
|
||||
fn handle<S>(capture: &Capture, event: &Event<'_>, ctx: &Context<'_, S>)
|
||||
fn handle<S>(layer: &Layer, capture: &Capture, event: &Event<'_>, ctx: &Context<'_, S>)
|
||||
where
|
||||
S: Subscriber + for<'a> LookupSpan<'a>,
|
||||
{
|
||||
@@ -56,18 +56,20 @@ where
|
||||
|
||||
let mut closure = capture.closure.lock().expect("exclusive lock");
|
||||
closure(Data {
|
||||
layer,
|
||||
event,
|
||||
current: &ctx.current_span(),
|
||||
values: Some(&mut visitor.values),
|
||||
});
|
||||
}
|
||||
|
||||
fn filter<S>(capture: &Capture, event: &Event<'_>, ctx: &Context<'_, S>) -> bool
|
||||
fn filter<S>(layer: &Layer, capture: &Capture, event: &Event<'_>, ctx: &Context<'_, S>) -> bool
|
||||
where
|
||||
S: Subscriber + for<'a> LookupSpan<'a>,
|
||||
{
|
||||
capture.filter.as_ref().map_or(true, |filter| {
|
||||
filter(Data {
|
||||
layer,
|
||||
event,
|
||||
current: &ctx.current_span(),
|
||||
values: None,
|
||||
|
||||
+16
-7
@@ -2,17 +2,26 @@ pub mod capture;
|
||||
pub mod color;
|
||||
pub mod fmt;
|
||||
mod reload;
|
||||
mod server;
|
||||
mod suppress;
|
||||
|
||||
pub use capture::Capture;
|
||||
pub use reload::{LogLevelReloadHandles, ReloadHandle};
|
||||
pub use server::Server;
|
||||
pub use suppress::Suppress;
|
||||
pub use tracing::Level;
|
||||
pub use tracing_core::{Event, Metadata};
|
||||
pub use tracing_subscriber::EnvFilter;
|
||||
|
||||
/// Logging subsystem. This is a singleton member of super::Server which holds
|
||||
/// all logging and tracing related state rather than shoving it all in
|
||||
/// super::Server directly.
|
||||
pub struct Log {
|
||||
/// General log level reload handles.
|
||||
pub reload: LogLevelReloadHandles,
|
||||
|
||||
/// Tracing capture state for ephemeral/oneshot uses.
|
||||
pub capture: std::sync::Arc<capture::State>,
|
||||
}
|
||||
|
||||
// Wraps for logging macros. Use these macros rather than extern tracing:: or
|
||||
// log:: crates in project code. ::log and ::tracing can still be used if
|
||||
// necessary but discouraged. Remember debug_ log macros are also exported to
|
||||
@@ -20,25 +29,25 @@ pub use tracing_subscriber::EnvFilter;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! error {
|
||||
( $($x:tt)+ ) => { tracing::error!( $($x)+ ); }
|
||||
( $($x:tt)+ ) => { ::tracing::error!( $($x)+ ); }
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! warn {
|
||||
( $($x:tt)+ ) => { tracing::warn!( $($x)+ ); }
|
||||
( $($x:tt)+ ) => { ::tracing::warn!( $($x)+ ); }
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! info {
|
||||
( $($x:tt)+ ) => { tracing::info!( $($x)+ ); }
|
||||
( $($x:tt)+ ) => { ::tracing::info!( $($x)+ ); }
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! debug {
|
||||
( $($x:tt)+ ) => { tracing::debug!( $($x)+ ); }
|
||||
( $($x:tt)+ ) => { ::tracing::debug!( $($x)+ ); }
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! trace {
|
||||
( $($x:tt)+ ) => { tracing::trace!( $($x)+ ); }
|
||||
( $($x:tt)+ ) => { ::tracing::trace!( $($x)+ ); }
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::{capture, reload::LogLevelReloadHandles};
|
||||
|
||||
/// Logging subsystem. This is a singleton member of super::Server which holds
|
||||
/// all logging and tracing related state rather than shoving it all in
|
||||
/// super::Server directly.
|
||||
pub struct Server {
|
||||
/// General log level reload handles.
|
||||
pub reload: LogLevelReloadHandles,
|
||||
|
||||
/// Tracing capture state for ephemeral/oneshot uses.
|
||||
pub capture: Arc<capture::State>,
|
||||
}
|
||||
+4
-1
@@ -10,9 +10,12 @@ pub mod utils;
|
||||
pub mod version;
|
||||
|
||||
pub use config::Config;
|
||||
pub use error::{Error, Result, RumaResponse};
|
||||
pub use error::{Error, RumaResponse};
|
||||
pub use pducount::PduCount;
|
||||
pub use server::Server;
|
||||
pub use version::version;
|
||||
|
||||
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
||||
|
||||
#[cfg(not(conduit_mods))]
|
||||
pub mod mods {
|
||||
|
||||
+2
-2
@@ -33,7 +33,7 @@ pub struct Server {
|
||||
pub signal: broadcast::Sender<&'static str>,
|
||||
|
||||
/// Logging subsystem state
|
||||
pub log: log::Server,
|
||||
pub log: log::Log,
|
||||
|
||||
/// TODO: move stats
|
||||
pub requests_spawn_active: AtomicU32,
|
||||
@@ -45,7 +45,7 @@ pub struct Server {
|
||||
|
||||
impl Server {
|
||||
#[must_use]
|
||||
pub fn new(config: Config, runtime: Option<runtime::Handle>, log: log::Server) -> Self {
|
||||
pub fn new(config: Config, runtime: Option<runtime::Handle>, log: log::Log) -> Self {
|
||||
Self {
|
||||
config,
|
||||
started: SystemTime::now(),
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
use crate::Result;
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn increment(old: Option<&[u8]>) -> [u8; 8] {
|
||||
old.map(TryInto::try_into)
|
||||
.map_or(0_u64, |val| val.map_or(0_u64, u64::from_be_bytes))
|
||||
.wrapping_add(1)
|
||||
.to_be_bytes()
|
||||
}
|
||||
|
||||
/// Parses the big-endian bytes into an u64.
|
||||
#[inline]
|
||||
pub fn u64_from_bytes(bytes: &[u8]) -> Result<u64> {
|
||||
let array: [u8; 8] = bytes.try_into()?;
|
||||
Ok(u64_from_u8x8(array))
|
||||
}
|
||||
|
||||
/// Parses the 8 big-endian bytes into an u64.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn u64_from_u8(bytes: &[u8]) -> u64 {
|
||||
let bytes: &[u8; 8] = bytes.try_into().expect("must slice at least 8 bytes");
|
||||
u64_from_u8x8(*bytes)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn u64_from_u8x8(bytes: [u8; 8]) -> u64 { u64::from_be_bytes(bytes) }
|
||||
+11
-67
@@ -1,72 +1,16 @@
|
||||
use std::sync::Mutex;
|
||||
mod argon;
|
||||
mod sha256;
|
||||
|
||||
use argon2::{
|
||||
password_hash, password_hash::SaltString, Algorithm, Argon2, Params, PasswordHash, PasswordHasher,
|
||||
PasswordVerifier, Version,
|
||||
};
|
||||
use crate::Result;
|
||||
|
||||
const M_COST: u32 = Params::DEFAULT_M_COST; // memory size in 1 KiB blocks
|
||||
const T_COST: u32 = Params::DEFAULT_T_COST; // nr of iterations
|
||||
const P_COST: u32 = Params::DEFAULT_P_COST; // parallelism
|
||||
#[inline]
|
||||
pub fn password(password: &str) -> Result<String> { argon::password(password) }
|
||||
|
||||
static STATE: Mutex<Option<Argon2<'static>>> = Mutex::new(None);
|
||||
|
||||
#[allow(clippy::let_underscore_must_use)]
|
||||
pub fn init() {
|
||||
// 19456 Kib blocks, iterations = 2, parallelism = 1
|
||||
// * <https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#argon2id>
|
||||
debug_assert!(M_COST == 19_456, "M_COST default changed");
|
||||
debug_assert!(T_COST == 2, "T_COST default changed");
|
||||
debug_assert!(P_COST == 1, "P_COST default changed");
|
||||
|
||||
let algorithm = Algorithm::Argon2id;
|
||||
let version = Version::default();
|
||||
let out_len: Option<usize> = None;
|
||||
let params = Params::new(M_COST, T_COST, P_COST, out_len).expect("valid parameters");
|
||||
let state = Argon2::new(algorithm, version, params);
|
||||
_ = STATE.lock().expect("hashing state locked").insert(state);
|
||||
#[inline]
|
||||
pub fn verify_password(password: &str, password_hash: &str) -> Result<()> {
|
||||
argon::verify_password(password, password_hash)
|
||||
}
|
||||
|
||||
pub fn password(password: &str) -> Result<String, password_hash::Error> {
|
||||
let salt = SaltString::generate(rand::thread_rng());
|
||||
STATE
|
||||
.lock()
|
||||
.expect("hashing state locked")
|
||||
.as_ref()
|
||||
.expect("hashing state initialized")
|
||||
.hash_password(password.as_bytes(), &salt)
|
||||
.map(|it| it.to_string())
|
||||
}
|
||||
|
||||
pub fn verify_password(password: &str, password_hash: &str) -> Result<(), password_hash::Error> {
|
||||
let password_hash = PasswordHash::new(password_hash)?;
|
||||
STATE
|
||||
.lock()
|
||||
.expect("hashing state locked")
|
||||
.as_ref()
|
||||
.expect("hashing state initialized")
|
||||
.verify_password(password.as_bytes(), &password_hash)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn password_hash_and_verify() {
|
||||
use crate::utils::hash;
|
||||
hash::init();
|
||||
let preimage = "temp123";
|
||||
let digest = hash::password(preimage).expect("digest");
|
||||
hash::verify_password(preimage, &digest).expect("verified");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "unverified")]
|
||||
fn password_hash_and_verify_fail() {
|
||||
use crate::utils::hash;
|
||||
hash::init();
|
||||
let preimage = "temp123";
|
||||
let fakeimage = "temp321";
|
||||
let digest = hash::password(preimage).expect("digest");
|
||||
hash::verify_password(fakeimage, &digest).expect("unverified");
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn calculate_hash(keys: &[&[u8]]) -> Vec<u8> { sha256::hash(keys) }
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use argon2::{
|
||||
password_hash, password_hash::SaltString, Algorithm, Argon2, Params, PasswordHash, PasswordHasher,
|
||||
PasswordVerifier, Version,
|
||||
};
|
||||
|
||||
use crate::{Error, Result};
|
||||
|
||||
const M_COST: u32 = Params::DEFAULT_M_COST; // memory size in 1 KiB blocks
|
||||
const T_COST: u32 = Params::DEFAULT_T_COST; // nr of iterations
|
||||
const P_COST: u32 = Params::DEFAULT_P_COST; // parallelism
|
||||
|
||||
static ARGON: OnceLock<Argon2<'static>> = OnceLock::new();
|
||||
|
||||
fn init_argon() -> Argon2<'static> {
|
||||
// 19456 Kib blocks, iterations = 2, parallelism = 1
|
||||
// * <https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#argon2id>
|
||||
debug_assert!(M_COST == 19_456, "M_COST default changed");
|
||||
debug_assert!(T_COST == 2, "T_COST default changed");
|
||||
debug_assert!(P_COST == 1, "P_COST default changed");
|
||||
|
||||
let algorithm = Algorithm::Argon2id;
|
||||
let version = Version::default();
|
||||
let out_len: Option<usize> = None;
|
||||
let params = Params::new(M_COST, T_COST, P_COST, out_len).expect("valid parameters");
|
||||
Argon2::new(algorithm, version, params)
|
||||
}
|
||||
|
||||
pub(super) fn password(password: &str) -> Result<String> {
|
||||
let salt = SaltString::generate(rand::thread_rng());
|
||||
ARGON
|
||||
.get_or_init(init_argon)
|
||||
.hash_password(password.as_bytes(), &salt)
|
||||
.map(|it| it.to_string())
|
||||
.map_err(map_err)
|
||||
}
|
||||
|
||||
pub(super) fn verify_password(password: &str, password_hash: &str) -> Result<()> {
|
||||
let password_hash = PasswordHash::new(password_hash).map_err(map_err)?;
|
||||
ARGON
|
||||
.get_or_init(init_argon)
|
||||
.verify_password(password.as_bytes(), &password_hash)
|
||||
.map_err(map_err)
|
||||
}
|
||||
|
||||
fn map_err(e: password_hash::Error) -> Error { Error::Err(e.to_string()) }
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn password_hash_and_verify() {
|
||||
use crate::utils::hash;
|
||||
let preimage = "temp123";
|
||||
let digest = hash::password(preimage).expect("digest");
|
||||
hash::verify_password(preimage, &digest).expect("verified");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "unverified")]
|
||||
fn password_hash_and_verify_fail() {
|
||||
use crate::utils::hash;
|
||||
let preimage = "temp123";
|
||||
let fakeimage = "temp321";
|
||||
let digest = hash::password(preimage).expect("digest");
|
||||
hash::verify_password(fakeimage, &digest).expect("unverified");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
use ring::{digest, digest::SHA256};
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub(super) fn hash(keys: &[&[u8]]) -> Vec<u8> {
|
||||
// We only hash the pdu's event ids, not the whole pdu
|
||||
let bytes = keys.join(&0xFF);
|
||||
let hash = digest::digest(&SHA256, &bytes);
|
||||
hash.as_ref().to_owned()
|
||||
}
|
||||
+19
-73
@@ -1,3 +1,4 @@
|
||||
pub mod bytes;
|
||||
pub mod content_disposition;
|
||||
pub mod debug;
|
||||
pub mod defer;
|
||||
@@ -5,45 +6,37 @@ pub mod hash;
|
||||
pub mod html;
|
||||
pub mod json;
|
||||
pub mod mutex_map;
|
||||
pub mod string;
|
||||
pub mod sys;
|
||||
mod tests;
|
||||
pub mod time;
|
||||
|
||||
use std::{
|
||||
cmp::{self, Ordering},
|
||||
time::{SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
use std::cmp::{self, Ordering};
|
||||
|
||||
pub use bytes::{increment, u64_from_bytes, u64_from_u8, u64_from_u8x8};
|
||||
pub use debug::slice_truncated as debug_slice_truncated;
|
||||
pub use hash::calculate_hash;
|
||||
pub use html::Escape as HtmlEscape;
|
||||
pub use json::{deserialize_from_str, to_canonical_object};
|
||||
pub use mutex_map::MutexMap;
|
||||
use rand::prelude::*;
|
||||
use ring::digest;
|
||||
use ruma::OwnedUserId;
|
||||
pub use string::{random_string, str_from_bytes, string_from_bytes};
|
||||
pub use sys::available_parallelism;
|
||||
pub use time::millis_since_unix_epoch;
|
||||
|
||||
use crate::{Error, Result};
|
||||
use crate::Result;
|
||||
|
||||
pub fn clamp<T: Ord>(val: T, min: T, max: T) -> T { cmp::min(cmp::max(val, min), max) }
|
||||
|
||||
/// Boilerplate for wraps which are typed to never error.
|
||||
///
|
||||
/// * <https://doc.rust-lang.org/std/convert/enum.Infallible.html>
|
||||
#[must_use]
|
||||
#[allow(clippy::as_conversions)]
|
||||
pub fn millis_since_unix_epoch() -> u64 {
|
||||
SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.expect("time is valid")
|
||||
.as_millis() as u64
|
||||
}
|
||||
|
||||
pub fn increment(old: Option<&[u8]>) -> Vec<u8> {
|
||||
let number = match old.map(TryInto::try_into) {
|
||||
Some(Ok(bytes)) => {
|
||||
let number = u64::from_be_bytes(bytes);
|
||||
number + 1
|
||||
},
|
||||
_ => 1, // Start at one. since 0 should return the first event in the db
|
||||
};
|
||||
|
||||
number.to_be_bytes().to_vec()
|
||||
#[inline(always)]
|
||||
pub fn unwrap_infallible<T>(result: Result<T, std::convert::Infallible>) -> T {
|
||||
match result {
|
||||
Ok(val) => val,
|
||||
Err(err) => match err {},
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
@@ -56,41 +49,6 @@ pub fn generate_keypair() -> Vec<u8> {
|
||||
value
|
||||
}
|
||||
|
||||
/// Parses the bytes into an u64.
|
||||
pub fn u64_from_bytes(bytes: &[u8]) -> Result<u64, std::array::TryFromSliceError> {
|
||||
let array: [u8; 8] = bytes.try_into()?;
|
||||
Ok(u64::from_be_bytes(array))
|
||||
}
|
||||
|
||||
/// Parses the bytes into a string.
|
||||
pub fn string_from_bytes(bytes: &[u8]) -> Result<String, std::string::FromUtf8Error> {
|
||||
String::from_utf8(bytes.to_vec())
|
||||
}
|
||||
|
||||
/// Parses a `OwnedUserId` from bytes.
|
||||
pub fn user_id_from_bytes(bytes: &[u8]) -> Result<OwnedUserId> {
|
||||
OwnedUserId::try_from(
|
||||
string_from_bytes(bytes).map_err(|_| Error::bad_database("Failed to parse string from bytes"))?,
|
||||
)
|
||||
.map_err(|_| Error::bad_database("Failed to parse user id from bytes"))
|
||||
}
|
||||
|
||||
pub fn random_string(length: usize) -> String {
|
||||
thread_rng()
|
||||
.sample_iter(&rand::distributions::Alphanumeric)
|
||||
.take(length)
|
||||
.map(char::from)
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(keys))]
|
||||
pub fn calculate_hash(keys: &[&[u8]]) -> Vec<u8> {
|
||||
// We only hash the pdu's event ids, not the whole pdu
|
||||
let bytes = keys.join(&0xFF);
|
||||
let hash = digest::digest(&digest::SHA256, &bytes);
|
||||
hash.as_ref().to_owned()
|
||||
}
|
||||
|
||||
#[allow(clippy::impl_trait_in_params)]
|
||||
pub fn common_elements(
|
||||
mut iterators: impl Iterator<Item = impl Iterator<Item = Vec<u8>>>, check_order: impl Fn(&[u8], &[u8]) -> Ordering,
|
||||
@@ -114,15 +72,3 @@ pub fn common_elements(
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
/// Boilerplate for wraps which are typed to never error.
|
||||
///
|
||||
/// * <https://doc.rust-lang.org/std/convert/enum.Infallible.html>
|
||||
#[must_use]
|
||||
#[inline(always)]
|
||||
pub fn unwrap_infallible<T>(result: Result<T, std::convert::Infallible>) -> T {
|
||||
match result {
|
||||
Ok(val) => val,
|
||||
Err(err) => match err {},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
use rand::prelude::*;
|
||||
|
||||
use crate::Result;
|
||||
|
||||
pub fn random_string(length: usize) -> String {
|
||||
thread_rng()
|
||||
.sample_iter(&rand::distributions::Alphanumeric)
|
||||
.take(length)
|
||||
.map(char::from)
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Parses the bytes into a string.
|
||||
#[inline]
|
||||
pub fn string_from_bytes(bytes: &[u8]) -> Result<String> {
|
||||
let str: &str = str_from_bytes(bytes)?;
|
||||
Ok(str.to_owned())
|
||||
}
|
||||
|
||||
/// Parses the bytes into a string.
|
||||
#[inline]
|
||||
pub fn str_from_bytes(bytes: &[u8]) -> Result<&str> { Ok(std::str::from_utf8(bytes)?) }
|
||||
@@ -34,3 +34,34 @@ pub fn available_parallelism() -> usize {
|
||||
.expect("Unable to query for available parallelism.")
|
||||
.get()
|
||||
}
|
||||
|
||||
/// Return a possibly corrected std::env::current_exe() even if the path is
|
||||
/// marked deleted.
|
||||
///
|
||||
/// # Safety
|
||||
/// This function is declared unsafe because the original result was altered for
|
||||
/// security purposes, and altering it back ignores those urposes and should be
|
||||
/// understood by the user.
|
||||
pub unsafe fn current_exe() -> Result<std::path::PathBuf> {
|
||||
use std::path::PathBuf;
|
||||
|
||||
let exe = std::env::current_exe()?;
|
||||
match exe.to_str() {
|
||||
None => Ok(exe),
|
||||
Some(str) => Ok(str
|
||||
.strip_suffix(" (deleted)")
|
||||
.map(PathBuf::from)
|
||||
.unwrap_or(exe)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Determine if the server's executable was removed or replaced. This is a
|
||||
/// specific check; useful for successful restarts. May not be available or
|
||||
/// accurate on all platforms; defaults to false.
|
||||
#[must_use]
|
||||
pub fn current_exe_deleted() -> bool {
|
||||
std::env::current_exe().map_or(false, |exe| {
|
||||
exe.to_str()
|
||||
.map_or(false, |exe| exe.ends_with(" (deleted)"))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
#![cfg(test)]
|
||||
|
||||
use crate::utils;
|
||||
|
||||
#[test]
|
||||
fn increment_none() {
|
||||
let bytes: [u8; 8] = utils::increment(None);
|
||||
let res = u64::from_be_bytes(bytes);
|
||||
assert_eq!(res, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn increment_fault() {
|
||||
let start: u8 = 127;
|
||||
let bytes: [u8; 1] = start.to_be_bytes();
|
||||
let bytes: [u8; 8] = utils::increment(Some(&bytes));
|
||||
let res = u64::from_be_bytes(bytes);
|
||||
assert_eq!(res, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn increment_norm() {
|
||||
let start: u64 = 1_234_567;
|
||||
let bytes: [u8; 8] = start.to_be_bytes();
|
||||
let bytes: [u8; 8] = utils::increment(Some(&bytes));
|
||||
let res = u64::from_be_bytes(bytes);
|
||||
assert_eq!(res, 1_234_568);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn increment_wrap() {
|
||||
let start = u64::MAX;
|
||||
let bytes: [u8; 8] = start.to_be_bytes();
|
||||
let bytes: [u8; 8] = utils::increment(Some(&bytes));
|
||||
let res = u64::from_be_bytes(bytes);
|
||||
assert_eq!(res, 0);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[allow(clippy::as_conversions)]
|
||||
pub fn millis_since_unix_epoch() -> u64 {
|
||||
SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.expect("time is valid")
|
||||
.as_millis() as u64
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn rfc2822_from_seconds(epoch: i64) -> String {
|
||||
use chrono::{DateTime, Utc};
|
||||
|
||||
DateTime::<Utc>::from_timestamp(epoch, 0)
|
||||
.unwrap_or_default()
|
||||
.to_rfc2822()
|
||||
}
|
||||
+23
-20
@@ -4,25 +4,28 @@
|
||||
/// Set the environment variable `CONDUWUIT_VERSION_EXTRA` to any UTF-8 string
|
||||
/// to include it in parenthesis after the SemVer version. A common value are
|
||||
/// git commit hashes.
|
||||
use std::sync::OnceLock;
|
||||
|
||||
static BRANDING: &str = "Conduwuit";
|
||||
static SEMANTIC: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
static VERSION: OnceLock<String> = OnceLock::new();
|
||||
static USER_AGENT: OnceLock<String> = OnceLock::new();
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn conduwuit() -> String {
|
||||
match option_env!("CONDUWUIT_VERSION_EXTRA") {
|
||||
Some(extra) => {
|
||||
if extra.is_empty() {
|
||||
env!("CARGO_PKG_VERSION").to_owned()
|
||||
} else {
|
||||
format!("{} ({})", env!("CARGO_PKG_VERSION"), extra)
|
||||
}
|
||||
},
|
||||
None => match option_env!("CONDUIT_VERSION_EXTRA") {
|
||||
Some(extra) => {
|
||||
if extra.is_empty() {
|
||||
env!("CARGO_PKG_VERSION").to_owned()
|
||||
} else {
|
||||
format!("{} ({})", env!("CARGO_PKG_VERSION"), extra)
|
||||
}
|
||||
},
|
||||
None => env!("CARGO_PKG_VERSION").to_owned(),
|
||||
},
|
||||
}
|
||||
pub fn name() -> &'static str { BRANDING }
|
||||
|
||||
#[inline]
|
||||
pub fn version() -> &'static str { VERSION.get_or_init(init_version) }
|
||||
|
||||
#[inline]
|
||||
pub fn user_agent() -> &'static str { USER_AGENT.get_or_init(init_user_agent) }
|
||||
|
||||
fn init_user_agent() -> String { format!("{}/{}", name(), version()) }
|
||||
|
||||
fn init_version() -> String {
|
||||
option_env!("CONDUWUIT_VERSION_EXTRA")
|
||||
.or(option_env!("CONDUIT_VERSION_EXTRA"))
|
||||
.map_or(SEMANTIC.to_owned(), |extra| format!("{SEMANTIC} ({extra})"))
|
||||
}
|
||||
|
||||
+1
-18
@@ -17,21 +17,13 @@ crate-type = [
|
||||
]
|
||||
|
||||
[features]
|
||||
dev_release_log_level = []
|
||||
#dev_release_log_level = []
|
||||
release_max_log_level = [
|
||||
"tracing/max_level_trace",
|
||||
"tracing/release_max_level_info",
|
||||
"log/max_level_trace",
|
||||
"log/release_max_level_info",
|
||||
]
|
||||
sqlite = [
|
||||
"dep:rusqlite",
|
||||
"dep:parking_lot",
|
||||
"dep:thread_local",
|
||||
]
|
||||
rocksdb = [
|
||||
"dep:rust-rocksdb",
|
||||
]
|
||||
jemalloc = [
|
||||
"rust-rocksdb/jemalloc",
|
||||
]
|
||||
@@ -43,19 +35,10 @@ zstd_compression = [
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
chrono.workspace = true
|
||||
conduit-core.workspace = true
|
||||
log.workspace = true
|
||||
lru-cache.workspace = true
|
||||
parking_lot.optional = true
|
||||
parking_lot.workspace = true
|
||||
ruma.workspace = true
|
||||
rusqlite.optional = true
|
||||
rusqlite.workspace = true
|
||||
rust-rocksdb.optional = true
|
||||
rust-rocksdb.workspace = true
|
||||
thread_local.optional = true
|
||||
thread_local.workspace = true
|
||||
tokio.workspace = true
|
||||
tracing.workspace = true
|
||||
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::KeyValueDatabaseEngine;
|
||||
use crate::Engine;
|
||||
|
||||
pub struct Cork {
|
||||
db: Arc<dyn KeyValueDatabaseEngine>,
|
||||
db: Arc<Engine>,
|
||||
flush: bool,
|
||||
sync: bool,
|
||||
}
|
||||
|
||||
impl Cork {
|
||||
pub fn new(db: &Arc<dyn KeyValueDatabaseEngine>, flush: bool, sync: bool) -> Self {
|
||||
db.cork().unwrap();
|
||||
pub(super) fn new(db: &Arc<Engine>, flush: bool, sync: bool) -> Self {
|
||||
db.cork();
|
||||
Self {
|
||||
db: db.clone(),
|
||||
flush,
|
||||
@@ -21,7 +21,7 @@ impl Cork {
|
||||
|
||||
impl Drop for Cork {
|
||||
fn drop(&mut self) {
|
||||
self.db.uncork().ok();
|
||||
self.db.uncork();
|
||||
if self.flush {
|
||||
self.db.flush().ok();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
use std::{ops::Index, sync::Arc};
|
||||
|
||||
use conduit::{Result, Server};
|
||||
|
||||
use crate::{cork::Cork, maps, maps::Maps, Engine, Map};
|
||||
|
||||
pub struct Database {
|
||||
pub db: Arc<Engine>,
|
||||
pub map: Maps,
|
||||
}
|
||||
|
||||
impl Database {
|
||||
/// Load an existing database or create a new one.
|
||||
pub async fn open(server: &Arc<Server>) -> Result<Self> {
|
||||
let db = Engine::open(server)?;
|
||||
Ok(Self {
|
||||
db: db.clone(),
|
||||
map: maps::open(&db)?,
|
||||
})
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn cork(&self) -> Cork { Cork::new(&self.db, false, false) }
|
||||
|
||||
#[must_use]
|
||||
pub fn cork_and_flush(&self) -> Cork { Cork::new(&self.db, true, false) }
|
||||
|
||||
#[must_use]
|
||||
pub fn cork_and_sync(&self) -> Cork { Cork::new(&self.db, true, true) }
|
||||
}
|
||||
|
||||
impl Index<&str> for Database {
|
||||
type Output = Arc<Map>;
|
||||
|
||||
fn index(&self, name: &str) -> &Self::Output {
|
||||
self.map
|
||||
.get(name)
|
||||
.expect("column in database does not exist")
|
||||
}
|
||||
}
|
||||
@@ -1,45 +1,37 @@
|
||||
// no_link to prevent double-inclusion of librocksdb.a here and with
|
||||
// libconduit_core.so
|
||||
#[no_link]
|
||||
extern crate rust_rocksdb;
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
collections::{BTreeSet, HashMap},
|
||||
fmt::Write,
|
||||
sync::{atomic::AtomicU32, Arc},
|
||||
sync::{atomic::AtomicU32, Arc, Mutex, RwLock},
|
||||
};
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use rust_rocksdb::{
|
||||
use conduit::{debug, error, info, utils::time::rfc2822_from_seconds, warn, Result, Server};
|
||||
use rocksdb::{
|
||||
backup::{BackupEngine, BackupEngineOptions},
|
||||
perf::get_memory_usage_stats,
|
||||
Cache, ColumnFamilyDescriptor, DBCommon, DBWithThreadMode as Db, Env, MultiThreaded, Options,
|
||||
BoundColumnFamily, Cache, ColumnFamilyDescriptor, DBCommon, DBWithThreadMode, Env, MultiThreaded, Options,
|
||||
};
|
||||
use tracing::{debug, error, info, warn};
|
||||
|
||||
use crate::{watchers::Watchers, Config, KeyValueDatabaseEngine, KvTree, Result};
|
||||
use crate::{
|
||||
opts::{cf_options, db_options},
|
||||
or_else, result,
|
||||
};
|
||||
|
||||
pub(crate) mod kvtree;
|
||||
pub(crate) mod opts;
|
||||
|
||||
use kvtree::RocksDbEngineTree;
|
||||
use opts::{cf_options, db_options};
|
||||
|
||||
use super::watchers;
|
||||
|
||||
pub(crate) struct Engine {
|
||||
config: Config,
|
||||
pub struct Engine {
|
||||
server: Arc<Server>,
|
||||
row_cache: Cache,
|
||||
col_cache: HashMap<String, Cache>,
|
||||
col_cache: RwLock<HashMap<String, Cache>>,
|
||||
opts: Options,
|
||||
env: Env,
|
||||
old_cfs: Vec<String>,
|
||||
rocks: Db<MultiThreaded>,
|
||||
cfs: Mutex<BTreeSet<String>>,
|
||||
pub(crate) db: Db,
|
||||
corks: AtomicU32,
|
||||
}
|
||||
|
||||
impl KeyValueDatabaseEngine for Arc<Engine> {
|
||||
fn open(config: &Config) -> Result<Self> {
|
||||
pub(crate) type Db = DBWithThreadMode<MultiThreaded>;
|
||||
|
||||
impl Engine {
|
||||
pub(crate) fn open(server: &Arc<Server>) -> Result<Arc<Self>> {
|
||||
let config = &server.config;
|
||||
let cache_capacity_bytes = config.db_cache_capacity_mb * 1024.0 * 1024.0;
|
||||
|
||||
#[allow(clippy::as_conversions, clippy::cast_sign_loss, clippy::cast_possible_truncation)]
|
||||
@@ -51,20 +43,23 @@ impl KeyValueDatabaseEngine for Arc<Engine> {
|
||||
let mut col_cache = HashMap::new();
|
||||
col_cache.insert("primary".to_owned(), Cache::new_lru_cache(col_cache_capacity_bytes));
|
||||
|
||||
let mut db_env = Env::new()?;
|
||||
let mut db_env = Env::new().or_else(or_else)?;
|
||||
let row_cache = Cache::new_lru_cache(row_cache_capacity_bytes);
|
||||
let db_opts = db_options(config, &mut db_env, &row_cache, col_cache.get("primary").expect("cache"));
|
||||
|
||||
let load_time = std::time::Instant::now();
|
||||
if config.rocksdb_repair {
|
||||
warn!("Starting database repair. This may take a long time...");
|
||||
if let Err(e) = Db::<MultiThreaded>::repair(&db_opts, &config.database_path) {
|
||||
if let Err(e) = Db::repair(&db_opts, &config.database_path) {
|
||||
error!("Repair failed: {:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
debug!("Listing column families in database");
|
||||
let cfs = Db::<MultiThreaded>::list_cf(&db_opts, &config.database_path).unwrap_or_default();
|
||||
let cfs = Db::list_cf(&db_opts, &config.database_path)
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.collect::<BTreeSet<_>>();
|
||||
|
||||
debug!("Opening {} column family descriptors in database", cfs.len());
|
||||
let cfds = cfs
|
||||
@@ -73,80 +68,75 @@ impl KeyValueDatabaseEngine for Arc<Engine> {
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
debug!("Opening database...");
|
||||
let db = if config.rocksdb_read_only {
|
||||
Db::<MultiThreaded>::open_cf_for_read_only(&db_opts, &config.database_path, cfs.clone(), false)?
|
||||
let res = if config.rocksdb_read_only {
|
||||
Db::open_cf_for_read_only(&db_opts, &config.database_path, cfs.clone(), false)
|
||||
} else {
|
||||
Db::<MultiThreaded>::open_cf_descriptors(&db_opts, &config.database_path, cfds)?
|
||||
Db::open_cf_descriptors(&db_opts, &config.database_path, cfds)
|
||||
};
|
||||
|
||||
let db = res.or_else(or_else)?;
|
||||
info!(
|
||||
"Opened database at sequence number {} in {:?}",
|
||||
db.latest_sequence_number(),
|
||||
load_time.elapsed()
|
||||
);
|
||||
Ok(Self::new(Engine {
|
||||
config: config.clone(),
|
||||
|
||||
Ok(Arc::new(Self {
|
||||
server: server.clone(),
|
||||
row_cache,
|
||||
col_cache,
|
||||
col_cache: RwLock::new(col_cache),
|
||||
opts: db_opts,
|
||||
env: db_env,
|
||||
old_cfs: cfs,
|
||||
rocks: db,
|
||||
cfs: Mutex::new(cfs),
|
||||
db,
|
||||
corks: AtomicU32::new(0),
|
||||
}))
|
||||
}
|
||||
|
||||
fn open_tree(&self, name: &'static str) -> Result<Arc<dyn KvTree>> {
|
||||
if !self.old_cfs.contains(&name.to_owned()) {
|
||||
// Create if it didn't exist
|
||||
pub(crate) fn open_cf(&self, name: &str) -> Result<Arc<BoundColumnFamily<'_>>> {
|
||||
let mut cfs = self.cfs.lock().expect("locked");
|
||||
if !cfs.contains(name) {
|
||||
debug!("Creating new column family in database: {}", name);
|
||||
|
||||
// TODO: the workaround for this needs to be extended to rocksdb caches, but i
|
||||
// dont know that code to safely do that
|
||||
#[allow(clippy::let_underscore_must_use)]
|
||||
#[allow(clippy::let_underscore_untyped)] // attributes on expressions are experimental
|
||||
let _ = self.rocks.create_cf(name, &self.opts);
|
||||
let mut col_cache = self.col_cache.write().expect("locked");
|
||||
let opts = cf_options(&self.server.config, name, self.opts.clone(), &mut col_cache);
|
||||
if let Err(e) = self.db.create_cf(name, &opts) {
|
||||
error!("Failed to create new column family: {e}");
|
||||
return or_else(e);
|
||||
}
|
||||
|
||||
cfs.insert(name.to_owned());
|
||||
}
|
||||
|
||||
Ok(Arc::new(RocksDbEngineTree {
|
||||
name,
|
||||
db: Self::clone(self),
|
||||
watchers: Watchers::default(),
|
||||
}))
|
||||
Ok(self.cf(name))
|
||||
}
|
||||
|
||||
fn flush(&self) -> Result<()> {
|
||||
DBCommon::flush_wal(&self.rocks, false)?;
|
||||
|
||||
Ok(())
|
||||
pub(crate) fn cf(&self, name: &str) -> Arc<BoundColumnFamily<'_>> {
|
||||
self.db
|
||||
.cf_handle(name)
|
||||
.expect("column was created and exists")
|
||||
}
|
||||
|
||||
fn sync(&self) -> Result<()> {
|
||||
DBCommon::flush_wal(&self.rocks, true)?;
|
||||
pub fn flush(&self) -> Result<()> { result(DBCommon::flush_wal(&self.db, false)) }
|
||||
|
||||
Ok(())
|
||||
}
|
||||
pub fn sync(&self) -> Result<()> { result(DBCommon::flush_wal(&self.db, true)) }
|
||||
|
||||
fn corked(&self) -> bool { self.corks.load(std::sync::atomic::Ordering::Relaxed) > 0 }
|
||||
pub fn corked(&self) -> bool { self.corks.load(std::sync::atomic::Ordering::Relaxed) > 0 }
|
||||
|
||||
fn cork(&self) -> Result<()> {
|
||||
pub(crate) fn cork(&self) {
|
||||
self.corks
|
||||
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn uncork(&self) -> Result<()> {
|
||||
pub(crate) fn uncork(&self) {
|
||||
self.corks
|
||||
.fetch_sub(1, std::sync::atomic::Ordering::Relaxed);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::as_conversions, clippy::cast_sign_loss, clippy::cast_possible_truncation)]
|
||||
fn memory_usage(&self) -> Result<String> {
|
||||
pub fn memory_usage(&self) -> Result<String> {
|
||||
let mut res = String::new();
|
||||
let stats = get_memory_usage_stats(Some(&[&self.rocks]), Some(&[&self.row_cache]))?;
|
||||
let stats = get_memory_usage_stats(Some(&[&self.db]), Some(&[&self.row_cache])).or_else(or_else)?;
|
||||
writeln!(
|
||||
res,
|
||||
"Memory buffers: {:.2} MiB\nPending write: {:.2} MiB\nTable readers: {:.2} MiB\nRow cache: {:.2} MiB",
|
||||
@@ -157,7 +147,7 @@ impl KeyValueDatabaseEngine for Arc<Engine> {
|
||||
)
|
||||
.expect("should be able to write to string buffer");
|
||||
|
||||
for (name, cache) in &self.col_cache {
|
||||
for (name, cache) in &*self.col_cache.read().expect("locked") {
|
||||
writeln!(res, "{} cache: {:.2} MiB", name, cache.get_usage() as f64 / 1024.0 / 1024.0,)
|
||||
.expect("should be able to write to string buffer");
|
||||
}
|
||||
@@ -165,25 +155,23 @@ impl KeyValueDatabaseEngine for Arc<Engine> {
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
fn cleanup(&self) -> Result<()> {
|
||||
pub fn cleanup(&self) -> Result<()> {
|
||||
debug!("Running flush_opt");
|
||||
let flushoptions = rust_rocksdb::FlushOptions::default();
|
||||
|
||||
DBCommon::flush_opt(&self.rocks, &flushoptions)?;
|
||||
|
||||
Ok(())
|
||||
let flushoptions = rocksdb::FlushOptions::default();
|
||||
result(DBCommon::flush_opt(&self.db, &flushoptions))
|
||||
}
|
||||
|
||||
fn backup(&self) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let path = self.config.database_backup_path.as_ref();
|
||||
pub fn backup(&self) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let config = &self.server.config;
|
||||
let path = config.database_backup_path.as_ref();
|
||||
if path.is_none() || path.is_some_and(|path| path.as_os_str().is_empty()) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let options = BackupEngineOptions::new(path.unwrap())?;
|
||||
let mut engine = BackupEngine::open(&options, &self.env)?;
|
||||
if self.config.database_backups_to_keep > 0 {
|
||||
if let Err(e) = engine.create_new_backup_flush(&self.rocks, true) {
|
||||
if config.database_backups_to_keep > 0 {
|
||||
if let Err(e) = engine.create_new_backup_flush(&self.db, true) {
|
||||
return Err(Box::new(e));
|
||||
}
|
||||
|
||||
@@ -195,8 +183,8 @@ impl KeyValueDatabaseEngine for Arc<Engine> {
|
||||
);
|
||||
}
|
||||
|
||||
if self.config.database_backups_to_keep >= 0 {
|
||||
let keep = u32::try_from(self.config.database_backups_to_keep)?;
|
||||
if config.database_backups_to_keep >= 0 {
|
||||
let keep = u32::try_from(config.database_backups_to_keep)?;
|
||||
if let Err(e) = engine.purge_old_backups(keep.try_into()?) {
|
||||
error!("Failed to purge old backup: {:?}", e.to_string());
|
||||
}
|
||||
@@ -205,8 +193,9 @@ impl KeyValueDatabaseEngine for Arc<Engine> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn backup_list(&self) -> Result<String> {
|
||||
let path = self.config.database_backup_path.as_ref();
|
||||
pub fn backup_list(&self) -> Result<String> {
|
||||
let config = &self.server.config;
|
||||
let path = config.database_backup_path.as_ref();
|
||||
if path.is_none() || path.is_some_and(|path| path.as_os_str().is_empty()) {
|
||||
return Ok(
|
||||
"Configure database_backup_path to enable backups, or the path specified is not valid".to_owned(),
|
||||
@@ -214,16 +203,14 @@ impl KeyValueDatabaseEngine for Arc<Engine> {
|
||||
}
|
||||
|
||||
let mut res = String::new();
|
||||
let options = BackupEngineOptions::new(path.unwrap())?;
|
||||
let engine = BackupEngine::open(&options, &self.env)?;
|
||||
let options = BackupEngineOptions::new(path.expect("valid path")).or_else(or_else)?;
|
||||
let engine = BackupEngine::open(&options, &self.env).or_else(or_else)?;
|
||||
for info in engine.get_backup_info() {
|
||||
writeln!(
|
||||
res,
|
||||
"#{} {}: {} bytes, {} files",
|
||||
info.backup_id,
|
||||
DateTime::<Utc>::from_timestamp(info.timestamp, 0)
|
||||
.unwrap_or_default()
|
||||
.to_rfc2822(),
|
||||
rfc2822_from_seconds(info.timestamp),
|
||||
info.size,
|
||||
info.num_files,
|
||||
)
|
||||
@@ -233,8 +220,8 @@ impl KeyValueDatabaseEngine for Arc<Engine> {
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
fn file_list(&self) -> Result<String> {
|
||||
match self.rocks.live_files() {
|
||||
pub fn file_list(&self) -> Result<String> {
|
||||
match self.db.live_files() {
|
||||
Err(e) => Ok(String::from(e)),
|
||||
Ok(files) => {
|
||||
let mut res = String::new();
|
||||
@@ -252,10 +239,6 @@ impl KeyValueDatabaseEngine for Arc<Engine> {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: figure out if this is needed for rocksdb
|
||||
#[allow(dead_code)]
|
||||
fn clear_caches(&self) {}
|
||||
}
|
||||
|
||||
impl Drop for Engine {
|
||||
@@ -263,7 +246,7 @@ impl Drop for Engine {
|
||||
const BLOCKING: bool = true;
|
||||
|
||||
debug!("Waiting for background tasks to finish...");
|
||||
self.rocks.cancel_all_background_work(BLOCKING);
|
||||
self.db.cancel_all_background_work(BLOCKING);
|
||||
|
||||
debug!("Shutting down background threads");
|
||||
self.env.set_high_priority_background_threads(0);
|
||||
@@ -0,0 +1,25 @@
|
||||
use std::ops::Deref;
|
||||
|
||||
use rocksdb::DBPinnableSlice;
|
||||
|
||||
pub struct Handle<'a> {
|
||||
val: DBPinnableSlice<'a>,
|
||||
}
|
||||
|
||||
impl<'a> From<DBPinnableSlice<'a>> for Handle<'a> {
|
||||
fn from(val: DBPinnableSlice<'a>) -> Self {
|
||||
Self {
|
||||
val,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Handle<'_> {
|
||||
type Target = [u8];
|
||||
|
||||
fn deref(&self) -> &Self::Target { &self.val }
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for Handle<'_> {
|
||||
fn as_ref(&self) -> &[u8] { &self.val }
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
use std::{iter::FusedIterator, sync::Arc};
|
||||
|
||||
use conduit::Result;
|
||||
use rocksdb::{ColumnFamily, DBRawIteratorWithThreadMode, Direction, IteratorMode, ReadOptions};
|
||||
|
||||
use crate::{
|
||||
engine::Db,
|
||||
result,
|
||||
slice::{OwnedKeyVal, OwnedKeyValPair},
|
||||
Engine,
|
||||
};
|
||||
|
||||
type Cursor<'cursor> = DBRawIteratorWithThreadMode<'cursor, Db>;
|
||||
|
||||
struct State<'cursor> {
|
||||
cursor: Cursor<'cursor>,
|
||||
direction: Direction,
|
||||
valid: bool,
|
||||
init: bool,
|
||||
}
|
||||
|
||||
impl<'cursor> State<'cursor> {
|
||||
pub(crate) fn new(
|
||||
db: &'cursor Arc<Engine>, cf: &'cursor Arc<ColumnFamily>, opts: ReadOptions, mode: &IteratorMode<'_>,
|
||||
) -> Self {
|
||||
let mut cursor = db.db.raw_iterator_cf_opt(&**cf, opts);
|
||||
let direction = into_direction(mode);
|
||||
let valid = seek_init(&mut cursor, mode);
|
||||
Self {
|
||||
cursor,
|
||||
direction,
|
||||
valid,
|
||||
init: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Iter<'cursor> {
|
||||
state: State<'cursor>,
|
||||
}
|
||||
|
||||
impl<'cursor> Iter<'cursor> {
|
||||
pub(crate) fn new(
|
||||
db: &'cursor Arc<Engine>, cf: &'cursor Arc<ColumnFamily>, opts: ReadOptions, mode: &IteratorMode<'_>,
|
||||
) -> Self {
|
||||
Self {
|
||||
state: State::new(db, cf, opts, mode),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for Iter<'_> {
|
||||
type Item = OwnedKeyValPair;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if !self.state.init && self.state.valid {
|
||||
seek_next(&mut self.state.cursor, self.state.direction);
|
||||
} else if self.state.init {
|
||||
self.state.init = false;
|
||||
}
|
||||
|
||||
self.state
|
||||
.cursor
|
||||
.item()
|
||||
.map(OwnedKeyVal::from)
|
||||
.map(OwnedKeyVal::to_tuple)
|
||||
.or_else(|| {
|
||||
when_invalid(&mut self.state).expect("iterator invalidated due to error");
|
||||
None
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl FusedIterator for Iter<'_> {}
|
||||
|
||||
fn when_invalid(state: &mut State<'_>) -> Result<()> {
|
||||
state.valid = false;
|
||||
result(state.cursor.status())
|
||||
}
|
||||
|
||||
fn seek_next(cursor: &mut Cursor<'_>, direction: Direction) {
|
||||
match direction {
|
||||
Direction::Forward => cursor.next(),
|
||||
Direction::Reverse => cursor.prev(),
|
||||
}
|
||||
}
|
||||
|
||||
fn seek_init(cursor: &mut Cursor<'_>, mode: &IteratorMode<'_>) -> bool {
|
||||
use Direction::{Forward, Reverse};
|
||||
use IteratorMode::{End, From, Start};
|
||||
|
||||
match mode {
|
||||
Start => cursor.seek_to_first(),
|
||||
End => cursor.seek_to_last(),
|
||||
From(key, Forward) => cursor.seek(key),
|
||||
From(key, Reverse) => cursor.seek_for_prev(key),
|
||||
};
|
||||
|
||||
cursor.valid()
|
||||
}
|
||||
|
||||
fn into_direction(mode: &IteratorMode<'_>) -> Direction {
|
||||
use Direction::{Forward, Reverse};
|
||||
use IteratorMode::{End, From, Start};
|
||||
|
||||
match mode {
|
||||
Start | From(_, Forward) => Forward,
|
||||
End | From(_, Reverse) => Reverse,
|
||||
}
|
||||
}
|
||||
@@ -1,320 +0,0 @@
|
||||
use std::{
|
||||
collections::{BTreeMap, HashMap},
|
||||
path::Path,
|
||||
sync::{Arc, Mutex, RwLock},
|
||||
};
|
||||
|
||||
use conduit::{Config, Error, PduCount, Result, Server};
|
||||
use lru_cache::LruCache;
|
||||
use ruma::{CanonicalJsonValue, OwnedDeviceId, OwnedRoomId, OwnedUserId};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::{KeyValueDatabaseEngine, KvTree};
|
||||
|
||||
pub struct KeyValueDatabase {
|
||||
pub db: Arc<dyn KeyValueDatabaseEngine>,
|
||||
|
||||
//pub globals: globals::Globals,
|
||||
pub global: Arc<dyn KvTree>,
|
||||
pub server_signingkeys: Arc<dyn KvTree>,
|
||||
|
||||
pub roomid_inviteviaservers: Arc<dyn KvTree>,
|
||||
|
||||
//pub users: users::Users,
|
||||
pub userid_password: Arc<dyn KvTree>,
|
||||
pub userid_displayname: Arc<dyn KvTree>,
|
||||
pub userid_avatarurl: Arc<dyn KvTree>,
|
||||
pub userid_blurhash: Arc<dyn KvTree>,
|
||||
pub userdeviceid_token: Arc<dyn KvTree>,
|
||||
pub userdeviceid_metadata: Arc<dyn KvTree>, // This is also used to check if a device exists
|
||||
pub userid_devicelistversion: Arc<dyn KvTree>, // DevicelistVersion = u64
|
||||
pub token_userdeviceid: Arc<dyn KvTree>,
|
||||
|
||||
pub onetimekeyid_onetimekeys: Arc<dyn KvTree>, // OneTimeKeyId = UserId + DeviceKeyId
|
||||
pub userid_lastonetimekeyupdate: Arc<dyn KvTree>, // LastOneTimeKeyUpdate = Count
|
||||
pub keychangeid_userid: Arc<dyn KvTree>, // KeyChangeId = UserId/RoomId + Count
|
||||
pub keyid_key: Arc<dyn KvTree>, // KeyId = UserId + KeyId (depends on key type)
|
||||
pub userid_masterkeyid: Arc<dyn KvTree>,
|
||||
pub userid_selfsigningkeyid: Arc<dyn KvTree>,
|
||||
pub userid_usersigningkeyid: Arc<dyn KvTree>,
|
||||
|
||||
pub userfilterid_filter: Arc<dyn KvTree>, // UserFilterId = UserId + FilterId
|
||||
pub todeviceid_events: Arc<dyn KvTree>, // ToDeviceId = UserId + DeviceId + Count
|
||||
pub userid_presenceid: Arc<dyn KvTree>, // UserId => Count
|
||||
pub presenceid_presence: Arc<dyn KvTree>, // Count + UserId => Presence
|
||||
|
||||
//pub uiaa: uiaa::Uiaa,
|
||||
pub userdevicesessionid_uiaainfo: Arc<dyn KvTree>, // User-interactive authentication
|
||||
pub userdevicesessionid_uiaarequest: RwLock<BTreeMap<(OwnedUserId, OwnedDeviceId, String), CanonicalJsonValue>>,
|
||||
|
||||
//pub edus: RoomEdus,
|
||||
pub readreceiptid_readreceipt: Arc<dyn KvTree>, // ReadReceiptId = RoomId + Count + UserId
|
||||
pub roomuserid_privateread: Arc<dyn KvTree>, // RoomUserId = Room + User, PrivateRead = Count
|
||||
pub roomuserid_lastprivatereadupdate: Arc<dyn KvTree>, // LastPrivateReadUpdate = Count
|
||||
|
||||
//pub rooms: rooms::Rooms,
|
||||
pub pduid_pdu: Arc<dyn KvTree>, // PduId = ShortRoomId + Count
|
||||
pub eventid_pduid: Arc<dyn KvTree>,
|
||||
pub roomid_pduleaves: Arc<dyn KvTree>,
|
||||
pub alias_roomid: Arc<dyn KvTree>,
|
||||
pub alias_userid: Arc<dyn KvTree>, // UserId = AliasId (User who created the alias)
|
||||
pub aliasid_alias: Arc<dyn KvTree>, // AliasId = RoomId + Count
|
||||
pub publicroomids: Arc<dyn KvTree>,
|
||||
|
||||
pub threadid_userids: Arc<dyn KvTree>, // ThreadId = RoomId + Count
|
||||
|
||||
pub tokenids: Arc<dyn KvTree>, // TokenId = ShortRoomId + Token + PduIdCount
|
||||
|
||||
/// Participating servers in a room.
|
||||
pub roomserverids: Arc<dyn KvTree>, // RoomServerId = RoomId + ServerName
|
||||
pub serverroomids: Arc<dyn KvTree>, // ServerRoomId = ServerName + RoomId
|
||||
|
||||
pub userroomid_joined: Arc<dyn KvTree>,
|
||||
pub roomuserid_joined: Arc<dyn KvTree>,
|
||||
pub roomid_joinedcount: Arc<dyn KvTree>,
|
||||
pub roomid_invitedcount: Arc<dyn KvTree>,
|
||||
pub roomuseroncejoinedids: Arc<dyn KvTree>,
|
||||
pub userroomid_invitestate: Arc<dyn KvTree>, // InviteState = Vec<Raw<Pdu>>
|
||||
pub roomuserid_invitecount: Arc<dyn KvTree>, // InviteCount = Count
|
||||
pub userroomid_leftstate: Arc<dyn KvTree>,
|
||||
pub roomuserid_leftcount: Arc<dyn KvTree>,
|
||||
|
||||
pub disabledroomids: Arc<dyn KvTree>, // Rooms where incoming federation handling is disabled
|
||||
|
||||
pub bannedroomids: Arc<dyn KvTree>, // Rooms where local users are not allowed to join
|
||||
|
||||
pub lazyloadedids: Arc<dyn KvTree>, // LazyLoadedIds = UserId + DeviceId + RoomId + LazyLoadedUserId
|
||||
|
||||
pub userroomid_notificationcount: Arc<dyn KvTree>, // NotifyCount = u64
|
||||
pub userroomid_highlightcount: Arc<dyn KvTree>, // HightlightCount = u64
|
||||
pub roomuserid_lastnotificationread: Arc<dyn KvTree>, // LastNotificationRead = u64
|
||||
|
||||
/// Remember the current state hash of a room.
|
||||
pub roomid_shortstatehash: Arc<dyn KvTree>,
|
||||
pub roomsynctoken_shortstatehash: Arc<dyn KvTree>,
|
||||
/// Remember the state hash at events in the past.
|
||||
pub shorteventid_shortstatehash: Arc<dyn KvTree>,
|
||||
pub statekey_shortstatekey: Arc<dyn KvTree>, /* StateKey = EventType + StateKey, ShortStateKey =
|
||||
* Count */
|
||||
pub shortstatekey_statekey: Arc<dyn KvTree>,
|
||||
|
||||
pub roomid_shortroomid: Arc<dyn KvTree>,
|
||||
|
||||
pub shorteventid_eventid: Arc<dyn KvTree>,
|
||||
pub eventid_shorteventid: Arc<dyn KvTree>,
|
||||
|
||||
pub statehash_shortstatehash: Arc<dyn KvTree>,
|
||||
pub shortstatehash_statediff: Arc<dyn KvTree>, /* StateDiff = parent (or 0) +
|
||||
* (shortstatekey+shorteventid++) + 0_u64 +
|
||||
* (shortstatekey+shorteventid--) */
|
||||
|
||||
pub shorteventid_authchain: Arc<dyn KvTree>,
|
||||
|
||||
/// RoomId + EventId -> outlier PDU.
|
||||
/// Any pdu that has passed the steps 1-8 in the incoming event
|
||||
/// /federation/send/txn.
|
||||
pub eventid_outlierpdu: Arc<dyn KvTree>,
|
||||
pub softfailedeventids: Arc<dyn KvTree>,
|
||||
|
||||
/// ShortEventId + ShortEventId -> ().
|
||||
pub tofrom_relation: Arc<dyn KvTree>,
|
||||
/// RoomId + EventId -> Parent PDU EventId.
|
||||
pub referencedevents: Arc<dyn KvTree>,
|
||||
|
||||
//pub account_data: account_data::AccountData,
|
||||
pub roomuserdataid_accountdata: Arc<dyn KvTree>, // RoomUserDataId = Room + User + Count + Type
|
||||
pub roomusertype_roomuserdataid: Arc<dyn KvTree>, // RoomUserType = Room + User + Type
|
||||
|
||||
//pub media: media::Media,
|
||||
pub mediaid_file: Arc<dyn KvTree>, // MediaId = MXC + WidthHeight + ContentDisposition + ContentType
|
||||
pub url_previews: Arc<dyn KvTree>,
|
||||
pub mediaid_user: Arc<dyn KvTree>,
|
||||
//pub key_backups: key_backups::KeyBackups,
|
||||
pub backupid_algorithm: Arc<dyn KvTree>, // BackupId = UserId + Version(Count)
|
||||
pub backupid_etag: Arc<dyn KvTree>, // BackupId = UserId + Version(Count)
|
||||
pub backupkeyid_backup: Arc<dyn KvTree>, // BackupKeyId = UserId + Version + RoomId + SessionId
|
||||
|
||||
//pub transaction_ids: transaction_ids::TransactionIds,
|
||||
pub userdevicetxnid_response: Arc<dyn KvTree>, /* Response can be empty (/sendToDevice) or the event id
|
||||
* (/send) */
|
||||
//pub sending: sending::Sending,
|
||||
pub servername_educount: Arc<dyn KvTree>, // EduCount: Count of last EDU sync
|
||||
pub servernameevent_data: Arc<dyn KvTree>, /* ServernameEvent = (+ / $)SenderKey / ServerName / UserId +
|
||||
* PduId / Id (for edus), Data = EDU content */
|
||||
pub servercurrentevent_data: Arc<dyn KvTree>, /* ServerCurrentEvents = (+ / $)ServerName / UserId + PduId
|
||||
* / Id (for edus), Data = EDU content */
|
||||
|
||||
//pub appservice: appservice::Appservice,
|
||||
pub id_appserviceregistrations: Arc<dyn KvTree>,
|
||||
|
||||
//pub pusher: pusher::PushData,
|
||||
pub senderkey_pusher: Arc<dyn KvTree>,
|
||||
|
||||
pub auth_chain_cache: Mutex<LruCache<Vec<u64>, Arc<[u64]>>>,
|
||||
pub appservice_in_room_cache: RwLock<HashMap<OwnedRoomId, HashMap<String, bool>>>,
|
||||
pub lasttimelinecount_cache: Mutex<HashMap<OwnedRoomId, PduCount>>,
|
||||
}
|
||||
|
||||
impl KeyValueDatabase {
|
||||
/// Load an existing database or create a new one.
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub async fn load_or_create(server: &Arc<Server>) -> Result<Self> {
|
||||
let config = &server.config;
|
||||
check_db_setup(config)?;
|
||||
let builder = build(config)?;
|
||||
Ok(Self {
|
||||
db: builder.clone(),
|
||||
userid_password: builder.open_tree("userid_password")?,
|
||||
userid_displayname: builder.open_tree("userid_displayname")?,
|
||||
userid_avatarurl: builder.open_tree("userid_avatarurl")?,
|
||||
userid_blurhash: builder.open_tree("userid_blurhash")?,
|
||||
userdeviceid_token: builder.open_tree("userdeviceid_token")?,
|
||||
userdeviceid_metadata: builder.open_tree("userdeviceid_metadata")?,
|
||||
userid_devicelistversion: builder.open_tree("userid_devicelistversion")?,
|
||||
token_userdeviceid: builder.open_tree("token_userdeviceid")?,
|
||||
onetimekeyid_onetimekeys: builder.open_tree("onetimekeyid_onetimekeys")?,
|
||||
userid_lastonetimekeyupdate: builder.open_tree("userid_lastonetimekeyupdate")?,
|
||||
keychangeid_userid: builder.open_tree("keychangeid_userid")?,
|
||||
keyid_key: builder.open_tree("keyid_key")?,
|
||||
userid_masterkeyid: builder.open_tree("userid_masterkeyid")?,
|
||||
userid_selfsigningkeyid: builder.open_tree("userid_selfsigningkeyid")?,
|
||||
userid_usersigningkeyid: builder.open_tree("userid_usersigningkeyid")?,
|
||||
userfilterid_filter: builder.open_tree("userfilterid_filter")?,
|
||||
todeviceid_events: builder.open_tree("todeviceid_events")?,
|
||||
userid_presenceid: builder.open_tree("userid_presenceid")?,
|
||||
presenceid_presence: builder.open_tree("presenceid_presence")?,
|
||||
|
||||
userdevicesessionid_uiaainfo: builder.open_tree("userdevicesessionid_uiaainfo")?,
|
||||
userdevicesessionid_uiaarequest: RwLock::new(BTreeMap::new()),
|
||||
readreceiptid_readreceipt: builder.open_tree("readreceiptid_readreceipt")?,
|
||||
roomuserid_privateread: builder.open_tree("roomuserid_privateread")?, // "Private" read receipt
|
||||
roomuserid_lastprivatereadupdate: builder.open_tree("roomuserid_lastprivatereadupdate")?,
|
||||
pduid_pdu: builder.open_tree("pduid_pdu")?,
|
||||
eventid_pduid: builder.open_tree("eventid_pduid")?,
|
||||
roomid_pduleaves: builder.open_tree("roomid_pduleaves")?,
|
||||
|
||||
alias_roomid: builder.open_tree("alias_roomid")?,
|
||||
alias_userid: builder.open_tree("alias_userid")?,
|
||||
aliasid_alias: builder.open_tree("aliasid_alias")?,
|
||||
publicroomids: builder.open_tree("publicroomids")?,
|
||||
|
||||
threadid_userids: builder.open_tree("threadid_userids")?,
|
||||
|
||||
tokenids: builder.open_tree("tokenids")?,
|
||||
|
||||
roomserverids: builder.open_tree("roomserverids")?,
|
||||
serverroomids: builder.open_tree("serverroomids")?,
|
||||
userroomid_joined: builder.open_tree("userroomid_joined")?,
|
||||
roomuserid_joined: builder.open_tree("roomuserid_joined")?,
|
||||
roomid_joinedcount: builder.open_tree("roomid_joinedcount")?,
|
||||
roomid_invitedcount: builder.open_tree("roomid_invitedcount")?,
|
||||
roomuseroncejoinedids: builder.open_tree("roomuseroncejoinedids")?,
|
||||
userroomid_invitestate: builder.open_tree("userroomid_invitestate")?,
|
||||
roomuserid_invitecount: builder.open_tree("roomuserid_invitecount")?,
|
||||
userroomid_leftstate: builder.open_tree("userroomid_leftstate")?,
|
||||
roomuserid_leftcount: builder.open_tree("roomuserid_leftcount")?,
|
||||
|
||||
disabledroomids: builder.open_tree("disabledroomids")?,
|
||||
|
||||
bannedroomids: builder.open_tree("bannedroomids")?,
|
||||
|
||||
lazyloadedids: builder.open_tree("lazyloadedids")?,
|
||||
|
||||
userroomid_notificationcount: builder.open_tree("userroomid_notificationcount")?,
|
||||
userroomid_highlightcount: builder.open_tree("userroomid_highlightcount")?,
|
||||
roomuserid_lastnotificationread: builder.open_tree("userroomid_highlightcount")?,
|
||||
|
||||
statekey_shortstatekey: builder.open_tree("statekey_shortstatekey")?,
|
||||
shortstatekey_statekey: builder.open_tree("shortstatekey_statekey")?,
|
||||
|
||||
shorteventid_authchain: builder.open_tree("shorteventid_authchain")?,
|
||||
|
||||
roomid_shortroomid: builder.open_tree("roomid_shortroomid")?,
|
||||
|
||||
shortstatehash_statediff: builder.open_tree("shortstatehash_statediff")?,
|
||||
eventid_shorteventid: builder.open_tree("eventid_shorteventid")?,
|
||||
shorteventid_eventid: builder.open_tree("shorteventid_eventid")?,
|
||||
shorteventid_shortstatehash: builder.open_tree("shorteventid_shortstatehash")?,
|
||||
roomid_shortstatehash: builder.open_tree("roomid_shortstatehash")?,
|
||||
roomsynctoken_shortstatehash: builder.open_tree("roomsynctoken_shortstatehash")?,
|
||||
statehash_shortstatehash: builder.open_tree("statehash_shortstatehash")?,
|
||||
|
||||
eventid_outlierpdu: builder.open_tree("eventid_outlierpdu")?,
|
||||
softfailedeventids: builder.open_tree("softfailedeventids")?,
|
||||
|
||||
tofrom_relation: builder.open_tree("tofrom_relation")?,
|
||||
referencedevents: builder.open_tree("referencedevents")?,
|
||||
roomuserdataid_accountdata: builder.open_tree("roomuserdataid_accountdata")?,
|
||||
roomusertype_roomuserdataid: builder.open_tree("roomusertype_roomuserdataid")?,
|
||||
mediaid_file: builder.open_tree("mediaid_file")?,
|
||||
url_previews: builder.open_tree("url_previews")?,
|
||||
mediaid_user: builder.open_tree("mediaid_user")?,
|
||||
backupid_algorithm: builder.open_tree("backupid_algorithm")?,
|
||||
backupid_etag: builder.open_tree("backupid_etag")?,
|
||||
backupkeyid_backup: builder.open_tree("backupkeyid_backup")?,
|
||||
userdevicetxnid_response: builder.open_tree("userdevicetxnid_response")?,
|
||||
servername_educount: builder.open_tree("servername_educount")?,
|
||||
servernameevent_data: builder.open_tree("servernameevent_data")?,
|
||||
servercurrentevent_data: builder.open_tree("servercurrentevent_data")?,
|
||||
id_appserviceregistrations: builder.open_tree("id_appserviceregistrations")?,
|
||||
senderkey_pusher: builder.open_tree("senderkey_pusher")?,
|
||||
global: builder.open_tree("global")?,
|
||||
server_signingkeys: builder.open_tree("server_signingkeys")?,
|
||||
|
||||
roomid_inviteviaservers: builder.open_tree("roomid_inviteviaservers")?,
|
||||
|
||||
auth_chain_cache: Mutex::new(LruCache::new(
|
||||
(f64::from(config.auth_chain_cache_capacity) * config.conduit_cache_capacity_modifier) as usize,
|
||||
)),
|
||||
appservice_in_room_cache: RwLock::new(HashMap::new()),
|
||||
lasttimelinecount_cache: Mutex::new(HashMap::new()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn build(config: &Config) -> Result<Arc<dyn KeyValueDatabaseEngine>> {
|
||||
match &*config.database_backend {
|
||||
"sqlite" => {
|
||||
debug!("Got sqlite database backend");
|
||||
#[cfg(not(feature = "sqlite"))]
|
||||
return Err(Error::bad_config("Database backend not found."));
|
||||
#[cfg(feature = "sqlite")]
|
||||
Ok(Arc::new(Arc::<crate::sqlite::Engine>::open(config)?))
|
||||
},
|
||||
"rocksdb" => {
|
||||
debug!("Got rocksdb database backend");
|
||||
#[cfg(not(feature = "rocksdb"))]
|
||||
return Err(Error::bad_config("Database backend not found."));
|
||||
#[cfg(feature = "rocksdb")]
|
||||
Ok(Arc::new(Arc::<crate::rocksdb::Engine>::open(config)?))
|
||||
},
|
||||
_ => Err(Error::bad_config(
|
||||
"Database backend not found. sqlite (not recommended) and rocksdb are the only supported backends.",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn check_db_setup(config: &Config) -> Result<()> {
|
||||
let path = Path::new(&config.database_path);
|
||||
|
||||
let sqlite_exists = path.join("conduit.db").exists();
|
||||
let rocksdb_exists = path.join("IDENTITY").exists();
|
||||
|
||||
if sqlite_exists && rocksdb_exists {
|
||||
return Err(Error::bad_config("Multiple databases at database_path detected."));
|
||||
}
|
||||
|
||||
if sqlite_exists && config.database_backend != "sqlite" {
|
||||
return Err(Error::bad_config(
|
||||
"Found sqlite at database_path, but is not specified in config.",
|
||||
));
|
||||
}
|
||||
|
||||
if rocksdb_exists && config.database_backend != "rocksdb" {
|
||||
return Err(Error::bad_config(
|
||||
"Found rocksdb at database_path, but is not specified in config.",
|
||||
));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
use std::{error::Error, sync::Arc};
|
||||
|
||||
use super::{Config, KvTree};
|
||||
use crate::Result;
|
||||
|
||||
pub trait KeyValueDatabaseEngine: Send + Sync {
|
||||
fn open(config: &Config) -> Result<Self>
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
fn open_tree(&self, name: &'static str) -> Result<Arc<dyn KvTree>>;
|
||||
|
||||
fn flush(&self) -> Result<()>;
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn sync(&self) -> Result<()> { Ok(()) }
|
||||
|
||||
fn cork(&self) -> Result<()> { Ok(()) }
|
||||
|
||||
fn uncork(&self) -> Result<()> { Ok(()) }
|
||||
|
||||
fn corked(&self) -> bool { false }
|
||||
|
||||
fn cleanup(&self) -> Result<()> { Ok(()) }
|
||||
|
||||
fn memory_usage(&self) -> Result<String> {
|
||||
Ok("Current database engine does not support memory usage reporting.".to_owned())
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn clear_caches(&self) {}
|
||||
|
||||
fn backup(&self) -> Result<(), Box<dyn Error>> { unimplemented!() }
|
||||
|
||||
fn backup_list(&self) -> Result<String> { Ok(String::new()) }
|
||||
|
||||
fn file_list(&self) -> Result<String> { Ok(String::new()) }
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
use std::{future::Future, pin::Pin};
|
||||
|
||||
use crate::Result;
|
||||
|
||||
pub trait KvTree: Send + Sync {
|
||||
fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>>;
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn multi_get(&self, keys: &[&[u8]]) -> Result<Vec<Option<Vec<u8>>>> {
|
||||
let mut ret: Vec<Option<Vec<u8>>> = Vec::with_capacity(keys.len());
|
||||
for key in keys {
|
||||
ret.push(self.get(key)?);
|
||||
}
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
fn insert(&self, key: &[u8], value: &[u8]) -> Result<()>;
|
||||
fn insert_batch(&self, iter: &mut dyn Iterator<Item = (Vec<u8>, Vec<u8>)>) -> Result<()> {
|
||||
for (key, value) in iter {
|
||||
self.insert(&key, &value)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn remove(&self, key: &[u8]) -> Result<()>;
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn remove_batch(&self, iter: &mut dyn Iterator<Item = Vec<u8>>) -> Result<()> {
|
||||
for key in iter {
|
||||
self.remove(&key)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = (Vec<u8>, Vec<u8>)> + 'a>;
|
||||
|
||||
fn iter_from<'a>(&'a self, from: &[u8], backwards: bool) -> Box<dyn Iterator<Item = (Vec<u8>, Vec<u8>)> + 'a>;
|
||||
|
||||
fn increment(&self, key: &[u8]) -> Result<Vec<u8>>;
|
||||
fn increment_batch(&self, iter: &mut dyn Iterator<Item = Vec<u8>>) -> Result<()> {
|
||||
for key in iter {
|
||||
self.increment(&key)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn scan_prefix<'a>(&'a self, prefix: Vec<u8>) -> Box<dyn Iterator<Item = (Vec<u8>, Vec<u8>)> + 'a>;
|
||||
|
||||
fn watch_prefix<'a>(&'a self, prefix: &[u8]) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>>;
|
||||
|
||||
fn clear(&self) -> Result<()> {
|
||||
for (key, _) in self.iter() {
|
||||
self.remove(&key)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,248 @@
|
||||
use std::{future::Future, mem::size_of, pin::Pin, sync::Arc};
|
||||
|
||||
use conduit::{utils, Result};
|
||||
use rocksdb::{
|
||||
AsColumnFamilyRef, ColumnFamily, Direction, IteratorMode, ReadOptions, WriteBatchWithTransaction, WriteOptions,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
or_else, result,
|
||||
slice::{Byte, Key, KeyVal, OwnedKey, OwnedKeyValPair, OwnedVal, Val},
|
||||
watchers::Watchers,
|
||||
Engine, Handle, Iter,
|
||||
};
|
||||
|
||||
pub struct Map {
|
||||
name: String,
|
||||
db: Arc<Engine>,
|
||||
cf: Arc<ColumnFamily>,
|
||||
watchers: Watchers,
|
||||
write_options: WriteOptions,
|
||||
read_options: ReadOptions,
|
||||
}
|
||||
|
||||
type OwnedKeyValPairIter<'a> = Box<dyn Iterator<Item = OwnedKeyValPair> + Send + 'a>;
|
||||
|
||||
impl Map {
|
||||
pub(crate) fn open(db: &Arc<Engine>, name: &str) -> Result<Arc<Self>> {
|
||||
Ok(Arc::new(Self {
|
||||
name: name.to_owned(),
|
||||
db: db.clone(),
|
||||
cf: open(db, name)?,
|
||||
watchers: Watchers::default(),
|
||||
write_options: write_options_default(),
|
||||
read_options: read_options_default(),
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn get(&self, key: &Key) -> Result<Option<Handle<'_>>> {
|
||||
let read_options = &self.read_options;
|
||||
let res = self.db.db.get_pinned_cf_opt(&self.cf(), key, read_options);
|
||||
|
||||
Ok(result(res)?.map(Handle::from))
|
||||
}
|
||||
|
||||
pub fn multi_get(&self, keys: &[&Key]) -> Result<Vec<Option<OwnedVal>>> {
|
||||
// Optimization can be `true` if key vector is pre-sorted **by the column
|
||||
// comparator**.
|
||||
const SORTED: bool = false;
|
||||
|
||||
let mut ret: Vec<Option<OwnedKey>> = Vec::with_capacity(keys.len());
|
||||
let read_options = &self.read_options;
|
||||
for res in self
|
||||
.db
|
||||
.db
|
||||
.batched_multi_get_cf_opt(&self.cf(), keys, SORTED, read_options)
|
||||
{
|
||||
match res {
|
||||
Ok(Some(res)) => ret.push(Some((*res).to_vec())),
|
||||
Ok(None) => ret.push(None),
|
||||
Err(e) => return or_else(e),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
pub fn insert(&self, key: &Key, value: &Val) -> Result<()> {
|
||||
let write_options = &self.write_options;
|
||||
self.db
|
||||
.db
|
||||
.put_cf_opt(&self.cf(), key, value, write_options)
|
||||
.or_else(or_else)?;
|
||||
|
||||
if !self.db.corked() {
|
||||
self.db.flush()?;
|
||||
}
|
||||
|
||||
self.watchers.wake(key);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn insert_batch<'a, I>(&'a self, iter: I) -> Result<()>
|
||||
where
|
||||
I: Iterator<Item = KeyVal<'a>>,
|
||||
{
|
||||
let mut batch = WriteBatchWithTransaction::<false>::default();
|
||||
for KeyVal(key, value) in iter {
|
||||
batch.put_cf(&self.cf(), key, value);
|
||||
}
|
||||
|
||||
let write_options = &self.write_options;
|
||||
let res = self.db.db.write_opt(batch, write_options);
|
||||
|
||||
if !self.db.corked() {
|
||||
self.db.flush()?;
|
||||
}
|
||||
|
||||
result(res)
|
||||
}
|
||||
|
||||
pub fn remove(&self, key: &Key) -> Result<()> {
|
||||
let write_options = &self.write_options;
|
||||
let res = self.db.db.delete_cf_opt(&self.cf(), key, write_options);
|
||||
|
||||
if !self.db.corked() {
|
||||
self.db.flush()?;
|
||||
}
|
||||
|
||||
result(res)
|
||||
}
|
||||
|
||||
pub fn remove_batch<'a, I>(&'a self, iter: I) -> Result<()>
|
||||
where
|
||||
I: Iterator<Item = &'a Key>,
|
||||
{
|
||||
let mut batch = WriteBatchWithTransaction::<false>::default();
|
||||
for key in iter {
|
||||
batch.delete_cf(&self.cf(), key);
|
||||
}
|
||||
|
||||
let write_options = &self.write_options;
|
||||
let res = self.db.db.write_opt(batch, write_options);
|
||||
|
||||
if !self.db.corked() {
|
||||
self.db.flush()?;
|
||||
}
|
||||
|
||||
result(res)
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> OwnedKeyValPairIter<'_> {
|
||||
let mode = IteratorMode::Start;
|
||||
let read_options = read_options_default();
|
||||
Box::new(Iter::new(&self.db, &self.cf, read_options, &mode))
|
||||
}
|
||||
|
||||
pub fn iter_from(&self, from: &Key, reverse: bool) -> OwnedKeyValPairIter<'_> {
|
||||
let direction = if reverse {
|
||||
Direction::Reverse
|
||||
} else {
|
||||
Direction::Forward
|
||||
};
|
||||
let mode = IteratorMode::From(from, direction);
|
||||
let read_options = read_options_default();
|
||||
Box::new(Iter::new(&self.db, &self.cf, read_options, &mode))
|
||||
}
|
||||
|
||||
pub fn scan_prefix(&self, prefix: OwnedKey) -> OwnedKeyValPairIter<'_> {
|
||||
let mode = IteratorMode::From(&prefix, Direction::Forward);
|
||||
let read_options = read_options_default();
|
||||
Box::new(Iter::new(&self.db, &self.cf, read_options, &mode).take_while(move |(k, _)| k.starts_with(&prefix)))
|
||||
}
|
||||
|
||||
pub fn increment(&self, key: &Key) -> Result<[Byte; size_of::<u64>()]> {
|
||||
let old = self.get(key)?;
|
||||
let new = utils::increment(old.as_deref());
|
||||
self.insert(key, &new)?;
|
||||
|
||||
if !self.db.corked() {
|
||||
self.db.flush()?;
|
||||
}
|
||||
|
||||
Ok(new)
|
||||
}
|
||||
|
||||
pub fn increment_batch<'a, I>(&'a self, iter: I) -> Result<()>
|
||||
where
|
||||
I: Iterator<Item = &'a Key>,
|
||||
{
|
||||
let mut batch = WriteBatchWithTransaction::<false>::default();
|
||||
for key in iter {
|
||||
let old = self.get(key)?;
|
||||
let new = utils::increment(old.as_deref());
|
||||
batch.put_cf(&self.cf(), key, new);
|
||||
}
|
||||
|
||||
let write_options = &self.write_options;
|
||||
let res = self.db.db.write_opt(batch, write_options);
|
||||
|
||||
if !self.db.corked() {
|
||||
self.db.flush()?;
|
||||
}
|
||||
|
||||
result(res)
|
||||
}
|
||||
|
||||
pub fn watch_prefix<'a>(&'a self, prefix: &Key) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||
self.watchers.watch(prefix)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn name(&self) -> &str { &self.name }
|
||||
|
||||
fn cf(&self) -> impl AsColumnFamilyRef + '_ { &*self.cf }
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a Map {
|
||||
type IntoIter = Box<dyn Iterator<Item = Self::Item> + Send + 'a>;
|
||||
type Item = OwnedKeyValPair;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter { self.iter() }
|
||||
}
|
||||
|
||||
fn open(db: &Arc<Engine>, name: &str) -> Result<Arc<ColumnFamily>> {
|
||||
let bounded_arc = db.open_cf(name)?;
|
||||
let bounded_ptr = Arc::into_raw(bounded_arc);
|
||||
let cf_ptr = bounded_ptr.cast::<ColumnFamily>();
|
||||
|
||||
// SAFETY: After thorough contemplation this appears to be the best solution,
|
||||
// even by a significant margin.
|
||||
//
|
||||
// BACKGROUND: Column family handles out of RocksDB are basic pointers and can
|
||||
// be invalidated: 1. when the database closes. 2. when the column is dropped or
|
||||
// closed. rust_rocksdb wraps this for us by storing handles in their own
|
||||
// `RwLock<BTreeMap>` map and returning an Arc<BoundColumnFamily<'_>>` to
|
||||
// provide expected safety. Similarly in "single-threaded mode" we would
|
||||
// receive `&'_ ColumnFamily`.
|
||||
//
|
||||
// PROBLEM: We need to hold these handles in a field, otherwise we have to take
|
||||
// a lock and get them by name from this map for every query, which is what
|
||||
// conduit was doing, but we're not going to make a query for every query so we
|
||||
// need to be holding it right. The lifetime parameter on these references makes
|
||||
// that complicated. If this can be done without polluting the userspace
|
||||
// with lifetimes on every instance of `Map` then this `unsafe` might not be
|
||||
// necessary.
|
||||
//
|
||||
// SOLUTION: After investigating the underlying types it appears valid to
|
||||
// Arc-swap `BoundColumnFamily<'_>` for `ColumnFamily`. They have the
|
||||
// same inner data, the same Drop behavior, Deref, etc. We're just losing the
|
||||
// lifetime parameter. We should not hold this handle, even in its Arc, after
|
||||
// closing the database (dropping `Engine`). Since `Arc<Engine>` is a sibling
|
||||
// member along with this handle in `Map`, that is prevented.
|
||||
Ok(unsafe {
|
||||
Arc::decrement_strong_count(cf_ptr);
|
||||
Arc::from_raw(cf_ptr)
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn read_options_default() -> ReadOptions {
|
||||
let mut read_options = ReadOptions::default();
|
||||
read_options.set_total_order_seek(true);
|
||||
read_options
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_options_default() -> WriteOptions { WriteOptions::default() }
|
||||
@@ -0,0 +1,99 @@
|
||||
use std::{collections::BTreeMap, sync::Arc};
|
||||
|
||||
use conduit::Result;
|
||||
|
||||
use crate::{Engine, Map};
|
||||
|
||||
pub type Maps = BTreeMap<String, Arc<Map>>;
|
||||
|
||||
pub(crate) fn open(db: &Arc<Engine>) -> Result<Maps> { open_list(db, MAPS) }
|
||||
|
||||
pub(crate) fn open_list(db: &Arc<Engine>, maps: &[&str]) -> Result<Maps> {
|
||||
Ok(maps
|
||||
.iter()
|
||||
.map(|&name| (name.to_owned(), Map::open(db, name).expect("valid column opened")))
|
||||
.collect::<Maps>())
|
||||
}
|
||||
|
||||
pub const MAPS: &[&str] = &[
|
||||
"alias_roomid",
|
||||
"alias_userid",
|
||||
"aliasid_alias",
|
||||
"backupid_algorithm",
|
||||
"backupid_etag",
|
||||
"backupkeyid_backup",
|
||||
"bannedroomids",
|
||||
"disabledroomids",
|
||||
"eventid_outlierpdu",
|
||||
"eventid_pduid",
|
||||
"eventid_shorteventid",
|
||||
"global",
|
||||
"id_appserviceregistrations",
|
||||
"keychangeid_userid",
|
||||
"keyid_key",
|
||||
"lazyloadedids",
|
||||
"mediaid_file",
|
||||
"mediaid_user",
|
||||
"onetimekeyid_onetimekeys",
|
||||
"pduid_pdu",
|
||||
"presenceid_presence",
|
||||
"publicroomids",
|
||||
"readreceiptid_readreceipt",
|
||||
"referencedevents",
|
||||
"roomid_invitedcount",
|
||||
"roomid_inviteviaservers",
|
||||
"roomid_joinedcount",
|
||||
"roomid_pduleaves",
|
||||
"roomid_shortroomid",
|
||||
"roomid_shortstatehash",
|
||||
"roomserverids",
|
||||
"roomsynctoken_shortstatehash",
|
||||
"roomuserdataid_accountdata",
|
||||
"roomuserid_invitecount",
|
||||
"roomuserid_joined",
|
||||
"roomuserid_lastprivatereadupdate",
|
||||
"roomuserid_leftcount",
|
||||
"roomuserid_privateread",
|
||||
"roomuseroncejoinedids",
|
||||
"roomusertype_roomuserdataid",
|
||||
"senderkey_pusher",
|
||||
"server_signingkeys",
|
||||
"servercurrentevent_data",
|
||||
"servername_educount",
|
||||
"servernameevent_data",
|
||||
"serverroomids",
|
||||
"shorteventid_authchain",
|
||||
"shorteventid_eventid",
|
||||
"shorteventid_shortstatehash",
|
||||
"shortstatehash_statediff",
|
||||
"shortstatekey_statekey",
|
||||
"softfailedeventids",
|
||||
"statehash_shortstatehash",
|
||||
"statekey_shortstatekey",
|
||||
"threadid_userids",
|
||||
"todeviceid_events",
|
||||
"tofrom_relation",
|
||||
"token_userdeviceid",
|
||||
"tokenids",
|
||||
"url_previews",
|
||||
"userdeviceid_metadata",
|
||||
"userdeviceid_token",
|
||||
"userdevicesessionid_uiaainfo",
|
||||
"userdevicetxnid_response",
|
||||
"userfilterid_filter",
|
||||
"userid_avatarurl",
|
||||
"userid_blurhash",
|
||||
"userid_devicelistversion",
|
||||
"userid_displayname",
|
||||
"userid_lastonetimekeyupdate",
|
||||
"userid_masterkeyid",
|
||||
"userid_password",
|
||||
"userid_presenceid",
|
||||
"userid_selfsigningkeyid",
|
||||
"userid_usersigningkeyid",
|
||||
"userroomid_highlightcount",
|
||||
"userroomid_invitestate",
|
||||
"userroomid_joined",
|
||||
"userroomid_leftstate",
|
||||
"userroomid_notificationcount",
|
||||
];
|
||||
+20
-18
@@ -1,23 +1,25 @@
|
||||
pub mod cork;
|
||||
mod kvdatabase;
|
||||
mod kvengine;
|
||||
mod kvtree;
|
||||
|
||||
#[cfg(feature = "rocksdb")]
|
||||
pub(crate) mod rocksdb;
|
||||
|
||||
#[cfg(feature = "sqlite")]
|
||||
pub(crate) mod sqlite;
|
||||
|
||||
#[cfg(any(feature = "sqlite", feature = "rocksdb"))]
|
||||
pub(crate) mod watchers;
|
||||
mod cork;
|
||||
mod database;
|
||||
mod engine;
|
||||
mod handle;
|
||||
mod iter;
|
||||
mod map;
|
||||
pub mod maps;
|
||||
mod opts;
|
||||
mod slice;
|
||||
mod util;
|
||||
mod watchers;
|
||||
|
||||
extern crate conduit_core as conduit;
|
||||
pub(crate) use conduit::{Config, Result};
|
||||
pub use cork::Cork;
|
||||
pub use kvdatabase::KeyValueDatabase;
|
||||
pub use kvengine::KeyValueDatabaseEngine;
|
||||
pub use kvtree::KvTree;
|
||||
extern crate rust_rocksdb as rocksdb;
|
||||
|
||||
pub use database::Database;
|
||||
pub(crate) use engine::Engine;
|
||||
pub use handle::Handle;
|
||||
pub use iter::Iter;
|
||||
pub use map::Map;
|
||||
pub use slice::{Key, KeyVal, OwnedKey, OwnedKeyVal, OwnedVal, Val};
|
||||
pub(crate) use util::{or_else, result};
|
||||
|
||||
conduit::mod_ctor! {}
|
||||
conduit::mod_dtor! {}
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
#![allow(dead_code)]
|
||||
use std::{cmp, collections::HashMap};
|
||||
|
||||
use conduit::utils;
|
||||
|
||||
use super::{
|
||||
rust_rocksdb::{
|
||||
BlockBasedOptions, Cache, DBCompactionStyle, DBCompressionType, DBRecoveryMode, Env, LogLevel, Options,
|
||||
UniversalCompactOptions, UniversalCompactionStopStyle,
|
||||
},
|
||||
Config,
|
||||
use conduit::{utils, Config};
|
||||
use rocksdb::{
|
||||
BlockBasedOptions, Cache, DBCompactionStyle, DBCompressionType, DBRecoveryMode, Env, LogLevel, Options,
|
||||
UniversalCompactOptions, UniversalCompactionStopStyle,
|
||||
};
|
||||
|
||||
/// Create database-wide options suitable for opening the database. This also
|
||||
@@ -149,16 +144,11 @@ pub(crate) fn cf_options(cfg: &Config, name: &str, mut opts: Options, cache: &mu
|
||||
cache_size(cfg, cfg.statekeyshort_cache_capacity, 1024),
|
||||
),
|
||||
|
||||
#[allow(clippy::as_conversions, clippy::cast_sign_loss, clippy::cast_possible_truncation)]
|
||||
"pduid_pdu" => set_table_with_new_cache(
|
||||
&mut opts,
|
||||
cfg,
|
||||
cache,
|
||||
name,
|
||||
(cfg.pdu_cache_capacity as usize).saturating_mul(1536),
|
||||
),
|
||||
"eventid_outlierpdu" => {
|
||||
set_table_with_new_cache(&mut opts, cfg, cache, name, cache_size(cfg, cfg.pdu_cache_capacity, 1536));
|
||||
},
|
||||
|
||||
"eventid_outlierpdu" => set_table_with_shared_cache(&mut opts, cfg, cache, name, "pduid_pdu"),
|
||||
"pduid_pdu" => set_table_with_shared_cache(&mut opts, cfg, cache, name, "eventid_outlierpdu"),
|
||||
|
||||
&_ => {},
|
||||
}
|
||||
@@ -210,6 +200,7 @@ fn set_compression_defaults(opts: &mut Options, config: &Config) {
|
||||
opts.set_compression_type(rocksdb_compression_algo);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn set_for_random_small_uc(opts: &mut Options, config: &Config) {
|
||||
let uco = uc_options(config);
|
||||
set_for_random_small(opts, config);
|
||||
@@ -224,6 +215,7 @@ fn set_for_sequential_small_uc(opts: &mut Options, config: &Config) {
|
||||
opts.set_compaction_style(DBCompactionStyle::Universal);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn set_for_random_small(opts: &mut Options, config: &Config) {
|
||||
set_for_random(opts, config);
|
||||
|
||||
@@ -1,210 +0,0 @@
|
||||
use std::{future::Future, pin::Pin, sync::Arc};
|
||||
|
||||
use conduit::{utils, Result};
|
||||
|
||||
use super::{rust_rocksdb::WriteBatchWithTransaction, watchers::Watchers, Engine, KeyValueDatabaseEngine, KvTree};
|
||||
|
||||
pub(crate) struct RocksDbEngineTree<'a> {
|
||||
pub(crate) db: Arc<Engine>,
|
||||
pub(crate) name: &'a str,
|
||||
pub(crate) watchers: Watchers,
|
||||
}
|
||||
|
||||
impl RocksDbEngineTree<'_> {
|
||||
fn cf(&self) -> Arc<rust_rocksdb::BoundColumnFamily<'_>> { self.db.rocks.cf_handle(self.name).unwrap() }
|
||||
}
|
||||
|
||||
impl KvTree for RocksDbEngineTree<'_> {
|
||||
fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>> {
|
||||
let mut readoptions = rust_rocksdb::ReadOptions::default();
|
||||
readoptions.set_total_order_seek(true);
|
||||
|
||||
Ok(self.db.rocks.get_cf_opt(&self.cf(), key, &readoptions)?)
|
||||
}
|
||||
|
||||
fn multi_get(&self, keys: &[&[u8]]) -> Result<Vec<Option<Vec<u8>>>> {
|
||||
// Optimization can be `true` if key vector is pre-sorted **by the column
|
||||
// comparator**.
|
||||
const SORTED: bool = false;
|
||||
|
||||
let mut readoptions = rust_rocksdb::ReadOptions::default();
|
||||
readoptions.set_total_order_seek(true);
|
||||
|
||||
let mut ret: Vec<Option<Vec<u8>>> = Vec::with_capacity(keys.len());
|
||||
for res in self
|
||||
.db
|
||||
.rocks
|
||||
.batched_multi_get_cf_opt(&self.cf(), keys, SORTED, &readoptions)
|
||||
{
|
||||
match res? {
|
||||
Some(res) => ret.push(Some((*res).to_vec())),
|
||||
None => ret.push(None),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
fn insert(&self, key: &[u8], value: &[u8]) -> Result<()> {
|
||||
let writeoptions = rust_rocksdb::WriteOptions::default();
|
||||
|
||||
self.db
|
||||
.rocks
|
||||
.put_cf_opt(&self.cf(), key, value, &writeoptions)?;
|
||||
|
||||
if !self.db.corked() {
|
||||
self.db.flush()?;
|
||||
}
|
||||
|
||||
self.watchers.wake(key);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn insert_batch(&self, iter: &mut dyn Iterator<Item = (Vec<u8>, Vec<u8>)>) -> Result<()> {
|
||||
let writeoptions = rust_rocksdb::WriteOptions::default();
|
||||
|
||||
let mut batch = WriteBatchWithTransaction::<false>::default();
|
||||
|
||||
for (key, value) in iter {
|
||||
batch.put_cf(&self.cf(), key, value);
|
||||
}
|
||||
|
||||
let result = self.db.rocks.write_opt(batch, &writeoptions);
|
||||
|
||||
if !self.db.corked() {
|
||||
self.db.flush()?;
|
||||
}
|
||||
|
||||
Ok(result?)
|
||||
}
|
||||
|
||||
fn remove(&self, key: &[u8]) -> Result<()> {
|
||||
let writeoptions = rust_rocksdb::WriteOptions::default();
|
||||
|
||||
let result = self.db.rocks.delete_cf_opt(&self.cf(), key, &writeoptions);
|
||||
|
||||
if !self.db.corked() {
|
||||
self.db.flush()?;
|
||||
}
|
||||
|
||||
Ok(result?)
|
||||
}
|
||||
|
||||
fn remove_batch(&self, iter: &mut dyn Iterator<Item = Vec<u8>>) -> Result<()> {
|
||||
let writeoptions = rust_rocksdb::WriteOptions::default();
|
||||
|
||||
let mut batch = WriteBatchWithTransaction::<false>::default();
|
||||
|
||||
for key in iter {
|
||||
batch.delete_cf(&self.cf(), key);
|
||||
}
|
||||
|
||||
let result = self.db.rocks.write_opt(batch, &writeoptions);
|
||||
|
||||
if !self.db.corked() {
|
||||
self.db.flush()?;
|
||||
}
|
||||
|
||||
Ok(result?)
|
||||
}
|
||||
|
||||
fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = (Vec<u8>, Vec<u8>)> + 'a> {
|
||||
let mut readoptions = rust_rocksdb::ReadOptions::default();
|
||||
readoptions.set_total_order_seek(true);
|
||||
|
||||
Box::new(
|
||||
self.db
|
||||
.rocks
|
||||
.iterator_cf_opt(&self.cf(), readoptions, rust_rocksdb::IteratorMode::Start)
|
||||
.map(Result::unwrap)
|
||||
.map(|(k, v)| (Vec::from(k), Vec::from(v))),
|
||||
)
|
||||
}
|
||||
|
||||
fn iter_from<'a>(&'a self, from: &[u8], backwards: bool) -> Box<dyn Iterator<Item = (Vec<u8>, Vec<u8>)> + 'a> {
|
||||
let mut readoptions = rust_rocksdb::ReadOptions::default();
|
||||
readoptions.set_total_order_seek(true);
|
||||
|
||||
Box::new(
|
||||
self.db
|
||||
.rocks
|
||||
.iterator_cf_opt(
|
||||
&self.cf(),
|
||||
readoptions,
|
||||
rust_rocksdb::IteratorMode::From(
|
||||
from,
|
||||
if backwards {
|
||||
rust_rocksdb::Direction::Reverse
|
||||
} else {
|
||||
rust_rocksdb::Direction::Forward
|
||||
},
|
||||
),
|
||||
)
|
||||
.map(Result::unwrap)
|
||||
.map(|(k, v)| (Vec::from(k), Vec::from(v))),
|
||||
)
|
||||
}
|
||||
|
||||
fn increment(&self, key: &[u8]) -> Result<Vec<u8>> {
|
||||
let mut readoptions = rust_rocksdb::ReadOptions::default();
|
||||
readoptions.set_total_order_seek(true);
|
||||
let writeoptions = rust_rocksdb::WriteOptions::default();
|
||||
|
||||
let old = self.db.rocks.get_cf_opt(&self.cf(), key, &readoptions)?;
|
||||
let new = utils::increment(old.as_deref());
|
||||
self.db
|
||||
.rocks
|
||||
.put_cf_opt(&self.cf(), key, &new, &writeoptions)?;
|
||||
|
||||
if !self.db.corked() {
|
||||
self.db.flush()?;
|
||||
}
|
||||
|
||||
Ok(new)
|
||||
}
|
||||
|
||||
fn increment_batch(&self, iter: &mut dyn Iterator<Item = Vec<u8>>) -> Result<()> {
|
||||
let mut readoptions = rust_rocksdb::ReadOptions::default();
|
||||
readoptions.set_total_order_seek(true);
|
||||
let writeoptions = rust_rocksdb::WriteOptions::default();
|
||||
|
||||
let mut batch = WriteBatchWithTransaction::<false>::default();
|
||||
|
||||
for key in iter {
|
||||
let old = self.db.rocks.get_cf_opt(&self.cf(), &key, &readoptions)?;
|
||||
let new = utils::increment(old.as_deref());
|
||||
batch.put_cf(&self.cf(), key, new);
|
||||
}
|
||||
|
||||
self.db.rocks.write_opt(batch, &writeoptions)?;
|
||||
|
||||
if !self.db.corked() {
|
||||
self.db.flush()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn scan_prefix<'a>(&'a self, prefix: Vec<u8>) -> Box<dyn Iterator<Item = (Vec<u8>, Vec<u8>)> + 'a> {
|
||||
let mut readoptions = rust_rocksdb::ReadOptions::default();
|
||||
readoptions.set_total_order_seek(true);
|
||||
|
||||
Box::new(
|
||||
self.db
|
||||
.rocks
|
||||
.iterator_cf_opt(
|
||||
&self.cf(),
|
||||
readoptions,
|
||||
rust_rocksdb::IteratorMode::From(&prefix, rust_rocksdb::Direction::Forward),
|
||||
)
|
||||
.map(Result::unwrap)
|
||||
.map(|(k, v)| (Vec::from(k), Vec::from(v)))
|
||||
.take_while(move |(k, _)| k.starts_with(&prefix)),
|
||||
)
|
||||
}
|
||||
|
||||
fn watch_prefix<'a>(&'a self, prefix: &[u8]) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||
self.watchers.watch(prefix)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
pub struct OwnedKeyVal(pub OwnedKey, pub OwnedVal);
|
||||
pub(crate) type OwnedKeyValPair = (OwnedKey, OwnedVal);
|
||||
pub type OwnedVal = Vec<Byte>;
|
||||
pub type OwnedKey = Vec<Byte>;
|
||||
|
||||
pub struct KeyVal<'item>(pub &'item Key, pub &'item Val);
|
||||
pub(crate) type KeyValPair<'item> = (&'item Key, &'item Val);
|
||||
pub type Val = [Byte];
|
||||
pub type Key = [Byte];
|
||||
|
||||
pub(crate) type Byte = u8;
|
||||
|
||||
impl OwnedKeyVal {
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn as_slice(&self) -> KeyVal<'_> { KeyVal(&self.0, &self.1) }
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn to_tuple(self) -> OwnedKeyValPair { (self.0, self.1) }
|
||||
}
|
||||
|
||||
impl From<OwnedKeyValPair> for OwnedKeyVal {
|
||||
#[inline]
|
||||
fn from((key, val): OwnedKeyValPair) -> Self { Self(key, val) }
|
||||
}
|
||||
|
||||
impl From<&KeyVal<'_>> for OwnedKeyVal {
|
||||
fn from(slice: &KeyVal<'_>) -> Self { slice.to_owned() }
|
||||
}
|
||||
|
||||
impl From<KeyValPair<'_>> for OwnedKeyVal {
|
||||
fn from((key, val): KeyValPair<'_>) -> Self { Self(Vec::from(key), Vec::from(val)) }
|
||||
}
|
||||
|
||||
impl From<OwnedKeyVal> for OwnedKeyValPair {
|
||||
#[inline]
|
||||
fn from(val: OwnedKeyVal) -> Self { val.to_tuple() }
|
||||
}
|
||||
|
||||
impl KeyVal<'_> {
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn to_owned(&self) -> OwnedKeyVal { OwnedKeyVal::from(self) }
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn as_tuple(&self) -> KeyValPair<'_> { (self.0, self.1) }
|
||||
}
|
||||
|
||||
impl<'a> From<&'a OwnedKeyVal> for KeyVal<'a> {
|
||||
#[inline]
|
||||
fn from(owned: &'a OwnedKeyVal) -> Self { owned.as_slice() }
|
||||
}
|
||||
|
||||
impl<'a> From<&'a OwnedKeyValPair> for KeyVal<'a> {
|
||||
#[inline]
|
||||
fn from((key, val): &'a OwnedKeyValPair) -> Self { KeyVal(key.as_slice(), val.as_slice()) }
|
||||
}
|
||||
|
||||
impl<'a> From<KeyValPair<'a>> for KeyVal<'a> {
|
||||
#[inline]
|
||||
fn from((key, val): KeyValPair<'a>) -> Self { KeyVal(key, val) }
|
||||
}
|
||||
@@ -1,337 +0,0 @@
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
future::Future,
|
||||
path::{Path, PathBuf},
|
||||
pin::Pin,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use conduit::{Config, Result};
|
||||
use parking_lot::{Mutex, MutexGuard};
|
||||
use rusqlite::{Connection, DatabaseName::Main, OptionalExtension};
|
||||
use thread_local::ThreadLocal;
|
||||
use tracing::debug;
|
||||
|
||||
use super::{watchers::Watchers, KeyValueDatabaseEngine, KvTree};
|
||||
|
||||
thread_local! {
|
||||
static READ_CONNECTION: RefCell<Option<&'static Connection>> = const { RefCell::new(None) };
|
||||
static READ_CONNECTION_ITERATOR: RefCell<Option<&'static Connection>> = const { RefCell::new(None) };
|
||||
}
|
||||
|
||||
struct PreparedStatementIterator<'a> {
|
||||
iterator: Box<dyn Iterator<Item = TupleOfBytes> + 'a>,
|
||||
_statement_ref: AliasableBox<rusqlite::Statement<'a>>,
|
||||
}
|
||||
|
||||
impl Iterator for PreparedStatementIterator<'_> {
|
||||
type Item = TupleOfBytes;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> { self.iterator.next() }
|
||||
}
|
||||
|
||||
struct AliasableBox<T>(*mut T);
|
||||
impl<T> Drop for AliasableBox<T> {
|
||||
fn drop(&mut self) {
|
||||
// SAFETY: This is cursed and relies on non-local reasoning.
|
||||
//
|
||||
// In order for this to be safe:
|
||||
//
|
||||
// * All aliased references to this value must have been dropped first, for
|
||||
// example by coming after its referrers in struct fields, because struct
|
||||
// fields are automatically dropped in order from top to bottom in the absence
|
||||
// of an explicit Drop impl. Otherwise, the referrers may read into
|
||||
// deallocated memory.
|
||||
// * This type must not be copyable or cloneable. Otherwise, double-free can
|
||||
// occur.
|
||||
//
|
||||
// These conditions are met, but again, note that changing safe code in
|
||||
// this module can result in unsoundness if any of these constraints are
|
||||
// violated.
|
||||
unsafe { drop(Box::from_raw(self.0)) }
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct Engine {
|
||||
writer: Mutex<Connection>,
|
||||
read_conn_tls: ThreadLocal<Connection>,
|
||||
read_iterator_conn_tls: ThreadLocal<Connection>,
|
||||
|
||||
path: PathBuf,
|
||||
cache_size_per_thread: u32,
|
||||
}
|
||||
|
||||
impl Engine {
|
||||
fn prepare_conn(path: &Path, cache_size_kb: u32) -> Result<Connection> {
|
||||
let conn = Connection::open(path)?;
|
||||
|
||||
conn.pragma_update(Some(Main), "page_size", 2048)?;
|
||||
conn.pragma_update(Some(Main), "journal_mode", "WAL")?;
|
||||
conn.pragma_update(Some(Main), "synchronous", "NORMAL")?;
|
||||
conn.pragma_update(Some(Main), "cache_size", -i64::from(cache_size_kb))?;
|
||||
conn.pragma_update(Some(Main), "wal_autocheckpoint", 0)?;
|
||||
|
||||
Ok(conn)
|
||||
}
|
||||
|
||||
fn write_lock(&self) -> MutexGuard<'_, Connection> { self.writer.lock() }
|
||||
|
||||
fn read_lock(&self) -> &Connection {
|
||||
self.read_conn_tls
|
||||
.get_or(|| Self::prepare_conn(&self.path, self.cache_size_per_thread).unwrap())
|
||||
}
|
||||
|
||||
fn read_lock_iterator(&self) -> &Connection {
|
||||
self.read_iterator_conn_tls
|
||||
.get_or(|| Self::prepare_conn(&self.path, self.cache_size_per_thread).unwrap())
|
||||
}
|
||||
|
||||
fn flush_wal(self: &Arc<Self>) -> Result<()> {
|
||||
self.write_lock()
|
||||
.pragma_update(Some(Main), "wal_checkpoint", "RESTART")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyValueDatabaseEngine for Arc<Engine> {
|
||||
fn open(config: &Config) -> Result<Self> {
|
||||
let path = Path::new(&config.database_path).join("conduit.db");
|
||||
|
||||
// calculates cache-size per permanent connection
|
||||
// 1. convert MB to KiB
|
||||
// 2. divide by permanent connections + permanent iter connections + write
|
||||
// connection
|
||||
// 3. round down to nearest integer
|
||||
#[allow(
|
||||
clippy::as_conversions,
|
||||
clippy::cast_possible_truncation,
|
||||
clippy::cast_precision_loss,
|
||||
clippy::cast_sign_loss
|
||||
)]
|
||||
let cache_size_per_thread = ((config.db_cache_capacity_mb * 1024.0)
|
||||
/ (conduit::utils::available_parallelism() as f64).mul_add(2.0, 1.0)) as u32;
|
||||
|
||||
let writer = Mutex::new(Engine::prepare_conn(&path, cache_size_per_thread)?);
|
||||
|
||||
let arc = Self::new(Engine {
|
||||
writer,
|
||||
read_conn_tls: ThreadLocal::new(),
|
||||
read_iterator_conn_tls: ThreadLocal::new(),
|
||||
path,
|
||||
cache_size_per_thread,
|
||||
});
|
||||
|
||||
Ok(arc)
|
||||
}
|
||||
|
||||
fn open_tree(&self, name: &str) -> Result<Arc<dyn KvTree>> {
|
||||
self.write_lock().execute(
|
||||
&format!("CREATE TABLE IF NOT EXISTS {name} ( \"key\" BLOB PRIMARY KEY, \"value\" BLOB NOT NULL )"),
|
||||
[],
|
||||
)?;
|
||||
|
||||
Ok(Arc::new(SqliteTable {
|
||||
engine: Self::clone(self),
|
||||
name: name.to_owned(),
|
||||
watchers: Watchers::default(),
|
||||
}))
|
||||
}
|
||||
|
||||
fn flush(&self) -> Result<()> {
|
||||
// we enabled PRAGMA synchronous=normal, so this should not be necessary
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cleanup(&self) -> Result<()> { self.flush_wal() }
|
||||
}
|
||||
|
||||
struct SqliteTable {
|
||||
engine: Arc<Engine>,
|
||||
name: String,
|
||||
watchers: Watchers,
|
||||
}
|
||||
|
||||
type TupleOfBytes = (Vec<u8>, Vec<u8>);
|
||||
|
||||
impl SqliteTable {
|
||||
fn get_with_guard(&self, guard: &Connection, key: &[u8]) -> Result<Option<Vec<u8>>> {
|
||||
Ok(guard
|
||||
.prepare(format!("SELECT value FROM {} WHERE key = ?", self.name).as_str())?
|
||||
.query_row([key], |row| row.get(0))
|
||||
.optional()?)
|
||||
}
|
||||
|
||||
fn insert_with_guard(&self, guard: &Connection, key: &[u8], value: &[u8]) -> Result<()> {
|
||||
guard.execute(
|
||||
format!("INSERT OR REPLACE INTO {} (key, value) VALUES (?, ?)", self.name).as_str(),
|
||||
[key, value],
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn iter_with_guard<'a>(&'a self, guard: &'a Connection) -> Box<dyn Iterator<Item = TupleOfBytes> + 'a> {
|
||||
let statement = Box::leak(Box::new(
|
||||
guard
|
||||
.prepare(&format!("SELECT key, value FROM {} ORDER BY key ASC", &self.name))
|
||||
.unwrap(),
|
||||
));
|
||||
|
||||
let statement_ref = AliasableBox(statement);
|
||||
|
||||
//let name = self.name.clone();
|
||||
|
||||
let iterator = Box::new(
|
||||
statement
|
||||
.query_map([], |row| Ok((row.get_unwrap(0), row.get_unwrap(1))))
|
||||
.unwrap()
|
||||
.map(Result::unwrap),
|
||||
);
|
||||
|
||||
Box::new(PreparedStatementIterator {
|
||||
iterator,
|
||||
_statement_ref: statement_ref,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl KvTree for SqliteTable {
|
||||
fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>> { self.get_with_guard(self.engine.read_lock(), key) }
|
||||
|
||||
fn insert(&self, key: &[u8], value: &[u8]) -> Result<()> {
|
||||
let guard = self.engine.write_lock();
|
||||
self.insert_with_guard(&guard, key, value)?;
|
||||
drop(guard);
|
||||
self.watchers.wake(key);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn insert_batch(&self, iter: &mut dyn Iterator<Item = (Vec<u8>, Vec<u8>)>) -> Result<()> {
|
||||
let guard = self.engine.write_lock();
|
||||
|
||||
guard.execute("BEGIN", [])?;
|
||||
for (key, value) in iter {
|
||||
self.insert_with_guard(&guard, &key, &value)?;
|
||||
}
|
||||
guard.execute("COMMIT", [])?;
|
||||
|
||||
drop(guard);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn increment_batch(&self, iter: &mut dyn Iterator<Item = Vec<u8>>) -> Result<()> {
|
||||
let guard = self.engine.write_lock();
|
||||
|
||||
guard.execute("BEGIN", [])?;
|
||||
for key in iter {
|
||||
let old = self.get_with_guard(&guard, &key)?;
|
||||
let new = conduit::utils::increment(old.as_deref());
|
||||
self.insert_with_guard(&guard, &key, &new)?;
|
||||
}
|
||||
guard.execute("COMMIT", [])?;
|
||||
|
||||
drop(guard);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn remove(&self, key: &[u8]) -> Result<()> {
|
||||
let guard = self.engine.write_lock();
|
||||
|
||||
guard.execute(format!("DELETE FROM {} WHERE key = ?", self.name).as_str(), [key])?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = TupleOfBytes> + 'a> {
|
||||
let guard = self.engine.read_lock_iterator();
|
||||
|
||||
self.iter_with_guard(guard)
|
||||
}
|
||||
|
||||
fn iter_from<'a>(&'a self, from: &[u8], backwards: bool) -> Box<dyn Iterator<Item = TupleOfBytes> + 'a> {
|
||||
let guard = self.engine.read_lock_iterator();
|
||||
let from = from.to_vec(); // TODO change interface?
|
||||
|
||||
//let name = self.name.clone();
|
||||
|
||||
if backwards {
|
||||
let statement = Box::leak(Box::new(
|
||||
guard
|
||||
.prepare(&format!(
|
||||
"SELECT key, value FROM {} WHERE key <= ? ORDER BY key DESC",
|
||||
&self.name
|
||||
))
|
||||
.unwrap(),
|
||||
));
|
||||
|
||||
let statement_ref = AliasableBox(statement);
|
||||
|
||||
let iterator = Box::new(
|
||||
statement
|
||||
.query_map([from], |row| Ok((row.get_unwrap(0), row.get_unwrap(1))))
|
||||
.unwrap()
|
||||
.map(Result::unwrap),
|
||||
);
|
||||
Box::new(PreparedStatementIterator {
|
||||
iterator,
|
||||
_statement_ref: statement_ref,
|
||||
})
|
||||
} else {
|
||||
let statement = Box::leak(Box::new(
|
||||
guard
|
||||
.prepare(&format!(
|
||||
"SELECT key, value FROM {} WHERE key >= ? ORDER BY key ASC",
|
||||
&self.name
|
||||
))
|
||||
.unwrap(),
|
||||
));
|
||||
|
||||
let statement_ref = AliasableBox(statement);
|
||||
|
||||
let iterator = Box::new(
|
||||
statement
|
||||
.query_map([from], |row| Ok((row.get_unwrap(0), row.get_unwrap(1))))
|
||||
.unwrap()
|
||||
.map(Result::unwrap),
|
||||
);
|
||||
|
||||
Box::new(PreparedStatementIterator {
|
||||
iterator,
|
||||
_statement_ref: statement_ref,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn increment(&self, key: &[u8]) -> Result<Vec<u8>> {
|
||||
let guard = self.engine.write_lock();
|
||||
|
||||
let old = self.get_with_guard(&guard, key)?;
|
||||
|
||||
let new = conduit::utils::increment(old.as_deref());
|
||||
|
||||
self.insert_with_guard(&guard, key, &new)?;
|
||||
|
||||
Ok(new)
|
||||
}
|
||||
|
||||
fn scan_prefix<'a>(&'a self, prefix: Vec<u8>) -> Box<dyn Iterator<Item = TupleOfBytes> + 'a> {
|
||||
Box::new(
|
||||
self.iter_from(&prefix, false)
|
||||
.take_while(move |(key, _)| key.starts_with(&prefix)),
|
||||
)
|
||||
}
|
||||
|
||||
fn watch_prefix<'a>(&'a self, prefix: &[u8]) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||
self.watchers.watch(prefix)
|
||||
}
|
||||
|
||||
fn clear(&self) -> Result<()> {
|
||||
debug!("clear: running");
|
||||
self.engine
|
||||
.write_lock()
|
||||
.execute(format!("DELETE FROM {}", self.name).as_str(), [])?;
|
||||
debug!("clear: ran");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
use conduit::Result;
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn result<T>(r: std::result::Result<T, rocksdb::Error>) -> Result<T, conduit::Error> {
|
||||
r.map_or_else(or_else, and_then)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn and_then<T>(t: T) -> Result<T, conduit::Error> { Ok(t) }
|
||||
|
||||
pub(crate) fn or_else<T>(e: rocksdb::Error) -> Result<T, conduit::Error> { Err(map_err(e)) }
|
||||
|
||||
pub(crate) fn map_err(e: rocksdb::Error) -> conduit::Error { conduit::Error::Database(e.into_string()) }
|
||||
+9
-26
@@ -43,7 +43,6 @@ default = [
|
||||
"io_uring",
|
||||
"jemalloc",
|
||||
"release_max_log_level",
|
||||
"rocksdb",
|
||||
"sentry_telemetry",
|
||||
"systemd",
|
||||
"zstd_compression",
|
||||
@@ -61,14 +60,14 @@ brotli_compression = [
|
||||
console = [
|
||||
"conduit-service/console",
|
||||
]
|
||||
dev_release_log_level = [
|
||||
"conduit-admin/dev_release_log_level",
|
||||
"conduit-api/dev_release_log_level",
|
||||
"conduit-core/dev_release_log_level",
|
||||
"conduit-database/dev_release_log_level",
|
||||
"conduit-router/dev_release_log_level",
|
||||
"conduit-service/dev_release_log_level",
|
||||
]
|
||||
#dev_release_log_level = [
|
||||
# "conduit-admin/dev_release_log_level",
|
||||
# "conduit-api/dev_release_log_level",
|
||||
# "conduit-core/dev_release_log_level",
|
||||
# "conduit-database/dev_release_log_level",
|
||||
# "conduit-router/dev_release_log_level",
|
||||
# "conduit-service/dev_release_log_level",
|
||||
#]
|
||||
element_hacks = [
|
||||
"conduit-api/element_hacks",
|
||||
"conduit-service/element_hacks",
|
||||
@@ -82,14 +81,11 @@ hardened_malloc = [
|
||||
"conduit-core/hardened_malloc",
|
||||
]
|
||||
io_uring = [
|
||||
"conduit-admin/io_uring",
|
||||
"conduit-core/io_uring",
|
||||
"conduit-database/io_uring",
|
||||
]
|
||||
jemalloc = [
|
||||
"conduit-admin/jemalloc",
|
||||
"conduit-core/jemalloc",
|
||||
"conduit-database/jemalloc",
|
||||
"conduit-database/jemalloc",
|
||||
]
|
||||
jemalloc_prof = [
|
||||
"conduit-core/jemalloc_prof",
|
||||
@@ -117,11 +113,6 @@ release_max_log_level = [
|
||||
"conduit-router/release_max_log_level",
|
||||
"conduit-service/release_max_log_level",
|
||||
]
|
||||
rocksdb = [
|
||||
"conduit-admin/rocksdb",
|
||||
"conduit-core/rocksdb",
|
||||
"conduit-database/rocksdb",
|
||||
]
|
||||
sentry_telemetry = [
|
||||
"dep:sentry",
|
||||
"dep:sentry-tracing",
|
||||
@@ -129,13 +120,6 @@ sentry_telemetry = [
|
||||
"conduit-core/sentry_telemetry",
|
||||
"conduit-router/sentry_telemetry",
|
||||
]
|
||||
sha256_media = [
|
||||
"conduit-service/sha256_media",
|
||||
]
|
||||
sqlite = [
|
||||
"conduit-core/sqlite",
|
||||
"conduit-database/sqlite",
|
||||
]
|
||||
systemd = [
|
||||
"conduit-router/systemd",
|
||||
]
|
||||
@@ -145,7 +129,6 @@ tokio_console = [
|
||||
"tokio/tracing",
|
||||
]
|
||||
zstd_compression = [
|
||||
"conduit-admin/zstd_compression",
|
||||
"conduit-core/zstd_compression",
|
||||
"conduit-database/zstd_compression",
|
||||
"conduit-router/zstd_compression",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user