mirror of
https://forgejo.ellis.link/continuwuation/continuwuity/
synced 2026-08-10 05:29:48 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9af15ecbba | ||
|
|
f66a83763e | ||
|
|
718c3adcb2 | ||
|
|
af80482c04 | ||
|
|
a20ddcd586 | ||
|
|
b483306367 | ||
|
|
f5e98467be | ||
|
|
d44db45f83 | ||
|
|
0397bb8237 | ||
|
|
4010fc62bc | ||
|
|
0d823a2822 | ||
|
|
9cd175b125 | ||
|
|
050a1a350a | ||
|
|
ec0f872f8f | ||
|
|
024e8eae62 | ||
|
|
4fd60b2605 | ||
|
|
e53968d9eb | ||
|
|
7cbc2ee385 | ||
|
|
0df5e5e7ac | ||
|
|
312eb69450 | ||
|
|
5b620a2c37 | ||
|
|
3b4fbb8c1a | ||
|
|
158d44e1a9 | ||
|
|
d2aab468cf | ||
|
|
aa4486dfdf | ||
|
|
2a662445b6 | ||
|
|
5b3f0fde23 | ||
|
|
9640afebff | ||
|
|
dd5c5c7a4a | ||
|
|
fc0f04defa | ||
|
|
7d8f7cbe5d | ||
|
|
4e0249cd2f | ||
|
|
d5b39aa995 | ||
|
|
46c940b863 | ||
|
|
ab8536d5c3 | ||
|
|
4918868632 | ||
|
|
9e00f70197 | ||
|
|
d3aaf9e4a9 | ||
|
|
96dc56ad07 | ||
|
|
e12b0262da | ||
|
|
e5bf005eaf | ||
|
|
02ccb1dceb | ||
|
|
a450eb96eb | ||
|
|
155af0fda3 | ||
|
|
5c61b4d4a3 | ||
|
|
97e709492c | ||
|
|
51fc2342a4 | ||
|
|
4ca68deef8 | ||
|
|
4d8d64f5c7 | ||
|
|
dff30e5924 | ||
|
|
7fee459b1a | ||
|
|
a6127fcd1a | ||
|
|
6c724bbc2f | ||
|
|
b1ea7b101d | ||
|
|
4baa25f66f | ||
|
|
227b77e58e | ||
|
|
54057da84e | ||
|
|
732825a390 | ||
|
|
6b74425f76 | ||
|
|
74a576caf7 | ||
|
|
2b7cf7d5d5 | ||
|
|
443248965d | ||
|
|
ee777bc287 | ||
|
|
aef38b1178 | ||
|
|
4743a8d968 | ||
|
|
53ab6742c8 | ||
|
|
46193de7e8 | ||
|
|
9253f46c80 | ||
|
|
5c127b5abd | ||
|
|
17f6f1a5a6 | ||
|
|
75509d50ca | ||
|
|
1d14426018 | ||
|
|
2b9563be67 | ||
|
|
68c4f60bb3 | ||
|
|
d95c3f126f | ||
|
|
f1c2548807 | ||
|
|
eda45e445c | ||
|
|
049defe977 | ||
|
|
3c073110b8 | ||
|
|
8d6bfde5a0 | ||
|
|
43f0882d83 | ||
|
|
fed52d24e4 | ||
|
|
e6c85c97c6 | ||
|
|
368ead20a6 | ||
|
|
a803b84b27 | ||
|
|
1058fbe9a7 | ||
|
|
ae4aad3641 | ||
|
|
95435ffe98 | ||
|
|
63e2cfa21b | ||
|
|
9383922d09 | ||
|
|
ae52676e33 | ||
|
|
292b601755 | ||
|
|
1313eb0b64 | ||
|
|
ba12773a5a | ||
|
|
83afe81f60 | ||
|
|
f2740822e2 | ||
|
|
2417764771 | ||
|
|
41ff81f843 | ||
|
|
fbcf4ba4f3 | ||
|
|
af3cdf9263 | ||
|
|
9d9ace1452 | ||
|
|
83d64e0879 | ||
|
|
4cae17e83d | ||
|
|
1c6992ccd4 | ||
|
|
89be6dc097 | ||
|
|
a47f8f8a82 | ||
|
|
aac5006bf5 | ||
|
|
6d3ed09a2b | ||
|
|
377b7166f0 | ||
|
|
85b3de055d | ||
|
|
b9c790326a | ||
|
|
4e5910471b | ||
|
|
c3bc8c14f7 | ||
|
|
ac3ceb1b95 | ||
|
|
aa37e32471 | ||
|
|
887a22dabd | ||
|
|
6dca02860c | ||
|
|
86103183b3 | ||
|
|
8f4cc87051 | ||
|
|
14721c90c9 | ||
|
|
6d3b2d864f | ||
|
|
7cf246eb73 | ||
|
|
bffb7f89c8 | ||
|
|
5467c9e486 | ||
|
|
89a67af607 | ||
|
|
6bb101ac51 | ||
|
|
1c0b4e94ac | ||
|
|
9c4d376bec | ||
|
|
d6e95c51c0 | ||
|
|
d5ce4b316f | ||
|
|
8f07a6c60f | ||
|
|
b882e7efdb | ||
|
|
e0169e3dca | ||
|
|
fe46755418 | ||
|
|
bbac80d2e6 | ||
|
|
a31c9b0c62 | ||
|
|
cf4e65c607 | ||
|
|
f32b6ae17d | ||
|
|
27222f23d2 | ||
|
|
ed324d5972 | ||
|
|
69075b166f | ||
|
|
e3a711482a | ||
|
|
6759187b37 | ||
|
|
83ed29eb65 | ||
|
|
6a685b7ee9 |
@@ -40,12 +40,16 @@ jobs:
|
||||
machines: ${{vars.MACHINES || env.default_machines}}
|
||||
package: ${{vars.PACKAGE || !contains(github.ref, 'refs/pull/')}}
|
||||
publish: ${{vars.PUBLISH || !contains(github.ref, 'refs/pull/')}}
|
||||
build_nix: ${{vars.BUILD_NIX || !contains(github.ref, 'refs/pull/')}}
|
||||
build_pkgs: ${{vars.BUILD_PKGS || github.ref == 'refs/heads/main' || contains(github.ref, 'tags/v')}}
|
||||
check_pkgs: ${{vars.CHECK_PKGS || 'false'}}
|
||||
complement: ${{vars.COMPLEMENT || 'true'}}
|
||||
complement_runner: 'het'
|
||||
docker_repo: ${{vars.DOCKER_REPO}}
|
||||
release_url: ${{steps.release.outputs.upload_url}}
|
||||
pages_url: 'https://matrix-construct.github.io/tuwunel/'
|
||||
rust_sdk_integration: ${{vars.RUST_SDK_INTEGRATION || 'true'}}
|
||||
head_msg: ${{github.event.head_commit.message || github.event.workflow_run.head_commit.message}}
|
||||
|
||||
strategy:
|
||||
fail-fast: true
|
||||
@@ -58,8 +62,8 @@ jobs:
|
||||
- name: Initialize Builder
|
||||
env:
|
||||
runner: ${{matrix.runner}}
|
||||
reserved_space: '{"het": "128GB", "aws": "48GB", "gcp": "160GB"}'
|
||||
max_used_space: '{"het": "256GB", "aws": "64GB", "gcp": "192GB"}'
|
||||
reserved_space: '{"het": "192GB", "aws": "48GB", "gcp": "160GB"}'
|
||||
max_used_space: '{"het": "384GB", "aws": "64GB", "gcp": "192GB"}'
|
||||
run: |
|
||||
set +e
|
||||
docker buildx inspect "${GITHUB_ACTOR}"
|
||||
@@ -114,6 +118,8 @@ jobs:
|
||||
if: >
|
||||
!failure() && !cancelled()
|
||||
&& !contains(github.ref, 'refs/tags/v')
|
||||
&& !contains(needs.init.outputs.head_msg, '[ci no lint]')
|
||||
&& !contains(needs.init.outputs.head_msg, '[ci only it]')
|
||||
|
||||
name: Lint
|
||||
needs: [init] #needs: [init, deps]
|
||||
@@ -132,6 +138,7 @@ jobs:
|
||||
{"cargo_profile": "test", "feat_set": "logging"},
|
||||
{"cargo_profile": "test", "rust_toolchain": "stable", "feat_set": "none"},
|
||||
{"cargo_profile": "test", "rust_target": "aarch64-unknown-linux-gnu"},
|
||||
{"cargo_profile": "bench"},
|
||||
{"cargo_profile": "release", "rust_toolchain": "nightly", "feat_set": "none"},
|
||||
{"cargo_profile": "release", "rust_toolchain": "nightly", "feat_set": "default"},
|
||||
{"cargo_profile": "release", "rust_toolchain": "nightly", "feat_set": "logging"},
|
||||
@@ -161,11 +168,15 @@ jobs:
|
||||
if: >
|
||||
!failure() && !cancelled()
|
||||
&& !contains(github.ref, 'refs/tags/v')
|
||||
&& !contains(needs.init.outputs.head_msg, '[ci no test]')
|
||||
|
||||
name: Test
|
||||
needs: [init, lint]
|
||||
uses: ./.github/workflows/test.yml
|
||||
with:
|
||||
head_msg: ${{needs.init.outputs.head_msg}}
|
||||
build_nix: ${{fromJSON(needs.init.outputs.build_nix)}}
|
||||
rust_sdk_integration: ${{fromJSON(needs.init.outputs.rust_sdk_integration)}}
|
||||
complement: ${{fromJSON(needs.init.outputs.complement)}}
|
||||
complement_runner: ${{needs.init.outputs.complement_runner}}
|
||||
cargo_profiles: ${{needs.init.outputs.cargo_profiles}}
|
||||
@@ -212,14 +223,18 @@ jobs:
|
||||
if: >
|
||||
!failure() && !cancelled()
|
||||
&& needs.init.outputs.package
|
||||
&& !contains(needs.init.outputs.head_msg, '[ci only it]')
|
||||
&& !contains(needs.init.outputs.head_msg, '[ci no build]')
|
||||
&& !contains(needs.init.outputs.head_msg, '[ci no package]')
|
||||
|
||||
name: Package
|
||||
needs: [init, lint]
|
||||
needs: [init, test]
|
||||
uses: ./.github/workflows/package.yml
|
||||
with:
|
||||
release_url: ${{needs.init.outputs.release_url}}
|
||||
check_pkgs: ${{needs.init.outputs.check_pkgs}}
|
||||
build_pkgs: ${{needs.init.outputs.build_pkgs}}
|
||||
build_nix: ${{fromJSON(needs.init.outputs.build_nix)}}
|
||||
cargo_profiles: ${{needs.init.outputs.cargo_profiles}}
|
||||
feat_sets: ${{needs.init.outputs.feat_sets}}
|
||||
rust_toolchains: ${{needs.init.outputs.rust_toolchains}}
|
||||
@@ -232,6 +247,7 @@ jobs:
|
||||
[
|
||||
{"feat_set": "none"},
|
||||
{"cargo_profile": "test"},
|
||||
{"cargo_profile": "bench"},
|
||||
{"cargo_profile": "release-native"},
|
||||
{"cargo_profile": "release-debuginfo", "feat_set": "default"},
|
||||
{"cargo_profile": "release-debuginfo", "feat_set": "logging"},
|
||||
@@ -244,6 +260,7 @@ jobs:
|
||||
{"cargo_profile": "release", "rust_toolchain": "nightly"},
|
||||
{"rust_target": "aarch64-unknown-linux-gnu", "feat_set": "default"},
|
||||
{"rust_target": "aarch64-unknown-linux-gnu", "feat_set": "logging"},
|
||||
{"rust_target": "aarch64-unknown-linux-gnu", "bake_target": "nix"},
|
||||
{"rust_target": "aarch64-unknown-linux-gnu", "sys_target": "x86_64-v1-linux-gnu"},
|
||||
{"rust_target": "aarch64-unknown-linux-gnu", "sys_target": "x86_64-v2-linux-gnu"},
|
||||
{"rust_target": "aarch64-unknown-linux-gnu", "sys_target": "x86_64-v3-linux-gnu"},
|
||||
@@ -254,18 +271,25 @@ jobs:
|
||||
{"sys_target": "x86_64-v2-linux-gnu", "machine": "ARM64"},
|
||||
{"sys_target": "x86_64-v2-linux-gnu", "feat_set": "default"},
|
||||
{"sys_target": "x86_64-v2-linux-gnu", "feat_set": "logging"},
|
||||
{"sys_target": "x86_64-v2-linux-gnu", "bake_target": "nix"},
|
||||
{"sys_target": "x86_64-v3-linux-gnu", "machine": "ARM64"},
|
||||
{"sys_target": "x86_64-v3-linux-gnu", "feat_set": "default"},
|
||||
{"sys_target": "x86_64-v3-linux-gnu", "feat_set": "logging"},
|
||||
{"sys_target": "x86_64-v3-linux-gnu", "bake_target": "nix"},
|
||||
{"sys_target": "x86_64-v4-linux-gnu", "machine": "ARM64"},
|
||||
{"sys_target": "x86_64-v4-linux-gnu", "feat_set": "default"},
|
||||
{"sys_target": "x86_64-v4-linux-gnu", "feat_set": "logging"},
|
||||
{"sys_target": "x86_64-v4-linux-gnu", "bake_target": "nix"},
|
||||
]
|
||||
|
||||
publish:
|
||||
if: >
|
||||
!failure() && !cancelled()
|
||||
&& needs.init.outputs.publish
|
||||
&& !contains(needs.init.outputs.head_msg, '[ci only it]')
|
||||
&& !contains(needs.init.outputs.head_msg, '[ci no build]')
|
||||
&& !contains(needs.init.outputs.head_msg, '[ci no package]')
|
||||
&& !contains(needs.init.outputs.head_msg, '[ci no publish]')
|
||||
|
||||
name: Publish
|
||||
needs: [init, test, package]
|
||||
@@ -273,6 +297,7 @@ jobs:
|
||||
with:
|
||||
docker_repo: ${{needs.init.outputs.docker_repo}}
|
||||
release_url: ${{needs.init.outputs.release_url}}
|
||||
pages_url: ${{needs.init.outputs.pages_url}}
|
||||
cargo_profiles: ${{needs.init.outputs.cargo_profiles}}
|
||||
feat_sets: ${{needs.init.outputs.feat_sets}}
|
||||
rust_toolchains: ${{needs.init.outputs.rust_toolchains}}
|
||||
@@ -284,12 +309,13 @@ jobs:
|
||||
excludes: >
|
||||
[
|
||||
{"feat_set": "none"},
|
||||
{"feat_set": "logging"},
|
||||
{"feat_set": "default"},
|
||||
{"cargo_profile": "test"},
|
||||
{"cargo_profile": "bench"},
|
||||
{"cargo_profile": "release-debuginfo"},
|
||||
{"cargo_profile": "release-native"},
|
||||
{"cargo_profile": "release", "rust_toolchain": "nightly"},
|
||||
{"rust_target": "aarch64-unknown-linux-gnu", "feat_set": "default"},
|
||||
{"rust_toolchain": "nightly"},
|
||||
{"rust_target": "aarch64-unknown-linux-gnu", "feat_set": "logging"},
|
||||
{"rust_target": "aarch64-unknown-linux-gnu", "sys_target": "x86_64-v1-linux-gnu"},
|
||||
{"rust_target": "aarch64-unknown-linux-gnu", "sys_target": "x86_64-v2-linux-gnu"},
|
||||
{"rust_target": "aarch64-unknown-linux-gnu", "sys_target": "x86_64-v3-linux-gnu"},
|
||||
@@ -298,11 +324,11 @@ jobs:
|
||||
{"sys_target": "aarch64-v8-linux-gnu", "machine": "X64"},
|
||||
{"sys_target": "x86_64-v1-linux-gnu", "machine": "ARM64"},
|
||||
{"sys_target": "x86_64-v2-linux-gnu", "machine": "ARM64"},
|
||||
{"sys_target": "x86_64-v2-linux-gnu", "feat_set": "default"},
|
||||
{"sys_target": "x86_64-v2-linux-gnu", "feat_set": "logging"},
|
||||
{"sys_target": "x86_64-v3-linux-gnu", "machine": "ARM64"},
|
||||
{"sys_target": "x86_64-v3-linux-gnu", "feat_set": "default"},
|
||||
{"sys_target": "x86_64-v3-linux-gnu", "feat_set": "logging"},
|
||||
{"sys_target": "x86_64-v4-linux-gnu", "machine": "ARM64"},
|
||||
{"sys_target": "x86_64-v4-linux-gnu", "feat_set": "default"},
|
||||
{"sys_target": "x86_64-v4-linux-gnu", "feat_set": "logging"},
|
||||
]
|
||||
|
||||
secrets:
|
||||
|
||||
@@ -44,6 +44,9 @@ on:
|
||||
check_pkgs:
|
||||
type: string
|
||||
default: 'false'
|
||||
build_nix:
|
||||
type: string
|
||||
default: 'true'
|
||||
|
||||
jobs:
|
||||
book:
|
||||
@@ -155,7 +158,7 @@ jobs:
|
||||
name: Distro Packages
|
||||
uses: ./.github/workflows/bake.yml
|
||||
with:
|
||||
bake_targets: '["deb", "rpm"]'
|
||||
bake_targets: '["deb", "rpm", "nix"]'
|
||||
cargo_profiles: '["release"]'
|
||||
feat_sets: '["all"]'
|
||||
rust_toolchains: '["stable"]'
|
||||
@@ -176,6 +179,12 @@ jobs:
|
||||
"rpm": {
|
||||
"dst": "tuwunel.rpm",
|
||||
"mime": "application/x-rpm"
|
||||
},
|
||||
"nix": {
|
||||
"dst": "tuwunel.drv.tar.zst",
|
||||
"src": "/opt/tuwunel/tuwunel.drv",
|
||||
"mime": "application/zstd",
|
||||
"zstd": 6
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,20 +44,27 @@ on:
|
||||
release_url:
|
||||
type: string
|
||||
description: For release assets
|
||||
pages_url:
|
||||
type: string
|
||||
description: For pages deployment
|
||||
|
||||
jobs:
|
||||
documents:
|
||||
if: >
|
||||
!failure() && !cancelled()
|
||||
&& contains(fromJSON(inputs.sys_targets), fromJSON('["X64"]')[0])
|
||||
&& (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v'))
|
||||
&& !contains(github.ref, '-draft')
|
||||
|
||||
name: Documents
|
||||
runs-on: ['X64', 'het']
|
||||
permissions:
|
||||
pages: write
|
||||
contents: read
|
||||
id-token: write
|
||||
pages: write
|
||||
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{inputs.pages_url}}
|
||||
|
||||
steps:
|
||||
- id: book
|
||||
|
||||
+135
-6
@@ -43,11 +43,20 @@ on:
|
||||
default: '["all"]'
|
||||
complement_runner:
|
||||
type: string
|
||||
rust_sdk_integration:
|
||||
type: boolean
|
||||
default: true
|
||||
build_nix:
|
||||
type: boolean
|
||||
default: true
|
||||
head_msg:
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
docs:
|
||||
if: >
|
||||
contains(fromJSON(inputs.cargo_profiles), fromJSON('["test"]')[0])
|
||||
!contains(inputs.head_msg, '[ci only it]')
|
||||
&& contains(fromJSON(inputs.cargo_profiles), fromJSON('["test"]')[0])
|
||||
&& contains(fromJSON(inputs.feat_sets), fromJSON('["all"]')[0])
|
||||
&& contains(fromJSON(inputs.rust_toolchains), fromJSON('["nightly"]')[0])
|
||||
&& contains(fromJSON(inputs.sys_targets), fromJSON('["x86_64-v1-linux-gnu"]')[0])
|
||||
@@ -69,7 +78,8 @@ jobs:
|
||||
|
||||
unit:
|
||||
if: >
|
||||
contains(fromJSON(inputs.cargo_profiles), fromJSON('["test"]')[0])
|
||||
!contains(inputs.head_msg, '[ci only it]')
|
||||
&& contains(fromJSON(inputs.cargo_profiles), fromJSON('["test"]')[0])
|
||||
&& contains(fromJSON(inputs.feat_sets), fromJSON('["all"]')[0])
|
||||
&& contains(fromJSON(inputs.rust_toolchains), fromJSON('["nightly"]')[0])
|
||||
&& contains(fromJSON(inputs.sys_targets), fromJSON('["x86_64-v1-linux-gnu"]')[0])
|
||||
@@ -77,7 +87,7 @@ jobs:
|
||||
name: Unit
|
||||
uses: ./.github/workflows/bake.yml
|
||||
with:
|
||||
bake_targets: '["unit"]'
|
||||
bake_targets: '["unit", "integration"]'
|
||||
cargo_profiles: '["test"]'
|
||||
feat_sets: '["all"]'
|
||||
rust_toolchains: ${{inputs.rust_toolchains}}
|
||||
@@ -89,9 +99,57 @@ jobs:
|
||||
excludes: ${{inputs.excludes}}
|
||||
includes: ${{inputs.includes}}
|
||||
|
||||
bench:
|
||||
if: >
|
||||
!contains(inputs.head_msg, '[ci only it]')
|
||||
&& contains(fromJSON(inputs.cargo_profiles), fromJSON('["bench"]')[0])
|
||||
&& contains(fromJSON(inputs.feat_sets), fromJSON('["all"]')[0])
|
||||
&& contains(fromJSON(inputs.rust_toolchains), fromJSON('["nightly"]')[0])
|
||||
&& contains(fromJSON(inputs.sys_targets), fromJSON('["x86_64-v3-linux-gnu"]')[0])
|
||||
|
||||
name: Bench
|
||||
uses: ./.github/workflows/bake.yml
|
||||
with:
|
||||
bake_targets: '["unit", "integration"]'
|
||||
cargo_profiles: '["bench"]'
|
||||
feat_sets: '["all"]'
|
||||
rust_toolchains: '["nightly"]'
|
||||
sys_names: ${{inputs.sys_names}}
|
||||
sys_versions: ${{inputs.sys_versions}}
|
||||
rust_targets: ${{inputs.rust_targets}}
|
||||
sys_targets: '["x86_64-v3-linux-gnu"]'
|
||||
machines: ${{inputs.machines}}
|
||||
excludes: ${{inputs.excludes}}
|
||||
includes: ${{inputs.includes}}
|
||||
|
||||
valgrind:
|
||||
if: >
|
||||
!contains(inputs.head_msg, '[ci only it]')
|
||||
&& contains(fromJSON(inputs.cargo_profiles), fromJSON('["bench"]')[0])
|
||||
&& contains(fromJSON(inputs.feat_sets), fromJSON('["all"]')[0])
|
||||
&& contains(fromJSON(inputs.rust_toolchains), fromJSON('["nightly"]')[0])
|
||||
&& contains(fromJSON(inputs.sys_targets), fromJSON('["x86_64-v3-linux-gnu"]')[0])
|
||||
|
||||
name: Memcheck
|
||||
uses: ./.github/workflows/bake.yml
|
||||
with:
|
||||
bake_targets: '["integration-valgrind"]'
|
||||
cargo_profiles: '["bench"]'
|
||||
feat_sets: '["all"]'
|
||||
rust_toolchains: '["nightly"]'
|
||||
sys_names: ${{inputs.sys_names}}
|
||||
sys_versions: ${{inputs.sys_versions}}
|
||||
rust_targets: ${{inputs.rust_targets}}
|
||||
sys_targets: '["x86_64-v3-linux-gnu"]'
|
||||
machines: ${{inputs.machines}}
|
||||
excludes: ${{inputs.excludes}}
|
||||
includes: ${{inputs.includes}}
|
||||
|
||||
smoke:
|
||||
if: >
|
||||
!failure() && !cancelled()
|
||||
&& !contains(inputs.head_msg, '[ci only it]')
|
||||
&& !contains(inputs.head_msg, '[ci no build]')
|
||||
&& inputs.cargo_profiles
|
||||
&& inputs.machines
|
||||
|
||||
@@ -117,6 +175,8 @@ jobs:
|
||||
{"cargo_profile": "test", "rust_target": "aarch64-unknown-linux-gnu"},
|
||||
{"cargo_profile": "test", "sys_target": "x86_64-v2-linux-gnu"},
|
||||
{"cargo_profile": "test", "sys_target": "x86_64-v3-linux-gnu"},
|
||||
{"cargo_profile": "test", "bake_target": "smoke-valgrind"},
|
||||
{"cargo_profile": "bench"},
|
||||
{"cargo_profile": "release", "rust_toolchain": "nightly"},
|
||||
{"cargo_profile": "release", "rust_toolchain": "stable", "feat_set": "none"},
|
||||
{"cargo_profile": "release", "bake_target": "smoke-valgrind"},
|
||||
@@ -160,9 +220,77 @@ jobs:
|
||||
{"sys_target": "x86_64-v4-linux-gnu", "bake_target": "smoke-valgrind"},
|
||||
]
|
||||
|
||||
nix:
|
||||
if: >
|
||||
!failure() && !cancelled()
|
||||
&& !contains(inputs.head_msg, '[ci only it]')
|
||||
&& !contains(inputs.head_msg, '[ci no build]')
|
||||
&& contains(fromJSON(inputs.cargo_profiles), fromJSON('["release"]')[0])
|
||||
&& contains(fromJSON(inputs.feat_sets), fromJSON('["all"]')[0])
|
||||
&& contains(fromJSON(inputs.rust_toolchains), fromJSON('["stable"]')[0])
|
||||
&& contains(fromJSON(inputs.sys_targets), fromJSON('["x86_64-v1-linux-gnu"]')[0])
|
||||
&& fromJSON(inputs.build_nix)
|
||||
|
||||
name: Smoke NixOS
|
||||
uses: ./.github/workflows/bake.yml
|
||||
with:
|
||||
bake_targets: '["smoke-nix"]'
|
||||
cargo_profiles: '["release"]'
|
||||
feat_sets: '["all"]'
|
||||
rust_toolchains: '["stable"]'
|
||||
sys_names: ${{inputs.sys_names}}
|
||||
sys_versions: ${{inputs.sys_versions}}
|
||||
rust_targets: ${{inputs.rust_targets}}
|
||||
sys_targets: '["x86_64-v1-linux-gnu"]'
|
||||
machines: ${{inputs.machines}}
|
||||
excludes: ${{inputs.excludes}}
|
||||
includes: ${{inputs.includes}}
|
||||
|
||||
matrix-rust-sdk-integration:
|
||||
if: >
|
||||
!failure() && !cancelled()
|
||||
&& !contains(inputs.head_msg, '[ci no build]')
|
||||
&& inputs.rust_sdk_integration
|
||||
&& contains(fromJSON(inputs.feat_sets), fromJSON('["all"]')[0])
|
||||
&& contains(fromJSON(inputs.rust_toolchains), fromJSON('["nightly"]')[0])
|
||||
&& contains(fromJSON(inputs.rust_targets), fromJSON('["x86_64-unknown-linux-gnu"]')[0])
|
||||
&& contains(fromJSON(inputs.sys_targets), fromJSON('["x86_64-v1-linux-gnu"]')[0])
|
||||
|
||||
name: Matrix SDK Integration
|
||||
needs: [smoke]
|
||||
uses: ./.github/workflows/bake.yml
|
||||
with:
|
||||
bake_targets: '["matrix-rust-sdk-integration"]'
|
||||
cargo_profiles: ${{inputs.cargo_profiles}}
|
||||
feat_sets: '["all"]'
|
||||
rust_toolchains: '["nightly"]'
|
||||
sys_names: ${{inputs.sys_names}}
|
||||
sys_versions: ${{inputs.sys_versions}}
|
||||
rust_targets: '["x86_64-unknown-linux-gnu"]'
|
||||
sys_targets: '["x86_64-v1-linux-gnu"]'
|
||||
machines: '["X64"]'
|
||||
runner: ${{inputs.complement_runner}}
|
||||
includes: ${{inputs.includes}}
|
||||
artifact: >
|
||||
{
|
||||
"matrix-rust-sdk-integration": {
|
||||
"src": "/var/log/tuwunel.log",
|
||||
"dst": "matrix-rust-sdk-integration.tuwunel.log",
|
||||
}
|
||||
}
|
||||
excludes: >
|
||||
[
|
||||
{"feat_set": "none"},
|
||||
{"feat_set": "logging"},
|
||||
{"cargo_profile": "release"},
|
||||
{"cargo_profile": "release-debuginfo"},
|
||||
{"cargo_profile": "release-native"},
|
||||
]
|
||||
|
||||
complement:
|
||||
if: >
|
||||
!failure() && !cancelled()
|
||||
&& !contains(inputs.head_msg, '[ci no build]')
|
||||
&& inputs.complement
|
||||
&& contains(fromJSON(inputs.cargo_profiles), fromJSON('["test"]')[0])
|
||||
&& contains(fromJSON(inputs.complement_feat_sets), fromJSON('["all"]')[0])
|
||||
@@ -188,6 +316,7 @@ jobs:
|
||||
compliance:
|
||||
if: >
|
||||
!failure() && !cancelled()
|
||||
&& !contains(inputs.head_msg, '[ci no build]')
|
||||
&& inputs.complement
|
||||
&& inputs.machines
|
||||
&& contains(fromJSON(inputs.cargo_profiles), fromJSON('["test"]')[0])
|
||||
@@ -196,7 +325,7 @@ jobs:
|
||||
&& contains(fromJSON(inputs.sys_targets), fromJSON('["x86_64-v1-linux-gnu"]')[0])
|
||||
|
||||
name: Matrix Compliance
|
||||
needs: [complement]
|
||||
needs: [complement, smoke]
|
||||
runs-on: ["${{matrix.machine}}", "${{inputs.complement_runner}}"]
|
||||
concurrency:
|
||||
group: complement-cant-walk-and-chew-bubblegum
|
||||
@@ -242,7 +371,7 @@ jobs:
|
||||
|
||||
run: |
|
||||
cid=$(cat "$name")
|
||||
docker cp "$cid:/usr/src/complement/new_results.jsonl" tests/test_results/complement/test_results.jsonl
|
||||
docker cp "$cid:/usr/src/complement/new_results.jsonl" tests/complement/results.jsonl
|
||||
|
||||
- if: success() || failure() && steps.execute.outcome == 'failure'
|
||||
name: Upload New Results
|
||||
@@ -250,7 +379,7 @@ jobs:
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: complement_results-${{matrix.feat_set}}-${{matrix.sys_name}}-${{matrix.sys_target}}.jsonl
|
||||
path: ./tests/test_results/complement/test_results.jsonl
|
||||
path: ./tests/complement/results.jsonl
|
||||
|
||||
- if: failure() && steps.execute.outcome == 'failure'
|
||||
name: Upload Failure Output
|
||||
|
||||
Generated
+370
-268
File diff suppressed because it is too large
Load Diff
+32
-11
@@ -29,7 +29,7 @@ license = "Apache-2.0"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/matrix-construct/tuwunel"
|
||||
rust-version = "1.88.0"
|
||||
version = "1.4.2"
|
||||
version = "1.4.5"
|
||||
|
||||
[workspace.metadata.crane]
|
||||
name = "tuwunel"
|
||||
@@ -140,10 +140,19 @@ features = [
|
||||
version = "0.8.3"
|
||||
|
||||
[workspace.dependencies.const-str]
|
||||
version = "0.6"
|
||||
version = "0.7"
|
||||
|
||||
[workspace.dependencies.criterion]
|
||||
version = "0.7"
|
||||
default-features = false
|
||||
features = [
|
||||
"cargo_bench_support",
|
||||
"async_futures",
|
||||
"async_tokio",
|
||||
]
|
||||
|
||||
[workspace.dependencies.ctor]
|
||||
version = "0.4"
|
||||
version = "0.5"
|
||||
|
||||
[workspace.dependencies.cyborgtime]
|
||||
version = "2.1"
|
||||
@@ -263,7 +272,10 @@ features = ["std"]
|
||||
[workspace.dependencies.nix]
|
||||
version = "0.30"
|
||||
default-features = false
|
||||
features = ["resource"]
|
||||
features = [
|
||||
"resource",
|
||||
"user",
|
||||
]
|
||||
|
||||
[workspace.dependencies.num-traits]
|
||||
version = "0.2"
|
||||
@@ -308,7 +320,7 @@ default-features = false
|
||||
|
||||
[workspace.dependencies.ruma]
|
||||
git = "https://github.com/matrix-construct/ruma"
|
||||
rev = "5682b88cf1bcaf0f47805d614b476b242ef075d4"
|
||||
rev = "b858d3478f2792a381463ef9b63f6c1469a5d81c"
|
||||
features = [
|
||||
"__compat",
|
||||
"appservice-api-c",
|
||||
@@ -338,9 +350,9 @@ features = [
|
||||
"unstable-msc4133",
|
||||
"unstable-msc4186",
|
||||
"unstable-msc4203", # sending to-device events to appservices
|
||||
"unstable-msc4310",
|
||||
"unstable-msc4311",
|
||||
"unstable-extensible-events",
|
||||
"unstable-hydra",
|
||||
]
|
||||
|
||||
[workspace.dependencies.rustls]
|
||||
@@ -454,7 +466,7 @@ features = [
|
||||
]
|
||||
|
||||
[workspace.dependencies.termimad]
|
||||
version = "0.33"
|
||||
version = "0.34"
|
||||
default-features = false
|
||||
|
||||
[workspace.dependencies.thiserror]
|
||||
@@ -690,7 +702,7 @@ inherits = "release-native.build-override"
|
||||
#]
|
||||
|
||||
[profile.bench]
|
||||
debug = "limited"
|
||||
debug = false
|
||||
strip = false
|
||||
#rustflags = [
|
||||
# "-Cremark=all",
|
||||
@@ -711,7 +723,7 @@ strip = false
|
||||
# and can be raised if build times are tolerable.
|
||||
|
||||
[profile.dev]
|
||||
debug = "full"
|
||||
debug = 0
|
||||
#rustflags = [
|
||||
# '--cfg', 'tuwunel_mods',
|
||||
# '-Ztime-passes',
|
||||
@@ -771,7 +783,7 @@ inherits = "dev"
|
||||
|
||||
[profile.dev.package.'*']
|
||||
inherits = "dev"
|
||||
debug = "limited"
|
||||
debug = 0
|
||||
incremental = false
|
||||
codegen-units = 1
|
||||
opt-level = 'z'
|
||||
@@ -788,6 +800,11 @@ opt-level = 'z'
|
||||
# '-Clink-arg=-Wl,-z,nodelete',
|
||||
#]
|
||||
|
||||
# same as dev but slower.
|
||||
[profile.dbg]
|
||||
inherits = "dev"
|
||||
debug = "full"
|
||||
|
||||
# primarily used for CI
|
||||
[profile.test]
|
||||
debug = "limited"
|
||||
@@ -879,6 +896,9 @@ multiple_crate_versions = { level = "allow", priority = 1 }
|
||||
###################
|
||||
complexity = { level = "warn", priority = -1 }
|
||||
|
||||
# promotes forward-compat for literal ..default() construction
|
||||
needless_update = { level = "allow", priority = 1 }
|
||||
|
||||
###################
|
||||
correctness = { level = "warn", priority = -1 }
|
||||
|
||||
@@ -891,6 +911,7 @@ option_if_let_else = { level = "allow", priority = 1 } # TODO
|
||||
redundant_pub_crate = { level = "allow", priority = 1 } # TODO
|
||||
significant_drop_in_scrutinee = { level = "allow", priority = 1 } # TODO
|
||||
significant_drop_tightening = { level = "allow", priority = 1 } # TODO
|
||||
tuple_array_conversions = { level = "allow", priority = 1 }
|
||||
|
||||
###################
|
||||
pedantic = { level = "warn", priority = -1 }
|
||||
@@ -905,6 +926,7 @@ if_then_some_else_none = { level = "allow", priority = 1 }
|
||||
inline_always = { level = "allow", priority = 1 }
|
||||
map_unwrap_or = { level = "allow", priority = 1 }
|
||||
match_bool = { level = "allow", priority = 1 }
|
||||
match_same_arms = { level = "allow", priority = 1 }
|
||||
missing_docs_in_private_items = { level = "allow", priority = 1 }
|
||||
missing_errors_doc = { level = "allow", priority = 1 }
|
||||
missing_panics_doc = { level = "allow", priority = 1 }
|
||||
@@ -950,7 +972,6 @@ pub_without_shorthand = "warn"
|
||||
rc_buffer = "warn"
|
||||
rc_mutex = "warn"
|
||||
redundant_type_annotations = "warn"
|
||||
rest_pat_in_fully_bound_structs = "warn"
|
||||
semicolon_outside_block = "warn"
|
||||
str_to_string = "warn"
|
||||
string_lit_chars_any = "warn"
|
||||
|
||||
+4
-52
@@ -1,57 +1,9 @@
|
||||
# Tuwunel 1.4.2
|
||||
# Tuwunel 1.4.5
|
||||
|
||||
September 12, 2025
|
||||
October 24, 2025
|
||||
|
||||
Users running maubot, neochat, or any client or bridge not excluded below should update to this patch as soon as possible to reduce unnecessary resource consumption. (see: Bug Fixes)
|
||||
|
||||
### New Features
|
||||
|
||||
- Requested by @alaviss an alternative DNS resolver has been implemented for use with appservices and other configured targets intended for local networks. This passthru performs minimal caching and cannot be used for federation. Enable with `dns_passthru_appservices = true` or specifying hosts in `dns_passthru_domains` (#158)
|
||||
|
||||
- Contributed by @tototomate123 a nifty experimental feature can disable push notifications when you're active on one device from being sent to others. This can be enabled with `suppress_push_when_active`. Please thank them when your pocket stops vibrating while chatting on your desktop! (#150)
|
||||
|
||||
- Thanks to a report by @DetermineAbsurd the `m.federate` field can be defaulted to false when creating a room using the new `federate_created_rooms` config option. (#151)
|
||||
|
||||
- At the request of @grinapo verbose logging builds are now bundled with this release. These builds are found with the feature-set `-logging-` which is otherwise similar to `-all-`. This contains more messages at all levels optimized away in other release modes; it comes at some performance penalty.
|
||||
|
||||
- JWT tokens can now be used for authentication on any endpoint which supports UIA. For example: an external forgot-password service can send a token to the `client/account/password` endpoint to reset a user's password. This feature was commissioned and made public by an enterprise sponsor.
|
||||
|
||||
### Enhancements
|
||||
|
||||
- Sliding-sync has been significantly refactored. Performance has massively increased with many bugs and compliance issues also fixed. Please be aware we are tracking an issue related to read-marker behavior in Element X. The 🟢 dot does not unconditionally clear at every touch. Whether this is a feature or a bug, or both, is being investigated for v1.5.
|
||||
|
||||
- Hydra backports are now enabled by default. The change should be completely transparent. If you do notice any increased load try to increase the `cache_capacity_modifier` above default.
|
||||
|
||||
- Room deletions now also purge synctokens which can be significant to the overall storage consumed by a room. Users who have already deleted rooms please be assured an update planned for v1.5 will deal with cleansing synctokens in general.
|
||||
|
||||
- Room version 1 and 2 support took a step forward, possibly working for some rooms but is not yet considered adequately supported and the ticket remains open. (#12)
|
||||
|
||||
- Thanks to @AreYouLoco for contributing an updated Kubernetes [Helm Chart](https://github.com/AreYouLoco/tuwunel-helm); link added to docs.
|
||||
All deployments serving ElementX, Element Pro, SchildiChat Next, or any client using sliding-sync should upgrade to this patch for an improved experience.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **Special thanks to @frebib for investigating a bug which triggers the uploading of unnecessary encryption one-time-keys.** Running over ten maubot instances it became obvious after observing increased resources and laggy bot response. This update removes any excess keys for a device. Thanks to @duckbuster for confirming neochat is affected. Clients confirmed unaffected include: Element, Element X, Nheko. Fractal, Cinny, matrix-rust-sdk and matrix-js-sdk clients and bots are probably unaffected. Mautrix-based bridges are probably affected. Users of unaffected clients should still upgrade.
|
||||
|
||||
- Thanks @dasha_uwu for refactoring alias resolution logic with fixes to remain compatible with the upcoming element-web release. This was an incredibly valuable contribution which will spare all of us from impending grief; the kind of ahead-of-the-game initiative I don't think a project like this could exist without. (adadafa88f3)
|
||||
|
||||
- Room deletions now preserve a small number of records to properly synchronize with local clients and remote servers after the room vanishes. Prior behavior is maintained with a `--force` flag added to the command.
|
||||
|
||||
- Thanks @scvalex for once again cleaning up our mess after Nix found the github CI was not running doctests. Thank you for contributing the patch 🙏 (#152).
|
||||
|
||||
- Thanks @Tronde for reporting a broken link to the CoC in the mdbook documentation. (#155)
|
||||
|
||||
- Specification compliance required the `/joined_rooms` endpoint be restricted to current members rather than including past members. (4b49aaad53a)
|
||||
|
||||
- Specification compliance required state events be made visible to prior members of a room where `history_visibility=shared`. (86781522b68)
|
||||
|
||||
- The `limit` parameter to the `/context` endpoint is now divided with de facto compatibility (matrix-org/matrix-spec#2202)
|
||||
|
||||
- The room avatar in sliding sync is now computed with greater compliance to the specification (3deebeab78f). This builds off earlier work done by @tmayoff in (a340e6786db).
|
||||
|
||||
- The canonical alias for a room is considered invalid if the primary alias is missing or removed (7221d466ce8). This is a T&S concern and we encourage reports for any other contexts where this condition should be applied.
|
||||
|
||||
- Presence is no longer updated by the private read-receipt or read-marker paths, only public receipts.
|
||||
|
||||
### Deprecations
|
||||
|
||||
- Hardened Malloc support had to be removed after the build broke. We will gladly add support back upon request or contribution.
|
||||
- Progress made for sliding-sync in 1.4.3 was effectively reverted by 1.4.4. This release puts sliding-sync back on track with further progress exceeding 1.4.3. This release is now shipping without any known issues. Please report all issues. **IMPORTANT:** Issues from prior releases (such as stuck or incorrect unread counts) benefit from clearing the app's cache to immediately resolve. Go to settings, tap the version number 7 times, then enter the menu to select `clear cache`.
|
||||
|
||||
@@ -4,7 +4,6 @@ Wants=network-online.target
|
||||
After=network-online.target
|
||||
Documentation=https://tuwunel.chat/
|
||||
RequiresMountsFor=/var/lib/private/tuwunel
|
||||
Alias=matrix-tuwunel.service
|
||||
|
||||
[Service]
|
||||
DynamicUser=yes
|
||||
@@ -77,3 +76,4 @@ StartLimitBurst=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Alias=matrix-tuwunel.service
|
||||
|
||||
Vendored
+1
-1
@@ -2,7 +2,6 @@
|
||||
Description=Tuwunel Matrix homeserver
|
||||
Wants=network-online.target
|
||||
After=network-online.target
|
||||
Alias=matrix-tuwunel.service
|
||||
Documentation=https://tuwunel.chat/
|
||||
|
||||
[Service]
|
||||
@@ -64,3 +63,4 @@ StartLimitBurst=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Alias=matrix-tuwunel.service
|
||||
|
||||
@@ -16,6 +16,7 @@ ARG CARGO_TERM_VERBOSE=0
|
||||
ARG RUST_BACKTRACE
|
||||
ARG JEMALLOC_OVERRIDE
|
||||
ARG ROCKSDB_LIB_DIR
|
||||
ARG VALGRINDFLAGS=""
|
||||
ARG CARGO_BUILD_RUSTFLAGS
|
||||
ARG CARGO_PROFILE_TEST_DEBUG
|
||||
ARG CARGO_PROFILE_TEST_INCREMENTAL
|
||||
@@ -47,6 +48,7 @@ ENV CARGO_TERM_VERBOSE="${CARGO_TERM_VERBOSE}"
|
||||
ENV RUST_BACKTRACE="${RUST_BACKTRACE}"
|
||||
ENV JEMALLOC_OVERRIDE="${JEMALLOC_OVERRIDE}"
|
||||
ENV ROCKSDB_LIB_DIR="${ROCKSDB_LIB_DIR}"
|
||||
ENV VALGRINDFLAGS="${VALGRINDFLAGS}"
|
||||
ENV CARGO_PROFILE_TEST_DEBUG="${CARGO_PROFILE_TEST_DEBUG}"
|
||||
ENV CARGO_PROFILE_TEST_INCREMENTAL="${CARGO_PROFILE_TEST_INCREMENTAL}"
|
||||
ENV CARGO_PROFILE_BENCH_DEBUG="${CARGO_PROFILE_BENCH_DEBUG}"
|
||||
@@ -74,6 +76,7 @@ RUN \
|
||||
--mount=type=cache,dst=${targ_targ_dir}/.fingerprint,id=${cargo_target_share}/fingerprint,sharing=locked \
|
||||
<<EOF
|
||||
set -eux
|
||||
ulimit -n 65535
|
||||
rustup run ${rust_toolchain} \
|
||||
cargo ${cargo_cmd} \
|
||||
--verbose \
|
||||
|
||||
@@ -1,22 +1,6 @@
|
||||
# syntax = docker/dockerfile:1.11-labs
|
||||
|
||||
FROM input AS key-gen-base
|
||||
ARG var_cache
|
||||
ARG var_lib_apt
|
||||
|
||||
WORKDIR /
|
||||
COPY --link --from=input . .
|
||||
|
||||
RUN \
|
||||
--mount=type=cache,dst=/var/cache,id=${var_cache},sharing=locked \
|
||||
--mount=type=cache,dst=/var/lib/apt,id=${var_lib_apt},sharing=locked \
|
||||
<<EOF
|
||||
set -eux
|
||||
apt-get -y -U install --no-install-recommends openssl gawk
|
||||
EOF
|
||||
|
||||
|
||||
FROM key-gen-base AS key-gen
|
||||
FROM input AS key-gen
|
||||
|
||||
WORKDIR /complement
|
||||
COPY <<EOF v3.ext
|
||||
@@ -62,12 +46,11 @@ RUN [ -f certificate.crt ] && [ -f private_key.pem ]
|
||||
FROM scratch AS complement-config
|
||||
WORKDIR /complement
|
||||
COPY --from=key-gen /complement/* .
|
||||
COPY --from=source /usr/src/tuwunel/tests/test_results/complement/test_results.jsonl old_results.jsonl
|
||||
COPY --from=source /usr/src/tuwunel/tests/complement/results.jsonl old_results.jsonl
|
||||
COPY <<EOF complement.toml
|
||||
[global]
|
||||
address = "0.0.0.0"
|
||||
admin_room_notices = false
|
||||
allow_check_for_updates = false
|
||||
allow_device_name_federation = true
|
||||
allow_guest_registration = true
|
||||
allow_invalid_tls_certificates = true
|
||||
@@ -89,7 +72,6 @@ COPY <<EOF complement.toml
|
||||
log_thread_ids = true
|
||||
media_compat_file_link = false
|
||||
media_startup_check = true
|
||||
only_query_trusted_key_servers = false
|
||||
port = [8008, 8448]
|
||||
prune_missing_media = true
|
||||
query_trusted_key_servers_first = false
|
||||
@@ -156,16 +138,6 @@ ARG complement_tags="conduwuit_blacklist"
|
||||
ARG complement_tests="./tests/..."
|
||||
ARG complement_run=".*"
|
||||
|
||||
WORKDIR /
|
||||
RUN \
|
||||
--mount=type=cache,dst=/var/cache,id=${var_cache},sharing=locked \
|
||||
--mount=type=cache,dst=/var/lib/apt,id=${var_lib_apt},sharing=locked \
|
||||
--mount=type=cache,dst=/go/pkg/mod/cache,sharing=locked \
|
||||
<<EOF
|
||||
set -eux
|
||||
apt-get -y -U install --no-install-recommends golang-go jq
|
||||
EOF
|
||||
|
||||
WORKDIR /usr/src
|
||||
ADD https://github.com/matrix-construct/complement.git#403840348f6bcc9cc8ed1671dc2f638c2b1ce4ac complement
|
||||
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
# syntax = docker/dockerfile:1.11-labs
|
||||
|
||||
FROM input AS matrix-rust-sdk-integration
|
||||
ARG sys_name
|
||||
ARG sys_version
|
||||
ARG rust_toolchain
|
||||
ARG rust_target
|
||||
ARG RUSTUP_HOME
|
||||
ARG CARGO_HOME
|
||||
ARG CARGO_TARGET
|
||||
ARG MRSDK_TARGET_DIR="/usr/src/matrix-rust-sdk"
|
||||
ARG mrsdk_profile="release"
|
||||
ARG mrsdk_target_share="${MRSDK_TARGET_DIR}/${sys_name}/${sys_version}/${rust_target}/${rust_toolchain}/${mrsdk_profile}/_shared_cache"
|
||||
ARG mrsdk_test_args=""
|
||||
ARG mrsdk_test_opts=""
|
||||
ARG mrsdk_skip_list=""
|
||||
|
||||
WORKDIR /
|
||||
COPY --link --from=input . .
|
||||
|
||||
WORKDIR /usr/src
|
||||
ADD --link https://github.com/matrix-construct/matrix-rust-sdk.git#integration matrix-rust-sdk
|
||||
|
||||
WORKDIR /etc
|
||||
COPY <<EOF tuwunel.toml
|
||||
[global]
|
||||
admin_room_notices = false
|
||||
allow_device_name_federation = true
|
||||
allow_guest_registration = true
|
||||
allow_legacy_media = true
|
||||
allow_public_room_directory_over_federation = true
|
||||
allow_public_room_directory_without_auth = true
|
||||
allow_registration = true
|
||||
create_admin_room = false
|
||||
ip_range_denylist = []
|
||||
log = "debug,tuwunel=trace,h2=warn,hyper=warn"
|
||||
log_colors = false
|
||||
log_guest_registrations = false
|
||||
log_span_events = "NONE"
|
||||
log_thread_ids = true
|
||||
media_compat_file_link = false
|
||||
media_startup_check = true
|
||||
query_trusted_key_servers_first = false
|
||||
query_trusted_key_servers_first_on_join = false
|
||||
rocksdb_log_level = "debug"
|
||||
rocksdb_max_log_files = 1
|
||||
rocksdb_paranoid_file_checks = true
|
||||
rocksdb_recovery_mode = 0
|
||||
trusted_servers = []
|
||||
url_preview_domain_contains_allowlist = ["*"]
|
||||
url_preview_domain_explicit_denylist = ["*"]
|
||||
yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse = true
|
||||
EOF
|
||||
|
||||
WORKDIR /usr/lib
|
||||
COPY --link --from=install /usr/lib .
|
||||
|
||||
WORKDIR /usr/bin
|
||||
COPY --link --from=install /usr/bin/tuwunel .
|
||||
|
||||
WORKDIR /usr/src/matrix-rust-sdk
|
||||
ENV targ_dir="${MRSDK_TARGET_DIR}/${mrsdk_profile}"
|
||||
ENV targ_targ_dir="${MRSDK_TARGET_DIR}/${rust_target}/${mrsdk_profile}"
|
||||
ENV RUST_BACKTRACE="full"
|
||||
ENV TUWUNEL_CONFIG="/etc/tuwunel.toml"
|
||||
ENV TUWUNEL_DATABASE_PATH="/var/db/tuwunel"
|
||||
ENV TUWUNEL_SERVER_NAME="localhost:6167"
|
||||
ENV TUWUNEL_PORT="[6167]"
|
||||
ENV HOMESERVER_URL="http://localhost:6167"
|
||||
RUN \
|
||||
--mount=type=cache,dst=${RUSTUP_HOME}/downloads,sharing=shared \
|
||||
--mount=type=cache,dst=${CARGO_HOME}/registry,sharing=shared \
|
||||
--mount=type=cache,dst=${CARGO_HOME}/git,sharing=shared \
|
||||
--mount=type=cache,dst=${targ_dir}/deps,id=${mrsdk_target_share}/deps,sharing=locked \
|
||||
--mount=type=cache,dst=${targ_dir}/build,id=${mrsdk_target_share}/build,sharing=locked \
|
||||
--mount=type=cache,dst=${targ_dir}/examples,id=${mrsdk_target_share}/examples,sharing=locked \
|
||||
--mount=type=cache,dst=${targ_dir}/incremental,id=${mrsdk_target_share}/incremental,sharing=locked \
|
||||
--mount=type=cache,dst=${targ_dir}/.fingerprint,id=${mrsdk_target_share}/fingerprint,sharing=locked \
|
||||
--mount=type=cache,dst=${targ_targ_dir}/deps,id=${mrsdk_target_share}/deps,sharing=locked \
|
||||
--mount=type=cache,dst=${targ_targ_dir}/build,id=${mrsdk_target_share}/build,sharing=locked \
|
||||
--mount=type=cache,dst=${targ_targ_dir}/examples,id=${mrsdk_target_share}/examples,sharing=locked \
|
||||
--mount=type=cache,dst=${targ_targ_dir}/incremental,id=${mrsdk_target_share}/incremental,sharing=locked \
|
||||
--mount=type=cache,dst=${targ_targ_dir}/.fingerprint,id=${mrsdk_target_share}/fingerprint,sharing=locked \
|
||||
<<EOF
|
||||
set -eux
|
||||
|
||||
nohup /usr/bin/tuwunel 1> /var/log/tuwunel.log &
|
||||
PID=$!; trap "sleep 10s; set +ex; kill -QUIT ${PID}; wait ${PID}" EXIT
|
||||
sleep 3s
|
||||
|
||||
rustup run ${rust_toolchain} \
|
||||
cargo test \
|
||||
--locked \
|
||||
--release \
|
||||
--color always \
|
||||
--features default \
|
||||
--target "${rust_target}" \
|
||||
--target-dir "${MRSDK_TARGET_DIR}" \
|
||||
--package matrix-sdk-integration-testing \
|
||||
${mrsdk_test_args} \
|
||||
-- \
|
||||
--test-threads 1 \
|
||||
--color always \
|
||||
${mrsdk_skip_list} \
|
||||
${mrsdk_test_opts} \
|
||||
;
|
||||
EOF
|
||||
@@ -0,0 +1,89 @@
|
||||
# syntax = docker/dockerfile:1.11-labs
|
||||
|
||||
FROM input AS nix-base
|
||||
ARG sys_name
|
||||
ARG sys_version
|
||||
ARG sys_target
|
||||
|
||||
WORKDIR /
|
||||
COPY --link --from=input . .
|
||||
RUN \
|
||||
--mount=type=cache,dst=/nix,sharing=shared \
|
||||
--mount=type=cache,dst=/root/.cache/nix,sharing=shared \
|
||||
--mount=type=cache,dst=/root/.local/state/nix,sharing=shared \
|
||||
<<EOF
|
||||
set -eux
|
||||
curl --proto '=https' --tlsv1.2 -L https://nixos.org/nix/install > nix-install
|
||||
sh ./nix-install --daemon
|
||||
rm nix-install
|
||||
EOF
|
||||
|
||||
|
||||
FROM nix-base AS build-nix
|
||||
ARG sys_name
|
||||
ARG sys_version
|
||||
ARG sys_target
|
||||
|
||||
WORKDIR /usr/src/tuwunel
|
||||
COPY --link --from=source /usr/src/tuwunel .
|
||||
RUN \
|
||||
--mount=type=cache,dst=/nix,sharing=shared \
|
||||
--mount=type=cache,dst=/root/.cache/nix,sharing=shared \
|
||||
--mount=type=cache,dst=/root/.local/state/nix,sharing=shared \
|
||||
<<EOF
|
||||
set -eux
|
||||
|
||||
nix-build \
|
||||
--verbose \
|
||||
--cores 0 \
|
||||
--max-jobs $(nproc) \
|
||||
--log-format raw \
|
||||
.
|
||||
|
||||
cp -afRL --copy-contents result /opt/tuwunel
|
||||
EOF
|
||||
|
||||
|
||||
FROM build-nix AS smoke-nix
|
||||
ARG sys_name
|
||||
ARG sys_version
|
||||
ARG sys_target
|
||||
|
||||
WORKDIR /
|
||||
COPY --link --from=build-nix . .
|
||||
|
||||
WORKDIR /opt/tuwunel
|
||||
ENV TUWUNEL_DATABASE_PATH="/tmp/tuwunel/smoketest.db"
|
||||
ENV TUWUNEL_LOG="info"
|
||||
RUN \
|
||||
--mount=type=cache,dst=/nix,sharing=shared \
|
||||
--mount=type=cache,dst=/root/.cache/nix,sharing=shared \
|
||||
--mount=type=cache,dst=/root/.local/state/nix,sharing=shared \
|
||||
<<EOF
|
||||
set -eux
|
||||
|
||||
bin/tuwunel \
|
||||
-Otest='["smoke", "fresh"]' \
|
||||
-Oserver_name=\"localhost\" \
|
||||
EOF
|
||||
|
||||
|
||||
FROM build-nix AS nix-pkg
|
||||
ARG sys_name
|
||||
ARG sys_version
|
||||
ARG sys_target
|
||||
|
||||
WORKDIR /
|
||||
COPY --link --from=build-nix . .
|
||||
|
||||
WORKDIR /usr/src/tuwunel
|
||||
RUN \
|
||||
--mount=type=cache,dst=/nix,sharing=shared \
|
||||
--mount=type=cache,dst=/root/.cache/nix,sharing=shared \
|
||||
--mount=type=cache,dst=/root/.local/state/nix,sharing=shared \
|
||||
<<EOF
|
||||
set -eux
|
||||
#TODO: extract derivation?
|
||||
mkdir -p /opt/tuwunel
|
||||
touch /opt/tuwunel/tuwunel.drv
|
||||
EOF
|
||||
+198
-38
@@ -184,7 +184,6 @@ dynamic_libs = [
|
||||
|
||||
nightly_rustflags = [
|
||||
"--cfg tokio_unstable",
|
||||
"--cfg tuwunel_bench",
|
||||
"--allow=unstable-features",
|
||||
"-Z crate-attr=feature(test)",
|
||||
"-Z enforce-type-length-limit",
|
||||
@@ -240,7 +239,15 @@ group "tests" {
|
||||
"docs",
|
||||
"unit",
|
||||
"smoke",
|
||||
"integration",
|
||||
"matrix-compliance",
|
||||
]
|
||||
}
|
||||
|
||||
group "matrix-compliance" {
|
||||
targets = [
|
||||
"complement",
|
||||
"matrix-rust-sdk-integration",
|
||||
]
|
||||
}
|
||||
|
||||
@@ -408,7 +415,7 @@ target "complement-base" {
|
||||
elem("complement-config", [sys_name, sys_version, sys_target])
|
||||
]
|
||||
contexts = {
|
||||
input = elem("target:base", [sys_name, sys_version, sys_target])
|
||||
input = elem("target:builder", [sys_name, sys_version, sys_target])
|
||||
}
|
||||
args = complement_args
|
||||
}
|
||||
@@ -429,6 +436,82 @@ target "complement-config" {
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Integration tests
|
||||
#
|
||||
|
||||
group "integrations" {
|
||||
targets = [
|
||||
"matrix-rust-sdk-integration",
|
||||
"integration",
|
||||
]
|
||||
}
|
||||
|
||||
target "matrix-rust-sdk-integration" {
|
||||
name = elem("matrix-rust-sdk-integration", [cargo_profile, rust_toolchain, rust_target, feat_set, sys_name, sys_version, sys_target])
|
||||
tags = [
|
||||
elem_tag("matrix-rust-sdk-integration", [cargo_profile, rust_toolchain, rust_target, feat_set, sys_name, sys_version, sys_target], "latest"),
|
||||
]
|
||||
output = ["type=docker,compression=zstd,mode=max,compression-level=${zstd_image_compress_level}"]
|
||||
cache_to = ["type=local,compression=zstd,mode=max,compression-level=${cache_compress_level}"]
|
||||
target = "matrix-rust-sdk-integration"
|
||||
dockerfile = "${docker_dir}/Dockerfile.matrix-rust-sdk"
|
||||
matrix = cargo_rust_feat_sys
|
||||
inherits = [
|
||||
elem("rust", [rust_toolchain, rust_target, sys_name, sys_version, sys_target])
|
||||
]
|
||||
contexts = {
|
||||
input = elem("target:rust", [rust_toolchain, rust_target, sys_name, sys_version, sys_target])
|
||||
install = elem("target:install", [cargo_profile, rust_toolchain, rust_target, feat_set, sys_name, sys_version, sys_target])
|
||||
}
|
||||
args = {
|
||||
mrsdk_test_args=""
|
||||
mrsdk_test_opts=""
|
||||
}
|
||||
}
|
||||
|
||||
target "integration-valgrind" {
|
||||
name = elem("integration-valgrind", [cargo_profile, rust_toolchain, rust_target, feat_set, sys_name, sys_version, sys_target])
|
||||
tags = [
|
||||
elem_tag("integration-valgrind", [cargo_profile, rust_toolchain, rust_target, feat_set, sys_name, sys_version, sys_target], "latest"),
|
||||
]
|
||||
matrix = cargo_rust_feat_sys
|
||||
inherits = [
|
||||
elem("integration", [cargo_profile, rust_toolchain, rust_target, feat_set, sys_name, sys_version, sys_target]),
|
||||
]
|
||||
contexts = {
|
||||
input = elem("target:integration", [cargo_profile, rust_toolchain, rust_target, feat_set, sys_name, sys_version, sys_target])
|
||||
}
|
||||
args = {
|
||||
VALGRINDFLAGS = "--error-exitcode=1 --exit-on-first-error=yes --undef-value-errors=no --leak-check=no"
|
||||
cargo_cmd = "valgrind test"
|
||||
cargo_args = "--test=*"
|
||||
}
|
||||
}
|
||||
|
||||
target "integration" {
|
||||
name = elem("integration", [cargo_profile, rust_toolchain, rust_target, feat_set, sys_name, sys_version, sys_target])
|
||||
tags = [
|
||||
elem_tag("integration", [cargo_profile, rust_toolchain, rust_target, feat_set, sys_name, sys_version, sys_target], "latest"),
|
||||
]
|
||||
target = "cargo"
|
||||
matrix = cargo_rust_feat_sys
|
||||
inherits = [
|
||||
elem("build-tests", [cargo_profile, rust_toolchain, rust_target, feat_set, sys_name, sys_version, sys_target]),
|
||||
]
|
||||
contexts = {
|
||||
input = elem("target:build-tests", [cargo_profile, rust_toolchain, rust_target, feat_set, sys_name, sys_version, sys_target])
|
||||
}
|
||||
args = {
|
||||
TUWUNEL_DATABASE_PATH = "/tmp/integration.test.db"
|
||||
|
||||
cargo_cmd = (cargo_profile == "bench"? "bench": "test")
|
||||
cargo_args = (cargo_profile == "bench"?
|
||||
"--no-fail-fast --bench=*": "--no-fail-fast --test=*"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Smoke tests
|
||||
#
|
||||
@@ -437,11 +520,26 @@ group "smoke" {
|
||||
targets = [
|
||||
"smoke-version",
|
||||
"smoke-startup",
|
||||
#"smoke-nix",
|
||||
#"smoke-valgrind",
|
||||
#"smoke-perf",
|
||||
]
|
||||
}
|
||||
|
||||
target "smoke-nix" {
|
||||
name = elem("smoke-nix", [sys_name, sys_version, sys_target])
|
||||
tags = [
|
||||
elem_tag("smoke-nix", [sys_name, sys_version, sys_target], "latest"),
|
||||
]
|
||||
output = ["type=cacheonly,compression=zstd,mode=min,compression-level=${cache_compress_level}"]
|
||||
dockerfile = "${docker_dir}/Dockerfile.nix"
|
||||
target = "smoke-nix"
|
||||
matrix = sys
|
||||
inherits = [
|
||||
elem("build-nix", [sys_name, sys_version, sys_target]),
|
||||
]
|
||||
}
|
||||
|
||||
target "smoke-valgrind" {
|
||||
name = elem("smoke-valgrind", [cargo_profile, rust_toolchain, rust_target, feat_set, sys_name, sys_version, sys_target])
|
||||
tags = [
|
||||
@@ -514,6 +612,70 @@ target "tests-smoke" {
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Unit tests
|
||||
#
|
||||
|
||||
target "unit-valgrind" {
|
||||
name = elem("unit-valgrind", [cargo_profile, rust_toolchain, rust_target, feat_set, sys_name, sys_version, sys_target])
|
||||
tags = [
|
||||
elem_tag("unit-valgrind", [cargo_profile, rust_toolchain, rust_target, feat_set, sys_name, sys_version, sys_target], "latest"),
|
||||
]
|
||||
target = "cargo"
|
||||
matrix = cargo_rust_feat_sys
|
||||
inherits = [
|
||||
elem("unit", [cargo_profile, rust_toolchain, rust_target, feat_set, sys_name, sys_version, sys_target]),
|
||||
]
|
||||
contexts = {
|
||||
input = elem("target:unit", [cargo_profile, rust_toolchain, rust_target, feat_set, sys_name, sys_version, sys_target])
|
||||
}
|
||||
args = {
|
||||
VALGRINDFLAGS = "--error-exitcode=1 --exit-on-first-error=yes --undef-value-errors=no --leak-check=no"
|
||||
cargo_cmd = "valgrind test"
|
||||
cargo_args = "--lib --bins"
|
||||
}
|
||||
}
|
||||
|
||||
target "unit" {
|
||||
name = elem("unit", [cargo_profile, rust_toolchain, rust_target, feat_set, sys_name, sys_version, sys_target])
|
||||
tags = [
|
||||
elem_tag("unit", [cargo_profile, rust_toolchain, rust_target, feat_set, sys_name, sys_version, sys_target], "latest"),
|
||||
]
|
||||
target = "cargo"
|
||||
matrix = cargo_rust_feat_sys
|
||||
inherits = [
|
||||
elem("build-tests", [cargo_profile, rust_toolchain, rust_target, feat_set, sys_name, sys_version, sys_target]),
|
||||
]
|
||||
contexts = {
|
||||
input = elem("target:build-tests", [cargo_profile, rust_toolchain, rust_target, feat_set, sys_name, sys_version, sys_target])
|
||||
}
|
||||
args = {
|
||||
cargo_cmd = (cargo_profile == "bench"? "bench": "test")
|
||||
cargo_args = (cargo_profile == "bench"?
|
||||
"--no-fail-fast --lib": "--no-fail-fast --lib --bins"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
target "docs" {
|
||||
name = elem("docs", [cargo_profile, rust_toolchain, rust_target, feat_set, sys_name, sys_version, sys_target])
|
||||
tags = [
|
||||
elem_tag("docs", [cargo_profile, rust_toolchain, rust_target, feat_set, sys_name, sys_version, sys_target], "latest"),
|
||||
]
|
||||
target = "cargo"
|
||||
matrix = cargo_rust_feat_sys
|
||||
inherits = [
|
||||
elem("build-tests", [cargo_profile, rust_toolchain, rust_target, feat_set, sys_name, sys_version, sys_target]),
|
||||
]
|
||||
contexts = {
|
||||
input = elem("target:build-tests", [cargo_profile, rust_toolchain, rust_target, feat_set, sys_name, sys_version, sys_target])
|
||||
}
|
||||
args = {
|
||||
cargo_cmd = "test"
|
||||
cargo_args = "--doc --no-fail-fast"
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Installation
|
||||
#
|
||||
@@ -678,6 +840,7 @@ target "install" {
|
||||
|
||||
group "pkg" {
|
||||
targets = [
|
||||
"nix",
|
||||
"deb",
|
||||
"rpm",
|
||||
"deb-install",
|
||||
@@ -789,47 +952,36 @@ target "build-deb" {
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Unit tests
|
||||
#
|
||||
|
||||
target "unit" {
|
||||
name = elem("unit", [cargo_profile, rust_toolchain, rust_target, feat_set, sys_name, sys_version, sys_target])
|
||||
target "nix" {
|
||||
name = elem("nix", [cargo_profile, rust_toolchain, rust_target, feat_set, sys_name, sys_version, sys_target])
|
||||
tags = [
|
||||
elem_tag("unit", [cargo_profile, rust_toolchain, rust_target, feat_set, sys_name, sys_version, sys_target], "latest"),
|
||||
elem_tag("nix", [cargo_profile, rust_toolchain, rust_target, feat_set, sys_name, sys_version, sys_target], "latest"),
|
||||
]
|
||||
target = "cargo"
|
||||
output = ["type=docker,compression=zstd,mode=min,compression-level=${zstd_image_compress_level}"]
|
||||
target = "nix-pkg"
|
||||
matrix = cargo_rust_feat_sys
|
||||
inherits = [
|
||||
elem("build-tests", [cargo_profile, rust_toolchain, rust_target, feat_set, sys_name, sys_version, sys_target]),
|
||||
elem("build-nix", [sys_name, sys_version, sys_target]),
|
||||
]
|
||||
contexts = {
|
||||
input = elem("target:build-tests", [cargo_profile, rust_toolchain, rust_target, feat_set, sys_name, sys_version, sys_target])
|
||||
}
|
||||
args = {
|
||||
cargo_cmd = (cargo_profile == "bench"? "bench": "test")
|
||||
cargo_args = (rust_toolchain == "nightly"?
|
||||
"--no-fail-fast --all-targets": "--no-fail-fast --bins --tests"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
target "docs" {
|
||||
name = elem("docs", [cargo_profile, rust_toolchain, rust_target, feat_set, sys_name, sys_version, sys_target])
|
||||
target "build-nix" {
|
||||
name = elem("build-nix", [sys_name, sys_version, sys_target])
|
||||
tags = [
|
||||
elem_tag("docs", [cargo_profile, rust_toolchain, rust_target, feat_set, sys_name, sys_version, sys_target], "latest"),
|
||||
elem_tag("build-nix", [sys_name, sys_version, sys_target], "latest"),
|
||||
]
|
||||
target = "cargo"
|
||||
matrix = cargo_rust_feat_sys
|
||||
output = ["type=cacheonly,compression=zstd,mode=min,compression-level=${cache_compress_level}"]
|
||||
cache_to = ["type=local,compression=zstd,mode=max,compression-level=${cache_compress_level}"]
|
||||
dockerfile = "${docker_dir}/Dockerfile.nix"
|
||||
target = "build-nix"
|
||||
matrix = sys
|
||||
inherits = [
|
||||
elem("build-tests", [cargo_profile, rust_toolchain, rust_target, feat_set, sys_name, sys_version, sys_target]),
|
||||
elem("builder", [sys_name, sys_version, sys_target]),
|
||||
elem("source", [sys_name, sys_version, sys_target]),
|
||||
]
|
||||
contexts = {
|
||||
input = elem("target:build-tests", [cargo_profile, rust_toolchain, rust_target, feat_set, sys_name, sys_version, sys_target])
|
||||
}
|
||||
args = {
|
||||
cargo_cmd = "test"
|
||||
cargo_args = "--doc --no-fail-fast"
|
||||
input = elem("target:builder", [sys_name, sys_version, sys_target]),
|
||||
source = elem("target:source", [sys_name, sys_version, sys_target]),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -922,7 +1074,7 @@ target "build-tests" {
|
||||
}
|
||||
args = {
|
||||
cargo_cmd = (cargo_profile == "bench"? "bench": "test")
|
||||
cargo_args = "--no-run"
|
||||
cargo_args = (cargo_profile == "bench"? "--no-run --benches": "--no-run --tests")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1210,8 +1362,8 @@ target "deps-base" {
|
||||
|
||||
CARGO_PROFILE_TEST_DEBUG = "false"
|
||||
CARGO_PROFILE_TEST_INCREMENTAL = "false"
|
||||
CARGO_PROFILE_BENCH_DEBUG = "limited"
|
||||
CARGO_PROFILE_BENCH_LTO = "false"
|
||||
CARGO_PROFILE_BENCH_DEBUG = "false"
|
||||
CARGO_PROFILE_BENCH_LTO = "thin"
|
||||
CARGO_PROFILE_RELEASE_LTO = "thin"
|
||||
CARGO_PROFILE_RELEASE_DEBUGINFO_DEBUG = "limited"
|
||||
CARGO_PROFILE_RELEASE_DEBUGINFO_LTO = "off"
|
||||
@@ -1238,7 +1390,7 @@ target "deps-base" {
|
||||
"-C link-arg=-l:libgcc.a": "",
|
||||
]):
|
||||
|
||||
cargo_profile == "release" && rust_toolchain == "nightly"?
|
||||
(cargo_profile == "release" || cargo_profile == "bench") && rust_toolchain == "nightly"?
|
||||
join(" ", [
|
||||
join(" ", rustflags),
|
||||
join(" ", nightly_rustflags),
|
||||
@@ -1260,7 +1412,7 @@ target "deps-base" {
|
||||
"-C link-arg=-l:libgcc.a": "",
|
||||
]):
|
||||
|
||||
cargo_profile == "release" || cargo_profile == "release-debuginfo"?
|
||||
cargo_profile == "release" || cargo_profile == "release-debuginfo" || cargo_profile == "bench"?
|
||||
join(" ", [
|
||||
join(" ", rustflags),
|
||||
join(" ", static_rustflags),
|
||||
@@ -1523,11 +1675,12 @@ rustup_components = [
|
||||
]
|
||||
|
||||
cargo_installs = [
|
||||
"cargo-chef",
|
||||
"cargo-audit",
|
||||
"cargo-deb",
|
||||
#"cargo-arch",
|
||||
"cargo-chef",
|
||||
"cargo-deb",
|
||||
"cargo-generate-rpm",
|
||||
"cargo-valgrind",
|
||||
#"lychee",
|
||||
"mdbook",
|
||||
"typos-cli",
|
||||
@@ -1611,13 +1764,20 @@ kitchen_packages = [
|
||||
"clang",
|
||||
"cmake",
|
||||
"curl",
|
||||
"gawk",
|
||||
"git",
|
||||
"golang-go",
|
||||
"gzip",
|
||||
"jq",
|
||||
"libc6-dev",
|
||||
"libssl-dev",
|
||||
"libsqlite3-dev",
|
||||
"make",
|
||||
"nix-bin",
|
||||
"openssl",
|
||||
"pkg-config",
|
||||
"pkgconf",
|
||||
"valgrind",
|
||||
"xz-utils",
|
||||
]
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ extract_output() {
|
||||
}
|
||||
|
||||
result_src="$cid:/usr/src/complement/new_results.jsonl"
|
||||
result_dst="tests/test_results/complement/test_results.jsonl"
|
||||
result_dst="tests/complement/results.jsonl"
|
||||
extract_results() {
|
||||
docker cp "$result_src" "$result_dst"
|
||||
}
|
||||
|
||||
+17
-18
@@ -12,24 +12,23 @@ ### Static prebuilt binary
|
||||
You may simply download the binary that fits your machine architecture (x86_64
|
||||
or aarch64). Run `uname -m` to see what you need.
|
||||
|
||||
Prebuilt fully static musl binaries can be downloaded from the latest tagged
|
||||
Prebuilt fully static binaries can be downloaded from the latest tagged
|
||||
release [here](https://github.com/matrix-construct/tuwunel/releases/latest) or
|
||||
`main` CI branch workflow artifact output. These also include Debian/Ubuntu
|
||||
packages.
|
||||
`main` CI branch workflow artifact output. These also include `.deb` packages
|
||||
for Debian or Ubuntu and `.rpm` packages for Red Hat or Fedora.
|
||||
|
||||
These can be curl'd directly from. `ci-bins` are CI workflow binaries by commit
|
||||
hash/revision, and `releases` are tagged releases. Sort by descending last
|
||||
modified for the latest.
|
||||
For the **best** performance; if using an `x86_64` CPU made in the last ~10 years,
|
||||
we recommend using the `-v3-` optimised packages. See below for a command to check
|
||||
what your system supports. If the server refuses to start or exits with an "Illegal
|
||||
Instruction" error you will need `-v2-` or `-v1-` packages instead. The database
|
||||
backend, RocksDB, benefits from `-v2-` or greater as it features performance
|
||||
critical hardware accelerated CRC32 hashing/checksumming.
|
||||
|
||||
These binaries have jemalloc and io_uring statically linked and included with
|
||||
them, so no additional dynamic dependencies need to be installed.
|
||||
|
||||
For the **best** performance; if using an `x86_64` CPU made in the last ~15 years,
|
||||
we recommend using the `-haswell-` optimised binaries. This sets
|
||||
`-march=haswell` which is the most compatible and highest performance with
|
||||
optimised binaries. The database backend, RocksDB, most benefits from this as it
|
||||
will then use hardware accelerated CRC32 hashing/checksumming which is critical
|
||||
for performance.
|
||||
Linux users can run this script to display which optimization levels they may
|
||||
choose:
|
||||
```
|
||||
cat /proc/cpuinfo | grep -Po '(avx|sse)[235]' | sort -u | sed 's/avx5/v4/;s/avx2/v3/;s/sse3/v2/;s/sse2/v1/' | sort
|
||||
```
|
||||
|
||||
### Compiling
|
||||
|
||||
@@ -167,7 +166,7 @@ ### Caddy
|
||||
|
||||
### Other Reverse Proxies
|
||||
|
||||
As we would prefer our users to use Caddy, we will not provide configuration files for other proxys.
|
||||
_Specific contributions for other proxies are welcome!_
|
||||
|
||||
You will need to reverse proxy everything under following routes:
|
||||
- `/_matrix/` - core Matrix C-S and S-S APIs
|
||||
@@ -187,8 +186,8 @@ ### Other Reverse Proxies
|
||||
- [`/.well-known/matrix/support`](https://spec.matrix.org/latest/client-server-api/#getwell-knownmatrixsupport)
|
||||
|
||||
Examples of delegation:
|
||||
- <https://puppygock.gay/.well-known/matrix/server>
|
||||
- <https://puppygock.gay/.well-known/matrix/client>
|
||||
- <https://matrix.org/.well-known/matrix/server>
|
||||
- <https://matrix.org/.well-known/matrix/client>
|
||||
|
||||
For Apache and Nginx there are many examples available online.
|
||||
|
||||
|
||||
Generated
+88
-132
@@ -10,11 +10,11 @@
|
||||
"nixpkgs-stable": "nixpkgs-stable"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1748532342,
|
||||
"narHash": "sha256-CvaKOUq8G10sghKpZhEB2UYjJoWhEkrDFggDgi7piUI=",
|
||||
"lastModified": 1758711588,
|
||||
"narHash": "sha256-0nZlCCDC5PfndsQJXXtcyrtrfW49I3KadGMDlutzaGU=",
|
||||
"owner": "zhaofengli",
|
||||
"repo": "attic",
|
||||
"rev": "ce9373715fe3fac7a174a65a7e6d6baeba8cb4f9",
|
||||
"rev": "12cbeca141f46e1ade76728bce8adc447f2166c6",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -29,14 +29,14 @@
|
||||
"devenv": "devenv",
|
||||
"flake-compat": "flake-compat_2",
|
||||
"git-hooks": "git-hooks",
|
||||
"nixpkgs": "nixpkgs_4"
|
||||
"nixpkgs": "nixpkgs_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1748883665,
|
||||
"narHash": "sha256-R0W7uAg+BLoHjMRMQ8+oiSbTq8nkGz5RDpQ+ZfxxP3A=",
|
||||
"lastModified": 1756385612,
|
||||
"narHash": "sha256-+NU5MMhuPHHRyvZZWNFG7zt+leRSPsJu1MwhOUzkPUk=",
|
||||
"owner": "cachix",
|
||||
"repo": "cachix",
|
||||
"rev": "f707778d902af4d62d8dd92c269f8e70de09acbe",
|
||||
"rev": "dc24688cd67518c3711d511fa369c0f5a131063a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -58,16 +58,21 @@
|
||||
],
|
||||
"git-hooks": [
|
||||
"cachix",
|
||||
"devenv"
|
||||
"devenv",
|
||||
"git-hooks"
|
||||
],
|
||||
"nixpkgs": "nixpkgs_2"
|
||||
"nixpkgs": [
|
||||
"cachix",
|
||||
"devenv",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1744206633,
|
||||
"narHash": "sha256-pb5aYkE8FOoa4n123slgHiOf1UbNSnKe5pEZC+xXD5g=",
|
||||
"lastModified": 1748883665,
|
||||
"narHash": "sha256-R0W7uAg+BLoHjMRMQ8+oiSbTq8nkGz5RDpQ+ZfxxP3A=",
|
||||
"owner": "cachix",
|
||||
"repo": "cachix",
|
||||
"rev": "8a60090640b96f9df95d1ab99e5763a586be1404",
|
||||
"rev": "f707778d902af4d62d8dd92c269f8e70de09acbe",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -80,11 +85,11 @@
|
||||
"complement": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1741891349,
|
||||
"narHash": "sha256-YvrzOWcX7DH1drp5SGa+E/fc7wN3hqFtPbqPjZpOu1Q=",
|
||||
"lastModified": 1756358878,
|
||||
"narHash": "sha256-H/4TkbFcGsMRDZ7gNaXZ7NxeO3b1YgkVt9zPb+3ATSg=",
|
||||
"owner": "matrix-construct",
|
||||
"repo": "complement",
|
||||
"rev": "e587b3df569cba411aeac7c20b6366d03c143745",
|
||||
"rev": "403840348f6bcc9cc8ed1671dc2f638c2b1ce4ac",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -95,18 +100,12 @@
|
||||
}
|
||||
},
|
||||
"crane": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"attic",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1722960479,
|
||||
"narHash": "sha256-NhCkJJQhD5GUib8zN9JrmYGMwt4lCRp6ZVNzIiYCl0Y=",
|
||||
"lastModified": 1751562746,
|
||||
"narHash": "sha256-smpugNIkmDeicNz301Ll1bD7nFOty97T79m4GUMUczA=",
|
||||
"owner": "ipetkov",
|
||||
"repo": "crane",
|
||||
"rev": "4c6c77920b8d44cd6660c1621dea6b3fc4b4c4f4",
|
||||
"rev": "aed2020fd3dc26e1e857d4107a5a67a33ab6c1fd",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -117,11 +116,11 @@
|
||||
},
|
||||
"crane_2": {
|
||||
"locked": {
|
||||
"lastModified": 1748970125,
|
||||
"narHash": "sha256-UDyigbDGv8fvs9aS95yzFfOKkEjx1LO3PL3DsKopohA=",
|
||||
"lastModified": 1758758545,
|
||||
"narHash": "sha256-NU5WaEdfwF6i8faJ2Yh+jcK9vVFrofLcwlD/mP65JrI=",
|
||||
"owner": "ipetkov",
|
||||
"repo": "crane",
|
||||
"rev": "323b5746d89e04b22554b061522dfce9e4c49b18",
|
||||
"rev": "95d528a5f54eaba0d12102249ce42f4d01f4e364",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -149,11 +148,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1748273445,
|
||||
"narHash": "sha256-5V0dzpNgQM0CHDsMzh+ludYeu1S+Y+IMjbaskSSdFh0=",
|
||||
"lastModified": 1754404745,
|
||||
"narHash": "sha256-BdbW/iTImczgcuATgQIa9sPGuYIBxVq2xqcvICsa2AQ=",
|
||||
"owner": "cachix",
|
||||
"repo": "devenv",
|
||||
"rev": "668a50d8b7bdb19a0131f53c9f6c25c9071e1ffb",
|
||||
"rev": "6563b21105168f90394dfaf58284b078af2d7275",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -170,11 +169,11 @@
|
||||
"rust-analyzer-src": "rust-analyzer-src"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1749883145,
|
||||
"narHash": "sha256-RlcGw3vAnbI3cfZn8aFaovNUd7312VZh+/FDWkqdA7E=",
|
||||
"lastModified": 1759251341,
|
||||
"narHash": "sha256-0vt4IQnTLyAhaeom3h9GOCpO2+av+wK4zP7O8BSKr6I=",
|
||||
"owner": "nix-community",
|
||||
"repo": "fenix",
|
||||
"rev": "a804172f150bcf81262655324e583bb0cd0f28dd",
|
||||
"rev": "af8a7505a1c62fab493157ea380a05c094bb63af",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -187,11 +186,11 @@
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1696426674,
|
||||
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
|
||||
"lastModified": 1747046372,
|
||||
"narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
|
||||
"rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -241,11 +240,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1722555600,
|
||||
"narHash": "sha256-XOQkdLafnb/p9ij77byFQjDf5m5QYl9b2REiVClC+x4=",
|
||||
"lastModified": 1751413152,
|
||||
"narHash": "sha256-Tyw1RjYEsp5scoigs1384gIg6e0GoBVjms4aXFfRssQ=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "8471fe90ad337a8074e957b69ca4d0089218391d",
|
||||
"rev": "77826244401ea9de6e3bac47c2db46005e1f30b5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -264,11 +263,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1712014858,
|
||||
"narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=",
|
||||
"lastModified": 1733312601,
|
||||
"narHash": "sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "9126214d0a59633752a136528f5f3b9aa8565b7d",
|
||||
"rev": "205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -309,11 +308,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1747372754,
|
||||
"narHash": "sha256-2Y53NGIX2vxfie1rOW0Qb86vjRZ7ngizoo+bnXU9D9k=",
|
||||
"lastModified": 1750779888,
|
||||
"narHash": "sha256-wibppH3g/E2lxU43ZQHC5yA/7kIKLGxVEnsnVK1BtRg=",
|
||||
"owner": "cachix",
|
||||
"repo": "git-hooks.nix",
|
||||
"rev": "80479b6ec16fefd9c1db3ea13aeb038c60530f46",
|
||||
"rev": "16ec914f6fb6f599ce988427d9d94efddf25fe6d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -344,30 +343,14 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"libgit2": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1697646580,
|
||||
"narHash": "sha256-oX4Z3S9WtJlwvj0uH9HlYcWv+x1hqp8mhXl7HsLu2f0=",
|
||||
"owner": "libgit2",
|
||||
"repo": "libgit2",
|
||||
"rev": "45fd9ed7ae1a9b74b957ef4f337bc3c8b3df01b5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "libgit2",
|
||||
"repo": "libgit2",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"liburing": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1749816983,
|
||||
"narHash": "sha256-p5hXfDe53Y4MVwL2+wKZYpy4OPGvqFFnOEvkMsFAO6c=",
|
||||
"lastModified": 1759268303,
|
||||
"narHash": "sha256-nT3JWey0P2lPWlnwN57XHmuzkeW8Mo795+Ahf8S5TMI=",
|
||||
"owner": "axboe",
|
||||
"repo": "liburing",
|
||||
"rev": "ad83d3ab64894c16eaf21ef869656a5bddb93ca4",
|
||||
"rev": "1671cb952a8f680214b04ae9546d4f649f1b367b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -381,11 +364,20 @@
|
||||
"inputs": {
|
||||
"flake-compat": [
|
||||
"cachix",
|
||||
"devenv"
|
||||
"devenv",
|
||||
"flake-compat"
|
||||
],
|
||||
"flake-parts": "flake-parts_2",
|
||||
"libgit2": "libgit2",
|
||||
"nixpkgs": "nixpkgs_3",
|
||||
"git-hooks-nix": [
|
||||
"cachix",
|
||||
"devenv",
|
||||
"git-hooks"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"cachix",
|
||||
"devenv",
|
||||
"nixpkgs"
|
||||
],
|
||||
"nixpkgs-23-11": [
|
||||
"cachix",
|
||||
"devenv"
|
||||
@@ -393,34 +385,30 @@
|
||||
"nixpkgs-regression": [
|
||||
"cachix",
|
||||
"devenv"
|
||||
],
|
||||
"pre-commit-hooks": [
|
||||
"cachix",
|
||||
"devenv"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1745930071,
|
||||
"narHash": "sha256-bYyjarS3qSNqxfgc89IoVz8cAFDkF9yPE63EJr+h50s=",
|
||||
"owner": "domenkozar",
|
||||
"lastModified": 1752773918,
|
||||
"narHash": "sha256-dOi/M6yNeuJlj88exI+7k154z+hAhFcuB8tZktiW7rg=",
|
||||
"owner": "cachix",
|
||||
"repo": "nix",
|
||||
"rev": "b455edf3505f1bf0172b39a735caef94687d0d9c",
|
||||
"rev": "031c3cf42d2e9391eee373507d8c12e0f9606779",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "domenkozar",
|
||||
"ref": "devenv-2.24",
|
||||
"owner": "cachix",
|
||||
"ref": "devenv-2.30",
|
||||
"repo": "nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix-filter": {
|
||||
"locked": {
|
||||
"lastModified": 1731533336,
|
||||
"narHash": "sha256-oRam5PS1vcrr5UPgALW0eo1m/5/pls27Z/pabHNy2Ms=",
|
||||
"lastModified": 1757882181,
|
||||
"narHash": "sha256-+cCxYIh2UNalTz364p+QYmWHs0P+6wDhiWR4jDIKQIU=",
|
||||
"owner": "numtide",
|
||||
"repo": "nix-filter",
|
||||
"rev": "f7653272fd234696ae94229839a99b73c9ab7de0",
|
||||
"rev": "59c44d1909c72441144b93cf0f054be7fe764de5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -438,11 +426,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1729742964,
|
||||
"narHash": "sha256-B4mzTcQ0FZHdpeWcpDYPERtyjJd/NIuaQ9+BV1h+MpA=",
|
||||
"lastModified": 1737420293,
|
||||
"narHash": "sha256-F1G5ifvqTpJq7fdkT34e/Jy9VCyzd5XfJ9TO8fHhJWE=",
|
||||
"owner": "nix-community",
|
||||
"repo": "nix-github-actions",
|
||||
"rev": "e04df33f62cdcf93d73e9a04142464753a16db67",
|
||||
"rev": "f4158fa080ef4503c8f4c820967d946c2af31ec9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -453,11 +441,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1726042813,
|
||||
"narHash": "sha256-LnNKCCxnwgF+575y0pxUdlGZBO/ru1CtGHIqQVfvjlA=",
|
||||
"lastModified": 1751949589,
|
||||
"narHash": "sha256-mgFxAPLWw0Kq+C8P3dRrZrOYEQXOtKuYVlo9xvPntt8=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "159be5db480d1df880a0135ca0bfed84c2f88353",
|
||||
"rev": "9b008d60392981ad674e04016d25619281550a9d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -469,27 +457,27 @@
|
||||
},
|
||||
"nixpkgs-stable": {
|
||||
"locked": {
|
||||
"lastModified": 1724316499,
|
||||
"narHash": "sha256-Qb9MhKBUTCfWg/wqqaxt89Xfi6qTD3XpTzQ9eXi3JmE=",
|
||||
"lastModified": 1751741127,
|
||||
"narHash": "sha256-t75Shs76NgxjZSgvvZZ9qOmz5zuBE8buUaYD28BMTxg=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "797f7dc49e0bc7fab4b57c021cdf68f595e47841",
|
||||
"rev": "29e290002bfff26af1db6f64d070698019460302",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-24.05",
|
||||
"ref": "nixos-25.05",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1733212471,
|
||||
"narHash": "sha256-M1+uCoV5igihRfcUKrr1riygbe73/dzNnzPsmaLCmpo=",
|
||||
"lastModified": 1754214453,
|
||||
"narHash": "sha256-Q/I2xJn/j1wpkGhWkQnm20nShYnG7TI99foDBpXm1SY=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "55d15ad12a74eb7d4646254e13638ad0c4128776",
|
||||
"rev": "5b09dc45f24cf32316283e62aec81ffee3c3e376",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -501,43 +489,11 @@
|
||||
},
|
||||
"nixpkgs_3": {
|
||||
"locked": {
|
||||
"lastModified": 1717432640,
|
||||
"narHash": "sha256-+f9c4/ZX5MWDOuB1rKoWj+lBNm0z0rs4CK47HBLxy1o=",
|
||||
"lastModified": 1759070547,
|
||||
"narHash": "sha256-JVZl8NaVRYb0+381nl7LvPE+A774/dRpif01FKLrYFQ=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "88269ab3044128b7c2f4c7d68448b2fb50456870",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "release-24.05",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_4": {
|
||||
"locked": {
|
||||
"lastModified": 1748190013,
|
||||
"narHash": "sha256-R5HJFflOfsP5FBtk+zE8FpL8uqE7n62jqOsADvVshhE=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "62b852f6c6742134ade1abdd2a21685fd617a291",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_5": {
|
||||
"locked": {
|
||||
"lastModified": 1749871736,
|
||||
"narHash": "sha256-K9yBph93OLTNw02Q6e9CYFGrUhvEXnh45vrZqIRWfvQ=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "6afe187897bef7933475e6af374c893f4c84a293",
|
||||
"rev": "647e5c14cbd5067f44ac86b74f014962df460840",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -575,18 +531,18 @@
|
||||
"flake-utils": "flake-utils",
|
||||
"liburing": "liburing",
|
||||
"nix-filter": "nix-filter",
|
||||
"nixpkgs": "nixpkgs_5",
|
||||
"nixpkgs": "nixpkgs_3",
|
||||
"rocksdb": "rocksdb"
|
||||
}
|
||||
},
|
||||
"rust-analyzer-src": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1749829309,
|
||||
"narHash": "sha256-t6x6/PKg8Shnkd3htrxf3WMgycfRLRWvN9JHAmGWf+s=",
|
||||
"lastModified": 1759134797,
|
||||
"narHash": "sha256-YPi+jL3tx/yC5J5l7/OB7Lnlr9BMTzYnZtm7tRJzUNg=",
|
||||
"owner": "rust-lang",
|
||||
"repo": "rust-analyzer",
|
||||
"rev": "a497f4114ccf24978accb56190e60d1e1659e0c7",
|
||||
"rev": "062ac7a5451e8e92a32e22a60d86882d6a034f3f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -129,6 +129,7 @@ buildDepsOnlyEnv =
|
||||
|
||||
buildPackageEnv = {
|
||||
TUWUNEL_VERSION_EXTRA = inputs.self.shortRev or inputs.self.dirtyShortRev or "";
|
||||
TUWUNEL_DATABASE_PATH = "/var/tmp/tuwunel.db";
|
||||
} // buildDepsOnlyEnv // {
|
||||
# Only needed in static stdenv because these are transitive dependencies of rocksdb
|
||||
CARGO_BUILD_RUSTFLAGS = buildDepsOnlyEnv.CARGO_BUILD_RUSTFLAGS
|
||||
@@ -201,7 +202,9 @@ craneLib.buildPackage ( commonAttrs // {
|
||||
env = buildDepsOnlyEnv;
|
||||
});
|
||||
|
||||
doCheck = true;
|
||||
# Disabled due to integration test failing to find /etc/resolv.conf
|
||||
doCheck = false;
|
||||
doBenchmark = false;
|
||||
|
||||
cargoExtraArgs = "--no-default-features --locked "
|
||||
+ lib.optionalString
|
||||
|
||||
+1
-1
@@ -2,7 +2,6 @@
|
||||
Description=Tuwunel Matrix homeserver
|
||||
Wants=network-online.target
|
||||
After=network-online.target
|
||||
Alias=matrix-tuwunel.service
|
||||
Documentation=https://tuwunel.chat/
|
||||
|
||||
[Service]
|
||||
@@ -63,3 +62,4 @@ StartLimitBurst=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Alias=matrix-tuwunel.service
|
||||
|
||||
@@ -11,6 +11,7 @@ version.workspace = true
|
||||
|
||||
[lib]
|
||||
path = "mod.rs"
|
||||
bench = false
|
||||
crate-type = [
|
||||
"rlib",
|
||||
# "dylib",
|
||||
|
||||
@@ -28,10 +28,7 @@
|
||||
},
|
||||
warn,
|
||||
};
|
||||
use tuwunel_service::rooms::{
|
||||
short::{ShortEventId, ShortRoomId},
|
||||
state_compressor::HashSetCompressStateEvent,
|
||||
};
|
||||
use tuwunel_service::rooms::{short::ShortRoomId, state_compressor::HashSetCompressStateEvent};
|
||||
|
||||
use crate::admin_command;
|
||||
|
||||
@@ -138,16 +135,8 @@ pub(super) async fn get_pdu(&self, event_id: OwnedEventId) -> Result {
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn get_short_pdu(
|
||||
&self,
|
||||
shortroomid: ShortRoomId,
|
||||
shorteventid: ShortEventId,
|
||||
) -> Result {
|
||||
let pdu_id: RawPduId = PduId {
|
||||
shortroomid,
|
||||
shorteventid: shorteventid.into(),
|
||||
}
|
||||
.into();
|
||||
pub(super) async fn get_short_pdu(&self, shortroomid: ShortRoomId, count: i64) -> Result {
|
||||
let pdu_id: RawPduId = PduId { shortroomid, count: count.into() }.into();
|
||||
|
||||
let pdu_json = self
|
||||
.services
|
||||
@@ -304,7 +293,7 @@ pub(super) async fn get_remote_pdu(
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn get_room_state(&self, room: OwnedRoomOrAliasId) -> Result {
|
||||
let room_id = self.services.alias.resolve(&room).await?;
|
||||
let room_id = self.services.alias.maybe_resolve(&room).await?;
|
||||
let room_state: Vec<Raw<AnyStateEvent>> = self
|
||||
.services
|
||||
.state_accessor
|
||||
@@ -918,7 +907,7 @@ pub(super) async fn database_files(&self, map: Option<String>, level: Option<i32
|
||||
let mut files: Vec<_> = self
|
||||
.services
|
||||
.db
|
||||
.db
|
||||
.engine
|
||||
.file_list()
|
||||
.collect::<Result<_>>()?;
|
||||
|
||||
@@ -1023,7 +1012,7 @@ pub(super) async fn resync_database(&self) -> Result {
|
||||
|
||||
self.services
|
||||
.db
|
||||
.db
|
||||
.engine
|
||||
.update()
|
||||
.map_err(|e| err!("Failed to update from primary: {e:?}"))
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
use clap::Subcommand;
|
||||
use ruma::{OwnedEventId, OwnedRoomId, OwnedRoomOrAliasId, OwnedServerName};
|
||||
use tuwunel_core::Result;
|
||||
use tuwunel_service::rooms::short::{ShortEventId, ShortRoomId};
|
||||
use tuwunel_service::rooms::short::ShortRoomId;
|
||||
|
||||
use self::tester::TesterCommand;
|
||||
use crate::admin_command_dispatch;
|
||||
@@ -43,8 +43,8 @@ pub(super) enum DebugCommand {
|
||||
/// Shortroomid integer
|
||||
shortroomid: ShortRoomId,
|
||||
|
||||
/// Shorteventid integer
|
||||
shorteventid: ShortEventId,
|
||||
/// PduCount integer
|
||||
count: i64,
|
||||
},
|
||||
|
||||
/// - Attempts to retrieve a PDU from a remote server. Inserts it into our
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
mod room_timeline;
|
||||
mod sending;
|
||||
mod short;
|
||||
mod sync;
|
||||
mod users;
|
||||
|
||||
use clap::Subcommand;
|
||||
@@ -20,7 +21,7 @@
|
||||
presence::PresenceCommand, pusher::PusherCommand, raw::RawCommand, resolver::ResolverCommand,
|
||||
room_alias::RoomAliasCommand, room_state_cache::RoomStateCacheCommand,
|
||||
room_timeline::RoomTimelineCommand, sending::SendingCommand, short::ShortCommand,
|
||||
users::UsersCommand,
|
||||
sync::SyncCommand, users::UsersCommand,
|
||||
};
|
||||
use crate::admin_command_dispatch;
|
||||
|
||||
@@ -76,6 +77,10 @@ pub(super) enum QueryCommand {
|
||||
#[command(subcommand)]
|
||||
Short(ShortCommand),
|
||||
|
||||
/// - sync service
|
||||
#[command(subcommand)]
|
||||
Sync(SyncCommand),
|
||||
|
||||
/// - raw service
|
||||
#[command(subcommand)]
|
||||
Raw(RawCommand),
|
||||
|
||||
+14
-14
@@ -16,16 +16,16 @@
|
||||
|
||||
use crate::{admin_command, admin_command_dispatch};
|
||||
|
||||
#[admin_command_dispatch]
|
||||
#[admin_command_dispatch(handler_prefix = "raw")]
|
||||
#[derive(Debug, Subcommand)]
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
/// Query tables from database
|
||||
pub(crate) enum RawCommand {
|
||||
/// - List database maps
|
||||
RawMaps,
|
||||
Maps,
|
||||
|
||||
/// - Raw database query
|
||||
RawGet {
|
||||
Get {
|
||||
/// Map name
|
||||
map: String,
|
||||
|
||||
@@ -38,7 +38,7 @@ pub(crate) enum RawCommand {
|
||||
},
|
||||
|
||||
/// - Raw database delete (for string keys)
|
||||
RawDel {
|
||||
Del {
|
||||
/// Map name
|
||||
map: String,
|
||||
|
||||
@@ -47,7 +47,7 @@ pub(crate) enum RawCommand {
|
||||
},
|
||||
|
||||
/// - Raw database keys iteration
|
||||
RawKeys {
|
||||
Keys {
|
||||
/// Map name
|
||||
map: String,
|
||||
|
||||
@@ -56,7 +56,7 @@ pub(crate) enum RawCommand {
|
||||
},
|
||||
|
||||
/// - Raw database key size breakdown
|
||||
RawKeysSizes {
|
||||
KeysSizes {
|
||||
/// Map name
|
||||
map: Option<String>,
|
||||
|
||||
@@ -65,7 +65,7 @@ pub(crate) enum RawCommand {
|
||||
},
|
||||
|
||||
/// - Raw database keys total bytes
|
||||
RawKeysTotal {
|
||||
KeysTotal {
|
||||
/// Map name
|
||||
map: Option<String>,
|
||||
|
||||
@@ -74,7 +74,7 @@ pub(crate) enum RawCommand {
|
||||
},
|
||||
|
||||
/// - Raw database values size breakdown
|
||||
RawValsSizes {
|
||||
ValsSizes {
|
||||
/// Map name
|
||||
map: Option<String>,
|
||||
|
||||
@@ -83,7 +83,7 @@ pub(crate) enum RawCommand {
|
||||
},
|
||||
|
||||
/// - Raw database values total bytes
|
||||
RawValsTotal {
|
||||
ValsTotal {
|
||||
/// Map name
|
||||
map: Option<String>,
|
||||
|
||||
@@ -92,7 +92,7 @@ pub(crate) enum RawCommand {
|
||||
},
|
||||
|
||||
/// - Raw database items iteration
|
||||
RawIter {
|
||||
Iter {
|
||||
/// Map name
|
||||
map: String,
|
||||
|
||||
@@ -101,7 +101,7 @@ pub(crate) enum RawCommand {
|
||||
},
|
||||
|
||||
/// - Raw database keys iteration
|
||||
RawKeysFrom {
|
||||
KeysFrom {
|
||||
/// Map name
|
||||
map: String,
|
||||
|
||||
@@ -114,7 +114,7 @@ pub(crate) enum RawCommand {
|
||||
},
|
||||
|
||||
/// - Raw database items iteration
|
||||
RawIterFrom {
|
||||
IterFrom {
|
||||
/// Map name
|
||||
map: String,
|
||||
|
||||
@@ -127,7 +127,7 @@ pub(crate) enum RawCommand {
|
||||
},
|
||||
|
||||
/// - Raw database record count
|
||||
RawCount {
|
||||
Count {
|
||||
/// Map name
|
||||
map: Option<String>,
|
||||
|
||||
@@ -165,7 +165,7 @@ pub(crate) enum RawCommand {
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn compact(
|
||||
pub(super) async fn raw_compact(
|
||||
&self,
|
||||
map: Option<Vec<String>>,
|
||||
start: Option<String>,
|
||||
|
||||
@@ -74,6 +74,10 @@ pub(crate) enum RoomStateCacheCommand {
|
||||
user_id: OwnedUserId,
|
||||
room_id: OwnedRoomId,
|
||||
},
|
||||
|
||||
UserMemberships {
|
||||
user_id: OwnedUserId,
|
||||
},
|
||||
}
|
||||
|
||||
pub(super) async fn process(subcommand: RoomStateCacheCommand, context: &Context<'_>) -> Result {
|
||||
@@ -282,7 +286,7 @@ pub(super) async fn process(subcommand: RoomStateCacheCommand, context: &Context
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results: Vec<_> = services
|
||||
.state_cache
|
||||
.rooms_invited(&user_id)
|
||||
.rooms_invited_state(&user_id)
|
||||
.collect()
|
||||
.await;
|
||||
let query_time = timer.elapsed();
|
||||
@@ -297,7 +301,7 @@ pub(super) async fn process(subcommand: RoomStateCacheCommand, context: &Context
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results: Vec<_> = services
|
||||
.state_cache
|
||||
.rooms_left(&user_id)
|
||||
.rooms_left_state(&user_id)
|
||||
.collect()
|
||||
.await;
|
||||
let query_time = timer.elapsed();
|
||||
@@ -316,6 +320,22 @@ pub(super) async fn process(subcommand: RoomStateCacheCommand, context: &Context
|
||||
.await;
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
context
|
||||
.write_str(&format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
))
|
||||
.await
|
||||
},
|
||||
| RoomStateCacheCommand::UserMemberships { user_id } => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = services
|
||||
.state_cache
|
||||
.all_user_memberships(&user_id)
|
||||
.map(|(membership, room_id)| (membership, room_id.to_owned()))
|
||||
.collect::<Vec<_>>()
|
||||
.await;
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
context
|
||||
.write_str(&format!(
|
||||
"Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```"
|
||||
|
||||
@@ -25,7 +25,11 @@ pub(crate) enum RoomTimelineCommand {
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn last(&self, room_id: OwnedRoomOrAliasId) -> Result {
|
||||
let room_id = self.services.alias.resolve(&room_id).await?;
|
||||
let room_id = self
|
||||
.services
|
||||
.alias
|
||||
.maybe_resolve(&room_id)
|
||||
.await?;
|
||||
|
||||
let result = self
|
||||
.services
|
||||
@@ -43,7 +47,11 @@ pub(super) async fn pdus(
|
||||
from: Option<String>,
|
||||
limit: Option<usize>,
|
||||
) -> Result {
|
||||
let room_id = self.services.alias.resolve(&room_id).await?;
|
||||
let room_id = self
|
||||
.services
|
||||
.alias
|
||||
.maybe_resolve(&room_id)
|
||||
.await?;
|
||||
|
||||
let from: Option<PduCount> = from.as_deref().map(str::parse).transpose()?;
|
||||
|
||||
|
||||
@@ -30,7 +30,11 @@ pub(super) async fn short_event_id(&self, event_id: OwnedEventId) -> Result {
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn short_room_id(&self, room_id: OwnedRoomOrAliasId) -> Result {
|
||||
let room_id = self.services.alias.resolve(&room_id).await?;
|
||||
let room_id = self
|
||||
.services
|
||||
.alias
|
||||
.maybe_resolve(&room_id)
|
||||
.await?;
|
||||
|
||||
let shortid = self
|
||||
.services
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
use clap::Subcommand;
|
||||
use ruma::{OwnedDeviceId, OwnedUserId};
|
||||
use tuwunel_core::Result;
|
||||
use tuwunel_service::sync::into_connection_key;
|
||||
|
||||
use crate::{admin_command, admin_command_dispatch};
|
||||
|
||||
#[admin_command_dispatch]
|
||||
#[derive(Debug, Subcommand)]
|
||||
/// Query sync service state
|
||||
pub(crate) enum SyncCommand {
|
||||
/// List sliding-sync connections.
|
||||
ListConnections,
|
||||
|
||||
/// Show details of sliding sync connection by ID.
|
||||
ShowConnection {
|
||||
user_id: OwnedUserId,
|
||||
device_id: OwnedDeviceId,
|
||||
conn_id: Option<String>,
|
||||
},
|
||||
|
||||
/// Drop connections for a user, device, or all.
|
||||
DropConnections {
|
||||
user_id: Option<OwnedUserId>,
|
||||
device_id: Option<OwnedDeviceId>,
|
||||
conn_id: Option<String>,
|
||||
},
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn list_connections(&self) -> Result {
|
||||
let connections = self.services.sync.list_loaded_connections().await;
|
||||
|
||||
for connection_key in connections {
|
||||
self.write_str(&format!("{connection_key:?}\n"))
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn show_connection(
|
||||
&self,
|
||||
user_id: OwnedUserId,
|
||||
device_id: OwnedDeviceId,
|
||||
conn_id: Option<String>,
|
||||
) -> Result {
|
||||
let key = into_connection_key(user_id, device_id, conn_id);
|
||||
let cache = self
|
||||
.services
|
||||
.sync
|
||||
.get_loaded_connection(&key)
|
||||
.await?;
|
||||
|
||||
let out;
|
||||
{
|
||||
let cached = cache.lock().await;
|
||||
out = format!("{cached:#?}");
|
||||
};
|
||||
|
||||
self.write_str(out.as_str()).await
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn drop_connections(
|
||||
&self,
|
||||
user_id: Option<OwnedUserId>,
|
||||
device_id: Option<OwnedDeviceId>,
|
||||
conn_id: Option<String>,
|
||||
) -> Result {
|
||||
self.services
|
||||
.sync
|
||||
.clear_connections(
|
||||
user_id.as_deref(),
|
||||
device_id.as_deref(),
|
||||
conn_id.map(Into::into).as_ref(),
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -48,7 +48,7 @@ pub(crate) enum RoomModerationCommand {
|
||||
async fn ban_room(&self, room: OwnedRoomOrAliasId) -> Result {
|
||||
debug!("Got room alias or ID: {}", room);
|
||||
|
||||
let admin_room_alias = &self.services.globals.admin_alias;
|
||||
let admin_room_alias = &self.services.admin.admin_alias;
|
||||
|
||||
if let Ok(admin_room_id) = self.services.admin.get_admin_room().await {
|
||||
if room.to_string().eq(&admin_room_id) || room.to_string().eq(admin_room_alias) {
|
||||
@@ -105,7 +105,7 @@ async fn ban_room(&self, room: OwnedRoomOrAliasId) -> Result {
|
||||
match self
|
||||
.services
|
||||
.alias
|
||||
.resolve_alias(room_alias, None)
|
||||
.resolve_alias(room_alias)
|
||||
.await
|
||||
{
|
||||
| Ok((room_id, servers)) => {
|
||||
@@ -209,7 +209,7 @@ async fn ban_list_of_rooms(&self) -> Result {
|
||||
.drain(1..self.body.len().saturating_sub(1))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let admin_room_alias = &self.services.globals.admin_alias;
|
||||
let admin_room_alias = &self.services.admin.admin_alias;
|
||||
|
||||
let mut room_ban_count: usize = 0;
|
||||
let mut room_ids: Vec<OwnedRoomId> = Vec::new();
|
||||
@@ -260,7 +260,7 @@ async fn ban_list_of_rooms(&self) -> Result {
|
||||
match self
|
||||
.services
|
||||
.alias
|
||||
.resolve_alias(room_alias, None)
|
||||
.resolve_alias(room_alias)
|
||||
.await
|
||||
{
|
||||
| Ok((room_id, servers)) => {
|
||||
@@ -423,7 +423,7 @@ async fn unban_room(&self, room: OwnedRoomOrAliasId) -> Result {
|
||||
match self
|
||||
.services
|
||||
.alias
|
||||
.resolve_alias(room_alias, None)
|
||||
.resolve_alias(room_alias)
|
||||
.await
|
||||
{
|
||||
| Ok((room_id, servers)) => {
|
||||
|
||||
@@ -67,7 +67,7 @@ pub(super) async fn list_features(&self, available: bool, enabled: bool, comma:
|
||||
#[admin_command]
|
||||
pub(super) async fn memory_usage(&self) -> Result {
|
||||
let services_usage = self.services.memory_usage().await?;
|
||||
let database_usage = self.services.db.db.memory_usage()?;
|
||||
let database_usage = self.services.db.engine.memory_usage()?;
|
||||
let allocator_usage = tuwunel_core::alloc::memory_usage()
|
||||
.map_or(String::new(), |s| format!("\nAllocator:\n{s}"));
|
||||
|
||||
@@ -88,7 +88,7 @@ pub(super) async fn clear_caches(&self) -> Result {
|
||||
pub(super) async fn list_backups(&self) -> Result {
|
||||
self.services
|
||||
.db
|
||||
.db
|
||||
.engine
|
||||
.backup_list()?
|
||||
.try_stream()
|
||||
.try_for_each(|result| write!(self, "{result}"))
|
||||
@@ -102,13 +102,13 @@ pub(super) async fn backup_database(&self) -> Result {
|
||||
.services
|
||||
.server
|
||||
.runtime()
|
||||
.spawn_blocking(move || match db.db.backup() {
|
||||
.spawn_blocking(move || match db.engine.backup() {
|
||||
| Ok(()) => "Done".to_owned(),
|
||||
| Err(e) => format!("Failed: {e}"),
|
||||
})
|
||||
.await?;
|
||||
|
||||
let count = self.services.db.db.backup_count()?;
|
||||
let count = self.services.db.engine.backup_count()?;
|
||||
self.write_str(&format!("{result}. Currently have {count} backups."))
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ pub(super) async fn create_user(&self, username: String, password: Option<String
|
||||
.is_empty()
|
||||
{
|
||||
for room in &self.services.server.config.auto_join_rooms {
|
||||
let Ok(room_id) = self.services.alias.resolve(room).await else {
|
||||
let Ok(room_id) = self.services.alias.maybe_resolve(room).await else {
|
||||
error!(
|
||||
%user_id,
|
||||
"Failed to resolve room alias to room ID when attempting to auto join {room}, skipping"
|
||||
@@ -408,7 +408,7 @@ pub(super) async fn force_join_list_of_local_users(
|
||||
let (room_id, servers) = self
|
||||
.services
|
||||
.alias
|
||||
.resolve_with_servers(&room_id, None)
|
||||
.maybe_resolve_with_servers(&room_id, None)
|
||||
.await?;
|
||||
|
||||
if !self
|
||||
@@ -532,7 +532,7 @@ pub(super) async fn force_join_all_local_users(
|
||||
let (room_id, servers) = self
|
||||
.services
|
||||
.alias
|
||||
.resolve_with_servers(&room_id, None)
|
||||
.maybe_resolve_with_servers(&room_id, None)
|
||||
.await?;
|
||||
|
||||
if !self
|
||||
@@ -617,7 +617,7 @@ pub(super) async fn force_join_room(
|
||||
let (room_id, servers) = self
|
||||
.services
|
||||
.alias
|
||||
.resolve_with_servers(&room_id, None)
|
||||
.maybe_resolve_with_servers(&room_id, None)
|
||||
.await?;
|
||||
|
||||
assert!(
|
||||
@@ -645,7 +645,11 @@ pub(super) async fn force_leave_room(
|
||||
room_id: OwnedRoomOrAliasId,
|
||||
) -> Result {
|
||||
let user_id = parse_local_user_id(self.services, &user_id)?;
|
||||
let room_id = self.services.alias.resolve(&room_id).await?;
|
||||
let room_id = self
|
||||
.services
|
||||
.alias
|
||||
.maybe_resolve(&room_id)
|
||||
.await?;
|
||||
|
||||
assert!(
|
||||
self.services.globals.user_is_local(&user_id),
|
||||
@@ -678,7 +682,11 @@ pub(super) async fn force_leave_room(
|
||||
#[admin_command]
|
||||
pub(super) async fn force_demote(&self, user_id: String, room_id: OwnedRoomOrAliasId) -> Result {
|
||||
let user_id = parse_local_user_id(self.services, &user_id)?;
|
||||
let room_id = self.services.alias.resolve(&room_id).await?;
|
||||
let room_id = self
|
||||
.services
|
||||
.alias
|
||||
.maybe_resolve(&room_id)
|
||||
.await?;
|
||||
|
||||
assert!(
|
||||
self.services.globals.user_is_local(&user_id),
|
||||
@@ -744,7 +752,11 @@ pub(super) async fn force_promote(
|
||||
room_id: OwnedRoomOrAliasId,
|
||||
) -> Result {
|
||||
let target_id = parse_user_id(self.services, &target_id)?;
|
||||
let room_id = self.services.alias.resolve(&room_id).await?;
|
||||
let room_id = self
|
||||
.services
|
||||
.alias
|
||||
.maybe_resolve(&room_id)
|
||||
.await?;
|
||||
|
||||
let state_lock = self.services.state.mutex.lock(&room_id).await;
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ version.workspace = true
|
||||
|
||||
[lib]
|
||||
path = "mod.rs"
|
||||
bench = false
|
||||
crate-type = [
|
||||
"rlib",
|
||||
# "dylib",
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
OwnedServerName, RoomAliasId, RoomId,
|
||||
api::client::alias::{create_alias, delete_alias, get_alias},
|
||||
};
|
||||
use tuwunel_core::{Err, Result, debug};
|
||||
use tuwunel_core::{Err, Result, debug, err};
|
||||
use tuwunel_service::Services;
|
||||
|
||||
use crate::Ruma;
|
||||
@@ -26,8 +26,8 @@ pub(crate) async fn create_alias_route(
|
||||
// this isn't apart of alias_checks or delete alias route because we should
|
||||
// allow removing forbidden room aliases
|
||||
if services
|
||||
.globals
|
||||
.forbidden_alias_names()
|
||||
.config
|
||||
.forbidden_alias_names
|
||||
.is_match(body.room_alias.alias())
|
||||
{
|
||||
return Err!(Request(Forbidden("Room alias is forbidden.")));
|
||||
@@ -83,13 +83,11 @@ pub(crate) async fn get_alias_route(
|
||||
) -> Result<get_alias::v3::Response> {
|
||||
let room_alias = body.body.room_alias;
|
||||
|
||||
let Ok((room_id, servers)) = services
|
||||
let (room_id, servers) = services
|
||||
.alias
|
||||
.resolve_alias(&room_alias, None)
|
||||
.resolve_alias(&room_alias)
|
||||
.await
|
||||
else {
|
||||
return Err!(Request(NotFound("Room with alias not found.")));
|
||||
};
|
||||
.map_err(|_| err!(Request(NotFound("Room with alias not found."))))?;
|
||||
|
||||
let servers = room_available_servers(&services, &room_id, &room_alias, servers).await;
|
||||
debug!(?room_alias, ?room_id, "available servers: {servers:?}");
|
||||
|
||||
@@ -134,7 +134,7 @@ pub(crate) async fn get_context_route(
|
||||
.map_or_else(|| body.event_id.as_ref(), |pdu| pdu.event_id.as_ref());
|
||||
|
||||
let state_ids = services
|
||||
.state_accessor
|
||||
.state
|
||||
.pdu_shortstatehash(state_at)
|
||||
.or_else(|_| services.state.get_room_shortstatehash(room_id))
|
||||
.map_ok(|shortstatehash| {
|
||||
|
||||
+39
-61
@@ -6,14 +6,10 @@
|
||||
CanonicalJsonObject, CanonicalJsonValue, OneTimeKeyAlgorithm, OwnedDeviceId, OwnedUserId,
|
||||
UserId,
|
||||
api::{
|
||||
client::{
|
||||
error::ErrorKind,
|
||||
keys::{
|
||||
claim_keys, get_key_changes, get_keys, upload_keys,
|
||||
upload_signatures::{self},
|
||||
upload_signing_keys,
|
||||
},
|
||||
uiaa::{AuthFlow, AuthType, UiaaInfo},
|
||||
client::keys::{
|
||||
claim_keys, get_key_changes, get_keys, upload_keys,
|
||||
upload_signatures::{self},
|
||||
upload_signing_keys,
|
||||
},
|
||||
federation,
|
||||
},
|
||||
@@ -21,11 +17,12 @@
|
||||
serde::Raw,
|
||||
};
|
||||
use serde_json::json;
|
||||
use tuwunel_core::{Err, Error, Result, debug, debug_warn, err, result::NotFound, utils};
|
||||
use tuwunel_core::{
|
||||
Err, Result, debug, debug_error, debug_warn, err, result::NotFound, utils::json,
|
||||
};
|
||||
use tuwunel_service::{Services, users::parse_master_key};
|
||||
|
||||
use super::SESSION_ID_LENGTH;
|
||||
use crate::Ruma;
|
||||
use crate::{Ruma, router::auth_uiaa};
|
||||
|
||||
/// # `POST /_matrix/client/r0/keys/upload`
|
||||
///
|
||||
@@ -162,23 +159,17 @@ pub(crate) async fn upload_signing_keys_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<upload_signing_keys::v3::Request>,
|
||||
) -> Result<upload_signing_keys::v3::Response> {
|
||||
let (sender_user, sender_device) = body.sender();
|
||||
|
||||
// UIAA
|
||||
let mut uiaainfo = UiaaInfo {
|
||||
flows: vec![AuthFlow { stages: vec![AuthType::Password] }],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
// Access token is required for this endpoint regardless of conditional UIAA so
|
||||
// we'll always have a sender_user.
|
||||
match check_for_new_keys(
|
||||
services,
|
||||
sender_user,
|
||||
body.sender_user(),
|
||||
body.self_signing_key.as_ref(),
|
||||
body.user_signing_key.as_ref(),
|
||||
body.master_key.as_ref(),
|
||||
)
|
||||
.await
|
||||
.inspect_err(|e| debug!(?e))
|
||||
.inspect_err(|e| debug_error!(?e))
|
||||
{
|
||||
| Ok(exists) => {
|
||||
if let Some(result) = exists {
|
||||
@@ -186,45 +177,25 @@ pub(crate) async fn upload_signing_keys_route(
|
||||
// (lost connection for example)
|
||||
return Ok(result);
|
||||
}
|
||||
debug!(
|
||||
"Skipping UIA in accordance with MSC3967, the user didn't have any existing keys"
|
||||
);
|
||||
|
||||
// Some of the keys weren't found, so we let them upload
|
||||
debug!("Skipping UIA in accordance with MSC3967, user had no existing keys");
|
||||
},
|
||||
| _ => {
|
||||
match &body.auth {
|
||||
| Some(auth) => {
|
||||
let (worked, uiaainfo) = services
|
||||
.uiaa
|
||||
.try_auth(sender_user, sender_device, auth, &uiaainfo)
|
||||
.await?;
|
||||
|
||||
if !worked {
|
||||
return Err(Error::Uiaa(uiaainfo));
|
||||
}
|
||||
// Success!
|
||||
},
|
||||
| _ => match body.json_body.as_ref() {
|
||||
| Some(json) => {
|
||||
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
||||
services
|
||||
.uiaa
|
||||
.create(sender_user, sender_device, &uiaainfo, json);
|
||||
|
||||
return Err(Error::Uiaa(uiaainfo));
|
||||
},
|
||||
| _ => {
|
||||
return Err(Error::BadRequest(ErrorKind::NotJson, "Not json."));
|
||||
},
|
||||
},
|
||||
}
|
||||
let authed_user = auth_uiaa(&services, &body).await?;
|
||||
assert_eq!(
|
||||
body.sender_user(),
|
||||
authed_user,
|
||||
"Expected UIAA of {0} and not {authed_user}",
|
||||
body.sender_user(),
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
services
|
||||
.users
|
||||
.add_cross_signing_keys(
|
||||
sender_user,
|
||||
body.sender_user(),
|
||||
&body.master_key,
|
||||
&body.self_signing_key,
|
||||
&body.user_signing_key,
|
||||
@@ -250,6 +221,7 @@ async fn check_for_new_keys(
|
||||
.users
|
||||
.get_master_key(None, user_id, &|_| true)
|
||||
.await;
|
||||
|
||||
if result.is_not_found() {
|
||||
empty = true;
|
||||
} else {
|
||||
@@ -262,6 +234,7 @@ async fn check_for_new_keys(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(user_signing_key) = user_signing_key {
|
||||
let key = services.users.get_user_signing_key(user_id).await;
|
||||
if key.is_not_found() && !empty {
|
||||
@@ -269,6 +242,7 @@ async fn check_for_new_keys(
|
||||
"Tried to update an existing user signing key, UIA required"
|
||||
)));
|
||||
}
|
||||
|
||||
if !key.is_not_found() {
|
||||
let existing_signing_key = key?.deserialize()?;
|
||||
if existing_signing_key != user_signing_key.deserialize()? {
|
||||
@@ -278,17 +252,20 @@ async fn check_for_new_keys(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(self_signing_key) = self_signing_key {
|
||||
let key = services
|
||||
.users
|
||||
.get_self_signing_key(None, user_id, &|_| true)
|
||||
.await;
|
||||
|
||||
if key.is_not_found() && !empty {
|
||||
debug!(?key);
|
||||
debug_error!(?key);
|
||||
return Err!(Request(Forbidden(
|
||||
"Tried to add a new signing key independently from the master key"
|
||||
)));
|
||||
}
|
||||
|
||||
if !key.is_not_found() {
|
||||
let existing_signing_key = key?.deserialize()?;
|
||||
if existing_signing_key != self_signing_key.deserialize()? {
|
||||
@@ -298,6 +275,7 @@ async fn check_for_new_keys(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if empty {
|
||||
return Ok(None);
|
||||
}
|
||||
@@ -381,12 +359,12 @@ pub(crate) async fn get_key_changes_route(
|
||||
let from = body
|
||||
.from
|
||||
.parse()
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Invalid `from`."))?;
|
||||
.map_err(|_| err!(Request(InvalidParam("Invalid `from`."))))?;
|
||||
|
||||
let to = body
|
||||
.to
|
||||
.parse()
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Invalid `to`."))?;
|
||||
.map_err(|_| err!(Request(InvalidParam("Invalid `to`."))))?;
|
||||
|
||||
device_list_updates.extend(
|
||||
services
|
||||
@@ -561,16 +539,16 @@ pub(crate) async fn get_keys_helper<F>(
|
||||
.signatures
|
||||
.append(&mut our_master_key.signatures);
|
||||
}
|
||||
let json = serde_json::to_value(master_key).expect("to_value always works");
|
||||
let raw = serde_json::from_value(json).expect("Raw::from_value always works");
|
||||
|
||||
// Dont notify. A notification would trigger another key request resulting in
|
||||
// an endless loop.
|
||||
let notify = false;
|
||||
let raw = Some(json::to_raw(master_key)?);
|
||||
services
|
||||
.users
|
||||
.add_cross_signing_keys(
|
||||
&user, &raw, &None, &None,
|
||||
false, /* Dont notify. A notification would trigger another key
|
||||
* request resulting in an endless loop */
|
||||
)
|
||||
.add_cross_signing_keys(&user, &raw, &None, &None, notify)
|
||||
.await?;
|
||||
|
||||
if let Some(raw) = raw {
|
||||
master_keys.insert(user.clone(), raw);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
use tuwunel_core::{
|
||||
Err, Result, debug, debug_info, debug_warn, err, extract_variant, info,
|
||||
matrix::{
|
||||
PduCount,
|
||||
event::{Event, gen_event_id},
|
||||
pdu::{PduBuilder, PduEvent},
|
||||
},
|
||||
@@ -276,6 +277,7 @@ async fn knock_room_helper_local(
|
||||
.map_err(|e| err!(BadServerResponse("Invalid knock event PDU: {e:?}")))?;
|
||||
|
||||
info!("Updating membership locally to knock state with provided stripped state events");
|
||||
let count = services.globals.next_count();
|
||||
services
|
||||
.state_cache
|
||||
.update_membership(
|
||||
@@ -294,6 +296,7 @@ async fn knock_room_helper_local(
|
||||
),
|
||||
None,
|
||||
false,
|
||||
PduCount::Normal(*count),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -490,6 +493,7 @@ async fn knock_room_helper_remote(
|
||||
.await?;
|
||||
|
||||
info!("Updating membership locally to knock state with provided stripped state events");
|
||||
let count = services.globals.next_count();
|
||||
services
|
||||
.state_cache
|
||||
.update_membership(
|
||||
@@ -508,6 +512,7 @@ async fn knock_room_helper_remote(
|
||||
),
|
||||
None,
|
||||
false,
|
||||
PduCount::Normal(*count),
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
use futures::{FutureExt, StreamExt, pin_mut};
|
||||
use ruma::{
|
||||
api::client::membership::{
|
||||
get_member_events::{self, v3::MembershipEventFilter},
|
||||
get_member_events::{self},
|
||||
joined_members::{self, v3::RoomMember},
|
||||
},
|
||||
events::{
|
||||
@@ -94,9 +94,7 @@ pub(crate) async fn joined_members_route(
|
||||
.ready_filter_map(Result::ok)
|
||||
.ready_filter(|((ty, _), _)| *ty == StateEventType::RoomMember)
|
||||
.map(at!(1))
|
||||
.ready_filter_map(|pdu| {
|
||||
membership_filter(pdu, Some(&MembershipEventFilter::Join), None)
|
||||
})
|
||||
.ready_filter_map(|pdu| membership_filter(pdu, Some(&MembershipState::Join), None))
|
||||
.ready_filter_map(|pdu| {
|
||||
let content = pdu.get_content::<RoomMemberEventContent>().ok()?;
|
||||
let sender = pdu.sender().to_owned();
|
||||
@@ -115,22 +113,22 @@ pub(crate) async fn joined_members_route(
|
||||
|
||||
fn membership_filter<Pdu: Event>(
|
||||
pdu: Pdu,
|
||||
for_membership: Option<&MembershipEventFilter>,
|
||||
not_membership: Option<&MembershipEventFilter>,
|
||||
for_membership: Option<&MembershipState>,
|
||||
not_membership: Option<&MembershipState>,
|
||||
) -> Option<impl Event> {
|
||||
let membership_state_filter = match for_membership {
|
||||
| Some(MembershipEventFilter::Ban) => MembershipState::Ban,
|
||||
| Some(MembershipEventFilter::Invite) => MembershipState::Invite,
|
||||
| Some(MembershipEventFilter::Knock) => MembershipState::Knock,
|
||||
| Some(MembershipEventFilter::Leave) => MembershipState::Leave,
|
||||
| Some(MembershipState::Ban) => MembershipState::Ban,
|
||||
| Some(MembershipState::Invite) => MembershipState::Invite,
|
||||
| Some(MembershipState::Knock) => MembershipState::Knock,
|
||||
| Some(MembershipState::Leave) => MembershipState::Leave,
|
||||
| Some(_) | None => MembershipState::Join,
|
||||
};
|
||||
|
||||
let not_membership_state_filter = match not_membership {
|
||||
| Some(MembershipEventFilter::Ban) => MembershipState::Ban,
|
||||
| Some(MembershipEventFilter::Invite) => MembershipState::Invite,
|
||||
| Some(MembershipEventFilter::Join) => MembershipState::Join,
|
||||
| Some(MembershipEventFilter::Knock) => MembershipState::Knock,
|
||||
| Some(MembershipState::Ban) => MembershipState::Ban,
|
||||
| Some(MembershipState::Invite) => MembershipState::Invite,
|
||||
| Some(MembershipState::Join) => MembershipState::Join,
|
||||
| Some(MembershipState::Knock) => MembershipState::Knock,
|
||||
| Some(_) | None => MembershipState::Leave,
|
||||
};
|
||||
|
||||
|
||||
@@ -165,10 +165,7 @@ async fn get_join_params(
|
||||
},
|
||||
// ... if room alias, resolve and don't shuffle ...
|
||||
| Err(room_alias) => {
|
||||
let (room_id, servers) = services
|
||||
.alias
|
||||
.resolve_alias(&room_alias, Some(via.to_vec()))
|
||||
.await?;
|
||||
let (room_id, servers) = services.alias.resolve_alias(&room_alias).await?;
|
||||
|
||||
(room_id, servers, Vec::new())
|
||||
},
|
||||
|
||||
+11
-15
@@ -46,13 +46,11 @@ pub(crate) async fn set_displayname_route(
|
||||
.update_displayname(&body.user_id, body.displayname.clone(), &all_joined_rooms)
|
||||
.await;
|
||||
|
||||
if services.config.allow_local_presence {
|
||||
// Presence update
|
||||
services
|
||||
.presence
|
||||
.ping_presence(&body.user_id, &PresenceState::Online)
|
||||
.await?;
|
||||
}
|
||||
// Presence update
|
||||
services
|
||||
.presence
|
||||
.maybe_ping_presence(&body.user_id, &PresenceState::Online)
|
||||
.await?;
|
||||
|
||||
Ok(set_display_name::v3::Response {})
|
||||
}
|
||||
@@ -148,14 +146,12 @@ pub(crate) async fn set_avatar_url_route(
|
||||
)
|
||||
.await;
|
||||
|
||||
if services.config.allow_local_presence {
|
||||
// Presence update
|
||||
services
|
||||
.presence
|
||||
.ping_presence(&body.user_id, &PresenceState::Online)
|
||||
.await
|
||||
.ok();
|
||||
}
|
||||
// Presence update
|
||||
services
|
||||
.presence
|
||||
.maybe_ping_presence(&body.user_id, &PresenceState::Online)
|
||||
.await
|
||||
.ok();
|
||||
|
||||
Ok(set_avatar_url::v3::Response {})
|
||||
}
|
||||
|
||||
@@ -26,6 +26,12 @@ pub(crate) async fn set_read_marker_route(
|
||||
) -> Result<set_read_marker::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
|
||||
if body.private_read_receipt.is_some() || body.read_receipt.is_some() {
|
||||
services
|
||||
.user
|
||||
.reset_notification_counts(sender_user, &body.room_id);
|
||||
}
|
||||
|
||||
if let Some(event) = &body.fully_read {
|
||||
let fully_read_event = ruma::events::fully_read::FullyReadEvent {
|
||||
content: ruma::events::fully_read::FullyReadEventContent { event_id: event.clone() },
|
||||
@@ -39,23 +45,29 @@ pub(crate) async fn set_read_marker_route(
|
||||
RoomAccountDataEventType::FullyRead,
|
||||
&serde_json::to_value(fully_read_event)?,
|
||||
)
|
||||
.await?;
|
||||
.await
|
||||
.ok();
|
||||
}
|
||||
|
||||
if body.private_read_receipt.is_some() || body.read_receipt.is_some() {
|
||||
if let Some(event) = &body.private_read_receipt {
|
||||
let count = services
|
||||
.timeline
|
||||
.get_pdu_count(event)
|
||||
.await
|
||||
.map_err(|_| err!(Request(NotFound("Event not found."))))?;
|
||||
|
||||
let PduCount::Normal(count) = count else {
|
||||
return Err!(Request(InvalidParam(
|
||||
"Event is a backfilled PDU and cannot be marked as read."
|
||||
)));
|
||||
};
|
||||
|
||||
services
|
||||
.user
|
||||
.reset_notification_counts(sender_user, &body.room_id);
|
||||
.read_receipt
|
||||
.private_read_set(&body.room_id, sender_user, count);
|
||||
}
|
||||
|
||||
if let Some(event) = &body.read_receipt {
|
||||
if services.config.allow_local_presence {
|
||||
services
|
||||
.presence
|
||||
.ping_presence(sender_user, &ruma::presence::PresenceState::Online)
|
||||
.await?;
|
||||
}
|
||||
|
||||
let receipt_content = BTreeMap::from_iter([(
|
||||
event.to_owned(),
|
||||
BTreeMap::from_iter([(
|
||||
@@ -78,24 +90,12 @@ pub(crate) async fn set_read_marker_route(
|
||||
},
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
if let Some(event) = &body.private_read_receipt {
|
||||
let count = services
|
||||
.timeline
|
||||
.get_pdu_count(event)
|
||||
.await
|
||||
.map_err(|_| err!(Request(NotFound("Event not found."))))?;
|
||||
|
||||
let PduCount::Normal(count) = count else {
|
||||
return Err!(Request(InvalidParam(
|
||||
"Event is a backfilled PDU and cannot be marked as read."
|
||||
)));
|
||||
};
|
||||
|
||||
services
|
||||
.read_receipt
|
||||
.private_read_set(&body.room_id, sender_user, count);
|
||||
.presence
|
||||
.maybe_ping_presence(sender_user, &ruma::presence::PresenceState::Online)
|
||||
.await
|
||||
.ok();
|
||||
}
|
||||
|
||||
Ok(set_read_marker::v3::Response {})
|
||||
@@ -137,13 +137,6 @@ pub(crate) async fn create_receipt_route(
|
||||
.await?;
|
||||
},
|
||||
| create_receipt::v3::ReceiptType::Read => {
|
||||
if services.config.allow_local_presence {
|
||||
services
|
||||
.presence
|
||||
.ping_presence(sender_user, &ruma::presence::PresenceState::Online)
|
||||
.await?;
|
||||
}
|
||||
|
||||
let receipt_content = BTreeMap::from_iter([(
|
||||
body.event_id.clone(),
|
||||
BTreeMap::from_iter([(
|
||||
@@ -169,6 +162,12 @@ pub(crate) async fn create_receipt_route(
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
services
|
||||
.presence
|
||||
.maybe_ping_presence(sender_user, &ruma::presence::PresenceState::Online)
|
||||
.await
|
||||
.ok();
|
||||
},
|
||||
| create_receipt::v3::ReceiptType::ReadPrivate => {
|
||||
let count = services
|
||||
|
||||
@@ -58,8 +58,8 @@ pub(crate) async fn get_register_available_route(
|
||||
});
|
||||
|
||||
if services
|
||||
.globals
|
||||
.forbidden_usernames()
|
||||
.config
|
||||
.forbidden_usernames
|
||||
.is_match(&body.username)
|
||||
{
|
||||
return Err!(Request(Forbidden("Username is forbidden")));
|
||||
@@ -228,8 +228,8 @@ pub(crate) async fn register_route(
|
||||
});
|
||||
|
||||
if services
|
||||
.globals
|
||||
.forbidden_usernames()
|
||||
.config
|
||||
.forbidden_usernames
|
||||
.is_match(username)
|
||||
&& !emergency_mode_enabled
|
||||
{
|
||||
@@ -384,8 +384,8 @@ pub(crate) async fn register_route(
|
||||
// If `new_user_displayname_suffix` is set, registration will push whatever
|
||||
// content is set to the user's display name with a space before it
|
||||
if !services
|
||||
.globals
|
||||
.new_user_displayname_suffix()
|
||||
.config
|
||||
.new_user_displayname_suffix
|
||||
.is_empty()
|
||||
&& body.appservice_info.is_none()
|
||||
{
|
||||
@@ -535,7 +535,7 @@ pub(crate) async fn register_route(
|
||||
&& (services.config.allow_guests_auto_join_rooms || !is_guest)
|
||||
{
|
||||
for room in &services.server.config.auto_join_rooms {
|
||||
let Ok(room_id) = services.alias.resolve(room).await else {
|
||||
let Ok(room_id) = services.alias.maybe_resolve(room).await else {
|
||||
error!(
|
||||
"Failed to resolve room alias to room ID when attempting to auto join \
|
||||
{room}, skipping"
|
||||
|
||||
@@ -678,8 +678,8 @@ async fn room_alias_check(
|
||||
|
||||
// check if room alias is forbidden
|
||||
if services
|
||||
.globals
|
||||
.forbidden_alias_names()
|
||||
.config
|
||||
.forbidden_alias_names
|
||||
.is_match(room_alias_name)
|
||||
{
|
||||
return Err!(Request(Unknown("Room alias name is forbidden.")));
|
||||
@@ -725,8 +725,8 @@ async fn room_alias_check(
|
||||
async fn custom_room_id_check(services: &Services, custom_room_id: &str) -> Result<OwnedRoomId> {
|
||||
// apply forbidden room alias checks to custom room IDs too
|
||||
if services
|
||||
.globals
|
||||
.forbidden_alias_names()
|
||||
.config
|
||||
.forbidden_alias_names
|
||||
.is_match(custom_room_id)
|
||||
{
|
||||
return Err!(Request(Unknown("Custom room ID is forbidden.")));
|
||||
@@ -798,7 +798,7 @@ async fn can_create_room_check(
|
||||
services: &Services,
|
||||
body: &Ruma<create_room::v3::Request>,
|
||||
) -> Result {
|
||||
if !services.globals.allow_room_creation()
|
||||
if !services.config.allow_room_creation
|
||||
&& body.appservice_info.is_none()
|
||||
&& !services.users.is_admin(body.sender_user()).await
|
||||
{
|
||||
|
||||
@@ -55,7 +55,7 @@ pub(crate) async fn get_room_summary(
|
||||
) -> Result<get_summary::v1::Response> {
|
||||
let (room_id, servers) = services
|
||||
.alias
|
||||
.resolve_with_servers(&body.room_id_or_alias, Some(body.via.clone()))
|
||||
.maybe_resolve_with_servers(&body.room_id_or_alias, Some(body.via.clone()))
|
||||
.await?;
|
||||
|
||||
if services.metadata.is_banned(&room_id).await {
|
||||
|
||||
@@ -44,7 +44,7 @@ pub(crate) async fn get_hierarchy_route(
|
||||
.as_ref()
|
||||
.and_then(|s| PaginationToken::from_str(s).ok());
|
||||
|
||||
// Should prevent unexpeded behaviour in (bad) clients
|
||||
// Should prevent unexpected behaviour in (bad) clients
|
||||
if let Some(ref token) = key {
|
||||
if token.suggested_only != body.suggested_only || token.max_depth != max_depth {
|
||||
return Err!(Request(InvalidParam(
|
||||
|
||||
@@ -195,6 +195,7 @@ async fn send_state_event_for_key_helper(
|
||||
room_id,
|
||||
&state_lock,
|
||||
)
|
||||
.boxed()
|
||||
.await?;
|
||||
|
||||
Ok(event_id)
|
||||
@@ -328,7 +329,7 @@ async fn allowed_to_send_state_event(
|
||||
for alias in aliases {
|
||||
let (alias_room_id, _servers) = services
|
||||
.alias
|
||||
.resolve_alias(&alias, None)
|
||||
.resolve_alias(&alias)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
err!(Request(BadAlias("Failed resolving alias \"{alias}\": {e}")))
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
mod v3;
|
||||
mod v5;
|
||||
|
||||
use futures::{StreamExt, pin_mut};
|
||||
use ruma::{
|
||||
RoomId, UserId,
|
||||
events::TimelineEventType::{
|
||||
self, Beacon, CallInvite, PollStart, RoomEncrypted, RoomMessage, Sticker,
|
||||
},
|
||||
};
|
||||
use futures::{FutureExt, StreamExt, pin_mut};
|
||||
use ruma::{RoomId, UserId};
|
||||
use tuwunel_core::{
|
||||
Error, PduCount, Result,
|
||||
matrix::pdu::PduEvent,
|
||||
@@ -17,9 +12,6 @@
|
||||
|
||||
pub(crate) use self::{v3::sync_events_route, v5::sync_events_v5_route};
|
||||
|
||||
pub(crate) const DEFAULT_BUMP_TYPES: &[TimelineEventType; 6] =
|
||||
&[CallInvite, PollStart, Beacon, RoomEncrypted, RoomMessage, Sticker];
|
||||
|
||||
async fn load_timeline(
|
||||
services: &Services,
|
||||
sender_user: &UserId,
|
||||
@@ -50,10 +42,12 @@ async fn load_timeline(
|
||||
.by_ref()
|
||||
.take(limit)
|
||||
.collect()
|
||||
.map(|mut pdus: Vec<_>| {
|
||||
pdus.reverse();
|
||||
pdus
|
||||
})
|
||||
.await;
|
||||
|
||||
let timeline_pdus: Vec<_> = timeline_pdus.into_iter().rev().collect();
|
||||
|
||||
// They /sync response doesn't always return all messages, so we say the output
|
||||
// is limited unless there are events in non_timeline_pdus
|
||||
let limited = non_timeline_pdus.next().await.is_some();
|
||||
|
||||
+204
-197
@@ -5,8 +5,8 @@
|
||||
|
||||
use axum::extract::State;
|
||||
use futures::{
|
||||
FutureExt, StreamExt, TryFutureExt, TryStreamExt,
|
||||
future::{OptionFuture, join, join3, join4, join5, try_join3},
|
||||
FutureExt, StreamExt, TryFutureExt,
|
||||
future::{OptionFuture, join, join3, join4, join5},
|
||||
pin_mut,
|
||||
};
|
||||
use ruma::{
|
||||
@@ -36,7 +36,11 @@
|
||||
};
|
||||
use tokio::time;
|
||||
use tuwunel_core::{
|
||||
Result, at, err, error, extract_variant, is_equal_to,
|
||||
Error, Result, at,
|
||||
debug::INFO_SPAN_LEVEL,
|
||||
err, error,
|
||||
error::{inspect_debug_log, inspect_log},
|
||||
extract_variant, is_equal_to,
|
||||
matrix::{
|
||||
Event,
|
||||
event::Matches,
|
||||
@@ -49,7 +53,6 @@
|
||||
self, BoolExt, FutureBoolExt, IterStream, ReadyExt, TryFutureExtExt,
|
||||
future::{OptionStream, ReadyEqExt},
|
||||
math::ruma_from_u64,
|
||||
result::LogErr,
|
||||
stream::{BroadbandExt, Tools, TryExpect, WidebandExt},
|
||||
},
|
||||
warn,
|
||||
@@ -125,20 +128,16 @@ pub(crate) async fn sync_events_route(
|
||||
) -> Result<sync_events::v3::Response, RumaResponse<UiaaResponse>> {
|
||||
let (sender_user, sender_device) = body.sender();
|
||||
|
||||
// Presence update
|
||||
if services.config.allow_local_presence {
|
||||
services
|
||||
.presence
|
||||
.ping_presence(sender_user, &body.body.set_presence)
|
||||
.await
|
||||
.log_err()
|
||||
.ok();
|
||||
let ping_presence = services
|
||||
.presence
|
||||
.maybe_ping_presence(sender_user, &body.body.set_presence)
|
||||
.inspect_err(inspect_log)
|
||||
.ok();
|
||||
|
||||
// Record user as actively syncing for push suppression heuristic.
|
||||
if services.config.suppress_push_when_active {
|
||||
services.presence.note_sync(sender_user).await;
|
||||
}
|
||||
}
|
||||
// Record user as actively syncing for push suppression heuristic.
|
||||
let note_sync = services.presence.note_sync(sender_user);
|
||||
|
||||
join(ping_presence, note_sync).await;
|
||||
|
||||
let mut since = body
|
||||
.body
|
||||
@@ -164,10 +163,18 @@ pub(crate) async fn sync_events_route(
|
||||
.expect("configuration must limit maximum timeout");
|
||||
|
||||
loop {
|
||||
let watchers = services.sync.watch(sender_user, sender_device);
|
||||
let next_batch = services.globals.wait_pending().await?;
|
||||
let watch_rooms = services
|
||||
.state_cache
|
||||
.rooms_joined(sender_user)
|
||||
.chain(services.state_cache.rooms_invited(sender_user));
|
||||
|
||||
let watchers = services
|
||||
.sync
|
||||
.watch(sender_user, sender_device, watch_rooms);
|
||||
|
||||
let next_batch = services.globals.wait_pending().await?;
|
||||
debug_assert!(since <= next_batch, "next_batch is monotonic");
|
||||
|
||||
if since < next_batch || body.body.full_state {
|
||||
let response = build_sync_events(&services, &body, since, next_batch).await?;
|
||||
let empty = response.rooms.is_empty()
|
||||
@@ -217,7 +224,7 @@ async fn build_empty_response(
|
||||
|
||||
#[tracing::instrument(
|
||||
name = "build",
|
||||
level = "debug",
|
||||
level = INFO_SPAN_LEVEL,
|
||||
ret(level = "trace"),
|
||||
skip_all,
|
||||
fields(
|
||||
@@ -248,6 +255,7 @@ async fn build_sync_events(
|
||||
let joined_rooms = services
|
||||
.state_cache
|
||||
.rooms_joined(sender_user)
|
||||
.ready_filter(|&room_id| filter.room.matches(room_id))
|
||||
.map(ToOwned::to_owned)
|
||||
.broad_filter_map(|room_id| {
|
||||
load_joined_room(
|
||||
@@ -279,7 +287,8 @@ async fn build_sync_events(
|
||||
|
||||
let left_rooms = services
|
||||
.state_cache
|
||||
.rooms_left(sender_user)
|
||||
.rooms_left_state(sender_user)
|
||||
.ready_filter(|(room_id, _)| filter.room.matches(room_id))
|
||||
.broad_filter_map(|(room_id, _)| {
|
||||
handle_left_room(
|
||||
services,
|
||||
@@ -298,7 +307,8 @@ async fn build_sync_events(
|
||||
|
||||
let invited_rooms = services
|
||||
.state_cache
|
||||
.rooms_invited(sender_user)
|
||||
.rooms_invited_state(sender_user)
|
||||
.ready_filter(|(room_id, _)| filter.room.matches(room_id))
|
||||
.fold_default(async |mut invited_rooms: BTreeMap<_, _>, (room_id, invite_state)| {
|
||||
let invite_count = services
|
||||
.state_cache
|
||||
@@ -321,7 +331,8 @@ async fn build_sync_events(
|
||||
|
||||
let knocked_rooms = services
|
||||
.state_cache
|
||||
.rooms_knocked(sender_user)
|
||||
.rooms_knocked_state(sender_user)
|
||||
.ready_filter(|(room_id, _)| filter.room.matches(room_id))
|
||||
.fold_default(async |mut knocked_rooms: BTreeMap<_, _>, (room_id, knock_state)| {
|
||||
let knock_count = services
|
||||
.state_cache
|
||||
@@ -345,7 +356,7 @@ async fn build_sync_events(
|
||||
let presence_updates: OptionFuture<_> = services
|
||||
.config
|
||||
.allow_local_presence
|
||||
.then(|| process_presence_updates(services, since, next_batch, sender_user))
|
||||
.then(|| process_presence_updates(services, since, next_batch, sender_user, &filter))
|
||||
.into();
|
||||
|
||||
let account_data = services
|
||||
@@ -401,7 +412,7 @@ async fn build_sync_events(
|
||||
|
||||
// If the user doesn't share an encrypted room with the target anymore, we need
|
||||
// to tell them
|
||||
let device_list_left: HashSet<_> = left_encrypted_users
|
||||
let device_list_left = left_encrypted_users
|
||||
.into_iter()
|
||||
.stream()
|
||||
.broad_filter_map(async |user_id: OwnedUserId| {
|
||||
@@ -424,8 +435,8 @@ async fn build_sync_events(
|
||||
Ok(sync_events::v3::Response {
|
||||
account_data: GlobalAccountData { events: account_data },
|
||||
device_lists: DeviceLists {
|
||||
left: device_list_left,
|
||||
changed: device_list_updates.into_iter().collect(),
|
||||
left: device_list_left.into_iter().collect(),
|
||||
},
|
||||
device_one_time_keys_count,
|
||||
// Fallback keys are not yet supported
|
||||
@@ -448,10 +459,12 @@ async fn process_presence_updates(
|
||||
since: u64,
|
||||
next_batch: u64,
|
||||
syncing_user: &UserId,
|
||||
filter: &FilterDefinition,
|
||||
) -> PresenceUpdates {
|
||||
services
|
||||
.presence
|
||||
.presence_since(since, Some(next_batch))
|
||||
.ready_filter(|(user_id, ..)| filter.presence.matches(user_id))
|
||||
.filter(|(user_id, ..)| {
|
||||
services
|
||||
.state_cache
|
||||
@@ -466,6 +479,7 @@ async fn process_presence_updates(
|
||||
})
|
||||
.map(|(user_id, event)| (user_id.to_owned(), event.content))
|
||||
.collect()
|
||||
.boxed()
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -538,9 +552,9 @@ async fn handle_left_room(
|
||||
unsigned: None,
|
||||
// The following keys are dropped on conversion
|
||||
room_id: room_id.clone(),
|
||||
prev_events: vec![],
|
||||
prev_events: Default::default(),
|
||||
auth_events: Default::default(),
|
||||
depth: uint!(1),
|
||||
auth_events: vec![],
|
||||
redacts: None,
|
||||
hashes: EventHash::default(),
|
||||
signatures: None,
|
||||
@@ -560,20 +574,22 @@ async fn handle_left_room(
|
||||
let mut left_state_events = Vec::new();
|
||||
|
||||
let since_shortstatehash = services
|
||||
.user
|
||||
.get_token_shortstatehash(room_id, since);
|
||||
.timeline
|
||||
.prev_shortstatehash(room_id, PduCount::Normal(since).saturating_add(1))
|
||||
.await
|
||||
.ok();
|
||||
|
||||
let since_state_ids: HashMap<_, OwnedEventId> = since_shortstatehash
|
||||
.map_ok(|since_shortstatehash| {
|
||||
.map(|since_shortstatehash| {
|
||||
services
|
||||
.state_accessor
|
||||
.state_full_ids(since_shortstatehash)
|
||||
.map(Ok)
|
||||
})
|
||||
.try_flatten_stream()
|
||||
.try_collect()
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
.into_iter()
|
||||
.stream()
|
||||
.flatten()
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
let Ok(left_event_id): Result<OwnedEventId> = services
|
||||
.state_accessor
|
||||
@@ -585,7 +601,7 @@ async fn handle_left_room(
|
||||
};
|
||||
|
||||
let Ok(left_shortstatehash) = services
|
||||
.state_accessor
|
||||
.state
|
||||
.pdu_shortstatehash(&left_event_id)
|
||||
.await
|
||||
else {
|
||||
@@ -664,12 +680,7 @@ async fn load_joined_room(
|
||||
full_state: bool,
|
||||
filter: &FilterDefinition,
|
||||
) -> Result<(JoinedRoom, HashSet<OwnedUserId>, HashSet<OwnedUserId>)> {
|
||||
let since_shortstatehash = services
|
||||
.user
|
||||
.get_token_shortstatehash(room_id, since)
|
||||
.ok()
|
||||
.map(Ok);
|
||||
|
||||
let initial = since == 0;
|
||||
let timeline_limit: usize = filter
|
||||
.room
|
||||
.timeline
|
||||
@@ -677,14 +688,38 @@ async fn load_joined_room(
|
||||
.unwrap_or_else(|| uint!(10))
|
||||
.try_into()?;
|
||||
|
||||
let timeline = load_timeline(
|
||||
let (timeline_pdus, limited, last_timeline_count) = load_timeline(
|
||||
services,
|
||||
sender_user,
|
||||
room_id,
|
||||
PduCount::Normal(since),
|
||||
Some(PduCount::Normal(next_batch)),
|
||||
timeline_limit,
|
||||
);
|
||||
)
|
||||
.await?;
|
||||
|
||||
let since_shortstatehash = services
|
||||
.timeline
|
||||
.prev_shortstatehash(room_id, PduCount::Normal(since).saturating_add(1))
|
||||
.ok();
|
||||
|
||||
let horizon_shortstatehash: OptionFuture<_> = timeline_pdus
|
||||
.first()
|
||||
.map(at!(0))
|
||||
.map(|count| {
|
||||
services
|
||||
.timeline
|
||||
.get_shortstatehash(room_id, count)
|
||||
.inspect_err(inspect_debug_log)
|
||||
})
|
||||
.into();
|
||||
|
||||
let current_shortstatehash = services
|
||||
.timeline
|
||||
.get_shortstatehash(room_id, last_timeline_count)
|
||||
.inspect_err(inspect_debug_log)
|
||||
.or_else(|_| services.state.get_room_shortstatehash(room_id))
|
||||
.map_err(|_| err!(Database(error!("Room {room_id} has no state"))));
|
||||
|
||||
let receipt_events = services
|
||||
.read_receipt
|
||||
@@ -696,50 +731,32 @@ async fn load_joined_room(
|
||||
.await
|
||||
.or_some((read_user.to_owned(), edu))
|
||||
})
|
||||
.collect::<HashMap<OwnedUserId, Raw<AnySyncEphemeralRoomEvent>>>()
|
||||
.map(Ok);
|
||||
.collect::<HashMap<OwnedUserId, Raw<AnySyncEphemeralRoomEvent>>>();
|
||||
|
||||
let (since_shortstatehash, (timeline_pdus, limited, last_timeline_count), receipt_events) =
|
||||
try_join3(since_shortstatehash, timeline, receipt_events)
|
||||
.boxed()
|
||||
.await?;
|
||||
let encrypted_room = services.state_accessor.is_encrypted_room(room_id);
|
||||
|
||||
let horizon_shortstatehash: OptionFuture<_> = timeline_pdus
|
||||
.iter()
|
||||
.map(at!(0))
|
||||
.map(PduCount::into_unsigned)
|
||||
.map(|shorteventid| {
|
||||
services
|
||||
.state_accessor
|
||||
.get_shortstatehash(shorteventid)
|
||||
})
|
||||
.next()
|
||||
.into();
|
||||
let (
|
||||
(since_shortstatehash, horizon_shortstatehash, current_shortstatehash),
|
||||
receipt_events,
|
||||
encrypted_room,
|
||||
) = join3(
|
||||
join3(since_shortstatehash, horizon_shortstatehash, current_shortstatehash),
|
||||
receipt_events,
|
||||
encrypted_room,
|
||||
)
|
||||
.map(|((since, horizon, current), receipt, encrypted_room)| {
|
||||
Ok::<_, Error>(((since, horizon.flat_ok(), current?), receipt, encrypted_room))
|
||||
})
|
||||
.boxed()
|
||||
.await?;
|
||||
|
||||
let current_shortstatehash = services
|
||||
.state_accessor
|
||||
.get_shortstatehash(last_timeline_count.into_unsigned())
|
||||
.or_else(|_| services.state.get_room_shortstatehash(room_id));
|
||||
let lazy_load_options =
|
||||
[&filter.room.state.lazy_load_options, &filter.room.timeline.lazy_load_options];
|
||||
|
||||
let (horizon_shortstatehash, current_shortstatehash) =
|
||||
join(horizon_shortstatehash, current_shortstatehash)
|
||||
.boxed()
|
||||
.await;
|
||||
|
||||
let current_shortstatehash = current_shortstatehash
|
||||
.map_err(|_| err!(Database(error!("Room {room_id} has no state"))))?;
|
||||
|
||||
let associate_token =
|
||||
services
|
||||
.user
|
||||
.associate_token_shortstatehash(room_id, next_batch, current_shortstatehash);
|
||||
|
||||
let lazy_loading_enabled = filter.room.state.lazy_load_options.is_enabled()
|
||||
|| filter
|
||||
.room
|
||||
.timeline
|
||||
.lazy_load_options
|
||||
.is_enabled();
|
||||
let lazy_loading_enabled = !encrypted_room
|
||||
&& lazy_load_options
|
||||
.iter()
|
||||
.any(|opts| opts.is_enabled());
|
||||
|
||||
let lazy_loading_context = &lazy_loading::Context {
|
||||
user_id: sender_user,
|
||||
@@ -749,8 +766,6 @@ async fn load_joined_room(
|
||||
options: Some(&filter.room.state.lazy_load_options),
|
||||
};
|
||||
|
||||
let initial = since == 0 || since_shortstatehash.is_none();
|
||||
|
||||
// Reset lazy loading because this is an initial sync
|
||||
let lazy_load_reset: OptionFuture<_> = initial
|
||||
.then(|| services.lazy_loading.reset(lazy_loading_context))
|
||||
@@ -773,6 +788,22 @@ async fn load_joined_room(
|
||||
})
|
||||
.into();
|
||||
|
||||
let sender_joined_count = services
|
||||
.state_cache
|
||||
.get_joined_count(room_id, sender_user);
|
||||
|
||||
let since_encryption: OptionFuture<_> = since_shortstatehash
|
||||
.map(|shortstatehash| {
|
||||
services
|
||||
.state_accessor
|
||||
.state_get(shortstatehash, &StateEventType::RoomEncryption, "")
|
||||
})
|
||||
.into();
|
||||
|
||||
let last_privateread_update = services
|
||||
.read_receipt
|
||||
.last_privateread_update(sender_user, room_id);
|
||||
|
||||
let last_notification_read: OptionFuture<_> = timeline_pdus
|
||||
.is_empty()
|
||||
.then(|| {
|
||||
@@ -782,39 +813,20 @@ async fn load_joined_room(
|
||||
})
|
||||
.into();
|
||||
|
||||
let since_sender_member: OptionFuture<_> = since_shortstatehash
|
||||
.map(|short| {
|
||||
services
|
||||
.state_accessor
|
||||
.state_get_content(short, &StateEventType::RoomMember, sender_user.as_str())
|
||||
.ok()
|
||||
})
|
||||
.into();
|
||||
|
||||
let encrypted_room = services.state_accessor.is_encrypted_room(room_id);
|
||||
|
||||
let last_privateread_update = services
|
||||
.read_receipt
|
||||
.last_privateread_update(sender_user, room_id);
|
||||
|
||||
let (
|
||||
(witness, since_sender_member),
|
||||
(encrypted_room, ()),
|
||||
(last_privateread_update, last_notification_read),
|
||||
(sender_joined_count, since_encryption),
|
||||
witness,
|
||||
) = join3(
|
||||
join(witness, since_sender_member),
|
||||
join(encrypted_room, associate_token),
|
||||
join(last_privateread_update, last_notification_read),
|
||||
join(sender_joined_count, since_encryption),
|
||||
witness,
|
||||
)
|
||||
.boxed()
|
||||
.await;
|
||||
|
||||
let joined_since_last_sync =
|
||||
since_sender_member
|
||||
.flatten()
|
||||
.is_none_or(|content: RoomMemberEventContent| {
|
||||
content.membership != MembershipState::Join
|
||||
});
|
||||
let _encrypted_since_last_sync = !initial && encrypted_room && since_encryption.is_none();
|
||||
|
||||
let joined_since_last_sync = sender_joined_count.is_ok_and(|count| count > since);
|
||||
|
||||
let StateChanges {
|
||||
heroes,
|
||||
@@ -825,19 +837,15 @@ async fn load_joined_room(
|
||||
services,
|
||||
sender_user,
|
||||
room_id,
|
||||
full_state,
|
||||
encrypted_room,
|
||||
full_state || initial,
|
||||
since_shortstatehash,
|
||||
horizon_shortstatehash.flat_ok(),
|
||||
horizon_shortstatehash,
|
||||
current_shortstatehash,
|
||||
joined_since_last_sync,
|
||||
witness.as_ref(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let send_notification_counts =
|
||||
last_notification_read.is_none_or(|last_count| last_count.gt(&since));
|
||||
|
||||
let is_sender_membership = |event: &PduEvent| {
|
||||
*event.event_type() == StateEventType::RoomMember.into()
|
||||
&& event
|
||||
@@ -845,14 +853,25 @@ async fn load_joined_room(
|
||||
.is_some_and(is_equal_to!(sender_user.as_str()))
|
||||
};
|
||||
|
||||
let joined_sender_member: Option<_> = (joined_since_last_sync && timeline_pdus.is_empty())
|
||||
.then(|| {
|
||||
state_events
|
||||
.iter()
|
||||
.position(is_sender_membership)
|
||||
.map(|pos| state_events.swap_remove(pos))
|
||||
})
|
||||
.flatten();
|
||||
let joined_sender_member: Option<_> =
|
||||
(joined_since_last_sync && timeline_pdus.is_empty() && !initial)
|
||||
.then(|| {
|
||||
state_events
|
||||
.iter()
|
||||
.position(is_sender_membership)
|
||||
.map(|pos| state_events.swap_remove(pos))
|
||||
})
|
||||
.flatten();
|
||||
|
||||
let prev_batch = timeline_pdus.first().map(at!(0)).or_else(|| {
|
||||
joined_sender_member
|
||||
.is_some()
|
||||
.then_some(since)
|
||||
.map(Into::into)
|
||||
});
|
||||
|
||||
let send_notification_counts =
|
||||
last_notification_read.is_none_or(|last_count| last_count.gt(&since));
|
||||
|
||||
let notification_count: OptionFuture<_> = send_notification_counts
|
||||
.then(|| {
|
||||
@@ -898,19 +917,26 @@ async fn load_joined_room(
|
||||
})
|
||||
.unwrap_or(Vec::new());
|
||||
|
||||
let keys_changed = services
|
||||
.users
|
||||
.room_keys_changed(room_id, since, Some(next_batch))
|
||||
.map(|(user_id, _)| user_id)
|
||||
.map(ToOwned::to_owned)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let extract_membership = |event: &PduEvent| {
|
||||
let content: RoomMemberEventContent = event.get_content().ok()?;
|
||||
let user_id: OwnedUserId = event.state_key()?.parse().ok()?;
|
||||
|
||||
Some((content, user_id))
|
||||
Some((content.membership, user_id))
|
||||
};
|
||||
|
||||
let timeline_membership_changes: Vec<_> = timeline_pdus
|
||||
let timeline_membership_changes = timeline_pdus
|
||||
.iter()
|
||||
.map(ref_at!(1))
|
||||
.filter(|_| !initial)
|
||||
.map(ref_at!(1))
|
||||
.filter_map(extract_membership)
|
||||
.collect();
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let device_list_updates = state_events
|
||||
.iter()
|
||||
@@ -918,31 +944,27 @@ async fn load_joined_room(
|
||||
.ready_filter(|_| !initial)
|
||||
.ready_filter(|state_event| *state_event.event_type() == RoomMember)
|
||||
.ready_filter_map(extract_membership)
|
||||
.chain(timeline_membership_changes.into_iter().stream())
|
||||
.fold_default(async |(mut dlu, mut leu): pair_of!(HashSet<_>), (content, user_id)| {
|
||||
.chain(timeline_membership_changes.stream())
|
||||
.fold_default(async |(mut dlu, mut leu): pair_of!(HashSet<_>), (membership, user_id)| {
|
||||
use MembershipState::*;
|
||||
|
||||
let shares_encrypted_room = async |user_id| {
|
||||
share_encrypted_room(services, sender_user, user_id, Some(room_id)).await
|
||||
let requires_update = async |user_id| {
|
||||
!share_encrypted_room(services, sender_user, user_id, Some(room_id)).await
|
||||
};
|
||||
|
||||
match content.membership {
|
||||
match membership {
|
||||
| Join if requires_update(&user_id).await => dlu.insert(user_id),
|
||||
| Leave => leu.insert(user_id),
|
||||
| Join if joined_since_last_sync || !shares_encrypted_room(&user_id).await =>
|
||||
dlu.insert(user_id),
|
||||
| _ => false,
|
||||
};
|
||||
|
||||
(dlu, leu)
|
||||
})
|
||||
.then(async |(mut dlu, leu)| {
|
||||
dlu.extend(keys_changed.await);
|
||||
(dlu, leu)
|
||||
});
|
||||
|
||||
let prev_batch = timeline_pdus.first().map(at!(0)).or_else(|| {
|
||||
joined_sender_member
|
||||
.is_some()
|
||||
.then_some(since)
|
||||
.map(Into::into)
|
||||
});
|
||||
|
||||
let include_in_timeline = |event: &PduEvent| {
|
||||
let filter = &filter.room.timeline;
|
||||
filter.matches(event)
|
||||
@@ -957,33 +979,26 @@ async fn load_joined_room(
|
||||
.ready_filter(include_in_timeline)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let device_updates = services
|
||||
.users
|
||||
.room_keys_changed(room_id, since, Some(next_batch))
|
||||
.map(|(user_id, _)| user_id)
|
||||
.map(ToOwned::to_owned)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let account_data_events = services
|
||||
.account_data
|
||||
.changes_since(Some(room_id), sender_user, since, Some(next_batch))
|
||||
.changes_since(Some(room_id), sender_user, since, None)
|
||||
.ready_filter_map(|e| extract_variant!(e, AnyRawAccountDataEvent::Room))
|
||||
.collect();
|
||||
|
||||
let (
|
||||
(room_events, account_data_events),
|
||||
(typing_events, private_read_event),
|
||||
(notification_count, highlight_count),
|
||||
((mut device_list_updates, left_encrypted_users), device_updates),
|
||||
(room_events, account_data_events, typing_events, private_read_event),
|
||||
) = join3(
|
||||
(device_list_updates, left_encrypted_users),
|
||||
) = join4(
|
||||
join(room_events, account_data_events),
|
||||
join(typing_events, private_read_event),
|
||||
join(notification_count, highlight_count),
|
||||
join(device_list_updates, device_updates),
|
||||
join4(room_events, account_data_events, typing_events, private_read_event),
|
||||
device_list_updates,
|
||||
)
|
||||
.boxed()
|
||||
.await;
|
||||
|
||||
device_list_updates.extend(device_updates);
|
||||
|
||||
let is_in_timeline = |event: &PduEvent| {
|
||||
room_events
|
||||
.iter()
|
||||
@@ -1045,8 +1060,9 @@ async fn load_joined_room(
|
||||
skip_all,
|
||||
fields(
|
||||
full = %full_state,
|
||||
cs = %current_shortstatehash,
|
||||
ss = ?since_shortstatehash,
|
||||
hs = ?horizon_shortstatehash,
|
||||
cs = %current_shortstatehash,
|
||||
)
|
||||
)]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
@@ -1055,16 +1071,13 @@ async fn calculate_state_changes<'a>(
|
||||
sender_user: &UserId,
|
||||
room_id: &RoomId,
|
||||
full_state: bool,
|
||||
encrypted_room: bool,
|
||||
since_shortstatehash: Option<ShortStateHash>,
|
||||
horizon_shortstatehash: Option<ShortStateHash>,
|
||||
current_shortstatehash: ShortStateHash,
|
||||
joined_since_last_sync: bool,
|
||||
witness: Option<&'a Witness>,
|
||||
) -> Result<StateChanges> {
|
||||
let initial = full_state || since_shortstatehash.is_none() || joined_since_last_sync;
|
||||
|
||||
let incremental = !initial && since_shortstatehash != Some(current_shortstatehash);
|
||||
let incremental = !full_state && !joined_since_last_sync && since_shortstatehash.is_some();
|
||||
|
||||
let horizon_shortstatehash = horizon_shortstatehash.unwrap_or(current_shortstatehash);
|
||||
|
||||
@@ -1082,36 +1095,33 @@ async fn calculate_state_changes<'a>(
|
||||
};
|
||||
|
||||
let lazy_state_ids: OptionFuture<_> = witness
|
||||
.filter(|_| !encrypted_room)
|
||||
.map(|witness| {
|
||||
StreamExt::into_future(
|
||||
witness
|
||||
.iter()
|
||||
.stream()
|
||||
.broad_filter_map(|user_id| state_get_shorteventid(user_id)),
|
||||
)
|
||||
witness
|
||||
.iter()
|
||||
.stream()
|
||||
.ready_filter(|&user_id| user_id != sender_user)
|
||||
.broad_filter_map(|user_id| state_get_shorteventid(user_id))
|
||||
.into_future()
|
||||
})
|
||||
.into();
|
||||
|
||||
let state_diff_ids: OptionFuture<_> = incremental
|
||||
.then(|| {
|
||||
StreamExt::into_future(
|
||||
services
|
||||
.state_accessor
|
||||
.state_added((since_shortstatehash, horizon_shortstatehash))
|
||||
.boxed(),
|
||||
)
|
||||
services
|
||||
.state_accessor
|
||||
.state_added((since_shortstatehash, horizon_shortstatehash))
|
||||
.boxed()
|
||||
.into_future()
|
||||
})
|
||||
.into();
|
||||
|
||||
let current_state_ids: OptionFuture<_> = initial
|
||||
let current_state_ids: OptionFuture<_> = (!incremental)
|
||||
.then(|| {
|
||||
StreamExt::into_future(
|
||||
services
|
||||
.state_accessor
|
||||
.state_full_shortids(horizon_shortstatehash)
|
||||
.expect_ok(),
|
||||
)
|
||||
services
|
||||
.state_accessor
|
||||
.state_full_shortids(horizon_shortstatehash)
|
||||
.expect_ok()
|
||||
.into_future()
|
||||
})
|
||||
.into();
|
||||
|
||||
@@ -1119,29 +1129,21 @@ async fn calculate_state_changes<'a>(
|
||||
.stream()
|
||||
.chain(state_diff_ids.stream())
|
||||
.broad_filter_map(async |(shortstatekey, shorteventid)| {
|
||||
if witness.is_none() || encrypted_room {
|
||||
return Some(shorteventid);
|
||||
}
|
||||
|
||||
lazy_filter(services, sender_user, shortstatekey, shorteventid).await
|
||||
lazy_filter(services, sender_user, witness, shortstatekey, shorteventid).await
|
||||
})
|
||||
.chain(lazy_state_ids.stream())
|
||||
.broad_filter_map(|shorteventid| {
|
||||
services
|
||||
.short
|
||||
.get_eventid_from_short(shorteventid)
|
||||
.timeline
|
||||
.get_pdu_from_shorteventid(shorteventid)
|
||||
.ok()
|
||||
})
|
||||
.broad_filter_map(async |event_id: OwnedEventId| {
|
||||
services.timeline.get_pdu(&event_id).ok().await
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.boxed()
|
||||
.await;
|
||||
|
||||
let send_member_counts = state_events
|
||||
.iter()
|
||||
.any(|event| event.kind == RoomMember);
|
||||
.any(|event| *event.kind() == RoomMember);
|
||||
|
||||
let member_counts: OptionFuture<_> = send_member_counts
|
||||
.then(|| calculate_counts(services, room_id, sender_user))
|
||||
@@ -1161,9 +1163,14 @@ async fn calculate_state_changes<'a>(
|
||||
async fn lazy_filter(
|
||||
services: &Services,
|
||||
sender_user: &UserId,
|
||||
witness: Option<&Witness>,
|
||||
shortstatekey: ShortStateKey,
|
||||
shorteventid: ShortEventId,
|
||||
) -> Option<ShortEventId> {
|
||||
if witness.is_none() {
|
||||
return Some(shorteventid);
|
||||
}
|
||||
|
||||
let (event_type, state_key) = services
|
||||
.short
|
||||
.get_statekey_from_short(shortstatekey)
|
||||
|
||||
+235
-1149
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,60 @@
|
||||
use futures::{StreamExt, future::join};
|
||||
use ruma::{api::client::sync::sync_events::v5::response, events::AnyRawAccountDataEvent};
|
||||
use tuwunel_core::{
|
||||
Result, extract_variant,
|
||||
utils::{IterStream, ReadyExt, stream::BroadbandExt},
|
||||
};
|
||||
use tuwunel_service::sync::Room;
|
||||
|
||||
use super::{Connection, SyncInfo, Window, extension_rooms_selector};
|
||||
|
||||
#[tracing::instrument(name = "account_data", level = "trace", skip_all)]
|
||||
pub(super) async fn collect(
|
||||
sync_info: SyncInfo<'_>,
|
||||
conn: &Connection,
|
||||
window: &Window,
|
||||
) -> Result<response::AccountData> {
|
||||
let SyncInfo { services, sender_user, .. } = sync_info;
|
||||
|
||||
let implicit = conn
|
||||
.extensions
|
||||
.account_data
|
||||
.lists
|
||||
.as_deref()
|
||||
.map(<[_]>::iter);
|
||||
|
||||
let explicit = conn
|
||||
.extensions
|
||||
.account_data
|
||||
.rooms
|
||||
.as_deref()
|
||||
.map(<[_]>::iter);
|
||||
|
||||
let rooms = extension_rooms_selector(sync_info, conn, window, implicit, explicit)
|
||||
.stream()
|
||||
.broad_filter_map(async |room_id| {
|
||||
let &Room { roomsince, .. } = conn.rooms.get(room_id)?;
|
||||
let changes: Vec<_> = services
|
||||
.account_data
|
||||
.changes_since(Some(room_id), sender_user, roomsince, Some(conn.next_batch))
|
||||
.ready_filter_map(|e| extract_variant!(e, AnyRawAccountDataEvent::Room))
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
changes
|
||||
.is_empty()
|
||||
.eq(&false)
|
||||
.then(move || (room_id.to_owned(), changes))
|
||||
})
|
||||
.collect();
|
||||
|
||||
let global = services
|
||||
.account_data
|
||||
.changes_since(None, sender_user, conn.globalsince, Some(conn.next_batch))
|
||||
.ready_filter_map(|e| extract_variant!(e, AnyRawAccountDataEvent::Global))
|
||||
.collect();
|
||||
|
||||
let (global, rooms) = join(global, rooms).await;
|
||||
|
||||
Ok(response::AccountData { global, rooms })
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use futures::{
|
||||
FutureExt, StreamExt, TryFutureExt,
|
||||
future::{OptionFuture, join, join3},
|
||||
stream::once,
|
||||
};
|
||||
use ruma::{
|
||||
OwnedUserId, RoomId,
|
||||
api::client::sync::sync_events::{DeviceLists, v5::response},
|
||||
events::{
|
||||
StateEventType, TimelineEventType,
|
||||
room::member::{MembershipState, RoomMemberEventContent},
|
||||
},
|
||||
};
|
||||
use tuwunel_core::{
|
||||
Result, error,
|
||||
matrix::{Event, pdu::PduCount},
|
||||
pair_of,
|
||||
utils::{
|
||||
BoolExt, IterStream, ReadyExt, TryFutureExtExt, future::OptionStream,
|
||||
stream::BroadbandExt,
|
||||
},
|
||||
};
|
||||
use tuwunel_service::sync::Connection;
|
||||
|
||||
use super::{SyncInfo, share_encrypted_room};
|
||||
|
||||
#[tracing::instrument(name = "e2ee", level = "trace", skip_all)]
|
||||
pub(super) async fn collect(
|
||||
sync_info: SyncInfo<'_>,
|
||||
conn: &Connection,
|
||||
) -> Result<response::E2EE> {
|
||||
let SyncInfo { services, sender_user, sender_device, .. } = sync_info;
|
||||
|
||||
let keys_changed = services
|
||||
.users
|
||||
.keys_changed(sender_user, conn.globalsince, Some(conn.next_batch))
|
||||
.map(ToOwned::to_owned)
|
||||
.collect::<HashSet<_>>()
|
||||
.map(|changed| (changed, HashSet::new()));
|
||||
|
||||
let (changed, left) = (HashSet::new(), HashSet::new());
|
||||
let (changed, left) = services
|
||||
.state_cache
|
||||
.rooms_joined(sender_user)
|
||||
.map(ToOwned::to_owned)
|
||||
.broad_filter_map(async |room_id| collect_room(sync_info, conn, &room_id).await.ok())
|
||||
.chain(once(keys_changed))
|
||||
.ready_fold((changed, left), |(mut changed, mut left), room| {
|
||||
changed.extend(room.0);
|
||||
left.extend(room.1);
|
||||
(changed, left)
|
||||
})
|
||||
.await;
|
||||
|
||||
let left = left
|
||||
.into_iter()
|
||||
.stream()
|
||||
.filter_map(async |user_id| {
|
||||
share_encrypted_room(services, sender_user, &user_id, None)
|
||||
.await
|
||||
.is_false()
|
||||
.then_some(user_id)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let device_one_time_keys_count = services
|
||||
.users
|
||||
.last_one_time_keys_update(sender_user)
|
||||
.then(|since| -> OptionFuture<_> {
|
||||
since
|
||||
.gt(&conn.globalsince)
|
||||
.then(|| {
|
||||
services
|
||||
.users
|
||||
.count_one_time_keys(sender_user, sender_device)
|
||||
})
|
||||
.into()
|
||||
})
|
||||
.map(Option::unwrap_or_default);
|
||||
|
||||
let (left, device_one_time_keys_count) = join(left, device_one_time_keys_count)
|
||||
.boxed()
|
||||
.await;
|
||||
|
||||
Ok(response::E2EE {
|
||||
device_one_time_keys_count,
|
||||
device_unused_fallback_key_types: None,
|
||||
device_lists: DeviceLists {
|
||||
changed: changed.into_iter().collect(),
|
||||
left,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "trace", skip_all, fields(room_id), ret)]
|
||||
async fn collect_room(
|
||||
SyncInfo { services, sender_user, .. }: SyncInfo<'_>,
|
||||
conn: &Connection,
|
||||
room_id: &RoomId,
|
||||
) -> Result<pair_of!(HashSet<OwnedUserId>)> {
|
||||
let current_shortstatehash = services
|
||||
.state
|
||||
.get_room_shortstatehash(room_id)
|
||||
.inspect_err(|e| error!("Room {room_id} has no state: {e}"));
|
||||
|
||||
let room_keys_changed = services
|
||||
.users
|
||||
.room_keys_changed(room_id, conn.globalsince, Some(conn.next_batch))
|
||||
.map(|(user_id, _)| user_id)
|
||||
.map(ToOwned::to_owned)
|
||||
.collect::<HashSet<_>>();
|
||||
|
||||
let (current_shortstatehash, device_list_changed) =
|
||||
join(current_shortstatehash, room_keys_changed)
|
||||
.boxed()
|
||||
.await;
|
||||
|
||||
let lists = (device_list_changed, HashSet::new());
|
||||
let Ok(current_shortstatehash) = current_shortstatehash else {
|
||||
return Ok(lists);
|
||||
};
|
||||
|
||||
if current_shortstatehash <= conn.globalsince {
|
||||
return Ok(lists);
|
||||
}
|
||||
|
||||
let Ok(since_shortstatehash) = services
|
||||
.timeline
|
||||
.prev_shortstatehash(room_id, PduCount::Normal(conn.globalsince).saturating_add(1))
|
||||
.await
|
||||
else {
|
||||
return Ok(lists);
|
||||
};
|
||||
|
||||
if since_shortstatehash == current_shortstatehash {
|
||||
return Ok(lists);
|
||||
}
|
||||
|
||||
let encrypted_room = services
|
||||
.state_accessor
|
||||
.state_get(current_shortstatehash, &StateEventType::RoomEncryption, "")
|
||||
.is_ok();
|
||||
|
||||
let since_encryption = services
|
||||
.state_accessor
|
||||
.state_get(since_shortstatehash, &StateEventType::RoomEncryption, "")
|
||||
.is_ok();
|
||||
|
||||
let sender_joined_count = services
|
||||
.state_cache
|
||||
.get_joined_count(room_id, sender_user);
|
||||
|
||||
let (encrypted_room, since_encryption, sender_joined_count) =
|
||||
join3(encrypted_room, since_encryption, sender_joined_count).await;
|
||||
|
||||
if !encrypted_room {
|
||||
return Ok(lists);
|
||||
}
|
||||
|
||||
let encrypted_since_last_sync = !since_encryption;
|
||||
let joined_since_last_sync = sender_joined_count.is_ok_and(|count| count > conn.globalsince);
|
||||
let joined_members_burst: OptionFuture<_> = (joined_since_last_sync
|
||||
|| encrypted_since_last_sync)
|
||||
.then(|| {
|
||||
services
|
||||
.state_cache
|
||||
.room_members(room_id)
|
||||
.ready_filter(|&user_id| user_id != sender_user)
|
||||
.map(ToOwned::to_owned)
|
||||
.map(|user_id| (MembershipState::Join, user_id))
|
||||
.into_future()
|
||||
})
|
||||
.into();
|
||||
|
||||
services
|
||||
.state_accessor
|
||||
.state_added((since_shortstatehash, current_shortstatehash))
|
||||
.broad_filter_map(async |(_shortstatekey, shorteventid)| {
|
||||
services
|
||||
.timeline
|
||||
.get_pdu_from_shorteventid(shorteventid)
|
||||
.ok()
|
||||
.await
|
||||
})
|
||||
.ready_filter(|event| *event.kind() == TimelineEventType::RoomMember)
|
||||
.ready_filter(|event| {
|
||||
event
|
||||
.state_key()
|
||||
.is_some_and(|state_key| state_key != sender_user)
|
||||
})
|
||||
.ready_filter_map(|event| {
|
||||
let content: RoomMemberEventContent = event.get_content().ok()?;
|
||||
let user_id: OwnedUserId = event.state_key()?.parse().ok()?;
|
||||
|
||||
Some((content.membership, user_id))
|
||||
})
|
||||
.chain(joined_members_burst.stream())
|
||||
.fold(lists, async |(mut changed, mut left), (membership, user_id)| {
|
||||
use MembershipState::*;
|
||||
|
||||
let should_add = async |user_id| {
|
||||
!share_encrypted_room(services, sender_user, user_id, Some(room_id)).await
|
||||
};
|
||||
|
||||
match membership {
|
||||
| Join if should_add(&user_id).await => changed.insert(user_id),
|
||||
| Leave => left.insert(user_id),
|
||||
| _ => false,
|
||||
};
|
||||
|
||||
(changed, left)
|
||||
})
|
||||
.map(Ok)
|
||||
.boxed()
|
||||
.await
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
use futures::{StreamExt, future::OptionFuture, pin_mut};
|
||||
use ruma::{
|
||||
RoomId, api::client::sync::sync_events::v5::request::ListFilters, directory::RoomTypeFilter,
|
||||
events::room::member::MembershipState,
|
||||
};
|
||||
use tuwunel_core::{
|
||||
is_equal_to, is_true,
|
||||
utils::{
|
||||
BoolExt, FutureBoolExt, IterStream, ReadyExt,
|
||||
future::{self, OptionExt, ReadyEqExt},
|
||||
},
|
||||
};
|
||||
|
||||
use super::SyncInfo;
|
||||
|
||||
#[tracing::instrument(name = "filter", level = "trace", skip_all)]
|
||||
pub(super) async fn filter_room(
|
||||
SyncInfo { services, sender_user, .. }: SyncInfo<'_>,
|
||||
filter: &ListFilters,
|
||||
room_id: &RoomId,
|
||||
membership: Option<&MembershipState>,
|
||||
) -> bool {
|
||||
let match_invite: OptionFuture<_> = filter
|
||||
.is_invite
|
||||
.map(async |is_invite| match (membership, is_invite) {
|
||||
| (Some(MembershipState::Invite), true) => true,
|
||||
| (Some(MembershipState::Invite), false) => false,
|
||||
| (Some(_), true) => false,
|
||||
| (Some(_), false) => true,
|
||||
| _ =>
|
||||
services
|
||||
.state_cache
|
||||
.is_invited(sender_user, room_id)
|
||||
.await == is_invite,
|
||||
})
|
||||
.into();
|
||||
|
||||
let match_direct: OptionFuture<_> = filter
|
||||
.is_dm
|
||||
.map(async |is_dm| {
|
||||
services
|
||||
.account_data
|
||||
.is_direct(sender_user, room_id)
|
||||
.await == is_dm
|
||||
})
|
||||
.into();
|
||||
|
||||
let match_direct_member: OptionFuture<_> = filter
|
||||
.is_dm
|
||||
.map(async |is_dm| {
|
||||
services
|
||||
.state_accessor
|
||||
.is_direct(room_id, sender_user)
|
||||
.await == is_dm
|
||||
})
|
||||
.into();
|
||||
|
||||
let match_encrypted: OptionFuture<_> = filter
|
||||
.is_encrypted
|
||||
.map(async |is_encrypted| {
|
||||
services
|
||||
.state_accessor
|
||||
.is_encrypted_room(room_id)
|
||||
.await == is_encrypted
|
||||
})
|
||||
.into();
|
||||
|
||||
let match_space_child: OptionFuture<_> = filter
|
||||
.spaces
|
||||
.is_empty()
|
||||
.is_false()
|
||||
.then(async || {
|
||||
filter
|
||||
.spaces
|
||||
.iter()
|
||||
.stream()
|
||||
.flat_map(|room_id| services.spaces.get_space_children(room_id))
|
||||
.ready_any(is_equal_to!(room_id))
|
||||
.await
|
||||
})
|
||||
.into();
|
||||
|
||||
let fetch_tags = !filter.tags.is_empty() || !filter.not_tags.is_empty();
|
||||
let match_room_tag: OptionFuture<_> = fetch_tags
|
||||
.then(async || {
|
||||
if let Some(tags) = services
|
||||
.account_data
|
||||
.get_room_tags(sender_user, room_id)
|
||||
.await
|
||||
.ok()
|
||||
.filter(|tags| !tags.is_empty())
|
||||
{
|
||||
tags.keys().any(|tag| {
|
||||
(filter.not_tags.is_empty() || !filter.not_tags.contains(tag))
|
||||
|| (!filter.tags.is_empty() && filter.tags.contains(tag))
|
||||
})
|
||||
} else {
|
||||
filter.tags.is_empty()
|
||||
}
|
||||
})
|
||||
.into();
|
||||
|
||||
let fetch_room_type = !filter.room_types.is_empty() || !filter.not_room_types.is_empty();
|
||||
let match_room_type: OptionFuture<_> = fetch_room_type
|
||||
.then(async || {
|
||||
let room_type = services
|
||||
.state_accessor
|
||||
.get_room_type(room_id)
|
||||
.await
|
||||
.ok();
|
||||
|
||||
let room_type = RoomTypeFilter::from(room_type);
|
||||
(filter.not_room_types.is_empty() || !filter.not_room_types.contains(&room_type))
|
||||
&& (filter.room_types.is_empty() || filter.room_types.contains(&room_type))
|
||||
})
|
||||
.into();
|
||||
|
||||
future::and7(
|
||||
match_invite.is_none_or(is_true!()),
|
||||
match_encrypted.is_none_or(is_true!()),
|
||||
match_direct.is_none_or(is_true!()),
|
||||
match_direct_member.is_none_or(is_true!()),
|
||||
match_space_child.is_none_or(is_true!()),
|
||||
match_room_type.is_none_or(is_true!()),
|
||||
match_room_tag.is_none_or(is_true!()),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[tracing::instrument(name = "filter_meta", level = "trace", skip_all)]
|
||||
pub(super) async fn filter_room_meta(
|
||||
SyncInfo { services, sender_user, .. }: SyncInfo<'_>,
|
||||
room_id: &RoomId,
|
||||
) -> bool {
|
||||
let not_exists = services.metadata.exists(room_id).eq(&false);
|
||||
|
||||
let is_disabled = services.metadata.is_disabled(room_id);
|
||||
|
||||
let is_banned = services.metadata.is_banned(room_id);
|
||||
|
||||
let not_visible = services
|
||||
.state_accessor
|
||||
.user_can_see_state_events(sender_user, room_id)
|
||||
.eq(&false);
|
||||
|
||||
pin_mut!(not_visible, not_exists, is_disabled, is_banned);
|
||||
not_visible
|
||||
.or(not_exists)
|
||||
.or(is_disabled)
|
||||
.or(is_banned)
|
||||
.await
|
||||
.eq(&false)
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use ruma::{
|
||||
OwnedRoomId, RoomId,
|
||||
api::client::sync::sync_events::v5::response,
|
||||
events::{AnySyncEphemeralRoomEvent, receipt::SyncReceiptEvent},
|
||||
serde::Raw,
|
||||
};
|
||||
use tuwunel_core::{
|
||||
Result,
|
||||
utils::{BoolExt, IterStream, stream::BroadbandExt},
|
||||
};
|
||||
use tuwunel_service::{rooms::read_receipt::pack_receipts, sync::Room};
|
||||
|
||||
use super::{Connection, SyncInfo, Window, extension_rooms_selector};
|
||||
|
||||
#[tracing::instrument(name = "receipts", level = "trace", skip_all)]
|
||||
pub(super) async fn collect(
|
||||
sync_info: SyncInfo<'_>,
|
||||
conn: &Connection,
|
||||
window: &Window,
|
||||
) -> Result<response::Receipts> {
|
||||
let SyncInfo { .. } = sync_info;
|
||||
|
||||
let implicit = conn
|
||||
.extensions
|
||||
.receipts
|
||||
.lists
|
||||
.as_deref()
|
||||
.map(<[_]>::iter);
|
||||
|
||||
let explicit = conn
|
||||
.extensions
|
||||
.receipts
|
||||
.rooms
|
||||
.as_deref()
|
||||
.map(<[_]>::iter);
|
||||
|
||||
let rooms = extension_rooms_selector(sync_info, conn, window, implicit, explicit)
|
||||
.stream()
|
||||
.broad_filter_map(|room_id| collect_room(sync_info, conn, window, room_id))
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
Ok(response::Receipts { rooms })
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "trace", skip_all, fields(room_id), ret)]
|
||||
async fn collect_room(
|
||||
SyncInfo { services, sender_user, .. }: SyncInfo<'_>,
|
||||
conn: &Connection,
|
||||
_window: &Window,
|
||||
room_id: &RoomId,
|
||||
) -> Option<(OwnedRoomId, Raw<SyncReceiptEvent>)> {
|
||||
let &Room { roomsince, .. } = conn.rooms.get(room_id)?;
|
||||
let private_receipt = services
|
||||
.read_receipt
|
||||
.last_privateread_update(sender_user, room_id)
|
||||
.then(async |last_private_update| {
|
||||
if last_private_update <= roomsince || last_private_update > conn.next_batch {
|
||||
return None;
|
||||
}
|
||||
|
||||
services
|
||||
.read_receipt
|
||||
.private_read_get(room_id, sender_user)
|
||||
.map(Some)
|
||||
.await
|
||||
})
|
||||
.map(Option::into_iter)
|
||||
.map(Iterator::flatten)
|
||||
.map(IterStream::stream)
|
||||
.flatten_stream();
|
||||
|
||||
let receipts: Vec<Raw<AnySyncEphemeralRoomEvent>> = services
|
||||
.read_receipt
|
||||
.readreceipts_since(room_id, roomsince, Some(conn.next_batch))
|
||||
.filter_map(async |(read_user, _ts, v)| {
|
||||
services
|
||||
.users
|
||||
.user_is_ignored(read_user, sender_user)
|
||||
.await
|
||||
.or_some(v)
|
||||
})
|
||||
.chain(private_receipt)
|
||||
.collect()
|
||||
.boxed()
|
||||
.await;
|
||||
|
||||
receipts
|
||||
.is_empty()
|
||||
.is_false()
|
||||
.then(|| (room_id.to_owned(), pack_receipts(receipts.into_iter())))
|
||||
}
|
||||
@@ -0,0 +1,415 @@
|
||||
use std::{cmp::Ordering, collections::HashSet};
|
||||
|
||||
use futures::{
|
||||
FutureExt, StreamExt, TryFutureExt, TryStreamExt,
|
||||
future::{OptionFuture, join, join3, join4},
|
||||
};
|
||||
use ruma::{
|
||||
JsOption, MxcUri, OwnedMxcUri, RoomId, UserId,
|
||||
api::client::sync::sync_events::{
|
||||
UnreadNotificationsCount,
|
||||
v5::{DisplayName, response, response::Heroes},
|
||||
},
|
||||
events::{
|
||||
StateEventType,
|
||||
TimelineEventType::{
|
||||
self, Beacon, CallInvite, PollStart, RoomEncrypted, RoomMessage, Sticker,
|
||||
},
|
||||
room::member::MembershipState,
|
||||
},
|
||||
};
|
||||
use tuwunel_core::{
|
||||
Result, at, err, is_equal_to,
|
||||
matrix::{Event, StateKey, pdu::PduCount},
|
||||
ref_at,
|
||||
utils::{
|
||||
BoolExt, IterStream, ReadyExt, TryFutureExtExt, math::usize_from_ruma, result::FlatOk,
|
||||
stream::BroadbandExt,
|
||||
},
|
||||
};
|
||||
use tuwunel_service::{Services, sync::Room};
|
||||
|
||||
use super::{super::load_timeline, Connection, SyncInfo, WindowRoom};
|
||||
use crate::client::ignored_filter;
|
||||
|
||||
static DEFAULT_BUMP_TYPES: [TimelineEventType; 6] =
|
||||
[CallInvite, PollStart, Beacon, RoomEncrypted, RoomMessage, Sticker];
|
||||
|
||||
#[tracing::instrument(
|
||||
name = "room",
|
||||
level = "debug",
|
||||
skip_all,
|
||||
fields(room_id, roomsince)
|
||||
)]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(super) async fn handle(
|
||||
SyncInfo { services, sender_user, .. }: SyncInfo<'_>,
|
||||
conn: &Connection,
|
||||
WindowRoom {
|
||||
lists, membership, room_id, last_count, ..
|
||||
}: &WindowRoom,
|
||||
) -> Result<Option<response::Room>> {
|
||||
debug_assert!(
|
||||
DEFAULT_BUMP_TYPES.is_sorted(),
|
||||
"DEFAULT_BUMP_TYPES must be sorted for binary search"
|
||||
);
|
||||
|
||||
let &Room { roomsince, .. } = conn
|
||||
.rooms
|
||||
.get(room_id)
|
||||
.ok_or_else(|| err!("Missing connection state for {room_id}"))?;
|
||||
|
||||
debug_assert!(
|
||||
*last_count > roomsince || *last_count == 0 || roomsince == 0,
|
||||
"Stale room shouldn't be in the window"
|
||||
);
|
||||
|
||||
if *membership == Some(MembershipState::Leave) {
|
||||
return Ok(Some(response::Room {
|
||||
initial: roomsince.eq(&0).then_some(true),
|
||||
lists: lists.clone(),
|
||||
membership: membership.clone(),
|
||||
prev_batch: Some(conn.next_batch.to_string().into()),
|
||||
limited: true,
|
||||
required_state: vec![
|
||||
services
|
||||
.state_accessor
|
||||
.room_state_get(room_id, &StateEventType::RoomMember, sender_user.as_str())
|
||||
.map_ok(Event::into_format)
|
||||
.await?,
|
||||
],
|
||||
|
||||
..Default::default()
|
||||
}));
|
||||
}
|
||||
|
||||
let is_invite = *membership == Some(MembershipState::Invite);
|
||||
let default_details = (0_usize, HashSet::new());
|
||||
let (timeline_limit, required_state) = lists
|
||||
.iter()
|
||||
.filter_map(|list_id| conn.lists.get(list_id))
|
||||
.map(|list| &list.room_details)
|
||||
.chain(conn.subscriptions.get(room_id).into_iter())
|
||||
.fold(default_details, |(mut timeline_limit, mut required_state), config| {
|
||||
let limit = usize_from_ruma(config.timeline_limit);
|
||||
|
||||
timeline_limit = timeline_limit.max(limit);
|
||||
required_state.extend(config.required_state.clone());
|
||||
|
||||
(timeline_limit, required_state)
|
||||
});
|
||||
|
||||
let timeline: OptionFuture<_> = is_invite
|
||||
.is_false()
|
||||
.then(|| {
|
||||
load_timeline(
|
||||
services,
|
||||
sender_user,
|
||||
room_id,
|
||||
PduCount::Normal(roomsince),
|
||||
Some(PduCount::from(conn.next_batch)),
|
||||
timeline_limit,
|
||||
)
|
||||
})
|
||||
.into();
|
||||
|
||||
let (timeline_pdus, limited, _lastcount) = timeline
|
||||
.await
|
||||
.flat_ok()
|
||||
.unwrap_or_else(|| (Vec::new(), true, PduCount::default()));
|
||||
|
||||
let required_state = required_state
|
||||
.into_iter()
|
||||
.filter(|_| !timeline_pdus.is_empty())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let prev_batch = timeline_pdus
|
||||
.first()
|
||||
.map(at!(0))
|
||||
.map(PduCount::into_unsigned)
|
||||
.as_ref()
|
||||
.map(ToString::to_string);
|
||||
|
||||
let bump_stamp = timeline_pdus
|
||||
.iter()
|
||||
.filter(|(_, pdu)| {
|
||||
if *pdu.event_type() == TimelineEventType::RoomMember {
|
||||
return pdu
|
||||
.state_key()
|
||||
.is_some_and(is_equal_to!(sender_user.as_str()));
|
||||
}
|
||||
|
||||
DEFAULT_BUMP_TYPES
|
||||
.binary_search(pdu.event_type())
|
||||
.is_ok()
|
||||
})
|
||||
.map(at!(0))
|
||||
.filter(|count| matches!(count, PduCount::Normal(_)))
|
||||
.map(PduCount::into_unsigned)
|
||||
.max()
|
||||
.map(TryInto::try_into)
|
||||
.flat_ok();
|
||||
|
||||
let num_live: OptionFuture<_> = roomsince
|
||||
.ne(&0)
|
||||
.and_is(limited || timeline_pdus.len() >= timeline_limit)
|
||||
.then(|| {
|
||||
services
|
||||
.timeline
|
||||
.pdus(None, room_id, Some(roomsince.into()))
|
||||
.count()
|
||||
.map(TryInto::try_into)
|
||||
.map(Result::ok)
|
||||
})
|
||||
.into();
|
||||
|
||||
let lazy = required_state
|
||||
.iter()
|
||||
.any(is_equal_to!(&(StateEventType::RoomMember, "$LAZY".into())));
|
||||
|
||||
let mut timeline_senders: Vec<_> = timeline_pdus
|
||||
.iter()
|
||||
.filter(|_| lazy)
|
||||
.map(ref_at!(1))
|
||||
.map(Event::sender)
|
||||
.collect();
|
||||
|
||||
timeline_senders.sort();
|
||||
timeline_senders.dedup();
|
||||
let timeline_senders = timeline_senders
|
||||
.iter()
|
||||
.map(|sender| (StateEventType::RoomMember, StateKey::from_str(sender.as_str())))
|
||||
.stream();
|
||||
|
||||
let wildcard_state = required_state
|
||||
.iter()
|
||||
.filter(|(_, state_key)| state_key == "*")
|
||||
.map(|(event_type, _)| {
|
||||
services
|
||||
.state_accessor
|
||||
.room_state_keys(room_id, event_type)
|
||||
.map_ok(|state_key| (event_type.clone(), state_key))
|
||||
.ready_filter_map(Result::ok)
|
||||
})
|
||||
.stream()
|
||||
.flatten();
|
||||
|
||||
let required_state = required_state
|
||||
.iter()
|
||||
.cloned()
|
||||
.stream()
|
||||
.chain(wildcard_state)
|
||||
.chain(timeline_senders)
|
||||
.broad_filter_map(async |state| {
|
||||
let state_key: StateKey = match state.1.as_str() {
|
||||
| "$LAZY" | "*" => return None,
|
||||
| "$ME" => sender_user.as_str().into(),
|
||||
| _ => state.1.clone(),
|
||||
};
|
||||
|
||||
services
|
||||
.state_accessor
|
||||
.room_state_get(room_id, &state.0, &state_key)
|
||||
.map_ok(Event::into_format)
|
||||
.ok()
|
||||
.await
|
||||
})
|
||||
.collect();
|
||||
|
||||
// TODO: figure out a timestamp we can use for remote invites
|
||||
let invite_state: OptionFuture<_> = is_invite
|
||||
.then(|| {
|
||||
services
|
||||
.state_cache
|
||||
.invite_state(sender_user, room_id)
|
||||
.ok()
|
||||
})
|
||||
.into();
|
||||
|
||||
let room_name = services
|
||||
.state_accessor
|
||||
.get_name(room_id)
|
||||
.map_ok(Into::into)
|
||||
.map(Result::ok);
|
||||
|
||||
let room_avatar = services
|
||||
.state_accessor
|
||||
.get_avatar(room_id)
|
||||
.map_ok(|content| content.url)
|
||||
.ok()
|
||||
.map(Option::flatten);
|
||||
|
||||
let highlight_count = services
|
||||
.user
|
||||
.highlight_count(sender_user, room_id)
|
||||
.map(TryInto::try_into)
|
||||
.map(Result::ok);
|
||||
|
||||
let notification_count = services
|
||||
.user
|
||||
.notification_count(sender_user, room_id)
|
||||
.map(TryInto::try_into)
|
||||
.map(Result::ok);
|
||||
|
||||
let joined_count = services
|
||||
.state_cache
|
||||
.room_joined_count(room_id)
|
||||
.map_ok(TryInto::try_into)
|
||||
.map_ok(Result::ok)
|
||||
.map(FlatOk::flat_ok);
|
||||
|
||||
let invited_count = services
|
||||
.state_cache
|
||||
.room_invited_count(room_id)
|
||||
.map_ok(TryInto::try_into)
|
||||
.map_ok(Result::ok)
|
||||
.map(FlatOk::flat_ok);
|
||||
|
||||
let is_dm = services
|
||||
.state_accessor
|
||||
.is_direct(room_id, sender_user)
|
||||
.map(|is_dm| is_dm.then_some(is_dm));
|
||||
|
||||
let last_read_count = services
|
||||
.user
|
||||
.last_notification_read(sender_user, room_id);
|
||||
|
||||
let timeline = timeline_pdus
|
||||
.iter()
|
||||
.stream()
|
||||
.filter_map(|item| ignored_filter(services, item.clone(), sender_user))
|
||||
.map(at!(1))
|
||||
.map(Event::into_format)
|
||||
.collect();
|
||||
|
||||
let meta = join3(room_name, room_avatar, is_dm);
|
||||
let events = join4(timeline, num_live, required_state, invite_state);
|
||||
let member_counts = join(joined_count, invited_count);
|
||||
let notification_counts = join3(highlight_count, notification_count, last_read_count);
|
||||
let (
|
||||
(room_name, room_avatar, is_dm),
|
||||
(timeline, num_live, required_state, invite_state),
|
||||
(joined_count, invited_count),
|
||||
(highlight_count, notification_count, _last_notification_read),
|
||||
) = join4(meta, events, member_counts, notification_counts)
|
||||
.boxed()
|
||||
.await;
|
||||
|
||||
let (heroes, hero_name, heroes_avatar) = calculate_heroes(
|
||||
services,
|
||||
sender_user,
|
||||
room_id,
|
||||
room_name.as_ref(),
|
||||
room_avatar.as_deref(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(Some(response::Room {
|
||||
initial: roomsince.eq(&0).then_some(true),
|
||||
lists: lists.clone(),
|
||||
membership: membership.clone(),
|
||||
name: room_name.or(hero_name),
|
||||
avatar: JsOption::from_option(room_avatar.or(heroes_avatar)),
|
||||
is_dm,
|
||||
heroes,
|
||||
required_state,
|
||||
invite_state: invite_state.flatten(),
|
||||
prev_batch: prev_batch.as_deref().map(Into::into),
|
||||
num_live: num_live.flatten(),
|
||||
limited,
|
||||
timeline,
|
||||
bump_stamp,
|
||||
joined_count,
|
||||
invited_count,
|
||||
unread_notifications: UnreadNotificationsCount { highlight_count, notification_count },
|
||||
}))
|
||||
}
|
||||
|
||||
#[tracing::instrument(name = "heroes", level = "trace", skip_all)]
|
||||
#[allow(clippy::type_complexity)]
|
||||
async fn calculate_heroes(
|
||||
services: &Services,
|
||||
sender_user: &UserId,
|
||||
room_id: &RoomId,
|
||||
room_name: Option<&DisplayName>,
|
||||
room_avatar: Option<&MxcUri>,
|
||||
) -> Result<(Option<Heroes>, Option<DisplayName>, Option<OwnedMxcUri>)> {
|
||||
const MAX_HEROES: usize = 5;
|
||||
let heroes: Heroes = services
|
||||
.state_cache
|
||||
.room_members(room_id)
|
||||
.ready_filter(|&member| member != sender_user)
|
||||
.ready_filter_map(|member| room_name.is_none().then_some(member))
|
||||
.map(ToOwned::to_owned)
|
||||
.broadn_filter_map(MAX_HEROES, async |user_id| {
|
||||
let content = services
|
||||
.state_accessor
|
||||
.get_member(room_id, &user_id)
|
||||
.await
|
||||
.ok()?;
|
||||
|
||||
let name: OptionFuture<_> = content
|
||||
.displayname
|
||||
.is_none()
|
||||
.then(|| services.users.displayname(&user_id).ok())
|
||||
.into();
|
||||
|
||||
let avatar: OptionFuture<_> = content
|
||||
.avatar_url
|
||||
.is_none()
|
||||
.then(|| services.users.avatar_url(&user_id).ok())
|
||||
.into();
|
||||
|
||||
let (name, avatar) = join(name, avatar).await;
|
||||
let hero = response::Hero {
|
||||
user_id,
|
||||
avatar: avatar.unwrap_or(content.avatar_url),
|
||||
name: name
|
||||
.unwrap_or(content.displayname)
|
||||
.map(Into::into),
|
||||
};
|
||||
|
||||
Some(hero)
|
||||
})
|
||||
.take(MAX_HEROES)
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
let hero_name = match heroes.len().cmp(&(1_usize)) {
|
||||
| Ordering::Less => None,
|
||||
| Ordering::Equal => Some(
|
||||
heroes[0]
|
||||
.name
|
||||
.clone()
|
||||
.unwrap_or_else(|| heroes[0].user_id.as_str().into()),
|
||||
),
|
||||
| Ordering::Greater => {
|
||||
let firsts = heroes[1..]
|
||||
.iter()
|
||||
.map(|h| {
|
||||
h.name
|
||||
.clone()
|
||||
.unwrap_or_else(|| h.user_id.as_str().into())
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
|
||||
let last = heroes[0]
|
||||
.name
|
||||
.clone()
|
||||
.unwrap_or_else(|| heroes[0].user_id.as_str().into());
|
||||
|
||||
Some(format!("{firsts} and {last}")).map(Into::into)
|
||||
},
|
||||
};
|
||||
|
||||
let heroes_avatar = (room_avatar.is_none() && room_name.is_none())
|
||||
.then(|| {
|
||||
heroes
|
||||
.first()
|
||||
.and_then(|hero| hero.avatar.clone())
|
||||
})
|
||||
.flatten();
|
||||
|
||||
Ok((Some(heroes), hero_name, heroes_avatar))
|
||||
}
|
||||
@@ -0,0 +1,266 @@
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use futures::{
|
||||
FutureExt, StreamExt, TryFutureExt,
|
||||
future::{OptionFuture, join5},
|
||||
};
|
||||
use ruma::{OwnedRoomId, UInt, events::room::member::MembershipState, uint};
|
||||
use tuwunel_core::{
|
||||
apply, is_true,
|
||||
matrix::PduCount,
|
||||
trace,
|
||||
utils::{
|
||||
BoolExt,
|
||||
future::TryExtExt,
|
||||
math::usize_from_ruma,
|
||||
stream::{BroadbandExt, IterStream},
|
||||
},
|
||||
};
|
||||
use tuwunel_service::sync::Connection;
|
||||
|
||||
use super::{
|
||||
ListIds, ResponseLists, SyncInfo, Window, WindowRoom, filter_room, filter_room_meta,
|
||||
};
|
||||
|
||||
#[tracing::instrument(level = "debug", skip_all)]
|
||||
pub(super) async fn selector(
|
||||
conn: &mut Connection,
|
||||
sync_info: SyncInfo<'_>,
|
||||
) -> (Window, ResponseLists) {
|
||||
use MembershipState::*;
|
||||
|
||||
let SyncInfo { services, sender_user, .. } = sync_info;
|
||||
|
||||
let mut rooms = services
|
||||
.state_cache
|
||||
.user_memberships(sender_user, Some(&[Join, Invite, Knock]))
|
||||
.map(|(membership, room_id)| (room_id.to_owned(), Some(membership)))
|
||||
.broad_filter_map(|(room_id, membership)| {
|
||||
match_lists_for_room(sync_info, conn, room_id, membership)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.await;
|
||||
|
||||
rooms.sort_by(room_sort);
|
||||
rooms
|
||||
.iter_mut()
|
||||
.enumerate()
|
||||
.for_each(|(i, room)| {
|
||||
room.ranked = i;
|
||||
|
||||
conn.rooms
|
||||
.entry(room.room_id.clone())
|
||||
.or_default();
|
||||
});
|
||||
|
||||
trace!(?rooms);
|
||||
let lists = response_lists(rooms.iter());
|
||||
|
||||
trace!(?lists);
|
||||
let window = select_window(sync_info, conn, rooms.iter(), &lists).await;
|
||||
|
||||
trace!(?window);
|
||||
(window, lists)
|
||||
}
|
||||
|
||||
#[tracing::instrument(
|
||||
name = "window",
|
||||
level = "debug",
|
||||
skip_all,
|
||||
fields(rooms = rooms.clone().count())
|
||||
)]
|
||||
async fn select_window<'a, Rooms>(
|
||||
sync_info: SyncInfo<'_>,
|
||||
conn: &Connection,
|
||||
rooms: Rooms,
|
||||
lists: &ResponseLists,
|
||||
) -> Window
|
||||
where
|
||||
Rooms: Iterator<Item = &'a WindowRoom> + Clone + Send + Sync,
|
||||
{
|
||||
static FULL_RANGE: (UInt, UInt) = (UInt::MIN, UInt::MAX);
|
||||
|
||||
let SyncInfo { services, sender_user, .. } = sync_info;
|
||||
|
||||
let selections = lists
|
||||
.keys()
|
||||
.cloned()
|
||||
.filter_map(|id| conn.lists.get(&id).map(|list| (id, list)))
|
||||
.flat_map(|(id, list)| {
|
||||
let full_range = list
|
||||
.ranges
|
||||
.is_empty()
|
||||
.then_some(&FULL_RANGE)
|
||||
.into_iter();
|
||||
|
||||
list.ranges
|
||||
.iter()
|
||||
.chain(full_range)
|
||||
.map(apply!(2, usize_from_ruma))
|
||||
.map(move |range| (id.clone(), range))
|
||||
})
|
||||
.flat_map(|(id, (start, end))| {
|
||||
rooms
|
||||
.clone()
|
||||
.filter(move |&room| room.lists.contains(&id))
|
||||
.filter(|&room| {
|
||||
conn.rooms
|
||||
.get(&room.room_id)
|
||||
.is_some_and(|conn_room| {
|
||||
conn_room.roomsince == 0 || room.last_count > conn_room.roomsince
|
||||
})
|
||||
})
|
||||
.enumerate()
|
||||
.skip_while(move |&(i, _)| i < start)
|
||||
.take(end.saturating_add(1).saturating_sub(start))
|
||||
.map(|(_, room)| (room.room_id.clone(), room.clone()))
|
||||
})
|
||||
.stream();
|
||||
|
||||
let subscriptions = conn
|
||||
.subscriptions
|
||||
.iter()
|
||||
.stream()
|
||||
.broad_filter_map(async |(room_id, _)| {
|
||||
filter_room_meta(sync_info, room_id)
|
||||
.await
|
||||
.into_option()?;
|
||||
|
||||
Some(WindowRoom {
|
||||
room_id: room_id.clone(),
|
||||
lists: Default::default(),
|
||||
ranked: usize::MAX,
|
||||
last_count: 0,
|
||||
membership: services
|
||||
.state_cache
|
||||
.user_membership(sender_user, room_id)
|
||||
.await,
|
||||
})
|
||||
})
|
||||
.map(|room| (room.room_id.clone(), room));
|
||||
|
||||
subscriptions.chain(selections).collect().await
|
||||
}
|
||||
|
||||
#[tracing::instrument(
|
||||
name = "matcher",
|
||||
level = "trace",
|
||||
skip_all,
|
||||
fields(?room_id, ?membership)
|
||||
)]
|
||||
async fn match_lists_for_room(
|
||||
sync_info: SyncInfo<'_>,
|
||||
conn: &Connection,
|
||||
room_id: OwnedRoomId,
|
||||
membership: Option<MembershipState>,
|
||||
) -> Option<WindowRoom> {
|
||||
let SyncInfo { services, sender_user, .. } = sync_info;
|
||||
|
||||
let (matched, lists) = conn
|
||||
.lists
|
||||
.iter()
|
||||
.stream()
|
||||
.filter_map(async |(id, list)| {
|
||||
let filter: OptionFuture<_> = list
|
||||
.filters
|
||||
.clone()
|
||||
.map(async |filters| {
|
||||
filter_room(sync_info, &filters, &room_id, membership.as_ref()).await
|
||||
})
|
||||
.into();
|
||||
|
||||
filter
|
||||
.await
|
||||
.is_none_or(is_true!())
|
||||
.then(|| id.clone())
|
||||
})
|
||||
.collect::<ListIds>()
|
||||
.map(|lists| (lists.is_empty().is_false(), lists))
|
||||
.await;
|
||||
|
||||
let last_notification: OptionFuture<_> = matched
|
||||
.then(|| {
|
||||
services
|
||||
.user
|
||||
.last_notification_read(sender_user, &room_id)
|
||||
})
|
||||
.into();
|
||||
|
||||
let last_privateread: OptionFuture<_> = matched
|
||||
.then(|| {
|
||||
services
|
||||
.read_receipt
|
||||
.last_privateread_update(sender_user, &room_id)
|
||||
})
|
||||
.into();
|
||||
|
||||
let last_receipt: OptionFuture<_> = matched
|
||||
.then(|| {
|
||||
services
|
||||
.read_receipt
|
||||
.last_receipt_count(&room_id, sender_user.into(), None)
|
||||
.unwrap_or_default()
|
||||
})
|
||||
.into();
|
||||
|
||||
let last_account: OptionFuture<_> = matched
|
||||
.then(|| {
|
||||
services
|
||||
.account_data
|
||||
.last_count(Some(room_id.as_ref()), sender_user, Some(conn.next_batch))
|
||||
.unwrap_or_default()
|
||||
})
|
||||
.into();
|
||||
|
||||
let last_timeline: OptionFuture<_> = matched
|
||||
.then(|| {
|
||||
services
|
||||
.timeline
|
||||
.last_timeline_count(None, &room_id, Some(conn.next_batch.into()))
|
||||
.map_ok(PduCount::into_unsigned)
|
||||
.unwrap_or_default()
|
||||
})
|
||||
.into();
|
||||
|
||||
let (last_timeline, last_notification, last_account, last_receipt, last_privateread) =
|
||||
join5(last_timeline, last_notification, last_account, last_receipt, last_privateread)
|
||||
.await;
|
||||
|
||||
Some(WindowRoom {
|
||||
room_id: room_id.clone(),
|
||||
membership,
|
||||
lists,
|
||||
ranked: 0,
|
||||
last_count: [
|
||||
last_timeline,
|
||||
last_notification,
|
||||
last_account,
|
||||
last_receipt,
|
||||
last_privateread,
|
||||
]
|
||||
.into_iter()
|
||||
.map(Option::unwrap_or_default)
|
||||
.filter(|count| conn.next_batch.ge(count))
|
||||
.max()
|
||||
.unwrap_or_default(),
|
||||
})
|
||||
}
|
||||
|
||||
fn response_lists<'a, Rooms>(rooms: Rooms) -> ResponseLists
|
||||
where
|
||||
Rooms: Iterator<Item = &'a WindowRoom>,
|
||||
{
|
||||
rooms
|
||||
.flat_map(|room| room.lists.iter())
|
||||
.fold(ResponseLists::default(), |mut lists, id| {
|
||||
let list = lists.entry(id.clone()).or_default();
|
||||
list.count = list
|
||||
.count
|
||||
.checked_add(uint!(1))
|
||||
.expect("list count must not overflow JsInt");
|
||||
|
||||
lists
|
||||
})
|
||||
}
|
||||
|
||||
fn room_sort(a: &WindowRoom, b: &WindowRoom) -> Ordering { b.last_count.cmp(&a.last_count) }
|
||||
@@ -0,0 +1,32 @@
|
||||
use futures::StreamExt;
|
||||
use ruma::api::client::sync::sync_events::v5::response;
|
||||
use tuwunel_core::{self, Result};
|
||||
|
||||
use super::{Connection, SyncInfo};
|
||||
|
||||
#[tracing::instrument(name = "to_device", level = "trace", skip_all, ret)]
|
||||
pub(super) async fn collect(
|
||||
SyncInfo { services, sender_user, sender_device, .. }: SyncInfo<'_>,
|
||||
conn: &Connection,
|
||||
) -> Result<Option<response::ToDevice>> {
|
||||
services
|
||||
.users
|
||||
.remove_to_device_events(sender_user, sender_device, conn.globalsince)
|
||||
.await;
|
||||
|
||||
let events: Vec<_> = services
|
||||
.users
|
||||
.get_to_device_events(sender_user, sender_device, None, Some(conn.next_batch))
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
let to_device = events
|
||||
.is_empty()
|
||||
.eq(&false)
|
||||
.then(|| response::ToDevice {
|
||||
next_batch: conn.next_batch.to_string().into(),
|
||||
events,
|
||||
});
|
||||
|
||||
Ok(to_device)
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use futures::{FutureExt, StreamExt, TryFutureExt};
|
||||
use ruma::{
|
||||
api::client::sync::sync_events::v5::response,
|
||||
events::typing::{SyncTypingEvent, TypingEventContent},
|
||||
serde::Raw,
|
||||
};
|
||||
use tuwunel_core::{
|
||||
Result, debug_error,
|
||||
utils::{IterStream, ReadyExt},
|
||||
};
|
||||
|
||||
use super::{Connection, SyncInfo, Window, extension_rooms_selector};
|
||||
|
||||
#[tracing::instrument(name = "typing", level = "trace", skip_all, ret)]
|
||||
pub(super) async fn collect(
|
||||
sync_info: SyncInfo<'_>,
|
||||
conn: &Connection,
|
||||
window: &Window,
|
||||
) -> Result<response::Typing> {
|
||||
use response::Typing;
|
||||
|
||||
let SyncInfo { services, sender_user, .. } = sync_info;
|
||||
|
||||
let implicit = conn
|
||||
.extensions
|
||||
.typing
|
||||
.lists
|
||||
.as_deref()
|
||||
.map(<[_]>::iter);
|
||||
|
||||
let explicit = conn
|
||||
.extensions
|
||||
.typing
|
||||
.rooms
|
||||
.as_deref()
|
||||
.map(<[_]>::iter);
|
||||
|
||||
extension_rooms_selector(sync_info, conn, window, implicit, explicit)
|
||||
.stream()
|
||||
.filter_map(async |room_id| {
|
||||
services
|
||||
.typing
|
||||
.typing_users_for_user(room_id, sender_user)
|
||||
.inspect_err(|e| debug_error!(%room_id, "Failed to get typing events: {e}"))
|
||||
.await
|
||||
.ok()
|
||||
.filter(|users| !users.is_empty())
|
||||
.map(|users| (room_id, users))
|
||||
})
|
||||
.ready_filter_map(|(room_id, users)| {
|
||||
let content = TypingEventContent::new(users);
|
||||
let event = SyncTypingEvent { content };
|
||||
let event = Raw::new(&event);
|
||||
|
||||
Some((room_id.to_owned(), event.ok()?))
|
||||
})
|
||||
.collect::<BTreeMap<_, _>>()
|
||||
.map(|rooms| Typing { rooms })
|
||||
.map(Ok)
|
||||
.await
|
||||
}
|
||||
@@ -28,7 +28,7 @@ pub(crate) async fn create_typing_event_route(
|
||||
|
||||
match body.state {
|
||||
| Typing::Yes(duration) => {
|
||||
let duration = utils::clamp(
|
||||
let duration = Ord::clamp(
|
||||
duration
|
||||
.as_millis()
|
||||
.try_into()
|
||||
@@ -64,12 +64,10 @@ pub(crate) async fn create_typing_event_route(
|
||||
}
|
||||
|
||||
// ping presence
|
||||
if services.config.allow_local_presence {
|
||||
services
|
||||
.presence
|
||||
.ping_presence(&body.user_id, &ruma::presence::PresenceState::Online)
|
||||
.await?;
|
||||
}
|
||||
services
|
||||
.presence
|
||||
.maybe_ping_presence(&body.user_id, &ruma::presence::PresenceState::Online)
|
||||
.await?;
|
||||
|
||||
Ok(create_typing_event::v3::Response {})
|
||||
}
|
||||
|
||||
+20
-28
@@ -73,13 +73,11 @@ pub(crate) async fn delete_timezone_key_route(
|
||||
|
||||
services.users.set_timezone(&body.user_id, None);
|
||||
|
||||
if services.config.allow_local_presence {
|
||||
// Presence update
|
||||
services
|
||||
.presence
|
||||
.ping_presence(&body.user_id, &PresenceState::Online)
|
||||
.await?;
|
||||
}
|
||||
// Presence update
|
||||
services
|
||||
.presence
|
||||
.maybe_ping_presence(&body.user_id, &PresenceState::Online)
|
||||
.await?;
|
||||
|
||||
Ok(delete_timezone_key::unstable::Response {})
|
||||
}
|
||||
@@ -103,13 +101,11 @@ pub(crate) async fn set_timezone_key_route(
|
||||
.users
|
||||
.set_timezone(&body.user_id, body.tz.clone());
|
||||
|
||||
if services.config.allow_local_presence {
|
||||
// Presence update
|
||||
services
|
||||
.presence
|
||||
.ping_presence(&body.user_id, &PresenceState::Online)
|
||||
.await?;
|
||||
}
|
||||
// Presence update
|
||||
services
|
||||
.presence
|
||||
.maybe_ping_presence(&body.user_id, &PresenceState::Online)
|
||||
.await?;
|
||||
|
||||
Ok(set_timezone_key::unstable::Response {})
|
||||
}
|
||||
@@ -171,13 +167,11 @@ pub(crate) async fn set_profile_field_route(
|
||||
);
|
||||
}
|
||||
|
||||
if services.config.allow_local_presence {
|
||||
// Presence update
|
||||
services
|
||||
.presence
|
||||
.ping_presence(&body.user_id, &PresenceState::Online)
|
||||
.await?;
|
||||
}
|
||||
// Presence update
|
||||
services
|
||||
.presence
|
||||
.maybe_ping_presence(&body.user_id, &PresenceState::Online)
|
||||
.await?;
|
||||
|
||||
Ok(set_profile_field::v3::Response {})
|
||||
}
|
||||
@@ -227,13 +221,11 @@ pub(crate) async fn delete_profile_field_route(
|
||||
.set_profile_key(&body.user_id, body.field.as_str(), None);
|
||||
}
|
||||
|
||||
if services.config.allow_local_presence {
|
||||
// Presence update
|
||||
services
|
||||
.presence
|
||||
.ping_presence(&body.user_id, &PresenceState::Online)
|
||||
.await?;
|
||||
}
|
||||
// Presence update
|
||||
services
|
||||
.presence
|
||||
.maybe_ping_presence(&body.user_id, &PresenceState::Online)
|
||||
.await?;
|
||||
|
||||
Ok(delete_profile_field::v3::Response {})
|
||||
}
|
||||
|
||||
@@ -25,12 +25,12 @@ pub(crate) async fn turn_server_route(
|
||||
return Err!(Request(NotFound("Not Found")));
|
||||
}
|
||||
|
||||
let turn_secret = services.globals.turn_secret.clone();
|
||||
let turn_secret = &services.config.turn_secret;
|
||||
|
||||
let (username, password) = if !turn_secret.is_empty() {
|
||||
let expiry = SecondsSinceUnixEpoch::from_system_time(
|
||||
SystemTime::now()
|
||||
.checked_add(Duration::from_secs(services.globals.turn_ttl()))
|
||||
.checked_add(Duration::from_secs(services.config.turn_ttl))
|
||||
.expect("TURN TTL should not get this high"),
|
||||
)
|
||||
.expect("time is valid");
|
||||
@@ -53,16 +53,13 @@ pub(crate) async fn turn_server_route(
|
||||
|
||||
(username, password)
|
||||
} else {
|
||||
(
|
||||
services.globals.turn_username().clone(),
|
||||
services.globals.turn_password().clone(),
|
||||
)
|
||||
(services.config.turn_username.clone(), services.config.turn_password.clone())
|
||||
};
|
||||
|
||||
Ok(get_turn_server_info::v3::Response {
|
||||
username,
|
||||
password,
|
||||
uris: services.globals.turn_uris().to_vec(),
|
||||
ttl: Duration::from_secs(services.globals.turn_ttl()),
|
||||
uris: services.config.turn_uris.clone(),
|
||||
ttl: Duration::from_secs(services.config.turn_ttl),
|
||||
})
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#![type_length_limit = "163840"] //TODO: REDUCE ME
|
||||
#![type_length_limit = "262144"] //TODO: REDUCE ME
|
||||
#![allow(clippy::toplevel_ref_arg)]
|
||||
|
||||
pub mod client;
|
||||
|
||||
@@ -53,6 +53,12 @@ pub(crate) async fn get_backfill_route(
|
||||
.ready_fold(PduCount::min(), cmp::max)
|
||||
.await;
|
||||
|
||||
let room_version = services
|
||||
.state
|
||||
.get_room_version(&body.room_id)
|
||||
.await
|
||||
.ok();
|
||||
|
||||
Ok(get_backfill::v1::Response {
|
||||
origin_server_ts: MilliSecondsSinceUnixEpoch::now(),
|
||||
|
||||
@@ -79,7 +85,7 @@ pub(crate) async fn get_backfill_route(
|
||||
.and_then(|pdu| {
|
||||
services
|
||||
.federation
|
||||
.format_pdu_into(pdu, None)
|
||||
.format_pdu_into(pdu, room_version.as_ref())
|
||||
.map(Ok)
|
||||
})
|
||||
.try_collect()
|
||||
|
||||
+14
-11
@@ -1,5 +1,6 @@
|
||||
use axum::extract::State;
|
||||
use ruma::{MilliSecondsSinceUnixEpoch, RoomId, api::federation::event::get_event};
|
||||
use futures::{FutureExt, future::try_join};
|
||||
use ruma::{MilliSecondsSinceUnixEpoch, OwnedRoomId, api::federation::event::get_event};
|
||||
use tuwunel_core::{Result, err};
|
||||
|
||||
use super::AccessCheck;
|
||||
@@ -21,28 +22,30 @@ pub(crate) async fn get_event_route(
|
||||
.await
|
||||
.map_err(|_| err!(Request(NotFound("Event not found."))))?;
|
||||
|
||||
let room_id: &RoomId = event
|
||||
let room_id: OwnedRoomId = event
|
||||
.get("room_id")
|
||||
.and_then(|val| val.as_str())
|
||||
.ok_or_else(|| err!(Database("Invalid event in database.")))?
|
||||
.try_into()
|
||||
.map_err(|_| err!(Database("Invalid room_id in event in database.")))?;
|
||||
|
||||
AccessCheck {
|
||||
let access_check = AccessCheck {
|
||||
services: &services,
|
||||
origin: body.origin(),
|
||||
room_id,
|
||||
room_id: &room_id,
|
||||
event_id: Some(&body.event_id),
|
||||
}
|
||||
.check()
|
||||
.await?;
|
||||
};
|
||||
|
||||
let pdu = services
|
||||
.federation
|
||||
.format_pdu_into(event, None)
|
||||
.map(Ok);
|
||||
|
||||
let ((), pdu) = try_join(access_check.check(), pdu).await?;
|
||||
|
||||
Ok(get_event::v1::Response {
|
||||
origin: services.globals.server_name().to_owned(),
|
||||
origin_server_ts: MilliSecondsSinceUnixEpoch::now(),
|
||||
pdu: services
|
||||
.federation
|
||||
.format_pdu_into(event, None)
|
||||
.await,
|
||||
pdu,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -6,7 +6,10 @@
|
||||
RoomId,
|
||||
api::{client::error::ErrorKind, federation::authorization::get_event_authorization},
|
||||
};
|
||||
use tuwunel_core::{Error, Result, utils::stream::ReadyExt};
|
||||
use tuwunel_core::{
|
||||
Error, Result,
|
||||
utils::stream::{BroadbandExt, ReadyExt},
|
||||
};
|
||||
|
||||
use super::AccessCheck;
|
||||
use crate::Ruma;
|
||||
@@ -43,12 +46,26 @@ pub(crate) async fn get_event_authorization_route(
|
||||
let room_id = <&RoomId>::try_from(room_id_str)
|
||||
.map_err(|_| Error::bad_database("Invalid room_id in event in database."))?;
|
||||
|
||||
let room_version = services
|
||||
.state
|
||||
.get_room_version(room_id)
|
||||
.await
|
||||
.ok();
|
||||
|
||||
let auth_chain = services
|
||||
.auth_chain
|
||||
.event_ids_iter(room_id, once(body.event_id.borrow()))
|
||||
.ready_filter_map(Result::ok)
|
||||
.filter_map(async |id| services.timeline.get_pdu_json(&id).await.ok())
|
||||
.then(|pdu| services.federation.format_pdu_into(pdu, None))
|
||||
.broad_filter_map(async |id| {
|
||||
let pdu = services.timeline.get_pdu_json(&id).await.ok()?;
|
||||
|
||||
let pdu = services
|
||||
.federation
|
||||
.format_pdu_into(pdu, room_version.as_ref())
|
||||
.await;
|
||||
|
||||
Some(pdu)
|
||||
})
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
|
||||
@@ -32,6 +32,12 @@ pub(crate) async fn get_missing_events_route(
|
||||
.unwrap_or(LIMIT_DEFAULT)
|
||||
.min(LIMIT_MAX);
|
||||
|
||||
let room_version = services
|
||||
.state
|
||||
.get_room_version(&body.room_id)
|
||||
.await
|
||||
.ok();
|
||||
|
||||
let mut queued_events = body.latest_events.clone();
|
||||
// the vec will never have more entries the limit
|
||||
let mut events = Vec::with_capacity(limit);
|
||||
@@ -78,7 +84,7 @@ pub(crate) async fn get_missing_events_route(
|
||||
|
||||
let event = services
|
||||
.federation
|
||||
.format_pdu_into(event, None)
|
||||
.format_pdu_into(event, room_version.as_ref())
|
||||
.await;
|
||||
|
||||
queued_events.extend(prev_events);
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
};
|
||||
use tuwunel_core::{
|
||||
Err, Error, Result, err, extract_variant,
|
||||
matrix::{Event, PduEvent, event::gen_event_id},
|
||||
matrix::{Event, PduCount, PduEvent, event::gen_event_id},
|
||||
utils,
|
||||
utils::hash::sha256,
|
||||
warn,
|
||||
@@ -143,6 +143,7 @@ pub(crate) async fn create_invite_route(
|
||||
.server_in_room(services.globals.server_name(), &body.room_id)
|
||||
.await
|
||||
{
|
||||
let count = services.globals.next_count();
|
||||
services
|
||||
.state_cache
|
||||
.update_membership(
|
||||
@@ -153,8 +154,10 @@ pub(crate) async fn create_invite_route(
|
||||
Some(invite_state),
|
||||
body.via.clone(),
|
||||
true,
|
||||
PduCount::Normal(*count),
|
||||
)
|
||||
.await?;
|
||||
drop(count);
|
||||
|
||||
for appservice in services.appservice.read().await.values() {
|
||||
if appservice.is_user_match(&invited_user) {
|
||||
|
||||
@@ -59,8 +59,8 @@ pub(crate) async fn get_public_rooms_route(
|
||||
body: Ruma<get_public_rooms::v1::Request>,
|
||||
) -> Result<get_public_rooms::v1::Response> {
|
||||
if !services
|
||||
.globals
|
||||
.allow_public_room_directory_over_federation()
|
||||
.config
|
||||
.allow_public_room_directory_over_federation
|
||||
{
|
||||
return Err(Error::BadRequest(ErrorKind::forbidden(), "Room directory is not public"));
|
||||
}
|
||||
|
||||
+51
-63
@@ -1,17 +1,15 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use axum::extract::State;
|
||||
use futures::StreamExt;
|
||||
use futures::{
|
||||
StreamExt,
|
||||
future::{join, join5},
|
||||
};
|
||||
use get_profile_information::v1::ProfileField;
|
||||
use rand::seq::SliceRandom;
|
||||
use ruma::{
|
||||
OwnedServerName,
|
||||
api::{
|
||||
client::error::ErrorKind,
|
||||
federation::query::{get_profile_information, get_room_information},
|
||||
},
|
||||
api::federation::query::{get_profile_information, get_room_information},
|
||||
};
|
||||
use tuwunel_core::{Error, Result, err};
|
||||
use tuwunel_core::{Err, Result, err, utils::future::TryExtExt};
|
||||
|
||||
use crate::Ruma;
|
||||
|
||||
@@ -60,48 +58,38 @@ pub(crate) async fn get_profile_information_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<get_profile_information::v1::Request>,
|
||||
) -> Result<get_profile_information::v1::Response> {
|
||||
use get_profile_information::v1::Response;
|
||||
|
||||
if !services
|
||||
.server
|
||||
.config
|
||||
.allow_inbound_profile_lookup_federation_requests
|
||||
{
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::forbidden(),
|
||||
return Err!(Request(Forbidden(
|
||||
"Profile lookup over federation is not allowed on this homeserver.",
|
||||
));
|
||||
)));
|
||||
}
|
||||
|
||||
if !services
|
||||
.globals
|
||||
.server_is_ours(body.user_id.server_name())
|
||||
{
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::InvalidParam,
|
||||
"User does not belong to this server.",
|
||||
));
|
||||
return Err!(Request(InvalidParam("User does not belong to this server.",)));
|
||||
}
|
||||
|
||||
let mut displayname = None;
|
||||
let mut avatar_url = None;
|
||||
let mut blurhash = None;
|
||||
let mut tz = None;
|
||||
let mut custom_profile_fields = BTreeMap::new();
|
||||
|
||||
match &body.field {
|
||||
| Some(ProfileField::DisplayName) => {
|
||||
displayname = services
|
||||
.users
|
||||
.displayname(&body.user_id)
|
||||
.await
|
||||
.ok();
|
||||
},
|
||||
| Some(ProfileField::AvatarUrl) => {
|
||||
avatar_url = services
|
||||
.users
|
||||
.avatar_url(&body.user_id)
|
||||
.await
|
||||
.ok();
|
||||
blurhash = services.users.blurhash(&body.user_id).await.ok();
|
||||
| Some(ProfileField::AvatarUrl | ProfileField::DisplayName) => {
|
||||
let avatar_url = services.users.avatar_url(&body.user_id).ok();
|
||||
|
||||
let displayname = services.users.displayname(&body.user_id).ok();
|
||||
|
||||
let (avatar_url, displayname) = join(avatar_url, displayname).await;
|
||||
|
||||
Ok(Response {
|
||||
avatar_url,
|
||||
displayname,
|
||||
..Response::default()
|
||||
})
|
||||
},
|
||||
| Some(custom_field) => {
|
||||
if let Ok(value) = services
|
||||
@@ -109,39 +97,39 @@ pub(crate) async fn get_profile_information_route(
|
||||
.profile_key(&body.user_id, custom_field.as_str())
|
||||
.await
|
||||
{
|
||||
custom_profile_fields.insert(custom_field.to_string(), value);
|
||||
Ok(Response {
|
||||
custom_profile_fields: [(custom_field.to_string(), value)].into(),
|
||||
..Response::default()
|
||||
})
|
||||
} else {
|
||||
Ok(Response::default())
|
||||
}
|
||||
},
|
||||
| None => {
|
||||
displayname = services
|
||||
.users
|
||||
.displayname(&body.user_id)
|
||||
.await
|
||||
.ok();
|
||||
avatar_url = services
|
||||
.users
|
||||
.avatar_url(&body.user_id)
|
||||
.await
|
||||
.ok();
|
||||
blurhash = services.users.blurhash(&body.user_id).await.ok();
|
||||
tz = services.users.timezone(&body.user_id).await.ok();
|
||||
custom_profile_fields = services
|
||||
let avatar_url = services.users.avatar_url(&body.user_id).ok();
|
||||
|
||||
let blurhash = services.users.blurhash(&body.user_id).ok();
|
||||
|
||||
let displayname = services.users.displayname(&body.user_id).ok();
|
||||
|
||||
let tz = services.users.timezone(&body.user_id).ok();
|
||||
|
||||
let custom_profile_fields = services
|
||||
.users
|
||||
.all_profile_keys(&body.user_id)
|
||||
.collect()
|
||||
.await;
|
||||
.collect();
|
||||
|
||||
let (avatar_url, blurhash, custom_profile_fields, displayname, tz) =
|
||||
join5(avatar_url, blurhash, custom_profile_fields, displayname, tz).await;
|
||||
|
||||
Ok(Response {
|
||||
avatar_url,
|
||||
blurhash,
|
||||
custom_profile_fields,
|
||||
displayname,
|
||||
tz,
|
||||
..Response::default()
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
// services.users.timezone will collect the MSC4175 timezone key if it exists
|
||||
custom_profile_fields.remove("us.cloke.msc4175.tz");
|
||||
custom_profile_fields.remove("m.tz");
|
||||
|
||||
Ok(get_profile_information::v1::Response {
|
||||
displayname,
|
||||
avatar_url,
|
||||
blurhash,
|
||||
tz,
|
||||
custom_profile_fields,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -161,12 +161,10 @@ async fn handle(
|
||||
})
|
||||
.try_flatten()
|
||||
.try_collect()
|
||||
.boxed()
|
||||
.await?;
|
||||
|
||||
// evaluate edus after pdus, at least for now.
|
||||
edus.for_each_concurrent(automatic_width(), |edu| handle_edu(services, client, origin, edu))
|
||||
.boxed()
|
||||
.await;
|
||||
|
||||
Ok(results)
|
||||
@@ -205,6 +203,7 @@ async fn handle_room(
|
||||
Ok((event_id, result))
|
||||
})
|
||||
.try_collect()
|
||||
.boxed()
|
||||
.await
|
||||
}
|
||||
|
||||
|
||||
+47
-37
@@ -3,7 +3,7 @@
|
||||
use std::borrow::Borrow;
|
||||
|
||||
use axum::extract::State;
|
||||
use futures::{FutureExt, StreamExt, TryStreamExt, future::try_join3};
|
||||
use futures::{FutureExt, StreamExt, TryFutureExt, TryStreamExt, future::try_join4};
|
||||
use ruma::{
|
||||
OwnedEventId, OwnedRoomId, OwnedServerName, OwnedUserId, RoomId, ServerName,
|
||||
api::federation::membership::create_join_event,
|
||||
@@ -189,6 +189,14 @@ async fn create_join_event(
|
||||
)
|
||||
.map_err(|e| err!(Request(BadJson("Event has an invalid origin server name: {e}"))))?;
|
||||
|
||||
// Prestart state gather here since it doesn't involve the new join event.
|
||||
let state_ids = services
|
||||
.state_accessor
|
||||
.state_full_ids(shortstatehash)
|
||||
.map(at!(1))
|
||||
.collect::<Vec<OwnedEventId>>()
|
||||
.boxed();
|
||||
|
||||
let mutex_lock = services
|
||||
.event_handler
|
||||
.mutex_federation
|
||||
@@ -204,49 +212,51 @@ async fn create_join_event(
|
||||
|
||||
drop(mutex_lock);
|
||||
|
||||
let state_ids: Vec<OwnedEventId> = services
|
||||
.state_accessor
|
||||
.state_full_ids(shortstatehash)
|
||||
.map(at!(1))
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
let starting_events = state_ids.iter().map(Borrow::borrow);
|
||||
let auth_chain = services
|
||||
.auth_chain
|
||||
.event_ids_iter(room_id, starting_events)
|
||||
.broad_and_then(async |event_id| services.timeline.get_pdu_json(&event_id).await)
|
||||
.broad_and_then(|pdu| {
|
||||
services
|
||||
.federation
|
||||
.format_pdu_into(pdu, Some(&room_version_id))
|
||||
.map(Ok)
|
||||
})
|
||||
.try_collect();
|
||||
|
||||
let state = state_ids
|
||||
.iter()
|
||||
.try_stream()
|
||||
.broad_and_then(|event_id| services.timeline.get_pdu_json(event_id))
|
||||
.broad_and_then(|pdu| {
|
||||
services
|
||||
.federation
|
||||
.format_pdu_into(pdu, Some(&room_version_id))
|
||||
.map(Ok)
|
||||
})
|
||||
.try_collect();
|
||||
|
||||
// Join event for new server.
|
||||
let event = services
|
||||
.federation
|
||||
.format_pdu_into(value, Some(&room_version_id))
|
||||
.map(Some)
|
||||
.map(Ok);
|
||||
|
||||
let (auth_chain, state, event) = try_join3(auth_chain, state, event).await?;
|
||||
// Join event revealed to existing servers.
|
||||
let broadcast = services.sending.send_pdu_room(room_id, &pdu_id);
|
||||
|
||||
services
|
||||
.sending
|
||||
.send_pdu_room(room_id, &pdu_id)
|
||||
// Wait for state gather which the remaining operations depend on.
|
||||
let state_ids = state_ids.await;
|
||||
let auth_heads = state_ids.iter().map(Borrow::borrow);
|
||||
let into_federation_format = |pdu| {
|
||||
services
|
||||
.federation
|
||||
.format_pdu_into(pdu, Some(&room_version_id))
|
||||
.map(Ok)
|
||||
};
|
||||
|
||||
let auth_chain = services
|
||||
.auth_chain
|
||||
.event_ids_iter(room_id, auth_heads)
|
||||
.broad_and_then(async |event_id| {
|
||||
services
|
||||
.timeline
|
||||
.get_pdu_json(&event_id)
|
||||
.and_then(into_federation_format)
|
||||
.await
|
||||
})
|
||||
.try_collect();
|
||||
|
||||
let state = state_ids
|
||||
.iter()
|
||||
.try_stream()
|
||||
.broad_and_then(|event_id| {
|
||||
services
|
||||
.timeline
|
||||
.get_pdu_json(event_id)
|
||||
.and_then(into_federation_format)
|
||||
})
|
||||
.try_collect();
|
||||
|
||||
let (auth_chain, state, event, ()) = try_join4(auth_chain, state, event, broadcast)
|
||||
.boxed()
|
||||
.await?;
|
||||
|
||||
Ok(create_join_event::v1::RoomState { auth_chain, state, event })
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use axum::extract::State;
|
||||
use futures::FutureExt;
|
||||
use futures::{FutureExt, future::try_join};
|
||||
use ruma::{
|
||||
OwnedServerName, OwnedUserId,
|
||||
RoomVersionId::*,
|
||||
@@ -174,16 +174,16 @@ pub(crate) async fn create_knock_event_v1_route(
|
||||
|
||||
drop(mutex_lock);
|
||||
|
||||
services
|
||||
let broadcast = services
|
||||
.sending
|
||||
.send_pdu_room(&body.room_id, &pdu_id)
|
||||
.await?;
|
||||
.send_pdu_room(&body.room_id, &pdu_id);
|
||||
|
||||
let knock_room_state = services.state.summary_stripped(&pdu).map(Ok);
|
||||
|
||||
let (knock_room_state, ()) = try_join(knock_room_state, broadcast).await?;
|
||||
|
||||
Ok(create_knock_event::v1::Response {
|
||||
knock_room_state: services
|
||||
.state
|
||||
.summary_stripped(&pdu)
|
||||
.await
|
||||
knock_room_state: knock_room_state
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect(),
|
||||
|
||||
+46
-25
@@ -1,9 +1,18 @@
|
||||
use std::{borrow::Borrow, iter::once};
|
||||
|
||||
use axum::extract::State;
|
||||
use futures::{FutureExt, StreamExt, TryStreamExt};
|
||||
use futures::{
|
||||
FutureExt, StreamExt, TryFutureExt, TryStreamExt,
|
||||
future::{join, try_join},
|
||||
};
|
||||
use ruma::{OwnedEventId, api::federation::event::get_room_state};
|
||||
use tuwunel_core::{Result, at, err, utils::IterStream};
|
||||
use tuwunel_core::{
|
||||
Result, at, err,
|
||||
utils::{
|
||||
future::TryExtExt,
|
||||
stream::{IterStream, TryBroadbandExt},
|
||||
},
|
||||
};
|
||||
|
||||
use super::AccessCheck;
|
||||
use crate::Ruma;
|
||||
@@ -25,43 +34,55 @@ pub(crate) async fn get_room_state_route(
|
||||
.await?;
|
||||
|
||||
let shortstatehash = services
|
||||
.state_accessor
|
||||
.state
|
||||
.pdu_shortstatehash(&body.event_id)
|
||||
.await
|
||||
.map_err(|_| err!(Request(NotFound("PDU state not found."))))?;
|
||||
|
||||
let state_ids: Vec<OwnedEventId> = services
|
||||
let state_ids = services
|
||||
.state_accessor
|
||||
.state_full_ids(shortstatehash)
|
||||
.map(at!(1))
|
||||
.collect()
|
||||
.await;
|
||||
.collect::<Vec<OwnedEventId>>();
|
||||
|
||||
let pdus = state_ids
|
||||
.iter()
|
||||
.try_stream()
|
||||
.and_then(|id| services.timeline.get_pdu_json(id))
|
||||
.and_then(|pdu| {
|
||||
services
|
||||
.federation
|
||||
.format_pdu_into(pdu, None)
|
||||
.map(Ok)
|
||||
})
|
||||
.try_collect()
|
||||
.await?;
|
||||
let room_version = services
|
||||
.state
|
||||
.get_room_version(&body.room_id)
|
||||
.ok();
|
||||
|
||||
let (room_version, state_ids) = join(room_version, state_ids).await;
|
||||
|
||||
let into_federation_format = |pdu| {
|
||||
services
|
||||
.federation
|
||||
.format_pdu_into(pdu, room_version.as_ref())
|
||||
.map(Ok)
|
||||
};
|
||||
|
||||
let auth_chain = services
|
||||
.auth_chain
|
||||
.event_ids_iter(&body.room_id, once(body.event_id.borrow()))
|
||||
.and_then(async |id| services.timeline.get_pdu_json(&id).await)
|
||||
.and_then(|pdu| {
|
||||
.broad_and_then(async |id| {
|
||||
services
|
||||
.federation
|
||||
.format_pdu_into(pdu, None)
|
||||
.map(Ok)
|
||||
.timeline
|
||||
.get_pdu_json(&id)
|
||||
.and_then(into_federation_format)
|
||||
.await
|
||||
})
|
||||
.try_collect()
|
||||
.await?;
|
||||
.try_collect();
|
||||
|
||||
let pdus = state_ids
|
||||
.iter()
|
||||
.try_stream()
|
||||
.broad_and_then(|id| {
|
||||
services
|
||||
.timeline
|
||||
.get_pdu_json(id)
|
||||
.and_then(into_federation_format)
|
||||
})
|
||||
.try_collect();
|
||||
|
||||
let (auth_chain, pdus) = try_join(auth_chain, pdus).await?;
|
||||
|
||||
Ok(get_room_state::v1::Response { auth_chain, pdus })
|
||||
}
|
||||
|
||||
+12
-11
@@ -1,7 +1,7 @@
|
||||
use std::{borrow::Borrow, iter::once};
|
||||
|
||||
use axum::extract::State;
|
||||
use futures::{StreamExt, TryStreamExt};
|
||||
use futures::{FutureExt, StreamExt, TryStreamExt, future::try_join};
|
||||
use ruma::{OwnedEventId, api::federation::event::get_room_state_ids};
|
||||
use tuwunel_core::{Result, at, err};
|
||||
|
||||
@@ -26,23 +26,24 @@ pub(crate) async fn get_room_state_ids_route(
|
||||
.await?;
|
||||
|
||||
let shortstatehash = services
|
||||
.state_accessor
|
||||
.state
|
||||
.pdu_shortstatehash(&body.event_id)
|
||||
.await
|
||||
.map_err(|_| err!(Request(NotFound("Pdu state not found."))))?;
|
||||
|
||||
let pdu_ids: Vec<OwnedEventId> = services
|
||||
.state_accessor
|
||||
.state_full_ids(shortstatehash)
|
||||
.map(at!(1))
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
let auth_chain_ids = services
|
||||
.auth_chain
|
||||
.event_ids_iter(&body.room_id, once(body.event_id.borrow()))
|
||||
.try_collect()
|
||||
.await?;
|
||||
.try_collect();
|
||||
|
||||
let pdu_ids = services
|
||||
.state_accessor
|
||||
.state_full_ids(shortstatehash)
|
||||
.map(at!(1))
|
||||
.collect::<Vec<OwnedEventId>>()
|
||||
.map(Ok);
|
||||
|
||||
let (auth_chain_ids, pdu_ids) = try_join(auth_chain_ids, pdu_ids).await?;
|
||||
|
||||
Ok(get_room_state_ids::v1::Response { auth_chain_ids, pdu_ids })
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ pub(crate) async fn get_devices_route(
|
||||
let device_id = metadata.device_id.clone();
|
||||
let device_id_clone = device_id.clone();
|
||||
let device_id_string = device_id.as_str().to_owned();
|
||||
let device_display_name = if services.globals.allow_device_name_federation() {
|
||||
let device_display_name = if services.config.allow_device_name_federation {
|
||||
metadata.display_name.clone()
|
||||
} else {
|
||||
Some(device_id_string)
|
||||
@@ -95,7 +95,7 @@ pub(crate) async fn get_keys_route(
|
||||
None,
|
||||
&body.device_keys,
|
||||
|u| Some(u.server_name()) == body.origin.as_deref(),
|
||||
services.globals.allow_device_name_federation(),
|
||||
services.config.allow_device_name_federation,
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
+10
-3
@@ -11,6 +11,7 @@ version.workspace = true
|
||||
|
||||
[lib]
|
||||
path = "mod.rs"
|
||||
bench = false
|
||||
crate-type = [
|
||||
"rlib",
|
||||
# "dylib",
|
||||
@@ -20,9 +21,7 @@ crate-type = [
|
||||
brotli_compression = [
|
||||
"reqwest/brotli",
|
||||
]
|
||||
tuwunel_mods = [
|
||||
"dep:libloading"
|
||||
]
|
||||
console = []
|
||||
gzip_compression = [
|
||||
"reqwest/gzip",
|
||||
]
|
||||
@@ -48,6 +47,9 @@ release_max_log_level = [
|
||||
"log/release_max_level_info",
|
||||
]
|
||||
sentry_telemetry = []
|
||||
tuwunel_mods = [
|
||||
"dep:libloading"
|
||||
]
|
||||
zstd_compression = [
|
||||
"reqwest/zstd",
|
||||
]
|
||||
@@ -112,9 +114,14 @@ url.workspace = true
|
||||
nix.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
criterion.workspace = true
|
||||
insta.workspace = true
|
||||
maplit.workspace = true
|
||||
similar.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[[bench]]
|
||||
name = "state_res"
|
||||
harness = false
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
#![cfg_attr(not(tuwunel_bench), allow(unused_imports, dead_code))]
|
||||
|
||||
#[cfg(tuwunel_bench)]
|
||||
extern crate test;
|
||||
|
||||
use std::{
|
||||
borrow::Borrow,
|
||||
collections::HashMap,
|
||||
sync::atomic::{AtomicU64, Ordering::SeqCst},
|
||||
};
|
||||
|
||||
use criterion::{Criterion, async_executor::FuturesExecutor, criterion_group, criterion_main};
|
||||
use ruma::{
|
||||
EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, RoomId, RoomVersionId, UserId,
|
||||
events::{
|
||||
@@ -24,36 +20,41 @@
|
||||
json,
|
||||
value::{RawValue as RawJsonValue, to_raw_value as to_raw_json_value},
|
||||
};
|
||||
|
||||
use super::{AuthSet, StateMap, test_utils::not_found};
|
||||
use crate::{
|
||||
Result,
|
||||
matrix::{Event, EventHash, PduEvent, event::TypeExt},
|
||||
use tuwunel_core::{
|
||||
Result, err,
|
||||
matrix::{
|
||||
Event, EventHash, PduEvent,
|
||||
event::TypeExt,
|
||||
state_res::{AuthSet, StateMap},
|
||||
},
|
||||
utils::stream::IterStream,
|
||||
};
|
||||
|
||||
criterion_group!(
|
||||
benches,
|
||||
lexico_topo_sort,
|
||||
resolution_shallow_auth_chain,
|
||||
resolve_deeper_event_set
|
||||
);
|
||||
|
||||
criterion_main!(benches);
|
||||
|
||||
static SERVER_TIMESTAMP: AtomicU64 = AtomicU64::new(0);
|
||||
|
||||
#[cfg(tuwunel_bench)]
|
||||
#[cfg_attr(tuwunel_bench, bench)]
|
||||
fn lexico_topo_sort(c: &mut test::Bencher) {
|
||||
use maplit::{hashmap, hashset};
|
||||
fn lexico_topo_sort(c: &mut Criterion) {
|
||||
c.bench_function("lexico_topo_sort", |c| {
|
||||
use maplit::{hashmap, hashset};
|
||||
|
||||
let rt = tokio::runtime::Builder::new_current_thread()
|
||||
.build()
|
||||
.unwrap();
|
||||
let graph = hashmap! {
|
||||
event_id("l") => hashset![event_id("o")],
|
||||
event_id("m") => hashset![event_id("n"), event_id("o")],
|
||||
event_id("n") => hashset![event_id("o")],
|
||||
event_id("o") => hashset![], // "o" has zero outgoing edges but 4 incoming edges
|
||||
event_id("p") => hashset![event_id("o")],
|
||||
};
|
||||
|
||||
let graph = hashmap! {
|
||||
event_id("l") => hashset![event_id("o")],
|
||||
event_id("m") => hashset![event_id("n"), event_id("o")],
|
||||
event_id("n") => hashset![event_id("o")],
|
||||
event_id("o") => hashset![], // "o" has zero outgoing edges but 4 incoming edges
|
||||
event_id("p") => hashset![event_id("o")],
|
||||
};
|
||||
|
||||
c.iter(move || {
|
||||
rt.block_on(async {
|
||||
_ = super::topological_sort(&graph, &async |_id| {
|
||||
c.to_async(FuturesExecutor).iter(async || {
|
||||
_ = tuwunel_core::matrix::state_res::topological_sort(&graph, &async |_id| {
|
||||
Ok((int!(0).into(), MilliSecondsSinceUnixEpoch(uint!(0))))
|
||||
})
|
||||
.await;
|
||||
@@ -61,131 +62,129 @@ fn lexico_topo_sort(c: &mut test::Bencher) {
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(tuwunel_bench)]
|
||||
#[cfg_attr(tuwunel_bench, bench)]
|
||||
fn resolution_shallow_auth_chain(c: &mut test::Bencher) {
|
||||
let rt = tokio::runtime::Builder::new_current_thread()
|
||||
.build()
|
||||
.unwrap();
|
||||
fn resolution_shallow_auth_chain(c: &mut Criterion) {
|
||||
c.bench_function("resolution_shallow_auth_chain", |c| {
|
||||
let mut store = TestStore(maplit::hashmap! {});
|
||||
|
||||
let mut store = TestStore(maplit::hashmap! {});
|
||||
// build up the DAG
|
||||
let (state_at_bob, state_at_charlie, _) = store.set_up();
|
||||
|
||||
// build up the DAG
|
||||
let (state_at_bob, state_at_charlie, _) = store.set_up();
|
||||
let rules = RoomVersionId::V6.rules().unwrap();
|
||||
let ev_map = store.0.clone();
|
||||
let state_sets = [state_at_bob, state_at_charlie];
|
||||
let auth_chains = state_sets
|
||||
.iter()
|
||||
.map(|map| {
|
||||
store
|
||||
.auth_event_ids(room_id(), map.values().cloned().collect())
|
||||
.unwrap()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let rules = RoomVersionId::V6.rules().unwrap();
|
||||
let ev_map = store.0.clone();
|
||||
let state_sets = [state_at_bob, state_at_charlie];
|
||||
let auth_chains = state_sets
|
||||
.iter()
|
||||
.map(|map| {
|
||||
store
|
||||
.auth_event_ids(room_id(), map.values().cloned().collect())
|
||||
.unwrap()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let func = async || {
|
||||
if let Err(e) = tuwunel_core::matrix::state_res::resolve(
|
||||
&rules,
|
||||
state_sets.clone().into_iter().stream(),
|
||||
auth_chains.clone().into_iter().stream(),
|
||||
&async |id| {
|
||||
ev_map
|
||||
.get(&id)
|
||||
.cloned()
|
||||
.ok_or(err!(Request(NotFound("Not Found"))))
|
||||
},
|
||||
&async |id| ev_map.contains_key(&id),
|
||||
false,
|
||||
)
|
||||
.await
|
||||
{
|
||||
panic!("{e}")
|
||||
}
|
||||
};
|
||||
|
||||
let func = async || {
|
||||
if let Err(e) = super::resolve(
|
||||
&rules,
|
||||
state_sets.clone().into_iter().stream(),
|
||||
auth_chains.clone().into_iter().stream(),
|
||||
&async |id| ev_map.get(&id).cloned().ok_or_else(not_found),
|
||||
&async |id| ev_map.contains_key(&id),
|
||||
false,
|
||||
)
|
||||
.await
|
||||
{
|
||||
panic!("{e}")
|
||||
}
|
||||
};
|
||||
|
||||
c.iter(move || {
|
||||
rt.block_on(async {
|
||||
c.to_async(FuturesExecutor).iter(async || {
|
||||
func().await;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(tuwunel_bench)]
|
||||
#[cfg_attr(tuwunel_bench, bench)]
|
||||
fn resolve_deeper_event_set(c: &mut test::Bencher) {
|
||||
let rt = tokio::runtime::Builder::new_current_thread()
|
||||
.build()
|
||||
.unwrap();
|
||||
fn resolve_deeper_event_set(c: &mut Criterion) {
|
||||
c.bench_function("resolver_deeper_event_set", |c| {
|
||||
let mut inner = INITIAL_EVENTS();
|
||||
let ban = BAN_STATE_SET();
|
||||
|
||||
let mut inner = INITIAL_EVENTS();
|
||||
let ban = BAN_STATE_SET();
|
||||
inner.extend(ban);
|
||||
let store = TestStore(inner.clone());
|
||||
|
||||
inner.extend(ban);
|
||||
let store = TestStore(inner.clone());
|
||||
|
||||
let state_set_a = [
|
||||
&inner[&event_id("CREATE")],
|
||||
&inner[&event_id("IJR")],
|
||||
&inner[&event_id("IMA")],
|
||||
&inner[&event_id("IMB")],
|
||||
&inner[&event_id("IMC")],
|
||||
&inner[&event_id("MB")],
|
||||
&inner[&event_id("PA")],
|
||||
]
|
||||
.iter()
|
||||
.map(|ev| {
|
||||
(
|
||||
ev.event_type()
|
||||
.with_state_key(ev.state_key().unwrap()),
|
||||
ev.event_id().to_owned(),
|
||||
)
|
||||
})
|
||||
.collect::<StateMap<_>>();
|
||||
|
||||
let state_set_b = [
|
||||
&inner[&event_id("CREATE")],
|
||||
&inner[&event_id("IJR")],
|
||||
&inner[&event_id("IMA")],
|
||||
&inner[&event_id("IMB")],
|
||||
&inner[&event_id("IMC")],
|
||||
&inner[&event_id("IME")],
|
||||
&inner[&event_id("PA")],
|
||||
]
|
||||
.iter()
|
||||
.map(|ev| {
|
||||
(
|
||||
ev.event_type()
|
||||
.with_state_key(ev.state_key().unwrap()),
|
||||
ev.event_id().to_owned(),
|
||||
)
|
||||
})
|
||||
.collect::<StateMap<_>>();
|
||||
|
||||
let rules = RoomVersionId::V6.rules().unwrap();
|
||||
let state_sets = [state_set_a, state_set_b];
|
||||
let auth_chains = state_sets
|
||||
let state_set_a = [
|
||||
&inner[&event_id("CREATE")],
|
||||
&inner[&event_id("IJR")],
|
||||
&inner[&event_id("IMA")],
|
||||
&inner[&event_id("IMB")],
|
||||
&inner[&event_id("IMC")],
|
||||
&inner[&event_id("MB")],
|
||||
&inner[&event_id("PA")],
|
||||
]
|
||||
.iter()
|
||||
.map(|map| {
|
||||
store
|
||||
.auth_event_ids(room_id(), map.values().cloned().collect())
|
||||
.unwrap()
|
||||
.map(|ev| {
|
||||
(
|
||||
ev.event_type()
|
||||
.with_state_key(ev.state_key().unwrap()),
|
||||
ev.event_id().to_owned(),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
.collect::<StateMap<_>>();
|
||||
|
||||
let func = async || {
|
||||
if let Err(e) = super::resolve(
|
||||
&rules,
|
||||
state_sets.clone().into_iter().stream(),
|
||||
auth_chains.clone().into_iter().stream(),
|
||||
&async |id| inner.get(&id).cloned().ok_or_else(not_found),
|
||||
&async |id| inner.contains_key(&id),
|
||||
false,
|
||||
)
|
||||
.await
|
||||
{
|
||||
panic!("{e}")
|
||||
}
|
||||
};
|
||||
let state_set_b = [
|
||||
&inner[&event_id("CREATE")],
|
||||
&inner[&event_id("IJR")],
|
||||
&inner[&event_id("IMA")],
|
||||
&inner[&event_id("IMB")],
|
||||
&inner[&event_id("IMC")],
|
||||
&inner[&event_id("IME")],
|
||||
&inner[&event_id("PA")],
|
||||
]
|
||||
.iter()
|
||||
.map(|ev| {
|
||||
(
|
||||
ev.event_type()
|
||||
.with_state_key(ev.state_key().unwrap()),
|
||||
ev.event_id().to_owned(),
|
||||
)
|
||||
})
|
||||
.collect::<StateMap<_>>();
|
||||
|
||||
c.iter(move || {
|
||||
rt.block_on(async {
|
||||
let rules = RoomVersionId::V6.rules().unwrap();
|
||||
let state_sets = [state_set_a, state_set_b];
|
||||
let auth_chains = state_sets
|
||||
.iter()
|
||||
.map(|map| {
|
||||
store
|
||||
.auth_event_ids(room_id(), map.values().cloned().collect())
|
||||
.unwrap()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let func = async || {
|
||||
if let Err(e) = tuwunel_core::matrix::state_res::resolve(
|
||||
&rules,
|
||||
state_sets.clone().into_iter().stream(),
|
||||
auth_chains.clone().into_iter().stream(),
|
||||
&async |id| {
|
||||
inner
|
||||
.get(&id)
|
||||
.cloned()
|
||||
.ok_or(err!(Request(NotFound("Not Found"))))
|
||||
},
|
||||
&async |id| inner.contains_key(&id),
|
||||
false,
|
||||
)
|
||||
.await
|
||||
{
|
||||
panic!("{e}")
|
||||
}
|
||||
};
|
||||
|
||||
c.to_async(FuturesExecutor).iter(async || {
|
||||
func().await;
|
||||
});
|
||||
});
|
||||
@@ -204,7 +203,7 @@ fn get_event(&self, room_id: &RoomId, event_id: &EventId) -> Result<E> {
|
||||
self.0
|
||||
.get(event_id)
|
||||
.cloned()
|
||||
.ok_or_else(not_found)
|
||||
.ok_or(err!(Request(NotFound("Not Found"))))
|
||||
}
|
||||
|
||||
/// Returns the events that correspond to the `event_ids` sorted in the same
|
||||
@@ -433,13 +432,13 @@ fn to_pdu_event<S>(
|
||||
.iter()
|
||||
.map(AsRef::as_ref)
|
||||
.map(event_id)
|
||||
.collect::<Vec<_>>();
|
||||
.collect();
|
||||
|
||||
let prev_events = prev_events
|
||||
.iter()
|
||||
.map(AsRef::as_ref)
|
||||
.map(event_id)
|
||||
.collect::<Vec<_>>();
|
||||
.collect();
|
||||
|
||||
let state_key = state_key.map(ToOwned::to_owned);
|
||||
PduEvent {
|
||||
@@ -458,8 +457,8 @@ fn to_pdu_event<S>(
|
||||
depth: uint!(0),
|
||||
hashes: EventHash::default(),
|
||||
signatures: None,
|
||||
#[cfg(test)]
|
||||
rejected: false,
|
||||
//#[cfg(test)]
|
||||
//rejected: false,
|
||||
}
|
||||
}
|
||||
|
||||
+76
-3
@@ -76,14 +76,14 @@ pub struct Config {
|
||||
/// WIPE.
|
||||
///
|
||||
/// example: "girlboss.ceo"
|
||||
#[cfg_attr(test, serde(default = "default_server_name"))]
|
||||
pub server_name: OwnedServerName,
|
||||
|
||||
/// This is the only directory where tuwunel will save its data, including
|
||||
/// media. Note: this was previously "/var/lib/matrix-conduit".
|
||||
///
|
||||
/// YOU NEED TO EDIT THIS.
|
||||
///
|
||||
/// example: "/var/lib/tuwunel"
|
||||
/// default: "/var/lib/tuwunel"
|
||||
#[serde(default = "default_database_path")]
|
||||
pub database_path: PathBuf,
|
||||
|
||||
/// Text which will be added to the end of the user's displayname upon
|
||||
@@ -385,6 +385,18 @@ pub struct Config {
|
||||
#[serde(default)]
|
||||
pub dns_passthru_appservices: bool,
|
||||
|
||||
/// Enable or disable case randomization for DNS queries. This is a security
|
||||
/// mitigation where answer spoofing is prevented by having to exactly match
|
||||
/// the question. Occasional errors seen in logs which may have lead you
|
||||
/// here tend to be from overloading DNS. Nevertheless for servers which
|
||||
/// are truly incapable this can be set to false.
|
||||
///
|
||||
/// This currently defaults to false due to user reports regarding some
|
||||
/// popular DNS caches which may or may not be patched soon. It may again
|
||||
/// default to true in an upcoming release.
|
||||
#[serde(default)]
|
||||
pub dns_case_randomization: bool,
|
||||
|
||||
/// Max request size for file uploads in bytes. Defaults to 20MB.
|
||||
///
|
||||
/// default: 20971520
|
||||
@@ -857,6 +869,10 @@ pub struct Config {
|
||||
#[serde(default = "true_fn", alias = "log_colours")]
|
||||
pub log_colors: bool,
|
||||
|
||||
/// Sets the log format to compact mode.
|
||||
#[serde(default)]
|
||||
pub log_compact: bool,
|
||||
|
||||
/// Configures the span events which will be outputted with the log.
|
||||
///
|
||||
/// default: "none"
|
||||
@@ -876,6 +892,15 @@ pub struct Config {
|
||||
#[serde(default)]
|
||||
pub log_thread_ids: bool,
|
||||
|
||||
/// Redirects logging to standard error (stderr). The default is false for
|
||||
/// stdout. For those using our systemd features the redirection to stderr
|
||||
/// occurs as necessary and setting this option should not be required. We
|
||||
/// offer this option for all other users who desire such redirection.
|
||||
///
|
||||
/// default: false
|
||||
#[serde(default)]
|
||||
pub log_to_stderr: bool,
|
||||
|
||||
/// OpenID token expiration/TTL in seconds.
|
||||
///
|
||||
/// These are the OpenID tokens that are primarily used for Matrix account
|
||||
@@ -1252,6 +1277,20 @@ pub struct Config {
|
||||
#[serde(default = "default_rocksdb_stats_level")]
|
||||
pub rocksdb_stats_level: u8,
|
||||
|
||||
/// Erases data no longer reachable in the current schema. The developers
|
||||
/// expect this to be set to true which simplifies the schema and prevents
|
||||
/// accumulation of old schemas remaining in the codebase forever. If this
|
||||
/// is set to false, old columns which are not described in the current
|
||||
/// schema will be ignored rather than erased, leaking their space.
|
||||
///
|
||||
/// This can be set to false when moving between versions in ways which are
|
||||
/// not recommended or otherwise forbidden, or for diagnostic and
|
||||
/// development purposes; requiring preservation across such movements.
|
||||
///
|
||||
/// default: true
|
||||
#[serde(default = "true_fn")]
|
||||
pub rocksdb_drop_missing_columns: bool,
|
||||
|
||||
/// This is a password that can be configured that will let you login to the
|
||||
/// server bot account (currently `@conduit`) for emergency troubleshooting
|
||||
/// purposes such as recovering/recreating your admin room, or inviting
|
||||
@@ -1835,6 +1874,12 @@ pub struct Config {
|
||||
#[serde(default)]
|
||||
pub tokio_console: bool,
|
||||
|
||||
/// Arbitrary argument vector for integration testing. Functionality in the
|
||||
/// server is altered or informed for the requirements of integration tests.
|
||||
/// - "smoke" performs a shutdown after startup admin commands rather than
|
||||
/// hanging on client handling.
|
||||
///
|
||||
/// default: []
|
||||
#[serde(default)]
|
||||
pub test: BTreeSet<String>,
|
||||
|
||||
@@ -2069,12 +2114,25 @@ pub struct WellKnownConfig {
|
||||
/// example: "matrix.example.com:443"
|
||||
pub server: Option<OwnedServerName>,
|
||||
|
||||
/// The URL of the support web page. This and the below generate the content
|
||||
/// of `/.well-known/matrix/support`.
|
||||
///
|
||||
/// example: "https://example.com/support"
|
||||
pub support_page: Option<Url>,
|
||||
|
||||
/// The name of the support role.
|
||||
///
|
||||
/// example: "m.role.admin"
|
||||
pub support_role: Option<ContactRole>,
|
||||
|
||||
/// The email address for the above support role.
|
||||
///
|
||||
/// example: "admin@example.com"
|
||||
pub support_email: Option<String>,
|
||||
|
||||
/// The Matrix User ID for the above support role.
|
||||
///
|
||||
/// example "@admin:example.com"
|
||||
pub support_mxid: Option<OwnedUserId>,
|
||||
}
|
||||
|
||||
@@ -2123,6 +2181,8 @@ pub struct LdapConfig {
|
||||
/// Root of the searches.
|
||||
///
|
||||
/// example: "ou=users,dc=example,dc=org"
|
||||
///
|
||||
/// default:
|
||||
#[serde(default)]
|
||||
pub base_dn: String,
|
||||
|
||||
@@ -2136,6 +2196,8 @@ pub struct LdapConfig {
|
||||
///
|
||||
/// example: "cn=ldap-reader,dc=example,dc=org" or
|
||||
/// "cn={username},ou=users,dc=example,dc=org"
|
||||
///
|
||||
/// default: ""
|
||||
#[serde(default)]
|
||||
pub bind_dn: Option<String>,
|
||||
|
||||
@@ -2143,6 +2205,8 @@ pub struct LdapConfig {
|
||||
/// `bind_dn`.
|
||||
///
|
||||
/// The server must be able to access the file, and it must not be empty.
|
||||
///
|
||||
/// default: ""
|
||||
#[serde(default)]
|
||||
pub bind_password_file: Option<PathBuf>,
|
||||
|
||||
@@ -2186,6 +2250,8 @@ pub struct LdapConfig {
|
||||
/// Defaults to `base_dn` if empty.
|
||||
///
|
||||
/// example: "ou=admins,dc=example,dc=org"
|
||||
///
|
||||
/// default:
|
||||
#[serde(default)]
|
||||
pub admin_base_dn: String,
|
||||
|
||||
@@ -2198,6 +2264,8 @@ pub struct LdapConfig {
|
||||
/// entered username for more complex filters.
|
||||
///
|
||||
/// example: "(objectClass=tuwunelAdmin)" or "(uid={username})"
|
||||
///
|
||||
/// default:
|
||||
#[serde(default)]
|
||||
pub admin_filter: String,
|
||||
}
|
||||
@@ -2513,6 +2581,11 @@ pub fn check(&self) -> Result<(), Error> { check(self) }
|
||||
|
||||
fn true_fn() -> bool { true }
|
||||
|
||||
#[cfg(test)]
|
||||
fn default_server_name() -> OwnedServerName { ruma::owned_server_name!("localhost") }
|
||||
|
||||
fn default_database_path() -> PathBuf { "/var/lib/tuwunel".to_owned().into() }
|
||||
|
||||
fn default_address() -> ListeningAddr {
|
||||
ListeningAddr {
|
||||
addrs: Right(vec![Ipv4Addr::LOCALHOST.into(), Ipv6Addr::LOCALHOST.into()]),
|
||||
|
||||
@@ -7,13 +7,8 @@
|
||||
use crate::{at, is_equal_to};
|
||||
|
||||
/// Partially supported non-compliant room versions
|
||||
pub const UNSTABLE_ROOM_VERSIONS: &[RoomVersionId] = &[
|
||||
RoomVersionId::V1,
|
||||
RoomVersionId::V2,
|
||||
RoomVersionId::V3,
|
||||
RoomVersionId::V4,
|
||||
RoomVersionId::V5,
|
||||
];
|
||||
pub const UNSTABLE_ROOM_VERSIONS: &[RoomVersionId] =
|
||||
&[RoomVersionId::V2, RoomVersionId::V3, RoomVersionId::V4, RoomVersionId::V5];
|
||||
|
||||
/// Supported and stable room versions
|
||||
pub const STABLE_ROOM_VERSIONS: &[RoomVersionId] = &[
|
||||
|
||||
+19
-14
@@ -14,7 +14,7 @@
|
||||
registry::LookupSpan,
|
||||
};
|
||||
|
||||
use crate::{Config, Result, apply};
|
||||
use crate::{Config, Result, apply, is_equal_to};
|
||||
|
||||
static SYSTEMD_MODE: LazyLock<bool> =
|
||||
LazyLock::new(|| env::var("SYSTEMD_EXEC_PID").is_ok() && env::var("JOURNAL_STREAM").is_ok());
|
||||
@@ -28,13 +28,13 @@ pub struct ConsoleWriter {
|
||||
|
||||
impl ConsoleWriter {
|
||||
#[must_use]
|
||||
pub fn new(_config: &Config) -> Self {
|
||||
pub fn new(config: &Config) -> Self {
|
||||
let journal_stream = get_journal_stream();
|
||||
Self {
|
||||
stdout: io::stdout(),
|
||||
stderr: io::stderr(),
|
||||
_journal_stream: journal_stream.into(),
|
||||
use_stderr: journal_stream.0 != 0,
|
||||
use_stderr: journal_stream.0 != 0 || config.log_to_stderr,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -64,23 +64,16 @@ fn flush(&mut self) -> io::Result<()> {
|
||||
}
|
||||
|
||||
pub struct ConsoleFormat {
|
||||
_compact: Format<Compact>,
|
||||
full: Format<Full>,
|
||||
pretty: Format<Pretty>,
|
||||
full: Format<Full>,
|
||||
compact: Format<Compact>,
|
||||
compact_mode: bool,
|
||||
}
|
||||
|
||||
impl ConsoleFormat {
|
||||
#[must_use]
|
||||
pub fn new(config: &Config) -> Self {
|
||||
Self {
|
||||
_compact: fmt::format()
|
||||
.compact()
|
||||
.with_ansi(config.log_colors),
|
||||
|
||||
full: Format::<Full>::default()
|
||||
.with_thread_ids(config.log_thread_ids)
|
||||
.with_ansi(config.log_colors),
|
||||
|
||||
pretty: fmt::format()
|
||||
.pretty()
|
||||
.with_ansi(config.log_colors)
|
||||
@@ -90,6 +83,16 @@ pub fn new(config: &Config) -> Self {
|
||||
.with_file(true)
|
||||
.with_line_number(true)
|
||||
.with_source_location(true),
|
||||
|
||||
full: Format::<Full>::default()
|
||||
.with_thread_ids(config.log_thread_ids)
|
||||
.with_ansi(config.log_colors),
|
||||
|
||||
compact: fmt::format()
|
||||
.compact()
|
||||
.with_ansi(config.log_colors),
|
||||
|
||||
compact_mode: config.log_compact,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -108,9 +111,11 @@ fn format_event(
|
||||
let is_debug = cfg!(debug_assertions)
|
||||
&& event
|
||||
.fields()
|
||||
.any(|field| field.name() == "_debug");
|
||||
.map(|field| field.name())
|
||||
.any(is_equal_to!("_debug"));
|
||||
|
||||
match *event.metadata().level() {
|
||||
| _ if self.compact_mode => self.compact.format_event(ctx, writer, event),
|
||||
| Level::ERROR if !is_debug => self.pretty.format_event(ctx, writer, event),
|
||||
| _ => self.full.format_event(ctx, writer, event),
|
||||
}
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
use ruma::api::client::filter::{RoomEventFilter, UrlFilter};
|
||||
use ruma::{
|
||||
RoomId, UserId,
|
||||
api::client::filter::{Filter, RoomEventFilter, RoomFilter, UrlFilter},
|
||||
};
|
||||
use serde_json::Value;
|
||||
|
||||
use super::Event;
|
||||
use crate::is_equal_to;
|
||||
|
||||
pub trait Matches<E: Event> {
|
||||
fn matches(&self, event: &E) -> bool;
|
||||
pub trait Matches<T> {
|
||||
fn matches(&self, t: T) -> bool;
|
||||
}
|
||||
|
||||
impl<E: Event> Matches<E> for &RoomEventFilter {
|
||||
impl<E: Event> Matches<&E> for RoomEventFilter {
|
||||
#[inline]
|
||||
fn matches(&self, event: &E) -> bool {
|
||||
if !matches_sender(event, self) {
|
||||
@@ -31,6 +34,60 @@ fn matches(&self, event: &E) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
impl Matches<&RoomId> for RoomFilter {
|
||||
#[inline]
|
||||
fn matches(&self, room_id: &RoomId) -> bool {
|
||||
if !matches_room_id(room_id, self) {
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl Matches<&UserId> for Filter {
|
||||
#[inline]
|
||||
fn matches(&self, user_id: &UserId) -> bool {
|
||||
if !matches_user_id(user_id, self) {
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
fn matches_user_id(user_id: &UserId, filter: &Filter) -> bool {
|
||||
if filter
|
||||
.not_senders
|
||||
.iter()
|
||||
.any(is_equal_to!(user_id))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if let Some(senders) = filter.senders.as_ref() {
|
||||
if !senders.iter().any(is_equal_to!(user_id)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
fn matches_room_id(room_id: &RoomId, filter: &RoomFilter) -> bool {
|
||||
if filter.not_rooms.iter().any(is_equal_to!(room_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if let Some(rooms) = filter.rooms.as_ref() {
|
||||
if !rooms.iter().any(is_equal_to!(room_id)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
fn matches_room<E: Event>(event: &E, filter: &RoomEventFilter) -> bool {
|
||||
if filter
|
||||
.not_rooms
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use serde::Deserialize;
|
||||
use serde_json::value::Value as JsonValue;
|
||||
use serde_json::value::{RawValue as RawJsonValue, Value as JsonValue};
|
||||
|
||||
use super::Event;
|
||||
use crate::{Result, err, is_true};
|
||||
@@ -43,8 +43,7 @@ pub(super) fn get_unsigned<T, E>(event: &E) -> Result<T>
|
||||
{
|
||||
event
|
||||
.unsigned()
|
||||
.as_ref()
|
||||
.map(|raw| raw.get())
|
||||
.map(RawJsonValue::get)
|
||||
.map(serde_json::from_str)
|
||||
.ok_or(err!(Request(NotFound("\"unsigned\" property not found in pdu"))))?
|
||||
.map_err(|e| err!(Database("Failed to deserialize \"unsigned\" into value: {e}")))
|
||||
|
||||
@@ -6,6 +6,11 @@
|
||||
pub mod state_res;
|
||||
|
||||
pub use event::{Event, StateKey, TypeExt as EventTypeExt, TypeStateKey, state_key};
|
||||
pub use pdu::{EventHash, Pdu, PduBuilder, PduCount, PduEvent, PduId, RawPduId, ShortId};
|
||||
pub use pdu::{EventHash, Pdu, PduBuilder, PduCount, PduEvent, PduId, RawPduId};
|
||||
pub use room_version::{RoomVersion, RoomVersionRules};
|
||||
pub use state_res::{StateMap, events};
|
||||
|
||||
pub type ShortStateKey = ShortId;
|
||||
pub type ShortEventId = ShortId;
|
||||
pub type ShortRoomId = ShortId;
|
||||
pub type ShortId = u64;
|
||||
|
||||
+31
-20
@@ -17,54 +17,55 @@
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::value::RawValue as RawJsonValue;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
pub use self::{
|
||||
Count as PduCount, Id as PduId, Pdu as PduEvent, RawId as RawPduId,
|
||||
builder::{Builder, Builder as PduBuilder},
|
||||
count::Count,
|
||||
hashes::EventHashes as EventHash,
|
||||
id::{ShortId, *},
|
||||
id::Id,
|
||||
raw_id::*,
|
||||
};
|
||||
use super::{Event, StateKey};
|
||||
use super::{Event, ShortRoomId, StateKey};
|
||||
use crate::Result;
|
||||
|
||||
/// Persistent Data Unit (Event)
|
||||
#[derive(Clone, Deserialize, Serialize, Debug)]
|
||||
pub struct Pdu {
|
||||
#[serde(rename = "type")]
|
||||
pub kind: TimelineEventType,
|
||||
|
||||
pub content: Box<RawJsonValue>,
|
||||
|
||||
pub event_id: OwnedEventId,
|
||||
|
||||
pub room_id: OwnedRoomId,
|
||||
|
||||
pub sender: OwnedUserId,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub origin: Option<OwnedServerName>,
|
||||
|
||||
pub origin_server_ts: UInt,
|
||||
|
||||
#[serde(rename = "type")]
|
||||
pub kind: TimelineEventType,
|
||||
|
||||
pub content: Box<RawJsonValue>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub state_key: Option<StateKey>,
|
||||
|
||||
pub prev_events: Vec<OwnedEventId>,
|
||||
|
||||
pub depth: UInt,
|
||||
|
||||
pub auth_events: Vec<OwnedEventId>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub redacts: Option<OwnedEventId>,
|
||||
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub unsigned: Option<Box<RawJsonValue>>,
|
||||
pub prev_events: PrevEvents,
|
||||
|
||||
pub auth_events: AuthEvents,
|
||||
|
||||
pub origin_server_ts: UInt,
|
||||
|
||||
pub depth: UInt,
|
||||
|
||||
pub hashes: EventHash,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub origin: Option<OwnedServerName>,
|
||||
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub unsigned: Option<Box<RawJsonValue>>,
|
||||
|
||||
// BTreeMap<Box<ServerName>, BTreeMap<ServerSigningKeyId, String>>
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub signatures: Option<Box<RawJsonValue>>,
|
||||
@@ -75,6 +76,16 @@ pub struct Pdu {
|
||||
pub rejected: bool,
|
||||
}
|
||||
|
||||
/// Tuned prev_events vector. Most events have one prev_event. Many events have
|
||||
/// more but allocations for all of those cases still beats allocations for all
|
||||
/// cases.
|
||||
pub type PrevEvents = SmallVec<[OwnedEventId; 1]>;
|
||||
|
||||
/// Tuned auth_events vector. Average events have three auth events. It is
|
||||
/// debatable whether this could be an ArrayVec but the realistic upper-bound is
|
||||
/// too high and non-deterministic in the era of restricted-type rooms.
|
||||
pub type AuthEvents = SmallVec<[OwnedEventId; 3]>;
|
||||
|
||||
/// The [maximum size allowed] for a PDU.
|
||||
/// [maximum size allowed]: https://spec.matrix.org/latest/client-server-api/#size-limits
|
||||
pub const MAX_PDU_BYTES: usize = 65_535;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::{collections::BTreeMap, fmt};
|
||||
|
||||
use ruma::{
|
||||
MilliSecondsSinceUnixEpoch, OwnedEventId,
|
||||
@@ -10,7 +10,7 @@
|
||||
use super::StateKey;
|
||||
|
||||
/// Build the start of a PDU in order to add it to the Database.
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Deserialize)]
|
||||
pub struct Builder {
|
||||
#[serde(rename = "type")]
|
||||
pub event_type: TimelineEventType,
|
||||
@@ -30,6 +30,19 @@ pub struct Builder {
|
||||
|
||||
type Unsigned = BTreeMap<String, serde_json::Value>;
|
||||
|
||||
impl Default for Builder {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
event_type: "m.room.message".into(),
|
||||
content: Box::<RawJsonValue>::default(),
|
||||
unsigned: None,
|
||||
state_key: None,
|
||||
redacts: None,
|
||||
timestamp: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
pub fn state<S, T>(state_key: S, content: &T) -> Self
|
||||
where
|
||||
@@ -58,15 +71,30 @@ pub fn timeline<T>(content: &T) -> Self
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Builder {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
event_type: "m.room.message".into(),
|
||||
content: Box::<RawJsonValue>::default(),
|
||||
unsigned: None,
|
||||
state_key: None,
|
||||
redacts: None,
|
||||
timestamp: None,
|
||||
impl fmt::Debug for Builder {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut d = f.debug_struct("Builder");
|
||||
|
||||
d.field("type", &self.event_type);
|
||||
|
||||
if let Some(state_key) = self.state_key.as_ref() {
|
||||
d.field("state_key", state_key);
|
||||
}
|
||||
|
||||
if let Some(redacts) = self.redacts.as_ref() {
|
||||
d.field("redacts", redacts);
|
||||
}
|
||||
|
||||
if let Some(timestamp) = self.timestamp.as_ref() {
|
||||
d.field("ts", timestamp);
|
||||
}
|
||||
|
||||
if let Some(unsigned) = self.unsigned.as_ref() {
|
||||
d.field("unsigned", unsigned);
|
||||
}
|
||||
|
||||
d.field("content", &self.content);
|
||||
|
||||
d.finish()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,10 @@ pub enum Count {
|
||||
}
|
||||
|
||||
impl Count {
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn to_be_bytes(self) -> [u8; size_of::<u64>()] { self.into_unsigned().to_be_bytes() }
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn from_unsigned(unsigned: u64) -> Self { Self::from_signed(unsigned as i64) }
|
||||
|
||||
@@ -1,23 +1,17 @@
|
||||
use super::{Count, RawId};
|
||||
use crate::utils::u64_from_u8x8;
|
||||
|
||||
pub type ShortRoomId = ShortId;
|
||||
pub type ShortEventId = ShortId;
|
||||
pub type ShortStateKey = ShortId;
|
||||
pub type ShortId = u64;
|
||||
use super::{Count, RawId, ShortRoomId};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub struct Id {
|
||||
pub shortroomid: ShortRoomId,
|
||||
pub shorteventid: Count,
|
||||
pub count: Count,
|
||||
}
|
||||
|
||||
impl From<RawId> for Id {
|
||||
#[inline]
|
||||
fn from(raw: RawId) -> Self {
|
||||
Self {
|
||||
shortroomid: u64_from_u8x8(raw.shortroomid()),
|
||||
shorteventid: Count::from_unsigned(u64_from_u8x8(raw.shorteventid())),
|
||||
shortroomid: u64::from_be_bytes(raw.shortroomid()),
|
||||
count: Count::from_unsigned(u64::from_be_bytes(raw.count())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
use std::fmt;
|
||||
|
||||
use arrayvec::ArrayVec;
|
||||
use serde::{Deserialize, Deserializer};
|
||||
|
||||
use super::{Count, Id, ShortEventId, ShortId, ShortRoomId};
|
||||
use super::{
|
||||
super::{ShortId, ShortRoomId},
|
||||
Count, Id,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
|
||||
pub enum RawId {
|
||||
@@ -13,18 +17,24 @@ pub enum RawId {
|
||||
type RawIdNormal = [u8; RawId::NORMAL_LEN];
|
||||
type RawIdBackfilled = [u8; RawId::BACKFILLED_LEN];
|
||||
|
||||
struct RawIdVisitor;
|
||||
|
||||
const INT_LEN: usize = size_of::<ShortId>();
|
||||
|
||||
impl RawId {
|
||||
const BACKFILLED_LEN: usize = size_of::<ShortRoomId>() + INT_LEN + size_of::<ShortEventId>();
|
||||
const BACKFILLED_LEN: usize = size_of::<ShortRoomId>() + INT_LEN + size_of::<i64>();
|
||||
const MAX_LEN: usize = Self::BACKFILLED_LEN;
|
||||
const NORMAL_LEN: usize = size_of::<ShortRoomId>() + size_of::<ShortEventId>();
|
||||
const NORMAL_LEN: usize = size_of::<ShortRoomId>() + size_of::<u64>();
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn pdu_count(&self) -> Count {
|
||||
let id: Id = (*self).into();
|
||||
id.shorteventid
|
||||
pub fn is_room_eq(self, other: Self) -> bool { self.shortroomid() == other.shortroomid() }
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn pdu_count(self) -> Count {
|
||||
let id: Id = self.into();
|
||||
id.count
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -42,14 +52,14 @@ pub fn pdu_count(&self) -> Count {
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn shorteventid(self) -> [u8; INT_LEN] {
|
||||
pub fn count(self) -> [u8; INT_LEN] {
|
||||
match self {
|
||||
| Self::Normal(raw) => raw[INT_LEN..INT_LEN * 2]
|
||||
.try_into()
|
||||
.expect("normal raw shorteventid array from slice"),
|
||||
.expect("normal raw indice array from slice"),
|
||||
| Self::Backfilled(raw) => raw[INT_LEN * 2..INT_LEN * 3]
|
||||
.try_into()
|
||||
.expect("backfilled raw shorteventid array from slice"),
|
||||
.expect("backfilled raw indice array from slice"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,9 +80,53 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for RawId {
|
||||
impl<'de> Deserialize<'de> for RawId {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &[u8] { self.as_bytes() }
|
||||
fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
|
||||
d.deserialize_bytes(RawIdVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::de::Visitor<'_> for RawIdVisitor {
|
||||
type Value = RawId;
|
||||
|
||||
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str("RawId byte array")
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_bytes<E>(self, buf: &[u8]) -> Result<RawId, E> { Ok(RawId::from(buf)) }
|
||||
}
|
||||
|
||||
impl From<Id> for RawId {
|
||||
#[inline]
|
||||
fn from(id: Id) -> Self {
|
||||
const MAX_LEN: usize = RawId::MAX_LEN;
|
||||
type RawVec = ArrayVec<u8, MAX_LEN>;
|
||||
|
||||
let mut vec = RawVec::new();
|
||||
vec.extend(id.shortroomid.to_be_bytes());
|
||||
id.count.debug_assert_valid();
|
||||
match id.count {
|
||||
| Count::Normal(count) => {
|
||||
vec.extend(count.to_be_bytes());
|
||||
Self::Normal(
|
||||
vec.as_ref()
|
||||
.try_into()
|
||||
.expect("RawVec into RawId::Normal"),
|
||||
)
|
||||
},
|
||||
| Count::Backfilled(count) => {
|
||||
vec.extend(0_u64.to_be_bytes());
|
||||
vec.extend(count.to_be_bytes());
|
||||
Self::Backfilled(
|
||||
vec.as_ref()
|
||||
.try_into()
|
||||
.expect("RawVec into RawId::Backfilled"),
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&[u8]> for RawId {
|
||||
@@ -94,33 +148,17 @@ fn from(id: &[u8]) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Id> for RawId {
|
||||
impl From<&[u8; Self::NORMAL_LEN]> for RawId {
|
||||
#[inline]
|
||||
fn from(id: Id) -> Self {
|
||||
const MAX_LEN: usize = RawId::MAX_LEN;
|
||||
type RawVec = ArrayVec<u8, MAX_LEN>;
|
||||
|
||||
let mut vec = RawVec::new();
|
||||
vec.extend(id.shortroomid.to_be_bytes());
|
||||
id.shorteventid.debug_assert_valid();
|
||||
match id.shorteventid {
|
||||
| Count::Normal(shorteventid) => {
|
||||
vec.extend(shorteventid.to_be_bytes());
|
||||
Self::Normal(
|
||||
vec.as_ref()
|
||||
.try_into()
|
||||
.expect("RawVec into RawId::Normal"),
|
||||
)
|
||||
},
|
||||
| Count::Backfilled(shorteventid) => {
|
||||
vec.extend(0_u64.to_be_bytes());
|
||||
vec.extend(shorteventid.to_be_bytes());
|
||||
Self::Backfilled(
|
||||
vec.as_ref()
|
||||
.try_into()
|
||||
.expect("RawVec into RawId::Backfilled"),
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
||||
fn from(id: &[u8; Self::NORMAL_LEN]) -> Self { Self::Normal(*id) }
|
||||
}
|
||||
|
||||
impl From<&[u8; Self::BACKFILLED_LEN]> for RawId {
|
||||
#[inline]
|
||||
fn from(id: &[u8; Self::BACKFILLED_LEN]) -> Self { Self::Backfilled(*id) }
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for RawId {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &[u8] { self.as_bytes() }
|
||||
}
|
||||
|
||||
@@ -669,8 +669,8 @@ async fn auth_event_in_different_room() {
|
||||
content: to_raw_json_value(&json!({ "users": { alice(): 100 } })).unwrap(),
|
||||
redacts: None,
|
||||
unsigned: None,
|
||||
auth_events: vec![event_id("CREATE"), event_id("IMA")],
|
||||
prev_events: vec![event_id("IMA")],
|
||||
auth_events: vec![event_id("CREATE"), event_id("IMA")].into(),
|
||||
prev_events: vec![event_id("IMA")].into(),
|
||||
depth: uint!(0),
|
||||
hashes: EventHash::default(),
|
||||
signatures: None,
|
||||
@@ -807,8 +807,8 @@ async fn rejected_auth_event() {
|
||||
content: to_raw_json_value(&json!({ "users": { alice(): 100 } })).unwrap(),
|
||||
redacts: None,
|
||||
unsigned: None,
|
||||
auth_events: vec![event_id("CREATE"), event_id("IMA")],
|
||||
prev_events: vec![event_id("IMA")],
|
||||
auth_events: vec![event_id("CREATE"), event_id("IMA")].into(),
|
||||
prev_events: vec![event_id("IMA")].into(),
|
||||
depth: uint!(0),
|
||||
hashes: EventHash::default(),
|
||||
signatures: None,
|
||||
@@ -893,12 +893,13 @@ async fn event_without_room_id() {
|
||||
content: to_raw_json_value(&RoomMessageEventContent::text_plain("Hi!")).unwrap(),
|
||||
redacts: None,
|
||||
unsigned: None,
|
||||
auth_events: vec![
|
||||
auth_events: [
|
||||
owned_event_id!("$CREATE"),
|
||||
owned_event_id!("$IMA"),
|
||||
owned_event_id!("$IPOWER"),
|
||||
],
|
||||
prev_events: vec![owned_event_id!("$IPOWER")],
|
||||
]
|
||||
.into(),
|
||||
prev_events: [owned_event_id!("$IPOWER")].into(),
|
||||
depth: uint!(0),
|
||||
hashes: EventHash::default(),
|
||||
signatures: None,
|
||||
|
||||
@@ -58,8 +58,6 @@
|
||||
//! [necessary checks on receipt of a PDU]: https://spec.matrix.org/latest/server-server-api/#checks-performed-on-receipt-of-a-pdu
|
||||
//! [ruma-events]: https://crates.io/crates/ruma-events
|
||||
|
||||
#[cfg(test)]
|
||||
mod benches;
|
||||
mod event_auth;
|
||||
mod event_format;
|
||||
pub mod events;
|
||||
|
||||
@@ -126,7 +126,7 @@ async fn mainline_position<Fetch, Fut, Pdu>(
|
||||
) -> Result<usize>
|
||||
where
|
||||
Fetch: Fn(OwnedEventId) -> Fut + Sync,
|
||||
Fut: Future<Output = Result<Pdu>>,
|
||||
Fut: Future<Output = Result<Pdu>> + Send,
|
||||
Pdu: Event,
|
||||
{
|
||||
let mut current_event = Some(event.clone());
|
||||
@@ -154,7 +154,7 @@ async fn get_power_levels_auth_event<Fetch, Fut, Pdu>(
|
||||
) -> Result<Option<Pdu>>
|
||||
where
|
||||
Fetch: Fn(OwnedEventId) -> Fut + Sync,
|
||||
Fut: Future<Output = Result<Pdu>>,
|
||||
Fut: Future<Output = Result<Pdu>> + Send,
|
||||
Pdu: Event,
|
||||
{
|
||||
let power_level_event = event
|
||||
|
||||
@@ -163,7 +163,7 @@ async fn topic_basic() {
|
||||
.finish(),
|
||||
);
|
||||
|
||||
let events = &[
|
||||
let events = vec![
|
||||
to_init_pdu_event(
|
||||
"T1",
|
||||
alice(),
|
||||
@@ -219,7 +219,7 @@ async fn topic_basic() {
|
||||
.map(event_id)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
do_check(events, edges, expected_state_ids).await;
|
||||
do_check(&events, edges, expected_state_ids).await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
@@ -428,13 +428,13 @@ pub(super) fn to_init_pdu_event(
|
||||
content: Box<RawJsonValue>,
|
||||
) -> PduEvent {
|
||||
let ts = SERVER_TIMESTAMP.fetch_add(1, SeqCst);
|
||||
let state_key = state_key.map(ToOwned::to_owned);
|
||||
let id = if id.contains('$') {
|
||||
id.to_owned()
|
||||
} else {
|
||||
format!("${id}:foo")
|
||||
};
|
||||
|
||||
let state_key = state_key.map(ToOwned::to_owned);
|
||||
PduEvent {
|
||||
event_id: id.try_into().unwrap(),
|
||||
room_id: room_id().to_owned(),
|
||||
@@ -446,8 +446,8 @@ pub(super) fn to_init_pdu_event(
|
||||
content,
|
||||
redacts: None,
|
||||
unsigned: None,
|
||||
auth_events: vec![],
|
||||
prev_events: vec![],
|
||||
auth_events: Default::default(),
|
||||
prev_events: Default::default(),
|
||||
depth: uint!(0),
|
||||
hashes: EventHash::default(),
|
||||
signatures: None,
|
||||
@@ -468,6 +468,7 @@ pub(super) fn to_pdu_event<S>(
|
||||
S: AsRef<str>,
|
||||
{
|
||||
let ts = SERVER_TIMESTAMP.fetch_add(1, SeqCst);
|
||||
let state_key = state_key.map(ToOwned::to_owned);
|
||||
let id = if id.contains('$') {
|
||||
id.to_owned()
|
||||
} else {
|
||||
@@ -477,14 +478,13 @@ pub(super) fn to_pdu_event<S>(
|
||||
.iter()
|
||||
.map(AsRef::as_ref)
|
||||
.map(event_id)
|
||||
.collect::<Vec<_>>();
|
||||
.collect();
|
||||
let prev_events = prev_events
|
||||
.iter()
|
||||
.map(AsRef::as_ref)
|
||||
.map(event_id)
|
||||
.collect::<Vec<_>>();
|
||||
.collect();
|
||||
|
||||
let state_key = state_key.map(ToOwned::to_owned);
|
||||
PduEvent {
|
||||
event_id: id.try_into().unwrap(),
|
||||
room_id: room_id().to_owned(),
|
||||
@@ -528,18 +528,18 @@ fn event_id(id: &str) -> OwnedEventId {
|
||||
}
|
||||
|
||||
let ts = SERVER_TIMESTAMP.fetch_add(1, SeqCst);
|
||||
let state_key = state_key.map(ToOwned::to_owned);
|
||||
let auth_events = auth_events
|
||||
.iter()
|
||||
.map(AsRef::as_ref)
|
||||
.map(event_id)
|
||||
.collect::<Vec<_>>();
|
||||
.collect();
|
||||
let prev_events = prev_events
|
||||
.iter()
|
||||
.map(AsRef::as_ref)
|
||||
.map(event_id)
|
||||
.collect::<Vec<_>>();
|
||||
.collect();
|
||||
|
||||
let state_key = state_key.map(ToOwned::to_owned);
|
||||
PduEvent {
|
||||
event_id: event_id(id),
|
||||
room_id: hydra_room_id().to_owned(),
|
||||
@@ -581,12 +581,12 @@ pub(super) fn room_redaction_pdu_event<S>(
|
||||
.iter()
|
||||
.map(AsRef::as_ref)
|
||||
.map(event_id)
|
||||
.collect::<Vec<_>>();
|
||||
.collect();
|
||||
let prev_events = prev_events
|
||||
.iter()
|
||||
.map(AsRef::as_ref)
|
||||
.map(event_id)
|
||||
.collect::<Vec<_>>();
|
||||
.collect();
|
||||
|
||||
PduEvent {
|
||||
event_id: id.try_into().unwrap(),
|
||||
@@ -636,8 +636,8 @@ pub(super) fn room_create_hydra_pdu_event(
|
||||
content,
|
||||
redacts: None,
|
||||
unsigned: None,
|
||||
auth_events: vec![],
|
||||
prev_events: vec![],
|
||||
auth_events: Default::default(),
|
||||
prev_events: Default::default(),
|
||||
depth: uint!(0),
|
||||
hashes: EventHash::default(),
|
||||
signatures: None,
|
||||
|
||||
+1
-4
@@ -104,10 +104,7 @@ pub fn shutdown(&self) -> Result {
|
||||
}
|
||||
|
||||
pub fn signal(&self, sig: &'static str) -> Result {
|
||||
if let Err(e) = self.signal.send(sig) {
|
||||
return Err!("Failed to send signal: {e}");
|
||||
}
|
||||
|
||||
self.signal.send(sig).ok();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user