mirror of
https://forgejo.ellis.link/continuwuation/continuwuity/
synced 2026-08-07 04:00:22 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
fb4dfafe2d | ||
|
|
8dbea51968 | ||
|
|
7b5905bbca | ||
|
|
42d143c013 | ||
|
|
10fb1cd192 | ||
|
|
1322ba1b00 | ||
|
|
d73b4332bf | ||
|
|
11eeca7e01 | ||
|
|
19dc5fafd4 | ||
|
|
54b347b855 | ||
|
|
8ed61aecb0 | ||
|
|
bf8aa57d03 | ||
|
|
a9f1926654 | ||
|
|
248d778290 | ||
|
|
2e13e87e43 | ||
|
|
ba19d407d0 | ||
|
|
de7c5dcbc8 | ||
|
|
adadafa88f | ||
|
|
deda746222 | ||
|
|
29c2c8a333 | ||
|
|
296018f0cc | ||
|
|
6295ca135a | ||
|
|
b5a9884194 | ||
|
|
1be7fd9247 | ||
|
|
be1264965a | ||
|
|
f6d2ce2f22 | ||
|
|
3cbe1e25cd | ||
|
|
897322964c | ||
|
|
996a5488be | ||
|
|
0ed4bd1e07 | ||
|
|
da79de5381 | ||
|
|
1bcf3ae19a | ||
|
|
91e500efe3 | ||
|
|
35b1ebaf3b | ||
|
|
ea5deb7e85 | ||
|
|
542cc51047 | ||
|
|
3225db0ddd | ||
|
|
a164da8b58 | ||
|
|
7221d466ce | ||
|
|
f6c88e3a16 | ||
|
|
3deebeab78 | ||
|
|
1d2818de58 | ||
|
|
86781522b6 | ||
|
|
4b49aaad53 | ||
|
|
3a78ba2b16 | ||
|
|
685946faed | ||
|
|
13c9385ef7 | ||
|
|
6a583a359e | ||
|
|
eec6e45358 | ||
|
|
804257eb52 | ||
|
|
a1677f05d3 | ||
|
|
1f77200299 | ||
|
|
888b93df91 | ||
|
|
a127724eaa | ||
|
|
00102c4276 | ||
|
|
b59af0530c | ||
|
|
44af849ed0 | ||
|
|
26e7a8e962 | ||
|
|
aa413ae601 | ||
|
|
7c19d1e1ff | ||
|
|
616f20f273 | ||
|
|
91efcf8a8e | ||
|
|
8708e98478 | ||
|
|
4bdc260015 | ||
|
|
7bc47e58d0 | ||
|
|
6172b9c8ec | ||
|
|
9dc16eeab7 | ||
|
|
402f7a2157 | ||
|
|
9c6e0e3350 | ||
|
|
12bdfe3b66 | ||
|
|
54ce221e88 | ||
|
|
33a16f4831 | ||
|
|
d8e6d95f4b | ||
|
|
9c290bc513 | ||
|
|
a4520424bd | ||
|
|
280d7733fc | ||
|
|
79e3c898ac | ||
|
|
757d38e891 | ||
|
|
4b66911886 | ||
|
|
9db3e35f41 | ||
|
|
7977512d5d | ||
|
|
e354be6830 | ||
|
|
f58081838e | ||
|
|
526a138021 | ||
|
|
7fa6894334 | ||
|
|
33d0ad1be2 | ||
|
|
fbc3892904 | ||
|
|
ce6212b161 | ||
|
|
0aea56f02a | ||
|
|
d05d3f710f | ||
|
|
7a3496869b | ||
|
|
146b72265d | ||
|
|
120a43cceb | ||
|
|
c40671bfb0 | ||
|
|
8e9c6661b2 | ||
|
|
6810604629 | ||
|
|
3dd984eff9 | ||
|
|
bcd4f80149 | ||
|
|
f51a7b12b5 | ||
|
|
3a1649bcaf | ||
|
|
d4421422b2 | ||
|
|
4884c55be7 | ||
|
|
a4f589f475 | ||
|
|
b5890b9664 | ||
|
|
26b3a84b88 | ||
|
|
3430d4ac86 | ||
|
|
3e2f2fbffa | ||
|
|
74deb14ca0 | ||
|
|
acd930b9ca | ||
|
|
e2db2029c5 | ||
|
|
4bb83a1972 | ||
|
|
751aa27859 | ||
|
|
628597c318 | ||
|
|
2c6dd78502 | ||
|
|
cdb0e31b21 | ||
|
|
6a6f77a100 | ||
|
|
a3666fe672 | ||
|
|
0c8df52d18 | ||
|
|
d7e5c0ccf3 | ||
|
|
e106e50ed0 | ||
|
|
00f11a9e8f | ||
|
|
bf48c7b1b4 | ||
|
|
8a8a5d598e | ||
|
|
fa84a7e6c2 | ||
|
|
2bdeca0ed3 | ||
|
|
c337ea1864 | ||
|
|
b00361d274 | ||
|
|
fb17aa19bf | ||
|
|
fc28e8e1dd | ||
|
|
cea25c6580 | ||
|
|
a0dc37e024 | ||
|
|
003257693b | ||
|
|
bc898efcce | ||
|
|
fa8ad1d629 | ||
|
|
44b60050b3 | ||
|
|
4b84e7c31a | ||
|
|
4237f21903 | ||
|
|
4ac61fd87b | ||
|
|
d217927000 | ||
|
|
4b9b85f671 | ||
|
|
fa3b72947a | ||
|
|
5d963abda6 | ||
|
|
a91380ac17 | ||
|
|
1664a2c225 | ||
|
|
0d7eeb7766 | ||
|
|
16447f7383 | ||
|
|
3612b04890 | ||
|
|
e6f1b98d6a | ||
|
|
6742e1d206 | ||
|
|
dc0976a2e5 | ||
|
|
f3f23ee7ee | ||
|
|
0b15ab2006 | ||
|
|
bd0c3e33e2 | ||
|
|
1bb16c8b73 | ||
|
|
59b62b1453 | ||
|
|
2e0b156de0 | ||
|
|
3593b8ebf7 | ||
|
|
80015a6a30 | ||
|
|
95e49d3cb8 | ||
|
|
25164e030d | ||
|
|
f04924d6be | ||
|
|
df7cd58618 | ||
|
|
bf12e05dae | ||
|
|
e53366a492 | ||
|
|
ce25d79e66 | ||
|
|
71ef093edc | ||
|
|
066097a83d | ||
|
|
1c24ed09fa | ||
|
|
d3dbe09380 | ||
|
|
e9d0baa32e | ||
|
|
7dd10ebc6c | ||
|
|
29ac62ec27 | ||
|
|
8c8985e8f2 | ||
|
|
730920b571 | ||
|
|
66334bdd53 | ||
|
|
6b7bf80de5 | ||
|
|
4c35bfbda4 | ||
|
|
ba51748ad3 | ||
|
|
63dfe8f7e3 | ||
|
|
c6836e51b2 | ||
|
|
33a9fce828 | ||
|
|
267fd4de5b | ||
|
|
ce30f83052 | ||
|
|
e09a2c0e0f | ||
|
|
992abfab8f | ||
|
|
51931de94c | ||
|
|
85a84f93c7 | ||
|
|
e58ef326d4 | ||
|
|
0d439188e8 | ||
|
|
22ef704ac6 | ||
|
|
7bdd4065c8 | ||
|
|
0fcb072239 | ||
|
|
05bb1f4ac7 | ||
|
|
89e3a8c182 | ||
|
|
f0576f8d7d | ||
|
|
11f77defc7 | ||
|
|
23c75855ba | ||
|
|
5253a8d36a | ||
|
|
c9adee86f5 | ||
|
|
3c47516c85 | ||
|
|
299d3230a1 | ||
|
|
9ea8fbc482 | ||
|
|
f7877d122b | ||
|
|
17119670c0 | ||
|
|
535fcab4b5 | ||
|
|
cbe34affa3 | ||
|
|
ed0b3c764e | ||
|
|
190269f616 | ||
|
|
3ea8c82ef0 | ||
|
|
2a627baa96 | ||
|
|
50de6277b8 | ||
|
|
4429323e11 | ||
|
|
18f8d6c65c | ||
|
|
aac49b09c7 | ||
|
|
c8d35cca57 | ||
|
|
cca0f20148 | ||
|
|
23d188f612 | ||
|
|
ae707ab465 | ||
|
|
8244d78cb2 | ||
|
|
b0315da3d7 | ||
|
|
eca01cfb3f | ||
|
|
cf13f5afc1 | ||
|
|
c944f1124a | ||
|
|
70fc95e800 | ||
|
|
507bd1edc7 | ||
|
|
ee0e594aef | ||
|
|
fd080914c2 | ||
|
|
9dd2b28605 | ||
|
|
34abe1cce4 | ||
|
|
a340e6786d | ||
|
|
c37ee97351 | ||
|
|
7d2d42542c | ||
|
|
fd0082fd2c | ||
|
|
ca8f3ae057 | ||
|
|
8a748e02df | ||
|
|
79ee7838e7 | ||
|
|
e9a441fb6e | ||
|
|
93f5557232 | ||
|
|
3d88767977 | ||
|
|
b68cff3b2f | ||
|
|
36c3845ff6 | ||
|
|
7f1c7b964e | ||
|
|
1cbcd3fc88 | ||
|
|
ad9ddd91cd | ||
|
|
a2b4c07cf7 | ||
|
|
2ead282bec | ||
|
|
9496d05cd2 | ||
|
|
c8ab1713c3 | ||
|
|
84acdccb93 | ||
|
|
6164f62395 | ||
|
|
b36ad9cfef | ||
|
|
d557f0f456 | ||
|
|
d858c5c1f4 | ||
|
|
972201da60 | ||
|
|
56d9c9a8b5 | ||
|
|
84ce3e0d0e | ||
|
|
578cf9d2d7 | ||
|
|
ca69af97c1 | ||
|
|
ac26193ffe | ||
|
|
a84e559640 | ||
|
|
2e559a0d3e | ||
|
|
2634fed2d4 | ||
|
|
31a3676cd8 | ||
|
|
aa1609d611 | ||
|
|
8e13f07d2a | ||
|
|
ab6da09c02 | ||
|
|
18b9d7bc1f | ||
|
|
b5dc933880 | ||
|
|
f68038a826 | ||
|
|
b3a47566ff | ||
|
|
61cbd38284 | ||
|
|
6cc251796e | ||
|
|
7e8c4c8883 | ||
|
|
73c179b20a | ||
|
|
cd8648dce5 | ||
|
|
83ceda808c | ||
|
|
fc3fcf4526 | ||
|
|
f4e6807a3c | ||
|
|
13e0113587 | ||
|
|
51778b9354 | ||
|
|
8fef40a37e | ||
|
|
ee6524b803 | ||
|
|
1645518859 | ||
|
|
6088d0e295 | ||
|
|
5a717266b8 | ||
|
|
f2c5314296 | ||
|
|
814d3b903e | ||
|
|
a114ab3ab6 | ||
|
|
b01c5d5ef1 | ||
|
|
21c0e4176b | ||
|
|
f1f31eff17 | ||
|
|
b0b441d534 | ||
|
|
5799059196 | ||
|
|
026d756ef4 | ||
|
|
37afdb0e90 | ||
|
|
2a2486182a | ||
|
|
3056b60159 | ||
|
|
000b876dcb | ||
|
|
fcd51ef30e | ||
|
|
7edbe11eb8 | ||
|
|
0f256d7191 | ||
|
|
72c231c613 | ||
|
|
66b36563b6 | ||
|
|
e6d782c701 | ||
|
|
f63cc84c98 | ||
|
|
83fe14c522 | ||
|
|
46ce426b87 | ||
|
|
9a975c0304 | ||
|
|
dc8bab9bcd | ||
|
|
ceb0f8f933 | ||
|
|
53f2ea8e58 | ||
|
|
8a83c23537 | ||
|
|
b0048fec6d | ||
|
|
9bad2fa1eb | ||
|
|
06f37e9e8a |
@@ -0,0 +1,2 @@
|
||||
[env]
|
||||
RUMA_UNSTABLE_EXHAUSTIVE_TYPES = "true"
|
||||
@@ -30,6 +30,10 @@ on:
|
||||
machines:
|
||||
type: string
|
||||
description: Hardware platforms
|
||||
runner:
|
||||
type: string
|
||||
description: Runner mask
|
||||
default: 'self-hosted'
|
||||
excludes:
|
||||
type: string
|
||||
default: '[]'
|
||||
@@ -86,7 +90,7 @@ jobs:
|
||||
${{matrix.sys_version}}
|
||||
${{matrix.sys_target}}
|
||||
|
||||
runs-on: ${{matrix.machine}}
|
||||
runs-on: ["${{matrix.machine}}", "${{inputs.runner}}"]
|
||||
strategy:
|
||||
fail-fast: ${{inputs.fail_fast}}
|
||||
max-parallel: ${{inputs.max-parallel}}
|
||||
@@ -141,12 +145,14 @@ jobs:
|
||||
iid: ${{matrix.bake_target}}--${{matrix.cargo_profile}}--${{matrix.rust_toolchain}}--${{matrix.rust_target}}--${{matrix.feat_set}}--${{matrix.sys_name}}--${{matrix.sys_version}}--${{matrix.sys_target}}
|
||||
dst: ${{ fromJSON(inputs.artifact)[matrix.bake_target].dst }}
|
||||
src: ${{ fromJSON(inputs.artifact)[matrix.bake_target].src || fromJSON(inputs.artifact)[matrix.bake_target].dst }}
|
||||
mod: ${{ fromJSON(inputs.artifact)[matrix.bake_target].chmod || 'u+r' }}
|
||||
|
||||
run: |
|
||||
cid=$(docker create "$iid" /)
|
||||
rm -rf _artifact
|
||||
mkdir -p "_artifact"
|
||||
docker cp "$cid:$src" "_artifact/$dst"
|
||||
chmod "$mod" "_artifact/$dst"
|
||||
|
||||
# Optionally extract the image itself as an artifact.
|
||||
- name: Extract Image Artifact
|
||||
@@ -174,11 +180,13 @@ jobs:
|
||||
env:
|
||||
dst: ${{ fromJSON(inputs.artifact)[matrix.bake_target].dst }}
|
||||
src: ${{ fromJSON(inputs.artifact)[matrix.bake_target].src || fromJSON(inputs.artifact)[matrix.bake_target].dst }}
|
||||
mod: ${{ fromJSON(inputs.artifact)[matrix.bake_target].chmod || 'u+r' }}
|
||||
|
||||
run: |
|
||||
rm -rf _artifact
|
||||
mkdir -p "_artifact"
|
||||
mv "$src" "_artifact/$dst"
|
||||
chmod "$mod" "_artifact/$dst"
|
||||
|
||||
# Optionally compress extracted artifact
|
||||
- name: Compress Artifact
|
||||
@@ -194,9 +202,9 @@ jobs:
|
||||
|
||||
run: |
|
||||
if test -n "$gzlev"; then
|
||||
mv "_artifact/$dst" "_artifact/foo"
|
||||
gzip "-$gzlev" "_artifact/foo"
|
||||
mv "_artifact/foo.gz" "_artifact/$dst"
|
||||
mv "_artifact/$dst" "_artifact/$dst.artifact"
|
||||
gzip "-$gzlev" "_artifact/$dst.artifact"
|
||||
mv "_artifact/$dst.artifact.gz" "_artifact/$dst"
|
||||
elif test -n "$zstlev"; then
|
||||
zstd "-$zstlev" "_artifact/$dst"
|
||||
mv "_artifact/$dst.zst" "_artifact/$dst"
|
||||
@@ -211,7 +219,7 @@ jobs:
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
path: _artifact/*
|
||||
name: ${{matrix.cargo_profile}}-${{matrix.feat_set}}-${{fromJSON(inputs.artifact)[matrix.bake_target].dst}}
|
||||
name: ${{matrix.cargo_profile}}-${{matrix.feat_set}}-${{matrix.sys_target}}-${{fromJSON(inputs.artifact)[matrix.bake_target].dst}}
|
||||
|
||||
# Optionally upload artifact as pages-artifact
|
||||
- name: Upload Pages Artifact
|
||||
@@ -240,5 +248,5 @@ jobs:
|
||||
with:
|
||||
upload_url: ${{inputs.release_url}}
|
||||
asset_path: _artifact/${{fromJSON(inputs.artifact)[matrix.bake_target].dst}}
|
||||
asset_name: ${{github.ref_name}}-${{matrix.cargo_profile}}-${{matrix.feat_set}}-${{fromJSON(inputs.artifact)[matrix.bake_target].dst}}
|
||||
asset_name: ${{github.ref_name}}-${{matrix.cargo_profile}}-${{matrix.feat_set}}-${{matrix.sys_target}}-${{fromJSON(inputs.artifact)[matrix.bake_target].dst}}
|
||||
asset_content_type: ${{fromJSON(inputs.artifact)[matrix.bake_target].mime || 'application/octet-stream'}}
|
||||
|
||||
@@ -1,163 +0,0 @@
|
||||
name: Dependencies
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
cargo_profiles:
|
||||
type: string
|
||||
description: Cargo profiles
|
||||
feat_sets:
|
||||
type: string
|
||||
description: Cargo feature groups
|
||||
rust_toolchains:
|
||||
type: string
|
||||
description: Rust toolchains
|
||||
sys_names:
|
||||
type: string
|
||||
description: System names
|
||||
sys_versions:
|
||||
type: string
|
||||
description: System versions
|
||||
rust_targets:
|
||||
type: string
|
||||
description: Rust targets
|
||||
sys_targets:
|
||||
type: string
|
||||
description: System targets
|
||||
machines:
|
||||
type: string
|
||||
description: Hardware platform vector
|
||||
excludes:
|
||||
type: string
|
||||
default: '[]'
|
||||
description: Matrix exclusions
|
||||
includes:
|
||||
type: string
|
||||
default: '[]'
|
||||
description: Matrix inclusions
|
||||
show_systems:
|
||||
type: boolean
|
||||
default: true
|
||||
description: Pre-build systems dependency targets
|
||||
show_sources:
|
||||
type: boolean
|
||||
default: true
|
||||
description: Pre-build sources dependency targets
|
||||
show_rocksdb:
|
||||
type: boolean
|
||||
default: true
|
||||
description: Pre-build rocksdb dependency targets
|
||||
show_cargo:
|
||||
type: boolean
|
||||
default: true
|
||||
description: Pre-build cargo dependency targets
|
||||
|
||||
jobs:
|
||||
systems:
|
||||
if: >
|
||||
!failure() && !cancelled()
|
||||
&& inputs.show_systems
|
||||
|
||||
name: System
|
||||
uses: ./.github/workflows/bake.yml
|
||||
with:
|
||||
bake_targets: '["systems"]'
|
||||
cargo_profiles: ${{inputs.cargo_profiles}}
|
||||
feat_sets: ${{inputs.feat_sets}}
|
||||
rust_toolchains: ${{inputs.rust_toolchains}}
|
||||
sys_names: ${{inputs.sys_names}}
|
||||
sys_versions: ${{inputs.sys_versions}}
|
||||
rust_targets: ${{inputs.rust_targets}}
|
||||
sys_targets: ${{inputs.sys_targets}}
|
||||
machines: ${{inputs.machines}}
|
||||
excludes: ${{inputs.excludes}}
|
||||
includes: ${{inputs.includes}}
|
||||
|
||||
buildsys:
|
||||
if: >
|
||||
!failure() && !cancelled()
|
||||
&& inputs.show_systems
|
||||
|
||||
name: Builder
|
||||
needs: [systems]
|
||||
uses: ./.github/workflows/bake.yml
|
||||
with:
|
||||
bake_targets: '["rust"]'
|
||||
cargo_profiles: ${{inputs.cargo_profiles}}
|
||||
feat_sets: ${{inputs.feat_sets}}
|
||||
rust_toolchains: ${{inputs.rust_toolchains}}
|
||||
sys_names: ${{inputs.sys_names}}
|
||||
sys_versions: ${{inputs.sys_versions}}
|
||||
rust_targets: ${{inputs.rust_targets}}
|
||||
sys_targets: ${{inputs.sys_targets}}
|
||||
machines: ${{inputs.machines}}
|
||||
excludes: ${{inputs.excludes}}
|
||||
includes: ${{inputs.includes}}
|
||||
|
||||
sources:
|
||||
if: >
|
||||
!failure() && !cancelled()
|
||||
&& inputs.show_sources
|
||||
|
||||
name: Acquire
|
||||
needs: [buildsys]
|
||||
uses: ./.github/workflows/bake.yml
|
||||
with:
|
||||
bake_targets: '["sources"]'
|
||||
cargo_profiles: ${{inputs.cargo_profiles}}
|
||||
feat_sets: ${{inputs.feat_sets}}
|
||||
rust_toolchains: ${{inputs.rust_toolchains}}
|
||||
sys_names: ${{inputs.sys_names}}
|
||||
sys_versions: ${{inputs.sys_versions}}
|
||||
rust_targets: ${{inputs.rust_targets}}
|
||||
sys_targets: ${{inputs.sys_targets}}
|
||||
machines: ${{inputs.machines}}
|
||||
excludes: ${{inputs.excludes}}
|
||||
includes: ${{inputs.includes}}
|
||||
|
||||
rocksdb:
|
||||
if: >
|
||||
!failure() && !cancelled()
|
||||
&& inputs.show_rocksdb
|
||||
|
||||
name: RocksDB
|
||||
needs: [sources]
|
||||
uses: ./.github/workflows/bake.yml
|
||||
with:
|
||||
bake_targets: '["rocksdb"]'
|
||||
cargo_profiles: ${{inputs.cargo_profiles}}
|
||||
feat_sets: ${{inputs.feat_sets}}
|
||||
rust_toolchains: ${{inputs.rust_toolchains}}
|
||||
sys_names: ${{inputs.sys_names}}
|
||||
sys_versions: ${{inputs.sys_versions}}
|
||||
rust_targets: ${{inputs.rust_targets}}
|
||||
sys_targets: ${{inputs.sys_targets}}
|
||||
machines: ${{inputs.machines}}
|
||||
excludes: ${{inputs.excludes}}
|
||||
includes: ${{inputs.includes}}
|
||||
|
||||
deps:
|
||||
if: >
|
||||
!failure() && !cancelled()
|
||||
&& inputs.show_cargo
|
||||
|
||||
name: Build
|
||||
needs: [rocksdb]
|
||||
uses: ./.github/workflows/bake.yml
|
||||
with:
|
||||
bake_targets: >
|
||||
[
|
||||
"deps-clippy",
|
||||
"deps-build-tests",
|
||||
"deps-build-bins"
|
||||
]
|
||||
cargo_profiles: ${{inputs.cargo_profiles}}
|
||||
feat_sets: ${{inputs.feat_sets}}
|
||||
rust_toolchains: ${{inputs.rust_toolchains}}
|
||||
sys_names: ${{inputs.sys_names}}
|
||||
sys_versions: ${{inputs.sys_versions}}
|
||||
rust_targets: ${{inputs.rust_targets}}
|
||||
sys_targets: ${{inputs.sys_targets}}
|
||||
machines: ${{inputs.machines}}
|
||||
excludes: ${{inputs.excludes}}
|
||||
includes: ${{inputs.includes}}
|
||||
@@ -43,6 +43,7 @@ jobs:
|
||||
&& 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])
|
||||
|
||||
name: Format
|
||||
uses: ./.github/workflows/bake.yml
|
||||
@@ -53,8 +54,8 @@ jobs:
|
||||
rust_toolchains: '["nightly"]'
|
||||
rust_targets: ${{inputs.rust_targets}}
|
||||
sys_names: ${{inputs.sys_names}}
|
||||
sys_targets: ${{inputs.sys_targets}}
|
||||
sys_versions: ${{inputs.sys_versions}}
|
||||
sys_targets: '["x86_64-v1-linux-gnu"]'
|
||||
machines: ${{inputs.machines}}
|
||||
excludes: ${{inputs.excludes}}
|
||||
includes: ${{inputs.includes}}
|
||||
@@ -65,6 +66,7 @@ jobs:
|
||||
&& 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])
|
||||
|
||||
name: Typos
|
||||
uses: ./.github/workflows/bake.yml
|
||||
@@ -76,7 +78,7 @@ jobs:
|
||||
sys_names: ${{inputs.sys_names}}
|
||||
sys_versions: ${{inputs.sys_versions}}
|
||||
rust_targets: ${{inputs.rust_targets}}
|
||||
sys_targets: ${{inputs.sys_targets}}
|
||||
sys_targets: '["x86_64-v1-linux-gnu"]'
|
||||
machines: ${{inputs.machines}}
|
||||
excludes: ${{inputs.excludes}}
|
||||
includes: ${{inputs.includes}}
|
||||
@@ -87,6 +89,7 @@ jobs:
|
||||
&& 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])
|
||||
|
||||
name: Audit
|
||||
uses: ./.github/workflows/bake.yml
|
||||
@@ -98,17 +101,20 @@ jobs:
|
||||
sys_names: ${{inputs.sys_names}}
|
||||
sys_versions: ${{inputs.sys_versions}}
|
||||
rust_targets: ${{inputs.rust_targets}}
|
||||
sys_targets: ${{inputs.sys_targets}}
|
||||
sys_targets: '["x86_64-v1-linux-gnu"]'
|
||||
machines: ${{inputs.machines}}
|
||||
excludes: ${{inputs.excludes}}
|
||||
includes: ${{inputs.includes}}
|
||||
|
||||
# disabled due to excessive build time issue installing cargo lychee
|
||||
lychee:
|
||||
if: >
|
||||
!failure() && !cancelled()
|
||||
&& 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])
|
||||
&& false
|
||||
|
||||
name: Lychee
|
||||
uses: ./.github/workflows/bake.yml
|
||||
@@ -120,7 +126,30 @@ jobs:
|
||||
sys_names: ${{inputs.sys_names}}
|
||||
sys_versions: ${{inputs.sys_versions}}
|
||||
rust_targets: ${{inputs.rust_targets}}
|
||||
sys_targets: ${{inputs.sys_targets}}
|
||||
sys_targets: '["x86_64-v1-linux-gnu"]'
|
||||
machines: ${{inputs.machines}}
|
||||
excludes: ${{inputs.excludes}}
|
||||
includes: ${{inputs.includes}}
|
||||
|
||||
check:
|
||||
if: >
|
||||
!failure() && !cancelled()
|
||||
&& 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])
|
||||
|
||||
name: Check
|
||||
uses: ./.github/workflows/bake.yml
|
||||
with:
|
||||
bake_targets: '["check"]'
|
||||
cargo_profiles: '["test"]'
|
||||
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-v1-linux-gnu"]'
|
||||
machines: ${{inputs.machines}}
|
||||
excludes: ${{inputs.excludes}}
|
||||
includes: ${{inputs.includes}}
|
||||
|
||||
+156
-50
@@ -18,7 +18,7 @@ concurrency:
|
||||
jobs:
|
||||
init:
|
||||
name: Init
|
||||
runs-on: self-hosted
|
||||
runs-on: ${{matrix.runner}}
|
||||
env:
|
||||
default_cargo_profiles: '["test", "release"]'
|
||||
default_feat_sets: '["none", "default", "all"]'
|
||||
@@ -26,8 +26,8 @@ jobs:
|
||||
default_sys_names: '["debian"]'
|
||||
default_sys_versions: '["testing-slim"]'
|
||||
default_rust_targets: '["x86_64-unknown-linux-gnu"]'
|
||||
default_sys_targets: '["x86_64-linux-gnu"]'
|
||||
default_machines: '["x86_64"]'
|
||||
default_sys_targets: '["x86_64-v1-linux-gnu"]'
|
||||
default_machines: '["X64"]'
|
||||
|
||||
outputs:
|
||||
cargo_profiles: ${{vars.CARGO_PROFILES || env.default_cargo_profiles}}
|
||||
@@ -38,10 +38,32 @@ jobs:
|
||||
sys_targets: ${{vars.SYS_TARGETS || env.default_sys_targets}}
|
||||
sys_versions: ${{vars.SYS_VERSIONS || env.default_sys_versions}}
|
||||
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
|
||||
matrix:
|
||||
runner: ${{fromJSON(vars.RUNNERS || '["het"]')}}
|
||||
machine: ${{fromJSON(vars.MACHINES || '["X64"]')}}
|
||||
exclude: ${{fromJSON(vars.RUNNERS_EXCLUDES || '[]')}}
|
||||
|
||||
steps:
|
||||
- name: Initialize Builder
|
||||
env:
|
||||
runner: ${{matrix.runner}}
|
||||
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}"
|
||||
@@ -49,7 +71,9 @@ jobs:
|
||||
exit 0
|
||||
fi
|
||||
|
||||
set -e
|
||||
set -eux
|
||||
reserved_space=$(echo -n "$reserved_space" | jq -r ".$runner")
|
||||
max_used_space=$(echo -n "$max_used_space" | jq -r ".$runner")
|
||||
cat <<EOF > ./buildkitd.toml
|
||||
[system]
|
||||
platformsCacheMaxAge = "504h"
|
||||
@@ -57,11 +81,11 @@ jobs:
|
||||
enabled = true
|
||||
rootless = false
|
||||
gc = true
|
||||
reservedSpace = "256GB"
|
||||
maxUsedSpace = "576GB"
|
||||
reservedSpace = "${reserved_space}"
|
||||
maxUsedSpace = "${max_used_space}"
|
||||
[[worker.oci.gcpolicy]]
|
||||
reservedSpace = "256GB"
|
||||
maxUsedSpace = "576GB"
|
||||
reservedSpace = "${reserved_space}"
|
||||
maxUsedSpace = "${max_used_space}"
|
||||
filters = ["label!=cache==pin"]
|
||||
all = true
|
||||
EOF
|
||||
@@ -76,6 +100,7 @@ jobs:
|
||||
- if: >
|
||||
!failure() && !cancelled()
|
||||
&& contains(github.ref, 'refs/tags/v')
|
||||
&& matrix.runner == 'het'
|
||||
|
||||
id: release
|
||||
name: Create Release
|
||||
@@ -85,36 +110,16 @@ jobs:
|
||||
with:
|
||||
release_name: Release ${{github.ref_name}}
|
||||
tag_name: ${{github.ref}}
|
||||
body_path: NEWS.md
|
||||
body_path: RELEASE.md
|
||||
draft: ${{ contains(github.ref, '-draft') }}
|
||||
prerelease: ${{ contains(github.ref, '-rc') }}
|
||||
|
||||
## commented due to https://github.com/orgs/community/discussions/18001
|
||||
#deps:
|
||||
# if: ${{fromJSON(vars.CI_VERBOSE_DEPS || 'false')}}
|
||||
# name: Deps
|
||||
# needs: [init]
|
||||
# uses: ./.github/workflows/deps.yml
|
||||
# with:
|
||||
# cargo_profiles: ${{needs.init.outputs.cargo_profiles}}
|
||||
# feat_sets: ${{needs.init.outputs.feat_sets}}
|
||||
# rust_toolchains: ${{needs.init.outputs.rust_toolchains}}
|
||||
# rust_targets: ${{needs.init.outputs.rust_targets}}
|
||||
# sys_names: ${{needs.init.outputs.sys_names}}
|
||||
# sys_targets: ${{needs.init.outputs.sys_targets}}
|
||||
# sys_versions: ${{needs.init.outputs.sys_versions}}
|
||||
# machines: ${{needs.init.outputs.machines}}
|
||||
# excludes: >
|
||||
# [
|
||||
# {"cargo_profile": "release-debuginfo", "rust_toolchain": "nightly"},
|
||||
# {"cargo_profile": "release-native", "rust_toolchain": "stable"},
|
||||
# {"cargo_profile": "release-native", "feat_set": "none"}
|
||||
# ]
|
||||
|
||||
lint:
|
||||
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]
|
||||
@@ -130,25 +135,50 @@ jobs:
|
||||
machines: ${{needs.init.outputs.machines}}
|
||||
excludes: >
|
||||
[
|
||||
{"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"},
|
||||
{"cargo_profile": "release-debuginfo"},
|
||||
{"cargo_profile": "release-native", "rust_toolchain": "stable"},
|
||||
{"cargo_profile": "release-native", "feat_set": "none"},
|
||||
{"cargo_profile": "release-native", "feat_set": "default"},
|
||||
{"cargo_profile": "test", "rust_toolchain": "stable", "feat_set": "none"},
|
||||
{"cargo_profile": "release", "rust_toolchain": "nightly", "feat_set": "none"},
|
||||
{"cargo_profile": "release", "rust_toolchain": "nightly", "feat_set": "default"}
|
||||
{"cargo_profile": "release-native", "feat_set": "logging"},
|
||||
{"cargo_profile": "release-native", "rust_toolchain": "stable"},
|
||||
{"cargo_profile": "release-native", "rust_target": "aarch64-unknown-linux-gnu"},
|
||||
{"rust_target": "aarch64-unknown-linux-gnu", "feat_set": "none"},
|
||||
{"rust_target": "aarch64-unknown-linux-gnu", "feat_set": "default"},
|
||||
{"rust_target": "aarch64-unknown-linux-gnu", "feat_set": "logging"},
|
||||
{"rust_target": "aarch64-unknown-linux-gnu", "rust_toolchain": "nightly"},
|
||||
{"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"},
|
||||
{"rust_target": "aarch64-unknown-linux-gnu", "sys_target": "x86_64-v4-linux-gnu"},
|
||||
{"rust_target": "x86_64-unknown-linux-gnu", "sys_target": "aarch64-v8-linux-gnu"},
|
||||
{"sys_target": "aarch64-v8-linux-gnu", "machine": "X64"},
|
||||
{"sys_target": "x86_64-v1-linux-gnu", "machine": "ARM64"},
|
||||
{"sys_target": "x86_64-v2-linux-gnu"},
|
||||
{"sys_target": "x86_64-v3-linux-gnu"},
|
||||
{"sys_target": "x86_64-v4-linux-gnu"},
|
||||
]
|
||||
|
||||
test:
|
||||
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:
|
||||
complement: ${{fromJSON(vars.COMPLEMENT || 'true')}}
|
||||
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}}
|
||||
feat_sets: ${{needs.init.outputs.feat_sets}}
|
||||
rust_toolchains: ${{needs.init.outputs.rust_toolchains}}
|
||||
@@ -159,22 +189,52 @@ jobs:
|
||||
machines: ${{needs.init.outputs.machines}}
|
||||
excludes: >
|
||||
[
|
||||
{"feat_set": "logging"},
|
||||
{"cargo_profile": "test", "rust_toolchain": "stable", "feat_set": "none"},
|
||||
{"cargo_profile": "release-native", "rust_toolchain": "stable"},
|
||||
{"cargo_profile": "release-native", "feat_set": "default"},
|
||||
{"cargo_profile": "test", "rust_target": "aarch64-unknown-linux-gnu"},
|
||||
{"cargo_profile": "release-debuginfo"},
|
||||
{"cargo_profile": "release-native", "feat_set": "none"},
|
||||
{"cargo_profile": "release-debuginfo"}
|
||||
{"cargo_profile": "release-native", "feat_set": "default"},
|
||||
{"cargo_profile": "release-native", "rust_toolchain": "stable"},
|
||||
{"cargo_profile": "release-native", "rust_target": "aarch64-unknown-linux-gnu"},
|
||||
{"rust_target": "aarch64-unknown-linux-gnu", "feat_set": "none"},
|
||||
{"rust_target": "aarch64-unknown-linux-gnu", "feat_set": "default"},
|
||||
{"rust_target": "aarch64-unknown-linux-gnu", "feat_set": "logging"},
|
||||
{"rust_target": "aarch64-unknown-linux-gnu", "rust_toolchain": "nightly"},
|
||||
{"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"},
|
||||
{"rust_target": "aarch64-unknown-linux-gnu", "sys_target": "x86_64-v4-linux-gnu"},
|
||||
{"rust_target": "x86_64-unknown-linux-gnu", "sys_target": "aarch64-v8-linux-gnu"},
|
||||
{"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": "none"},
|
||||
{"sys_target": "x86_64-v2-linux-gnu", "feat_set": "default"},
|
||||
{"sys_target": "x86_64-v3-linux-gnu", "machine": "ARM64"},
|
||||
{"sys_target": "x86_64-v3-linux-gnu", "feat_set": "none"},
|
||||
{"sys_target": "x86_64-v3-linux-gnu", "feat_set": "default"},
|
||||
{"sys_target": "x86_64-v4-linux-gnu", "machine": "ARM64"},
|
||||
{"sys_target": "x86_64-v4-linux-gnu", "feat_set": "none"},
|
||||
{"sys_target": "x86_64-v4-linux-gnu", "feat_set": "default"},
|
||||
]
|
||||
|
||||
package:
|
||||
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}}
|
||||
@@ -187,27 +247,57 @@ 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"},
|
||||
{"cargo_profile": "release-debuginfo", "rust_toolchain": "nightly"},
|
||||
{"cargo_profile": "release-debuginfo", "bake_target": "oci"},
|
||||
{"cargo_profile": "release-native", "feat_set": "default"},
|
||||
{"cargo_profile": "release-native", "rust_toolchain": "stable"},
|
||||
{"cargo_profile": "release-native", "feat_set": "default"},
|
||||
{"cargo_profile": "release-native", "bake_target": "docker"},
|
||||
{"cargo_profile": "release-native", "bake_target": "oci"},
|
||||
{"cargo_profile": "release", "rust_toolchain": "nightly"}
|
||||
{"cargo_profile": "release-debuginfo", "rust_target": "aarch64-unknown-linux-gnu"},
|
||||
{"cargo_profile": "release-debuginfo", "sys_target": "x86_64-v2-linux-gnu"},
|
||||
{"cargo_profile": "release-debuginfo", "sys_target": "x86_64-v3-linux-gnu"},
|
||||
{"cargo_profile": "release-debuginfo", "sys_target": "x86_64-v4-linux-gnu"},
|
||||
{"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"},
|
||||
{"rust_target": "aarch64-unknown-linux-gnu", "sys_target": "x86_64-v4-linux-gnu"},
|
||||
{"rust_target": "x86_64-unknown-linux-gnu", "sys_target": "aarch64-v8-linux-gnu"},
|
||||
{"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-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, package, test]
|
||||
needs: [init, test, package]
|
||||
uses: ./.github/workflows/publish.yml
|
||||
with:
|
||||
docker_repo: ${{vars.DOCKER_REPO}}
|
||||
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}}
|
||||
@@ -219,10 +309,26 @@ jobs:
|
||||
excludes: >
|
||||
[
|
||||
{"feat_set": "none"},
|
||||
{"feat_set": "default"},
|
||||
{"cargo_profile": "test"},
|
||||
{"rust_toolchain": "nightly", "cargo_profile": "release"},
|
||||
{"cargo_profile": "bench"},
|
||||
{"cargo_profile": "release-debuginfo"},
|
||||
{"cargo_profile": "release-native"}
|
||||
{"cargo_profile": "release-native"},
|
||||
{"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"},
|
||||
{"rust_target": "aarch64-unknown-linux-gnu", "sys_target": "x86_64-v4-linux-gnu"},
|
||||
{"rust_target": "x86_64-unknown-linux-gnu", "sys_target": "aarch64-v8-linux-gnu"},
|
||||
{"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": "logging"},
|
||||
{"sys_target": "x86_64-v3-linux-gnu", "machine": "ARM64"},
|
||||
{"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": "logging"},
|
||||
]
|
||||
|
||||
secrets:
|
||||
|
||||
@@ -38,6 +38,15 @@ on:
|
||||
release_url:
|
||||
type: string
|
||||
description: For release assets
|
||||
build_pkgs:
|
||||
type: string
|
||||
default: 'true'
|
||||
check_pkgs:
|
||||
type: string
|
||||
default: 'false'
|
||||
build_nix:
|
||||
type: string
|
||||
default: 'true'
|
||||
|
||||
jobs:
|
||||
book:
|
||||
@@ -46,6 +55,7 @@ jobs:
|
||||
&& contains(fromJSON(inputs.cargo_profiles), fromJSON('["release"]')[0])
|
||||
&& contains(fromJSON(inputs.feat_sets), fromJSON('["default"]')[0])
|
||||
&& contains(fromJSON(inputs.rust_toolchains), fromJSON('["stable"]')[0])
|
||||
&& contains(fromJSON(inputs.sys_targets), fromJSON('["x86_64-v1-linux-gnu"]')[0])
|
||||
|
||||
name: Book
|
||||
uses: ./.github/workflows/bake.yml
|
||||
@@ -56,9 +66,10 @@ jobs:
|
||||
rust_toolchains: '["stable"]'
|
||||
rust_targets: ${{inputs.rust_targets}}
|
||||
sys_names: ${{inputs.sys_names}}
|
||||
sys_targets: ${{inputs.sys_targets}}
|
||||
sys_versions: ${{inputs.sys_versions}}
|
||||
sys_targets: '["x86_64-v1-linux-gnu"]'
|
||||
machines: ${{inputs.machines}}
|
||||
runner: 'het'
|
||||
excludes: ${{inputs.excludes}}
|
||||
includes: ${{inputs.includes}}
|
||||
release_url: ${{inputs.release_url}}
|
||||
@@ -96,7 +107,8 @@ jobs:
|
||||
"dst": "tuwunel.zst",
|
||||
"src": "/usr/bin/tuwunel",
|
||||
"mime": "application/zstd",
|
||||
"zstd": 11
|
||||
"zstd": 11,
|
||||
"chmod": "a+x"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,11 +153,12 @@ jobs:
|
||||
&& contains(fromJSON(inputs.feat_sets), fromJSON('["all"]')[0])
|
||||
&& contains(fromJSON(inputs.cargo_profiles), fromJSON('["release"]')[0])
|
||||
&& contains(fromJSON(inputs.rust_toolchains), fromJSON('["stable"]')[0])
|
||||
&& fromJSON(inputs.build_pkgs)
|
||||
|
||||
name: Distro Packages
|
||||
uses: ./.github/workflows/bake.yml
|
||||
with:
|
||||
bake_targets: '["pkg-deb", "pkg-rpm"]'
|
||||
bake_targets: '["deb", "rpm", "nix"]'
|
||||
cargo_profiles: '["release"]'
|
||||
feat_sets: '["all"]'
|
||||
rust_toolchains: '["stable"]'
|
||||
@@ -159,15 +172,19 @@ jobs:
|
||||
release_url: ${{inputs.release_url}}
|
||||
artifact: >
|
||||
{
|
||||
"pkg-deb": {
|
||||
"deb": {
|
||||
"dst": "tuwunel.deb",
|
||||
"src": "tuwunel_1.0.0-1_amd64.deb",
|
||||
"mime": "application/vnd.debian.binary-package"
|
||||
},
|
||||
"pkg-rpm": {
|
||||
"rpm": {
|
||||
"dst": "tuwunel.rpm",
|
||||
"src": "tuwunel-1.0.0-1.x86_64.rpm",
|
||||
"mime": "application/x-rpm"
|
||||
},
|
||||
"nix": {
|
||||
"dst": "tuwunel.drv.tar.zst",
|
||||
"src": "/opt/tuwunel/tuwunel.drv",
|
||||
"mime": "application/zstd",
|
||||
"zstd": 6
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,13 +194,13 @@ jobs:
|
||||
&& contains(fromJSON(inputs.feat_sets), fromJSON('["all"]')[0])
|
||||
&& contains(fromJSON(inputs.cargo_profiles), fromJSON('["release"]')[0])
|
||||
&& contains(fromJSON(inputs.rust_toolchains), fromJSON('["stable"]')[0])
|
||||
&& false
|
||||
&& fromJSON(inputs.check_pkgs)
|
||||
|
||||
name: Check Packages
|
||||
needs: [pkgs]
|
||||
uses: ./.github/workflows/bake.yml
|
||||
with:
|
||||
bake_targets: '["pkg-deb-install", "pkg-rpm-install"]'
|
||||
bake_targets: '["deb-install", "rpm-install"]'
|
||||
cargo_profiles: '["release"]'
|
||||
feat_sets: '["all"]'
|
||||
rust_toolchains: '["stable"]'
|
||||
|
||||
@@ -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()
|
||||
&& inputs.machines
|
||||
&& (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v'))
|
||||
&& !contains(github.ref, '-draft')
|
||||
|
||||
name: Documents
|
||||
runs-on: self-hosted
|
||||
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
|
||||
@@ -70,16 +77,14 @@ jobs:
|
||||
&& inputs.machines
|
||||
&& inputs.docker_repo != ''
|
||||
&& (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v'))
|
||||
&& !contains(github.ref, '-draft')
|
||||
|
||||
name: Containers
|
||||
runs-on: ${{matrix.machine}}
|
||||
permissions: write-all
|
||||
continue-on-error: false
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
bake_target: ${{fromJSON('["github", "dockerhub"]')}}
|
||||
bake_target: ${{fromJSON('["docker_io", "ghcr_io"]')}}
|
||||
cargo_profile: ${{fromJSON(inputs.cargo_profiles)}}
|
||||
rust_toolchain: ${{fromJSON(inputs.rust_toolchains)}}
|
||||
feat_set: ${{fromJSON(inputs.feat_sets)}}
|
||||
@@ -92,6 +97,10 @@ jobs:
|
||||
include: ${{fromJSON(inputs.includes)}}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: GitHub Login
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
@@ -120,8 +129,200 @@ jobs:
|
||||
acct: ${{github.actor}}
|
||||
repo: ${{github.repository}}
|
||||
docker_repo: ${{inputs.docker_repo}}
|
||||
docker_tag_preview: ${{ contains(github.ref, 'refs/tags/v') }}
|
||||
docker_tag_latest: ${{ contains(github.ref, 'refs/tags/v') && !contains(github.ref, '-rc') }}
|
||||
|
||||
run: |
|
||||
docker/bake.sh "${{matrix.bake_target}}"
|
||||
|
||||
bundles:
|
||||
if: >
|
||||
!failure() && !cancelled()
|
||||
&& inputs.machines
|
||||
&& inputs.docker_repo != ''
|
||||
&& contains(fromJSON(inputs.cargo_profiles), fromJSON('["release"]')[0])
|
||||
&& contains(fromJSON(inputs.feat_sets), fromJSON('["all"]')[0])
|
||||
&& contains(fromJSON(inputs.rust_toolchains), fromJSON('["stable"]')[0])
|
||||
&& (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v'))
|
||||
|
||||
name: Bundles
|
||||
needs: [containers]
|
||||
runs-on: 'het'
|
||||
permissions: write-all
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
bake_target: ${{fromJSON('["docker_io", "ghcr_io"]')}}
|
||||
cargo_profile: ${{fromJSON('["release"]')}}
|
||||
feat_set: ${{fromJSON('["all"]')}}
|
||||
rust_toolchain: ${{fromJSON('["stable"]')}}
|
||||
sys_name: ${{fromJSON(inputs.sys_names)}}
|
||||
sys_version: ${{fromJSON(inputs.sys_versions)}}
|
||||
rust_target: ${{fromJSON(inputs.rust_targets)}}
|
||||
sys_target: ${{fromJSON(inputs.sys_targets)}}
|
||||
machine: ${{fromJSON(inputs.machines)}}
|
||||
exclude: ${{fromJSON(inputs.excludes)}}
|
||||
include: ${{fromJSON(inputs.includes)}}
|
||||
|
||||
steps:
|
||||
- name: GitHub Login
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.ghcr_token }}
|
||||
|
||||
- name: DockerHub Login
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: docker.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.dockerhub_token }}
|
||||
|
||||
- name: Release Branch
|
||||
if: >
|
||||
!failure() && !cancelled()
|
||||
&& (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v'))
|
||||
|
||||
env:
|
||||
acct: ${{github.actor}}
|
||||
ghcr_repo: ${{github.repository}}
|
||||
docker_repo: ${{inputs.docker_repo}}
|
||||
registry: ${{matrix.bake_target}}
|
||||
cargo_profile: ${{matrix.cargo_profile}}
|
||||
feat_set: ${{matrix.feat_set}}
|
||||
sys_target: ${{matrix.sys_target}}
|
||||
ref_name: ${{github.ref_name}}
|
||||
|
||||
run: |
|
||||
registry="$(echo -n $registry | sed s/_/./)"
|
||||
if test "$registry" = "ghcr.io"; then
|
||||
repo="$ghcr_repo"
|
||||
else
|
||||
repo="$docker_repo"
|
||||
fi
|
||||
|
||||
docker manifest create "${registry}/${repo}:${ref_name}" \
|
||||
--amend "${registry}/${repo}:${ref_name}-${cargo_profile}-${feat_set}-${sys_target}"
|
||||
|
||||
- name: Release Preview
|
||||
if: >
|
||||
!failure() && !cancelled()
|
||||
&& contains(github.ref, 'refs/tags/v')
|
||||
&& !contains(github.ref, '-draft')
|
||||
|
||||
env:
|
||||
acct: ${{github.actor}}
|
||||
ghcr_repo: ${{github.repository}}
|
||||
docker_repo: ${{inputs.docker_repo}}
|
||||
registry: ${{matrix.bake_target}}
|
||||
cargo_profile: ${{matrix.cargo_profile}}
|
||||
feat_set: ${{matrix.feat_set}}
|
||||
sys_target: ${{matrix.sys_target}}
|
||||
ref_name: ${{github.ref_name}}
|
||||
|
||||
run: |
|
||||
registry="$(echo -n $registry | sed s/_/./)"
|
||||
if test "$registry" = "ghcr.io"; then
|
||||
repo="$ghcr_repo"
|
||||
else
|
||||
repo="$docker_repo"
|
||||
fi
|
||||
|
||||
docker manifest create "${registry}/${repo}:preview" \
|
||||
--amend "${registry}/${repo}:${ref_name}-${cargo_profile}-${feat_set}-${sys_target}"
|
||||
|
||||
- name: Release Latest
|
||||
if: >
|
||||
!failure() && !cancelled()
|
||||
&& contains(github.ref, 'refs/tags/v')
|
||||
&& !contains(github.ref, '-draft')
|
||||
&& !contains(github.ref, '-rc')
|
||||
|
||||
env:
|
||||
acct: ${{github.actor}}
|
||||
ghcr_repo: ${{github.repository}}
|
||||
docker_repo: ${{inputs.docker_repo}}
|
||||
registry: ${{matrix.bake_target}}
|
||||
cargo_profile: ${{matrix.cargo_profile}}
|
||||
feat_set: ${{matrix.feat_set}}
|
||||
sys_target: ${{matrix.sys_target}}
|
||||
ref_name: ${{github.ref_name}}
|
||||
|
||||
run: |
|
||||
registry="$(echo -n $registry | sed s/_/./)"
|
||||
if test "$registry" = "ghcr.io"; then
|
||||
repo="$ghcr_repo"
|
||||
else
|
||||
repo="$docker_repo"
|
||||
fi
|
||||
|
||||
docker manifest create "${registry}/${repo}:latest" \
|
||||
--amend "${registry}/${repo}:${ref_name}-${cargo_profile}-${feat_set}-${sys_target}"
|
||||
|
||||
delivery:
|
||||
if: >
|
||||
!failure() && !cancelled()
|
||||
&& inputs.machines
|
||||
&& inputs.docker_repo != ''
|
||||
&& (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v'))
|
||||
|
||||
name: Delivery
|
||||
needs: [bundles, documents]
|
||||
runs-on: 'het'
|
||||
permissions: write-all
|
||||
steps:
|
||||
- name: GitHub Login
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.ghcr_token }}
|
||||
|
||||
- name: DockerHub Login
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: docker.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.dockerhub_token }}
|
||||
|
||||
- name: Push Branch
|
||||
if: >
|
||||
!failure() && !cancelled()
|
||||
&& (github.ref == 'refs/heads/main' || contains(github.ref, 'refs/tags/v'))
|
||||
|
||||
env:
|
||||
ref_name: ${{github.ref_name}}
|
||||
ghcr_repo: ${{github.repository}}
|
||||
docker_repo: ${{inputs.docker_repo}}
|
||||
|
||||
run: |
|
||||
docker manifest push -p "ghcr.io/${ghcr_repo}:${ref_name}"
|
||||
docker manifest push -p "docker.io/${docker_repo}:${ref_name}"
|
||||
|
||||
- name: Push Preview
|
||||
if: >
|
||||
!failure() && !cancelled()
|
||||
&& contains(github.ref, 'refs/tags/v')
|
||||
&& !contains(github.ref, '-draft')
|
||||
|
||||
env:
|
||||
ghcr_repo: ${{github.repository}}
|
||||
docker_repo: ${{inputs.docker_repo}}
|
||||
|
||||
run: |
|
||||
docker manifest push -p "ghcr.io/${ghcr_repo}:preview"
|
||||
docker manifest push -p "docker.io/${docker_repo}:preview"
|
||||
|
||||
- name: Push Latest
|
||||
if: >
|
||||
!failure() && !cancelled()
|
||||
&& contains(github.ref, 'refs/tags/v')
|
||||
&& !contains(github.ref, '-draft')
|
||||
&& !contains(github.ref, '-rc')
|
||||
|
||||
env:
|
||||
ghcr_repo: ${{github.repository}}
|
||||
docker_repo: ${{inputs.docker_repo}}
|
||||
|
||||
run: |
|
||||
docker manifest push -p "ghcr.io/${ghcr_repo}:latest"
|
||||
docker manifest push -p "docker.io/${docker_repo}:latest"
|
||||
|
||||
+223
-20
@@ -41,31 +41,118 @@ on:
|
||||
complement_feat_sets:
|
||||
type: string
|
||||
default: '["all"]'
|
||||
complement_runner:
|
||||
type: string
|
||||
rust_sdk_integration:
|
||||
type: boolean
|
||||
default: true
|
||||
build_nix:
|
||||
type: boolean
|
||||
default: true
|
||||
head_msg:
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
unit:
|
||||
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])
|
||||
|
||||
name: Docs
|
||||
uses: ./.github/workflows/bake.yml
|
||||
with:
|
||||
bake_targets: '["docs"]'
|
||||
cargo_profiles: '["test"]'
|
||||
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-v1-linux-gnu"]'
|
||||
machines: ${{inputs.machines}}
|
||||
excludes: ${{inputs.excludes}}
|
||||
includes: ${{inputs.includes}}
|
||||
|
||||
unit:
|
||||
if: >
|
||||
!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])
|
||||
|
||||
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}}
|
||||
sys_names: ${{inputs.sys_names}}
|
||||
sys_versions: ${{inputs.sys_versions}}
|
||||
rust_targets: ${{inputs.rust_targets}}
|
||||
sys_targets: ${{inputs.sys_targets}}
|
||||
sys_targets: '["x86_64-v1-linux-gnu"]'
|
||||
machines: ${{inputs.machines}}
|
||||
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: inputs.cargo_profiles
|
||||
if: >
|
||||
!failure() && !cancelled()
|
||||
&& !contains(inputs.head_msg, '[ci only it]')
|
||||
&& !contains(inputs.head_msg, '[ci no build]')
|
||||
&& inputs.cargo_profiles
|
||||
&& inputs.machines
|
||||
|
||||
name: Smoke
|
||||
uses: ./.github/workflows/bake.yml
|
||||
with:
|
||||
@@ -82,26 +169,126 @@ jobs:
|
||||
excludes: >
|
||||
[
|
||||
{"cargo_profile": "test", "feat_set": "default"},
|
||||
{"cargo_profile": "test", "rust_toolchain": "stable"},
|
||||
{"cargo_profile": "test", "feat_set": "logging"},
|
||||
{"cargo_profile": "test", "feat_set": "none", "bake_target": "smoke-valgrind"},
|
||||
{"cargo_profile": "test", "rust_toolchain": "stable"},
|
||||
{"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"},
|
||||
{"cargo_profile": "release-debuginfo", "feat_set": "logging"},
|
||||
{"cargo_profile": "release-debuginfo", "rust_toolchain": "nightly"},
|
||||
{"cargo_profile": "release-debuginfo", "rust_toolchain": "stable", "feat_set": "none"},
|
||||
{"cargo_profile": "release-debuginfo", "bake_target": "smoke"},
|
||||
{"cargo_profile": "release-debuginfo", "rust_target": "aarch64-unknown-linux-gnu"},
|
||||
{"cargo_profile": "release-native", "rust_toolchain": "stable"},
|
||||
{"cargo_profile": "release-native", "feat_set": "none"},
|
||||
{"cargo_profile": "release-native", "feat_set": "default"},
|
||||
{"cargo_profile": "release-native", "bake_target": "smoke-valgrind"}
|
||||
{"cargo_profile": "release-native", "feat_set": "logging"},
|
||||
{"cargo_profile": "release-native", "bake_target": "smoke-valgrind"},
|
||||
{"cargo_profile": "release-native", "rust_target": "aarch64-unknown-linux-gnu"},
|
||||
{"cargo_profile": "release-native", "sys_target": "x86_64-v2-linux-gnu"},
|
||||
{"cargo_profile": "release-native", "sys_target": "x86_64-v3-linux-gnu"},
|
||||
{"cargo_profile": "release-native", "sys_target": "x86_64-v4-linux-gnu"},
|
||||
{"rust_target": "aarch64-unknown-linux-gnu", "feat_set": "none"},
|
||||
{"rust_target": "aarch64-unknown-linux-gnu", "feat_set": "default"},
|
||||
{"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"},
|
||||
{"rust_target": "aarch64-unknown-linux-gnu", "sys_target": "x86_64-v4-linux-gnu"},
|
||||
{"rust_target": "x86_64-unknown-linux-gnu", "sys_target": "aarch64-v8-linux-gnu"},
|
||||
{"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": "none"},
|
||||
{"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": "none"},
|
||||
{"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": "smoke-valgrind"},
|
||||
{"sys_target": "x86_64-v4-linux-gnu", "machine": "ARM64"},
|
||||
{"sys_target": "x86_64-v4-linux-gnu", "feat_set": "none"},
|
||||
{"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": "smoke-valgrind"},
|
||||
]
|
||||
|
||||
complement:
|
||||
nix:
|
||||
if: >
|
||||
inputs.complement
|
||||
!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.complement_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.cargo_profiles), fromJSON('["test"]')[0])
|
||||
&& contains(fromJSON(inputs.complement_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])
|
||||
|
||||
name: Matrix SDK Integration
|
||||
needs: [smoke]
|
||||
uses: ./.github/workflows/bake.yml
|
||||
with:
|
||||
bake_targets: '["matrix-rust-sdk-integration"]'
|
||||
cargo_profiles: '["test"]'
|
||||
feat_sets: ${{inputs.complement_feat_sets}}
|
||||
rust_toolchains: '["nightly"]'
|
||||
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}}
|
||||
runner: ${{inputs.complement_runner}}
|
||||
excludes: ${{inputs.excludes}}
|
||||
includes: ${{inputs.includes}}
|
||||
artifact: >
|
||||
{
|
||||
"matrix-rust-sdk-integration": {
|
||||
"src": "/var/log/tuwunel.log",
|
||||
"dst": "matrix-rust-sdk-integration.tuwunel.log",
|
||||
}
|
||||
}
|
||||
|
||||
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])
|
||||
&& contains(fromJSON(inputs.rust_toolchains), fromJSON('["nightly"]')[0])
|
||||
&& contains(fromJSON(inputs.sys_targets), fromJSON('["x86_64-v1-linux-gnu"]')[0])
|
||||
|
||||
name: Complement
|
||||
uses: ./.github/workflows/bake.yml
|
||||
@@ -113,21 +300,26 @@ jobs:
|
||||
sys_names: ${{inputs.sys_names}}
|
||||
sys_versions: ${{inputs.sys_versions}}
|
||||
rust_targets: ${{inputs.rust_targets}}
|
||||
sys_targets: ${{inputs.sys_targets}}
|
||||
sys_targets: '["x86_64-v1-linux-gnu"]'
|
||||
machines: ${{inputs.machines}}
|
||||
runner: ${{inputs.complement_runner}}
|
||||
excludes: ${{inputs.excludes}}
|
||||
includes: ${{inputs.includes}}
|
||||
|
||||
compliance:
|
||||
if: >
|
||||
inputs.complement
|
||||
!failure() && !cancelled()
|
||||
&& !contains(inputs.head_msg, '[ci no build]')
|
||||
&& inputs.complement
|
||||
&& inputs.machines
|
||||
&& 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])
|
||||
|
||||
name: Matrix Compliance
|
||||
needs: [complement]
|
||||
runs-on: ${{matrix.machine}}
|
||||
needs: [complement, smoke]
|
||||
runs-on: ["${{matrix.machine}}", "${{inputs.complement_runner}}"]
|
||||
concurrency:
|
||||
group: complement-cant-walk-and-chew-bubblegum
|
||||
cancel-in-progress: false
|
||||
@@ -141,7 +333,7 @@ jobs:
|
||||
sys_name: ${{fromJSON(inputs.sys_names)}}
|
||||
sys_version: ${{fromJSON(inputs.sys_versions)}}
|
||||
rust_target: ${{fromJSON(inputs.rust_targets)}}
|
||||
sys_target: ${{fromJSON(inputs.sys_targets)}}
|
||||
sys_target: ${{fromJSON('["x86_64-v1-linux-gnu"]')}}
|
||||
machine: ${{fromJSON(inputs.machines)}}
|
||||
exclude: ${{fromJSON(inputs.excludes)}}
|
||||
include: ${{fromJSON(inputs.includes)}}
|
||||
@@ -163,7 +355,8 @@ jobs:
|
||||
run: |
|
||||
docker/complement.sh
|
||||
|
||||
- name: Extract
|
||||
- if: success() || failure() && steps.execute.outcome == 'failure'
|
||||
name: Extract
|
||||
id: extract
|
||||
env:
|
||||
name: complement_tester__${{matrix.sys_name}}__${{matrix.sys_version}}__${{matrix.sys_target}}
|
||||
@@ -171,14 +364,24 @@ 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
|
||||
|
||||
- name: Upload
|
||||
id: upload
|
||||
- if: success() || failure() && steps.execute.outcome == 'failure'
|
||||
name: Upload New Results
|
||||
id: upload-result
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: test_results.${{matrix.feat_set}}.${{matrix.sys_name}}.${{matrix.sys_target}}.jsonl
|
||||
path: ./tests/test_results/complement/test_results.jsonl
|
||||
name: complement_results-${{matrix.feat_set}}-${{matrix.sys_name}}-${{matrix.sys_target}}.jsonl
|
||||
path: ./tests/complement/results.jsonl
|
||||
|
||||
- if: failure() && steps.execute.outcome == 'failure'
|
||||
name: Upload Failure Output
|
||||
id: upload-output
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: complement_output-${{matrix.feat_set}}-${{matrix.sys_name}}-${{matrix.sys_target}}.jsonl
|
||||
path: ./complement.jsonl
|
||||
if-no-files-found: ignore
|
||||
|
||||
- name: Accept
|
||||
id: accept
|
||||
|
||||
@@ -95,3 +95,10 @@ rustc-ice-*
|
||||
|
||||
# complement test logs are huge
|
||||
tests/test_results/complement/test_logs.jsonl
|
||||
|
||||
# cargo profiles from -Z self-profile
|
||||
*.mm_profdata
|
||||
|
||||
# Editor crashfiles
|
||||
*.save
|
||||
*.swp
|
||||
|
||||
Generated
+1262
-1185
File diff suppressed because it is too large
Load Diff
+147
-129
@@ -28,8 +28,8 @@ keywords = [
|
||||
license = "Apache-2.0"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/matrix-construct/tuwunel"
|
||||
rust-version = "1.86.0"
|
||||
version = "1.0.0"
|
||||
rust-version = "1.88.0"
|
||||
version = "1.4.3"
|
||||
|
||||
[workspace.metadata.crane]
|
||||
name = "tuwunel"
|
||||
@@ -39,7 +39,7 @@ name = "tuwunel"
|
||||
#
|
||||
|
||||
[workspace.dependencies.argon2]
|
||||
version = "0.5.3"
|
||||
version = "0.5"
|
||||
default-features = false
|
||||
features = [
|
||||
"alloc",
|
||||
@@ -47,17 +47,17 @@ features = [
|
||||
]
|
||||
|
||||
[workspace.dependencies.arrayvec]
|
||||
version = "0.7.6"
|
||||
version = "0.7"
|
||||
features = ["serde"]
|
||||
|
||||
[workspace.dependencies.async-channel]
|
||||
version = "2.3.1"
|
||||
|
||||
[workspace.dependencies.async-trait]
|
||||
version = "0.1.88"
|
||||
version = "0.1"
|
||||
|
||||
[workspace.dependencies.axum]
|
||||
version = "0.7.9"
|
||||
version = "0.8"
|
||||
default-features = false
|
||||
features = [
|
||||
"form",
|
||||
@@ -70,10 +70,10 @@ features = [
|
||||
]
|
||||
|
||||
[workspace.dependencies.axum-client-ip]
|
||||
version = "0.6.1"
|
||||
version = "0.7"
|
||||
|
||||
[workspace.dependencies.axum-extra]
|
||||
version = "0.9.6"
|
||||
version = "0.10"
|
||||
default-features = false
|
||||
features = [
|
||||
"typed-header",
|
||||
@@ -81,18 +81,19 @@ features = [
|
||||
]
|
||||
|
||||
[workspace.dependencies.axum-server]
|
||||
version = "0.7.2"
|
||||
version = "0.7"
|
||||
default-features = false
|
||||
|
||||
[workspace.dependencies.axum-server-dual-protocol]
|
||||
version = "0.7"
|
||||
default-features = false
|
||||
|
||||
[workspace.dependencies.base64]
|
||||
version = "0.22.1"
|
||||
version = "0.22"
|
||||
default-features = false
|
||||
|
||||
[workspace.dependencies.blurhash]
|
||||
version = "0.2.3"
|
||||
version = "0.2"
|
||||
default-features = false
|
||||
features = [
|
||||
"fast-linear-to-srgb",
|
||||
@@ -100,13 +101,13 @@ features = [
|
||||
]
|
||||
|
||||
[workspace.dependencies.bytes]
|
||||
version = "1.10.1"
|
||||
version = "1.10"
|
||||
|
||||
[workspace.dependencies.bytesize]
|
||||
version = "2.0"
|
||||
|
||||
[workspace.dependencies.cargo_toml]
|
||||
version = "0.21"
|
||||
version = "0.22"
|
||||
default-features = false
|
||||
features = ["features"]
|
||||
|
||||
@@ -114,12 +115,12 @@ features = ["features"]
|
||||
version = "0.1"
|
||||
|
||||
[workspace.dependencies.chrono]
|
||||
version = "0.4.38"
|
||||
version = "0.4"
|
||||
features = ["alloc", "std"]
|
||||
default-features = false
|
||||
|
||||
[workspace.dependencies.clap]
|
||||
version = "4.5.35"
|
||||
version = "4.5"
|
||||
default-features = false
|
||||
features = [
|
||||
"derive",
|
||||
@@ -131,47 +132,48 @@ features = [
|
||||
"usage",
|
||||
]
|
||||
|
||||
[workspace.dependencies.console-subscriber]
|
||||
version = "0.4"
|
||||
# Disabled until Axum updated. Remember to remove cfgs for tuwunel_disable.
|
||||
#[workspace.dependencies.console-subscriber]
|
||||
#version = "0.4"
|
||||
|
||||
[workspace.dependencies.core_affinity]
|
||||
version = "0.8.1"
|
||||
version = "0.8.3"
|
||||
|
||||
[workspace.dependencies.const-str]
|
||||
version = "0.6.2"
|
||||
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.2.9"
|
||||
version = "0.5"
|
||||
|
||||
[workspace.dependencies.cyborgtime]
|
||||
version = "2.1.1"
|
||||
version = "2.1"
|
||||
|
||||
[workspace.dependencies.either]
|
||||
version = "1.15.0"
|
||||
version = "1.15"
|
||||
default-features = false
|
||||
features = ["serde"]
|
||||
|
||||
[workspace.dependencies.figment]
|
||||
version = "0.10.19"
|
||||
version = "0.10"
|
||||
default-features = false
|
||||
features = ["env", "toml"]
|
||||
|
||||
[workspace.dependencies.futures]
|
||||
version = "0.3.31"
|
||||
version = "0.3"
|
||||
default-features = false
|
||||
features = ["std", "async-await"]
|
||||
|
||||
[workspace.dependencies.hardened_malloc-rs]
|
||||
version = "0.1.2"
|
||||
default-features = false
|
||||
features = [
|
||||
"static",
|
||||
"gcc",
|
||||
"light",
|
||||
]
|
||||
|
||||
[workspace.dependencies.hickory-resolver]
|
||||
version = "0.25.1"
|
||||
version = "0.25"
|
||||
default-features = false
|
||||
features = [
|
||||
"serde",
|
||||
@@ -180,17 +182,17 @@ features = [
|
||||
]
|
||||
|
||||
[workspace.dependencies.hmac]
|
||||
version = "0.12.1"
|
||||
version = "0.12"
|
||||
default-features = false
|
||||
|
||||
[workspace.dependencies.http]
|
||||
version = "1.3.1"
|
||||
version = "1.3"
|
||||
|
||||
[workspace.dependencies.http-body-util]
|
||||
version = "0.1.3"
|
||||
version = "0.1"
|
||||
|
||||
[workspace.dependencies.hyper]
|
||||
version = "1.6.0"
|
||||
version = "1.6"
|
||||
default-features = false
|
||||
features = [
|
||||
"server",
|
||||
@@ -199,7 +201,7 @@ features = [
|
||||
]
|
||||
|
||||
[workspace.dependencies.hyper-util]
|
||||
version = "0.1.11"
|
||||
version = "0.1.14"
|
||||
default-features = false
|
||||
features = [
|
||||
"server-auto",
|
||||
@@ -208,7 +210,7 @@ features = [
|
||||
]
|
||||
|
||||
[workspace.dependencies.image]
|
||||
version = "0.25.5"
|
||||
version = "0.25"
|
||||
default-features = false
|
||||
features = [
|
||||
"jpeg",
|
||||
@@ -217,11 +219,22 @@ features = [
|
||||
"webp",
|
||||
]
|
||||
|
||||
[workspace.dependencies.insta]
|
||||
version = "1.43"
|
||||
features = [
|
||||
"json",
|
||||
]
|
||||
|
||||
[workspace.dependencies.ipaddress]
|
||||
version = "0.1.3"
|
||||
version = "0.1"
|
||||
|
||||
[workspace.dependencies.itertools]
|
||||
version = "0.14.0"
|
||||
version = "0.14"
|
||||
|
||||
[workspace.dependencies.jsonwebtoken]
|
||||
version = "9.3"
|
||||
default-features = false
|
||||
features = ["use_pem"]
|
||||
|
||||
[workspace.dependencies.ldap3]
|
||||
git = "https://github.com/matrix-construct/ldap3"
|
||||
@@ -233,46 +246,50 @@ features = ["sync", "tls-rustls"]
|
||||
version = "0.2"
|
||||
|
||||
[workspace.dependencies.libloading]
|
||||
version = "0.8.6"
|
||||
version = "0.8"
|
||||
|
||||
[workspace.dependencies.log]
|
||||
version = "0.4.27"
|
||||
version = "0.4"
|
||||
default-features = false
|
||||
|
||||
[workspace.dependencies.loole]
|
||||
version = "0.4.0"
|
||||
version = "0.4"
|
||||
|
||||
[workspace.dependencies.lru-cache]
|
||||
version = "0.1.2"
|
||||
version = "0.1"
|
||||
|
||||
[workspace.dependencies.maplit]
|
||||
version = "1.0.2"
|
||||
version = "1.0"
|
||||
|
||||
[workspace.dependencies.minicbor]
|
||||
version = "0.26.3"
|
||||
version = "2.0"
|
||||
features = ["std"]
|
||||
|
||||
[workspace.dependencies.minicbor-serde]
|
||||
version = "0.4.1"
|
||||
version = "0.6"
|
||||
features = ["std"]
|
||||
|
||||
[workspace.dependencies.nix]
|
||||
version = "0.29.0"
|
||||
version = "0.30"
|
||||
default-features = false
|
||||
features = ["resource"]
|
||||
features = [
|
||||
"resource",
|
||||
"user",
|
||||
]
|
||||
|
||||
[workspace.dependencies.num-traits]
|
||||
version = "0.2"
|
||||
|
||||
[workspace.dependencies.opentelemetry]
|
||||
version = "0.21.0"
|
||||
version = "0.30.0"
|
||||
|
||||
[workspace.dependencies.opentelemetry-jaeger]
|
||||
version = "0.20.0"
|
||||
features = ["rt-tokio"]
|
||||
# Disabled until they move to opentelemetry 0.30
|
||||
#[workspace.dependencies.opentelemetry-jaeger]
|
||||
#version = "0.22.0"
|
||||
#features = ["rt-tokio"]
|
||||
|
||||
[workspace.dependencies.opentelemetry_sdk]
|
||||
version = "0.21.2"
|
||||
version = "0.30"
|
||||
features = ["rt-tokio"]
|
||||
|
||||
[workspace.dependencies.proc-macro2]
|
||||
@@ -282,13 +299,13 @@ version = "1.0"
|
||||
version = "1.0"
|
||||
|
||||
[workspace.dependencies.rand]
|
||||
version = "0.8.5"
|
||||
version = "0.8"
|
||||
|
||||
[workspace.dependencies.regex]
|
||||
version = "1.11.1"
|
||||
version = "1.11"
|
||||
|
||||
[workspace.dependencies.reqwest]
|
||||
version = "0.12.15"
|
||||
version = "0.12"
|
||||
default-features = false
|
||||
features = [
|
||||
"rustls-tls-native-roots",
|
||||
@@ -298,25 +315,24 @@ features = [
|
||||
]
|
||||
|
||||
[workspace.dependencies.ring]
|
||||
version = "0.17.14"
|
||||
version = "0.17"
|
||||
default-features = false
|
||||
|
||||
[workspace.dependencies.ruma]
|
||||
git = "https://github.com/matrix-construct/ruma"
|
||||
rev = "920148dca1076454ca0ca5d43b5ce1aa708381d4"
|
||||
rev = "3c26be1e4d670c2c71e2bc84b3f09c67c4188612"
|
||||
features = [
|
||||
"compat",
|
||||
"rand",
|
||||
"__compat",
|
||||
"appservice-api-c",
|
||||
"client-api",
|
||||
"client-api-s",
|
||||
"compat-upload-signatures",
|
||||
"federation-api",
|
||||
"identifiers-validation",
|
||||
"markdown",
|
||||
"push-gateway-api-c",
|
||||
"unstable-exhaustive-types",
|
||||
"rand",
|
||||
"ring-compat",
|
||||
"compat-upload-signatures",
|
||||
"identifiers-validation",
|
||||
"unstable-unspecified",
|
||||
"unstable-msc2448",
|
||||
"unstable-msc2666",
|
||||
"unstable-msc2867",
|
||||
@@ -324,52 +340,51 @@ features = [
|
||||
"unstable-msc3026",
|
||||
"unstable-msc3061",
|
||||
"unstable-msc3245",
|
||||
"unstable-msc3266",
|
||||
"unstable-msc3381", # polls
|
||||
"unstable-msc3489", # beacon / live location
|
||||
"unstable-msc3575",
|
||||
"unstable-msc3930", # polls push rules
|
||||
"unstable-msc4075",
|
||||
"unstable-msc4095",
|
||||
"unstable-msc4121",
|
||||
"unstable-msc4125",
|
||||
"unstable-msc4133",
|
||||
"unstable-msc4186",
|
||||
"unstable-msc4203", # sending to-device events to appservices
|
||||
"unstable-msc4210", # remove legacy mentions
|
||||
"unstable-msc4310",
|
||||
"unstable-msc4311",
|
||||
"unstable-extensible-events",
|
||||
"unstable-pdu",
|
||||
]
|
||||
|
||||
[workspace.dependencies.rustls]
|
||||
version = "0.23.25"
|
||||
version = "0.23"
|
||||
default-features = false
|
||||
features = ["aws_lc_rs"]
|
||||
|
||||
[workspace.dependencies.rustyline-async]
|
||||
version = "0.4.3"
|
||||
version = "0.4.6"
|
||||
default-features = false
|
||||
|
||||
[workspace.dependencies.rust-rocksdb]
|
||||
git = "https://github.com/matrix-construct/rust-rocksdb"
|
||||
rev = "cca241c5ca48f7c7f5c1469294f11dc156dd987a"
|
||||
rev = "225a42519276e502205bdc845cebdb22d70ee245"
|
||||
default-features = false
|
||||
features = [
|
||||
"bzip2",
|
||||
"lz4",
|
||||
"multi-threaded-cf",
|
||||
"mt_static",
|
||||
"lz4",
|
||||
"zstd",
|
||||
"bzip2",
|
||||
]
|
||||
|
||||
[workspace.dependencies.sanitize-filename]
|
||||
version = "0.6.0"
|
||||
version = "0.6"
|
||||
|
||||
[workspace.dependencies.sd-notify]
|
||||
version = "0.4.5"
|
||||
version = "0.4"
|
||||
default-features = false
|
||||
|
||||
[workspace.dependencies.sentry]
|
||||
version = "0.37.0"
|
||||
version = "0.42"
|
||||
default-features = false
|
||||
features = [
|
||||
"backtrace",
|
||||
@@ -385,48 +400,52 @@ features = [
|
||||
]
|
||||
|
||||
[workspace.dependencies.sentry-tower]
|
||||
version = "0.37.0"
|
||||
version = "0.42"
|
||||
|
||||
[workspace.dependencies.sentry-tracing]
|
||||
version = "0.37.0"
|
||||
version = "0.42"
|
||||
|
||||
[workspace.dependencies.serde]
|
||||
version = "1.0.219"
|
||||
version = "1.0"
|
||||
default-features = false
|
||||
features = ["rc"]
|
||||
|
||||
[workspace.dependencies.serde_html_form]
|
||||
version = "0.2.6"
|
||||
version = "0.2"
|
||||
|
||||
[workspace.dependencies.serde_json]
|
||||
version = "1.0.140"
|
||||
version = "1.0"
|
||||
default-features = false
|
||||
features = ["raw_value"]
|
||||
|
||||
[workspace.dependencies.serde_regex]
|
||||
version = "1.1.0"
|
||||
version = "1.1"
|
||||
|
||||
[workspace.dependencies.serde_yaml]
|
||||
version = "0.9.34"
|
||||
version = "0.9"
|
||||
|
||||
[workspace.dependencies.sha1]
|
||||
version = "0.10.6"
|
||||
version = "0.10"
|
||||
default-features = false
|
||||
|
||||
[workspace.dependencies.sha2]
|
||||
version = "0.10.8"
|
||||
version = "0.10"
|
||||
default-features = false
|
||||
|
||||
[workspace.dependencies.similar]
|
||||
version = "2.7"
|
||||
|
||||
[workspace.dependencies.smallstr]
|
||||
version = "0.3"
|
||||
features = [
|
||||
"ffi",
|
||||
"serde",
|
||||
"std",
|
||||
"union",
|
||||
]
|
||||
|
||||
[workspace.dependencies.smallvec]
|
||||
version = "1.14.0"
|
||||
version = "1.15"
|
||||
features = [
|
||||
"const_generics",
|
||||
"const_new",
|
||||
@@ -447,16 +466,16 @@ features = [
|
||||
]
|
||||
|
||||
[workspace.dependencies.termimad]
|
||||
version = "0.31.2"
|
||||
version = "0.34"
|
||||
default-features = false
|
||||
|
||||
[workspace.dependencies.thiserror]
|
||||
version = "2.0.12"
|
||||
version = "2.0"
|
||||
default-features = false
|
||||
|
||||
[workspace.dependencies.tikv-jemallocator]
|
||||
git = "https://github.com/matrix-construct/jemallocator"
|
||||
rev = "82af58d6a13ddd5dcdc7d4e91eae3b63292995b8"
|
||||
rev = "03bed96afbbc898bef4d4f7d335c0519e3d1afad"
|
||||
default-features = false
|
||||
features = [
|
||||
"background_threads_runtime_support",
|
||||
@@ -465,13 +484,13 @@ features = [
|
||||
|
||||
[workspace.dependencies.tikv-jemalloc-ctl]
|
||||
git = "https://github.com/matrix-construct/jemallocator"
|
||||
rev = "82af58d6a13ddd5dcdc7d4e91eae3b63292995b8"
|
||||
rev = "03bed96afbbc898bef4d4f7d335c0519e3d1afad"
|
||||
default-features = false
|
||||
features = ["use_std"]
|
||||
|
||||
[workspace.dependencies.tikv-jemalloc-sys]
|
||||
git = "https://github.com/matrix-construct/jemallocator"
|
||||
rev = "82af58d6a13ddd5dcdc7d4e91eae3b63292995b8"
|
||||
rev = "03bed96afbbc898bef4d4f7d335c0519e3d1afad"
|
||||
default-features = false
|
||||
features = [
|
||||
"background_threads_runtime_support",
|
||||
@@ -479,7 +498,7 @@ features = [
|
||||
]
|
||||
|
||||
[workspace.dependencies.tokio]
|
||||
version = "1.44.2"
|
||||
version = "1.47"
|
||||
default-features = false
|
||||
features = [
|
||||
"fs",
|
||||
@@ -494,20 +513,20 @@ features = [
|
||||
]
|
||||
|
||||
[workspace.dependencies.tokio-metrics]
|
||||
version = "0.4.0"
|
||||
version = "0.4"
|
||||
|
||||
[workspace.dependencies.toml]
|
||||
version = "0.8.14"
|
||||
version = "0.9"
|
||||
default-features = false
|
||||
features = ["parse"]
|
||||
|
||||
[workspace.dependencies.tower]
|
||||
version = "0.5.2"
|
||||
version = "0.5"
|
||||
default-features = false
|
||||
features = ["util"]
|
||||
|
||||
[workspace.dependencies.tower-http]
|
||||
version = "0.6.2"
|
||||
version = "0.6"
|
||||
default-features = false
|
||||
features = [
|
||||
"add-extension",
|
||||
@@ -521,21 +540,21 @@ features = [
|
||||
]
|
||||
|
||||
[workspace.dependencies.tracing]
|
||||
version = "0.1.41"
|
||||
version = "0.1"
|
||||
default-features = false
|
||||
|
||||
[workspace.dependencies.tracing-core]
|
||||
version = "0.1.33"
|
||||
version = "0.1"
|
||||
default-features = false
|
||||
|
||||
[workspace.dependencies.tracing-flame]
|
||||
version = "0.2.0"
|
||||
version = "0.2"
|
||||
|
||||
[workspace.dependencies.tracing-opentelemetry]
|
||||
version = "0.22.0"
|
||||
version = "0.31"
|
||||
|
||||
[workspace.dependencies.tracing-subscriber]
|
||||
version = "0.3.19"
|
||||
version = "0.3"
|
||||
default-features = false
|
||||
features = [
|
||||
"ansi",
|
||||
@@ -547,42 +566,25 @@ features = [
|
||||
]
|
||||
|
||||
[workspace.dependencies.url]
|
||||
version = "2.5.4"
|
||||
version = "2.5"
|
||||
default-features = false
|
||||
features = ["serde"]
|
||||
|
||||
[workspace.dependencies.webpage]
|
||||
version = "2.0.1"
|
||||
version = "2.0"
|
||||
default-features = false
|
||||
|
||||
#
|
||||
# Patches
|
||||
#
|
||||
|
||||
# backport of [https://github.com/tokio-rs/tracing/pull/2956] to the 0.1.x
|
||||
# branch of tracing. we can switch back to upstream if #2956 is merged and
|
||||
# backported in the upstream repo.
|
||||
# https://github.com/girlbossceo/tracing/commit/b348dca742af641c47bc390261f60711c2af573c
|
||||
[patch.crates-io.tracing-subscriber]
|
||||
git = "https://github.com/matrix-construct/tracing"
|
||||
rev = "1e64095a8051a1adf0d1faa307f9f030889ec2aa"
|
||||
[patch.crates-io.tracing]
|
||||
git = "https://github.com/matrix-construct/tracing"
|
||||
rev = "1e64095a8051a1adf0d1faa307f9f030889ec2aa"
|
||||
[patch.crates-io.tracing-core]
|
||||
git = "https://github.com/matrix-construct/tracing"
|
||||
rev = "1e64095a8051a1adf0d1faa307f9f030889ec2aa"
|
||||
[patch.crates-io.tracing-log]
|
||||
git = "https://github.com/matrix-construct/tracing"
|
||||
rev = "1e64095a8051a1adf0d1faa307f9f030889ec2aa"
|
||||
|
||||
# adds a tab completion callback:
|
||||
# https://github.com/girlbossceo/rustyline-async/commit/de26100b0db03e419a3d8e1dd26895d170d1fe50
|
||||
# adds event for CTRL+\:
|
||||
# https://github.com/girlbossceo/rustyline-async/commit/67d8c49aeac03a5ef4e818f663eaa94dd7bf339b
|
||||
[patch.crates-io.rustyline-async]
|
||||
git = "https://github.com/matrix-construct/rustyline-async"
|
||||
rev = "deaeb0694e2083f53d363b648da06e10fc13900c"
|
||||
rev = "ba743cae940659e95d7b8604bbd6fdd755171c8c"
|
||||
|
||||
# adds LIFO queue scheduling; this should be updated with PR progress.
|
||||
[patch.crates-io.event-listener]
|
||||
@@ -595,12 +597,12 @@ rev = "92e5e74063bf2a3b10414bcc8a0d68b235644280"
|
||||
# adds affinity masks for selecting more than one core at a time
|
||||
[patch.crates-io.core_affinity]
|
||||
git = "https://github.com/matrix-construct/core_affinity_rs"
|
||||
rev = "9c8e51510c35077df888ee72a36b4b05637147da"
|
||||
rev = "ff148c2985cbaff3e6e953d45cda4d7696f38d75"
|
||||
|
||||
# reverts hyperium#148 conflicting with our delicate federation resolver hooks
|
||||
[patch.crates-io.hyper-util]
|
||||
git = "https://github.com/matrix-construct/hyper-util"
|
||||
rev = "e4ae7628fe4fcdacef9788c4c8415317a4489941"
|
||||
rev = "c4fd9b4cd3a59fcfd28ca3802d68a8ea8988d9a3"
|
||||
|
||||
# allows no-aaaa option in resolv.conf
|
||||
# bumps rust edition and toolchain to 1.86.0 and 2024
|
||||
@@ -700,7 +702,7 @@ inherits = "release-native.build-override"
|
||||
#]
|
||||
|
||||
[profile.bench]
|
||||
debug = "limited"
|
||||
debug = false
|
||||
strip = false
|
||||
#rustflags = [
|
||||
# "-Cremark=all",
|
||||
@@ -848,6 +850,7 @@ unused-qualifications = "warn"
|
||||
|
||||
## some sadness
|
||||
elided_named_lifetimes = "allow" # TODO!
|
||||
mismatched_lifetime_syntaxes = "allow" # TODO!
|
||||
let_underscore_drop = "allow"
|
||||
missing_docs = "allow"
|
||||
# cfgs cannot be limited to expected cfgs or their de facto non-transitive/opt-in use-case e.g.
|
||||
@@ -863,6 +866,15 @@ variant_size_differences = "allow"
|
||||
# we check nightly clippy lints
|
||||
unknown_lints = "allow"
|
||||
|
||||
# rust claims elided_named_lifetimes has been replaced by
|
||||
# mismatched_lifetime_syntaxes but it actually has not.
|
||||
renamed_and_removed_lints = "allow"
|
||||
|
||||
# nightly has a new lint that conflicts with our BoolExt trait at
|
||||
# src/core/utils/bool.rs:68:48
|
||||
# <https://github.com/rust-lang/rust/issues/142748>
|
||||
unstable_name_collisions = "allow" # TODO
|
||||
|
||||
#######################################
|
||||
#
|
||||
# Clippy lints
|
||||
@@ -879,6 +891,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,18 +906,22 @@ 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 }
|
||||
|
||||
## some sadness
|
||||
too_long_first_doc_paragraph = { level = "allow", priority = 1 }
|
||||
default_trait_access = { level = "allow", priority = 1 }
|
||||
doc_markdown = { level = "allow", priority = 1 }
|
||||
enum_glob_use = { level = "allow", priority = 1 }
|
||||
if_not_else = { level = "allow", priority = 1 }
|
||||
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 }
|
||||
@@ -948,12 +967,11 @@ 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"
|
||||
string_slice = "warn"
|
||||
string_to_string = "warn"
|
||||
implicit_clone = "warn"
|
||||
suspicious_xor_used_as_pow = "warn"
|
||||
tests_outside_test_module = "warn"
|
||||
try_err = "warn"
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
# One Tuwunel
|
||||
|
||||
June 6, 2025
|
||||
|
||||
### New Features
|
||||
|
||||
- LDAP authentication. Thanks @RatCornu!
|
||||
- Podman systemd. Thanks @everypizza1!
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Several panics fixed by Jade Ellis (@JadedBlueEyes) et al. Thank you 🙏
|
||||
|
||||
### Honorable Mentions
|
||||
|
||||
- Special thanks to Peter Gervai (@grinapo) for hosting the fanclub room; also
|
||||
for documentation and wiki contributions.
|
||||
@@ -1,4 +1,4 @@
|
||||
# Tuwunel <sup>🎔</sup>
|
||||
# Tuwunel<sup>💕</sup>
|
||||
|
||||

|
||||

|
||||
@@ -21,7 +21,7 @@ ## High Performance Matrix Homeserver in Rust!
|
||||
[bot](https://matrix.org/ecosystem/integrations/). It is written entirely in Rust to be a scalable,
|
||||
lightweight, low-cost, community-driven alternative covering all but the most niche uses.
|
||||
|
||||
This project is the official successor to [conduwuit](https://github.com/girlbossceo/conduwuit), which
|
||||
This project is the official successor to conduwuit, which
|
||||
was a featureful and high-performance fork of [Conduit](https://gitlab.com/famedly/conduit), all
|
||||
community-lead homeservers implementing the compatible
|
||||
[Matrix Specification](https://spec.matrix.org/latest/).
|
||||
@@ -35,9 +35,10 @@ ### Getting Started
|
||||
- [Sourcecode](https://github.com/matrix-construct/tuwunel/) `git clone https://github.com/matrix-construct/tuwunel.git`
|
||||
- [DockerHub](https://hub.docker.com/r/jevolk/tuwunel) or `docker pull jevolk/tuwunel:latest`
|
||||
- [GHCR](https://github.com/matrix-construct/tuwunel/pkgs/container/tuwunel) or `docker pull ghcr.io/matrix-construct/tuwunel:latest`
|
||||
- Static binaries available as [releases](https://github.com/matrix-construct/tuwunel/releases) or [build artifacts](https://github.com/matrix-construct/tuwunel/actions?query=branch%3Amain).
|
||||
- Deb and RPM packages available as [releases](https://github.com/matrix-construct/tuwunel/releases) or [build artifacts](https://github.com/matrix-construct/tuwunel/actions?query=branch%3Amain).
|
||||
- Static binaries also available as [releases](https://github.com/matrix-construct/tuwunel/releases) or [build artifacts](https://github.com/matrix-construct/tuwunel/actions?query=branch%3Amain).
|
||||
- Arch package is expected shortly. Nix package still needs some love.
|
||||
- Arch package available as [tuwunel](https://aur.archlinux.org/packages/tuwunel) or [tuwunel-git](https://aur.archlinux.org/packages/tuwunel-git).
|
||||
- Nix package still [needs some love](https://github.com/NixOS/nixpkgs/issues/415469).
|
||||
|
||||
**1.** [Configure](https://matrix-construct.github.io/tuwunel/configuration.html) by
|
||||
copying and editing the `tuwunel-example.toml`. The `server_name` and `database_path` must be
|
||||
@@ -46,7 +47,7 @@ ### Getting Started
|
||||
This is just a summary for the impatient. See the full
|
||||
[documentation](https://matrix-construct.github.io/tuwunel/).
|
||||
|
||||
> [!WARNING]
|
||||
> [!TIP]
|
||||
> Avoid using a sub-domain for your `server_name`. You can always delegate later with a [`.well-known`](https://github.com/spantaleev/matrix-docker-ansible-deploy/blob/master/docs/configuring-well-known.md)
|
||||
> file, but you can never change your `server_name`.
|
||||
|
||||
@@ -56,12 +57,12 @@ ### Getting Started
|
||||
`/etc/caddy/Caddyfile` configuration with [Element](https://github.com/element-hq/element-web/releases)
|
||||
unzipped to `/var/www/element`:
|
||||
```
|
||||
https://tuwunel.me:8448 {
|
||||
reverse_proxy http://127.0.0.1:8008
|
||||
tuwunel.me, tuwunel.me:8448 {
|
||||
reverse_proxy localhost:8008
|
||||
}
|
||||
https://tuwunel.me:443 {
|
||||
root * /var/www/element/
|
||||
file_server
|
||||
web.tuwunel.me {
|
||||
root * /var/www/element/
|
||||
file_server
|
||||
}
|
||||
```
|
||||
`caddy reload --config /etc/caddy/Caddyfile`
|
||||
@@ -74,7 +75,7 @@ ### Getting Started
|
||||
|
||||
🤗 Did you find this and other documentation helpful? We would love to hear feedback about setting
|
||||
up Tuwunel.
|
||||
|
||||
|
||||
|
||||
### Migrating to Tuwunel
|
||||
|
||||
@@ -109,22 +110,11 @@ ### Upgrading & Downgrading Tuwunel
|
||||
safe but often prevented by a guard. An error will indicate the downgrade is not possible and a
|
||||
newer version which does not error must be sought.
|
||||
|
||||
#### Versioning
|
||||
|
||||
Tuwunel uses a semantic version tag in the format of `v<major>.<minor>.<patch>`. The `patch` value will
|
||||
always correspond to the number of commits from the last `minor` change with significance to developers.
|
||||
The `minor` version is changed for fixes or features significant to users. The `major` value corresponds
|
||||
to significant feature evolutions, and does not indicate any "breaking change" nor connote stability.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **Users should attempt to keep up to date with the latest minor version.**
|
||||
|
||||
#### Branches
|
||||
|
||||
The main branch is always _reasonably safe_ to run. We understand the propensity for users to simply clone
|
||||
the main branch to get up and running, and we're obliged to ensure it's always viable. Nevertheless, only
|
||||
tagged releases are true releases. If you don't care to update often, find the latest `minor` version
|
||||
change rather than `patch`. We don't recommend simply following `major` version changes at this time.
|
||||
tagged releases are true releases.
|
||||
|
||||
#### Container Tracking
|
||||
|
||||
@@ -139,31 +129,26 @@ #### Container Tracking
|
||||
|
||||
### Getting Help & Support
|
||||
|
||||
The official community will be found at [#tuwunel:tuwunel.chat](https://matrix.to/#/#tuwunel:tuwunel.chat).
|
||||
If this is currently inaccessible please be patient as it's still coming online at the time of
|
||||
the first release; we will have updates to this section. This is a fully moderated space to protect
|
||||
the wellbeing of our users and create a non-toxic work environment for staff. If you require assistance
|
||||
with anything that is not remedied by the documentation, don't hesitate to open an issue on github.
|
||||
|
||||
If you are opposed to using github, or private discussion is required, or for any other reason,
|
||||
I would be happy to receive your DM at [@jason:tuwunel.me](https://matrix.to/#/@jason:tuwunel.me),
|
||||
you will not be bothering me and it would be my pleasure to help you anytime. As an emergency contact
|
||||
you can send an email to jasonzemos@gmail.com.
|
||||
If you are opposed to using github, or if private discussion is required such as for security
|
||||
disclosures, or for any other reason, I would be happy to receive your DM at
|
||||
[@jason:tuwunel.me](https://matrix.to/#/@jason:tuwunel.me). This will not be bothering me as it would
|
||||
be my pleasure to help you when possible. As an emergency contact you can send an email to
|
||||
jasonzemos@gmail.com.
|
||||
|
||||
##### Tuwunel Fanclub
|
||||
|
||||
We also have an unofficial community-run chat which is publicly accessible at
|
||||
[#tuwunel:grin.hu](https://matrix.to/#/#tuwunel:grin.hu). The members, content, or moderation
|
||||
We have an unofficial community-run chat which is publicly accessible at
|
||||
[#tuwunel:matrix.org](https://matrix.to/#/#tuwunel:matrix.org). The members, content, or moderation
|
||||
decisions of this room are not in any way related or endorsed by this project or its sponsors,
|
||||
and not all project staff will be present there. There will be at least some presence by staff to
|
||||
offer assistance so long as the room remains in minimally good standing.
|
||||
|
||||
|
||||
## Tuwunel <sup>🎔</sup>
|
||||
## Tuwunel<sup>💕</sup>
|
||||
|
||||
Tuwunel's theme is **empathy** in communication defined by the works of
|
||||
[Edith Stein](https://plato.stanford.edu/entries/stein/). Empathy is the basis for how we approach
|
||||
every message, and a reminder for how we should all conduct ourselves in every conversation.
|
||||
every message and our responsibility to the other in every conversation.
|
||||
|
||||
<!-- ANCHOR_END: body -->
|
||||
|
||||
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
# Tuwunel 1.4.3
|
||||
|
||||
October 10, 2025
|
||||
|
||||
### Featured
|
||||
|
||||
**Sync Tokens have been eliminated** now with stateless sync. Users should notice a reduction in database size after running this version. Long-time users, including from conduwuit and even Conduit will benefit the most. New users may not notice reductions, but nevertheless will be preventing database growth going forward. With the prior addition of room deletions courtesy of @dasha-uwu, only enhanced media retention remains between Tuwunel and sustainable cost-of-ownership.
|
||||
|
||||
**Sliding Sync has been fixed (Element X)** after a third pass was made to install an entirely new architecture based on the latest evolution of the highly active MSC4186. For background, the first work done earlier this year was for optimization without logical changes to what was inherited from conduwuit. The existing implementation worked by all appearances, but didn't meet specific production quality demands. The second pass made last month to rapidly prepare this passé implementation for production use against highly developed modern clients did not turn out well. More tests passed; fewer things worked. This time the core logic had been rewritten. These three iterations have now modernized the entire module to keep up with the final stages of the specification's development. It is still not perfect, so your input and issue reports are greatly appreciated as always.
|
||||
|
||||
### Enhancements
|
||||
|
||||
- Configuration options passed after arguments `--option` or `-O` now accept dots to address the TOML tables found in the config file. Thanks to the suggestion by @lucat1 while trying to configure `ldap.bind_password_file` from the command line (#162). This was separately uncovered by @andrewerf trying to configure the `tls` section (#167).
|
||||
|
||||
- Configuring `log_to_stderr` now provides an explicit way to redirect logging and tracing to stderr. This is often requested for use with systemd.
|
||||
|
||||
- The `!admin query raw` commands no longer require the redundant `raw-` prefix on every command name.
|
||||
|
||||
- Thanks to @SophiaH67 for pointing out that our new verbose-logging docker images aren't very useful without being pushed to registries, then taking the initiative to host it themselves until we corrected that.
|
||||
|
||||
- After a report by @munkinasack in (#186), @dasha-uwu determined we could solve a lot of recurring network issues by clearing the well-known cache entry for a destination that becomes unreachable.
|
||||
|
||||
- Thanks to @dasha-uwu for general improvements, refactoring and fixes for the room alias and presence subsystems.
|
||||
|
||||
- Thanks to a report by @ohitsdylan a cryptic error message from the DNS resolver has now been caught and reworded to indicate a missing or empty `/etc/resolv.conf` (#179).
|
||||
|
||||
- Inspired by @boarfish offering due confusion about our many build variants in (#175), some documentation about `x86_64-v1` `-v2` and `-v3` has been improved. Special thanks to @Hashbrown777 for providing a quick one-liner shell script which made its way to the documentation.
|
||||
|
||||
- Various performance improvements took place in s2s request handlers, and the ubiquitous matrix event `Pdu` structure.
|
||||
|
||||
- Nix builds have been added to CI.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Thanks to @harharlinks for reporting the Code of Conduct link on our github-pages was 404. Also thanks to @Tronde for reporting additional broken links in (#165). This helped discover pages had stopped deploying at some point and needed fixing.
|
||||
|
||||
- Thanks to @agx for contributing a fix for the systemd units on multiple platforms which contained unknown or deprecated keys (#168). And thanks again for adding missing documentation to the well-known sections of the example configuration which many users will greatly appreciate (#173).
|
||||
|
||||
- @mitch9911:matrix.org reported the `device_signing/upload` endpoint was omitted when adding JWT as a UIAA flow to other API's. This was subsequently patched (#169).
|
||||
|
||||
- The report by @orhtej2 of an invalid `?via` parameter sent by FluffyChat when joining a version 12 room was greatly appreciated, even though there was nothing more we could do on our end (#171). Thanks to @dasha-uwu for triaging and investigating this issue to conclusion.
|
||||
|
||||
- Thanks to a follow-up by @alaviss in (#176) the DNS-passthru feature was found to still be enforcing a large minimum-TTL for its DNS cache. This was subsequently corrected.
|
||||
|
||||
- Thank you @canarysnort01 for the apropos and rather surgical off-by-one fix to sliding-sync range selection in (#188). Unfortunately this entire unit had to be rewritten for the latest iteration of sliding-sync, but the fix carried value in any case to improve the rewrite.
|
||||
|
||||
### Notices
|
||||
|
||||
It has come to our attention courtesy of @andrewerf that the Arch packages are built with `--no-default-features`. This may be a problem for an ideal experience. The backstory is that conduwuit underwent a "feature skew" over its lifetime which still remains today: our default-features are basically minimal requirements, while `--all-features` should be default features. Let us first take a moment to reiterate our gratitude to AUR package maintainers @drrossum and @kimiblock who have supported this project from the first hours of its existence. No action is required on their part as the plan now is to remove several optional features by the next release to make `--no-default-features` viable. We still recommend default features in general unless this conflicts with AUR policies or philosophies.
|
||||
|
||||
@@ -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
|
||||
@@ -59,7 +58,7 @@ StateDirectory=tuwunel
|
||||
RuntimeDirectory=tuwunel
|
||||
RuntimeDirectoryMode=0750
|
||||
|
||||
Environment="CONDUWUIT_CONFIG=/etc/tuwunel/tuwunel.toml"
|
||||
Environment="TUWUNEL_CONFIG=/etc/tuwunel/tuwunel.toml"
|
||||
BindPaths=/var/lib/private/tuwunel:/var/lib/conduwuit
|
||||
BindPaths=/var/lib/private/tuwunel:/var/lib/private/conduwuit
|
||||
BindPaths=/var/lib/private/tuwunel:/var/lib/matrix-conduit
|
||||
@@ -77,3 +76,4 @@ StartLimitBurst=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Alias=matrix-tuwunel.service
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
array-size-threshold = 4096
|
||||
cognitive-complexity-threshold = 100 # TODO reduce me ALARA
|
||||
excessive-nesting-threshold = 11 # TODO reduce me to 4 or 5
|
||||
future-size-threshold = 7745 # TODO reduce me ALARA
|
||||
excessive-nesting-threshold = 8
|
||||
future-size-threshold = 8192
|
||||
stack-size-threshold = 196608 # TODO reduce me ALARA
|
||||
too-many-lines-threshold = 780 # TODO reduce me to <= 100
|
||||
type-complexity-threshold = 250 # reduce me to ~200
|
||||
|
||||
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
|
||||
|
||||
+52
-8
@@ -1,9 +1,10 @@
|
||||
# syntax = docker/dockerfile:1.11-labs
|
||||
|
||||
FROM input AS cargo
|
||||
ARG sys_target
|
||||
ARG sys_triple
|
||||
ARG rust_target
|
||||
ARG rust_toolchain
|
||||
ARG RUST_HOME
|
||||
ARG RUSTUP_HOME
|
||||
ARG CARGO_HOME
|
||||
ARG CARGO_TARGET
|
||||
@@ -11,19 +12,52 @@ ARG CARGO_TARGET_DIR
|
||||
ARG cargo_target_profile
|
||||
ARG cargo_target_artifact
|
||||
ARG cargo_target_share
|
||||
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
|
||||
ARG CARGO_PROFILE_BENCH_DEBUG
|
||||
ARG CARGO_PROFILE_BENCH_LTO
|
||||
ARG CARGO_PROFILE_RELEASE_LTO
|
||||
ARG CARGO_PROFILE_RELEASE_DEBUGINFO_DEBUG
|
||||
ARG CARGO_PROFILE_RELEASE_DEBUGINFO_LTO
|
||||
ARG cargo_profile
|
||||
ARG cargo_features
|
||||
ARG cargo_spec_features
|
||||
ARG cargo_cmd
|
||||
ARG color_args="--color always"
|
||||
ARG recipe_args=""
|
||||
ARG cargo_args=""
|
||||
ARG git_checkout
|
||||
|
||||
WORKDIR /
|
||||
COPY --link --from=input . .
|
||||
COPY --link --from=rust ${RUST_HOME} ${RUST_HOME}
|
||||
COPY --link --from=deps /usr/src/tuwunel /usr/src/tuwunel
|
||||
|
||||
WORKDIR /usr/lib/${sys_target}
|
||||
WORKDIR /usr/lib/${sys_triple}
|
||||
COPY --link --from=rocksdb . .
|
||||
|
||||
WORKDIR /usr/src/tuwunel
|
||||
ENV PATH="${CARGO_HOME}/bin:$PATH"
|
||||
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}"
|
||||
ENV CARGO_PROFILE_BENCH_LTO="${CARGO_PROFILE_BENCH_LTO}"
|
||||
ENV CARGO_PROFILE_RELEASE_LTO="${CARGO_PROFILE_RELEASE_LTO}"
|
||||
ENV CARGO_PROFILE_RELEASE_DEBUGINFO_DEBUG="${CARGO_PROFILE_RELEASE_DEBUGINFO_DEBUG}"
|
||||
ENV CARGO_PROFILE_RELEASE_DEBUGINFO_LTO="${CARGO_PROFILE_RELEASE_DEBUGINFO_LTO}"
|
||||
ENV CARGO_BUILD_RUSTFLAGS="${CARGO_BUILD_RUSTFLAGS}"
|
||||
ENV CARGO_TARGET_DIR="${CARGO_TARGET_DIR}"
|
||||
ENV targ_dir="${CARGO_TARGET_DIR}/${cargo_target_profile}"
|
||||
ENV targ_targ_dir="${CARGO_TARGET_DIR}/${rust_target}/${cargo_target_profile}"
|
||||
RUN \
|
||||
@@ -42,17 +76,27 @@ 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 \
|
||||
--locked \
|
||||
--workspace \
|
||||
"${cargo_spec_features}" \
|
||||
--features "${cargo_features}" \
|
||||
--profile "${cargo_profile}" \
|
||||
--target "${CARGO_TARGET}" \
|
||||
--target-dir "${CARGO_TARGET_DIR}" \
|
||||
--manifest-path Cargo.toml \
|
||||
--color always \
|
||||
"--features=${cargo_features}" \
|
||||
"--profile=${cargo_profile}" \
|
||||
"--target=${CARGO_TARGET}" \
|
||||
"--target-dir=${CARGO_TARGET_DIR}" \
|
||||
"--manifest-path=Cargo.toml" \
|
||||
${recipe_args} \
|
||||
${color_args} \
|
||||
${cargo_args}
|
||||
|
||||
# If this image is further reused with other cargo commands, all
|
||||
# modifications made by cargo chef cook outside of target-dir have to be
|
||||
# cleared. If not, resulting build artifacts will link incorrectly, even
|
||||
# without error. For example, a target executable may be produced which
|
||||
# does nothing except exit(0). If you have observed a smoketest failing in
|
||||
# such a manner, investigate this as a cause of the issue.
|
||||
git restore -W -S --source=${git_checkout} .
|
||||
EOF
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# syntax = docker/dockerfile:1.11-labs
|
||||
|
||||
FROM input AS audit
|
||||
ARG sys_target
|
||||
ARG rust_toolchain="nightly"
|
||||
ARG RUSTUP_HOME
|
||||
ARG CARGO_HOME
|
||||
@@ -12,9 +11,9 @@ COPY --link --from=input . .
|
||||
|
||||
WORKDIR /usr/src/tuwunel
|
||||
RUN \
|
||||
--mount=type=cache,dst=${RUSTUP_HOME}/downloads,sharing=shared,ro \
|
||||
--mount=type=cache,dst=${CARGO_HOME}/registry,sharing=shared,ro \
|
||||
--mount=type=cache,dst=${CARGO_HOME}/git,sharing=shared,ro \
|
||||
--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 \
|
||||
<<EOF
|
||||
set -eux
|
||||
rustup run ${rust_toolchain} \
|
||||
|
||||
+11
-11
@@ -1,7 +1,6 @@
|
||||
# syntax = docker/dockerfile:1.11-labs
|
||||
|
||||
FROM input AS deb
|
||||
ARG sys_target
|
||||
FROM input AS build-deb
|
||||
ARG rust_target
|
||||
ARG rust_toolchain
|
||||
ARG RUSTUP_HOME
|
||||
@@ -55,28 +54,29 @@ RUN \
|
||||
--output "${pkg_dir}" \
|
||||
${deb_args}
|
||||
|
||||
dpkg --info ${pkg_dir}/*
|
||||
dpkg --contents ${pkg_dir}/*
|
||||
mv ${pkg_dir}/* "${pkg_dir}/tuwunel.deb"
|
||||
dpkg --info "${pkg_dir}/tuwunel.deb"
|
||||
dpkg --contents "${pkg_dir}/tuwunel.deb"
|
||||
EOF
|
||||
|
||||
|
||||
FROM scratch AS pkg-deb
|
||||
FROM scratch AS deb
|
||||
ARG pkg_dir
|
||||
|
||||
COPY --from=input ${pkg_dir}/* .
|
||||
COPY --from=input ${pkg_dir}/tuwunel.deb .
|
||||
|
||||
|
||||
FROM input AS pkg-deb-install
|
||||
FROM input AS deb-install
|
||||
ARG pkg_dir
|
||||
|
||||
WORKDIR /
|
||||
COPY --link --from=input . .
|
||||
|
||||
WORKDIR ${pkg_dir}
|
||||
COPY --from=pkg-deb . .
|
||||
COPY --from=deb . .
|
||||
RUN <<EOF
|
||||
set -eux
|
||||
dpkg --info *
|
||||
dpkg --contents *
|
||||
dpkg --install *
|
||||
dpkg --info tuwunel.deb
|
||||
dpkg --contents tuwunel.deb
|
||||
dpkg --install tuwunel.deb
|
||||
EOF
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
# syntax = docker/dockerfile:1.11-labs
|
||||
|
||||
FROM input AS deps
|
||||
ARG sys_target
|
||||
ARG rust_target
|
||||
ARG rust_toolchain
|
||||
ARG RUSTUP_HOME
|
||||
ARG CARGO_HOME
|
||||
ARG CARGO_TARGET
|
||||
ARG CARGO_TARGET_DIR
|
||||
ARG cargo_target_profile
|
||||
ARG cargo_target_artifact
|
||||
ARG cargo_target_share
|
||||
ARG CARGO_BUILD_RUSTFLAGS
|
||||
ARG CARGO_PROFILE_TEST_DEBUG
|
||||
ARG CARGO_PROFILE_TEST_INCREMENTAL
|
||||
ARG CARGO_PROFILE_BENCH_DEBUG
|
||||
ARG CARGO_PROFILE_BENCH_LTO
|
||||
ARG CARGO_PROFILE_RELEASE_LTO
|
||||
ARG CARGO_PROFILE_RELEASE_DEBUGINFO_DEBUG
|
||||
ARG CARGO_PROFILE_RELEASE_DEBUGINFO_LTO
|
||||
ARG cargo_profile
|
||||
ARG cargo_features
|
||||
ARG cargo_spec_features
|
||||
ARG cook_args
|
||||
ARG git_checkout
|
||||
|
||||
WORKDIR /
|
||||
COPY --link --from=input . .
|
||||
|
||||
WORKDIR /usr/lib/${sys_target}
|
||||
COPY --link --from=rocksdb . .
|
||||
|
||||
WORKDIR /usr/src/tuwunel
|
||||
COPY --link --from=recipe recipe.json .
|
||||
|
||||
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}"
|
||||
ENV CARGO_PROFILE_BENCH_LTO="${CARGO_PROFILE_BENCH_LTO}"
|
||||
ENV CARGO_PROFILE_RELEASE_LTO="${CARGO_PROFILE_RELEASE_LTO}"
|
||||
ENV CARGO_PROFILE_RELEASE_DEBUGINFO_DEBUG="${CARGO_PROFILE_RELEASE_DEBUGINFO_DEBUG}"
|
||||
ENV CARGO_PROFILE_RELEASE_DEBUGINFO_LTO="${CARGO_PROFILE_RELEASE_DEBUGINFO_LTO}"
|
||||
ENV CARGO_BUILD_RUSTFLAGS="${CARGO_BUILD_RUSTFLAGS}"
|
||||
ENV CARGO_TARGET_DIR="${CARGO_TARGET_DIR}"
|
||||
ENV targ_dir="${CARGO_TARGET_DIR}/${cargo_target_profile}"
|
||||
ENV targ_targ_dir="${CARGO_TARGET_DIR}/${rust_target}/${cargo_target_profile}"
|
||||
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=${cargo_target_share}/deps,sharing=locked \
|
||||
--mount=type=cache,dst=${targ_dir}/build,id=${cargo_target_share}/build,sharing=locked \
|
||||
--mount=type=cache,dst=${targ_dir}/examples,id=${cargo_target_share}/examples,sharing=locked \
|
||||
--mount=type=cache,dst=${targ_dir}/incremental,id=${cargo_target_share}/incremental,sharing=locked \
|
||||
--mount=type=cache,dst=${targ_dir}/.fingerprint,id=${cargo_target_share}/fingerprint,sharing=locked \
|
||||
--mount=type=cache,dst=${targ_targ_dir}/deps,id=${cargo_target_share}/deps,sharing=locked \
|
||||
--mount=type=cache,dst=${targ_targ_dir}/build,id=${cargo_target_share}/build,sharing=locked \
|
||||
--mount=type=cache,dst=${targ_targ_dir}/examples,id=${cargo_target_share}/examples,sharing=locked \
|
||||
--mount=type=cache,dst=${targ_targ_dir}/incremental,id=${cargo_target_share}/incremental,sharing=locked \
|
||||
--mount=type=cache,dst=${targ_targ_dir}/.fingerprint,id=${cargo_target_share}/fingerprint,sharing=locked \
|
||||
<<EOF
|
||||
set -eux
|
||||
rustup run ${rust_toolchain} \
|
||||
cargo chef cook ${cook_args} \
|
||||
--verbose \
|
||||
--locked \
|
||||
--workspace \
|
||||
"${cargo_spec_features}" \
|
||||
--features "${cargo_features}" \
|
||||
--profile "${cargo_profile}" \
|
||||
--target "${CARGO_TARGET}" \
|
||||
--target-dir "${CARGO_TARGET_DIR}" \
|
||||
--manifest-path Cargo.toml \
|
||||
--recipe-path recipe.json
|
||||
|
||||
# If this image is further reused with other cargo commands, all
|
||||
# modifications made by cargo chef cook outside of target-dir have to be
|
||||
# cleared. If not, resulting build artifacts will link incorrectly, even
|
||||
# without error. For example, a target executable may be produced which
|
||||
# does nothing except exit(0). If you have observed a smoketest failing in
|
||||
# such a manner, investigate this as a cause of the issue.
|
||||
git restore -W -S --source=${git_checkout} .
|
||||
EOF
|
||||
@@ -1,7 +1,6 @@
|
||||
# syntax = docker/dockerfile:1.11-labs
|
||||
|
||||
FROM input AS fmt
|
||||
ARG sys_target
|
||||
ARG rust_toolchain="nightly"
|
||||
ARG RUSTUP_HOME
|
||||
ARG CARGO_HOME
|
||||
@@ -12,9 +11,9 @@ COPY --link --from=input . .
|
||||
|
||||
WORKDIR /usr/src/tuwunel
|
||||
RUN \
|
||||
--mount=type=cache,dst=${RUSTUP_HOME}/downloads,sharing=shared,ro \
|
||||
--mount=type=cache,dst=${CARGO_HOME}/registry,sharing=shared,ro \
|
||||
--mount=type=cache,dst=${CARGO_HOME}/git,sharing=shared,ro \
|
||||
--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 \
|
||||
<<EOF
|
||||
set -eux
|
||||
rustup run ${rust_toolchain} \
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# syntax = docker/dockerfile:1.11-labs
|
||||
|
||||
FROM input AS lychee
|
||||
ARG sys_target
|
||||
ARG rust_toolchain="nightly"
|
||||
ARG RUSTUP_HOME
|
||||
ARG CARGO_HOME
|
||||
@@ -16,9 +15,9 @@ COPY --link --from=input . .
|
||||
|
||||
WORKDIR /usr/src/
|
||||
RUN \
|
||||
--mount=type=cache,dst=${RUSTUP_HOME}/downloads,sharing=shared,ro \
|
||||
--mount=type=cache,dst=${CARGO_HOME}/registry,sharing=shared,ro \
|
||||
--mount=type=cache,dst=${CARGO_HOME}/git,sharing=shared,ro \
|
||||
--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=lychee,sharing=locked \
|
||||
<<EOF
|
||||
set -eux
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# syntax = docker/dockerfile:1.11-labs
|
||||
|
||||
FROM input AS rpm
|
||||
ARG sys_target
|
||||
FROM input AS build-rpm
|
||||
ARG rust_toolchain
|
||||
ARG RUSTUP_HOME
|
||||
ARG CARGO_HOME
|
||||
@@ -47,25 +46,27 @@ RUN \
|
||||
--payload-compress zstd \
|
||||
--output "${pkg_dir}" \
|
||||
${gen_rpm_args}
|
||||
|
||||
mv ${pkg_dir}/* "${pkg_dir}/tuwunel.rpm"
|
||||
EOF
|
||||
|
||||
|
||||
FROM scratch AS pkg-rpm
|
||||
FROM scratch AS rpm
|
||||
ARG pkg_dir
|
||||
|
||||
COPY --link --from=input ${pkg_dir}/* .
|
||||
COPY --from=input ${pkg_dir}/tuwunel.rpm .
|
||||
|
||||
|
||||
FROM input AS pkg-rpm-install
|
||||
FROM input AS rpm-install
|
||||
ARG pkg_dir
|
||||
|
||||
WORKDIR /
|
||||
COPY --link --from=input . .
|
||||
|
||||
WORKDIR ${pkg_dir}
|
||||
COPY --link --from=pkg-rpm . .
|
||||
COPY --link --from=rpm . .
|
||||
RUN <<EOF
|
||||
set -eux
|
||||
rpm -i --test *
|
||||
rpm -i *
|
||||
rpm -i --test tuwunel.rpm
|
||||
rpm -i tuwunel.rpm
|
||||
EOF
|
||||
|
||||
@@ -11,9 +11,9 @@ COPY --link --from=input . .
|
||||
|
||||
WORKDIR /usr/src/tuwunel
|
||||
RUN \
|
||||
--mount=type=cache,dst=${RUSTUP_HOME}/downloads,sharing=shared,ro \
|
||||
--mount=type=cache,dst=${CARGO_HOME}/registry,sharing=shared,ro \
|
||||
--mount=type=cache,dst=${CARGO_HOME}/git,sharing=shared,ro \
|
||||
--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 \
|
||||
<<EOF
|
||||
set -eux
|
||||
rustup run ${rust_toolchain} \
|
||||
|
||||
@@ -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},sharing=locked \
|
||||
--mount=type=cache,dst=${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,53 +46,53 @@ 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_device_name_federation = true
|
||||
allow_guest_registration = true
|
||||
allow_invalid_tls_certificates = 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
|
||||
database_path = "/database"
|
||||
log = "debug,tuwunel=trace,h2=warn,hyper=warn"
|
||||
port = [8008, 8448]
|
||||
trusted_servers = []
|
||||
only_query_trusted_key_servers = false
|
||||
query_trusted_key_servers_first = false
|
||||
query_trusted_key_servers_first_on_join = false
|
||||
yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse = true
|
||||
dns_attempts = 20
|
||||
dns_timeout = 60
|
||||
federation_idle_timeout = 300
|
||||
intentionally_unknown_config_option_for_testing = true
|
||||
ip_range_denylist = []
|
||||
url_preview_domain_contains_allowlist = ["*"]
|
||||
url_preview_domain_explicit_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
|
||||
port = [8008, 8448]
|
||||
prune_missing_media = true
|
||||
log_colors = false
|
||||
admin_room_notices = false
|
||||
allow_check_for_updates = false
|
||||
intentionally_unknown_config_option_for_testing = true
|
||||
rocksdb_log_level = "debug"
|
||||
rocksdb_max_log_files = 1
|
||||
rocksdb_recovery_mode = 0
|
||||
rocksdb_paranoid_file_checks = true
|
||||
log_guest_registrations = false
|
||||
allow_legacy_media = true
|
||||
startup_netburst = true
|
||||
startup_netburst_keep = -1
|
||||
# valgrind makes things so slow
|
||||
dns_timeout = 60
|
||||
dns_attempts = 20
|
||||
query_trusted_key_servers_first = false
|
||||
query_trusted_key_servers_first_on_join = false
|
||||
request_conn_timeout = 60
|
||||
request_timeout = 120
|
||||
well_known_conn_timeout = 60
|
||||
well_known_timeout = 60
|
||||
federation_idle_timeout = 300
|
||||
sender_timeout = 300
|
||||
rocksdb_log_level = "debug"
|
||||
rocksdb_max_log_files = 1
|
||||
rocksdb_paranoid_file_checks = true
|
||||
rocksdb_recovery_mode = 0
|
||||
sender_idle_timeout = 300
|
||||
sender_retry_backoff_limit = 300
|
||||
allow_invalid_tls_certificates = true
|
||||
sender_timeout = 300
|
||||
startup_netburst = true
|
||||
startup_netburst_keep = -1
|
||||
trusted_servers = []
|
||||
url_preview_domain_contains_allowlist = ["*"]
|
||||
url_preview_domain_explicit_denylist = ["*"]
|
||||
well_known_conn_timeout = 60
|
||||
well_known_timeout = 60
|
||||
yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse = true
|
||||
|
||||
[global.tls]
|
||||
certs = "/complement/certificate.crt"
|
||||
@@ -150,22 +134,12 @@ ENTRYPOINT valgrind \
|
||||
FROM input AS complement-base
|
||||
ARG var_cache
|
||||
ARG var_lib_apt
|
||||
ARG complement_tags="tuwunel_blacklist"
|
||||
ARG complement_tags="conduwuit_blacklist"
|
||||
ARG complement_tests="./tests/..."
|
||||
ARG complement_run=".*"
|
||||
|
||||
WORKDIR /
|
||||
RUN \
|
||||
--mount=type=cache,dst=${var_cache},sharing=locked \
|
||||
--mount=type=cache,dst=${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 complement
|
||||
ADD https://github.com/matrix-construct/complement.git#403840348f6bcc9cc8ed1671dc2f638c2b1ce4ac complement
|
||||
|
||||
WORKDIR /usr/src/complement
|
||||
ENV COMPLEMENT_BASE_IMAGE="complement-testee"
|
||||
@@ -211,6 +185,7 @@ ENV COMPLEMENT_HOST_MOUNTS="/var/run/docker.sock:/var/run/docker.sock"
|
||||
ENV jq_res='{Action: .Action, Test: .Test}'
|
||||
ENV jq_sel='select((.Action == \"pass\" or .Action == \"fail\" or .Action == \"skip\") and .Test != null)'
|
||||
ENV jq_tab='[.Action, .Test] | @tsv'
|
||||
ENV jq_out='select(.Test != null) | {Test: .Test, Output: .Output}'
|
||||
COPY --from=complement-config /complement/old_results.jsonl .
|
||||
COPY <<EOF uwu.sh
|
||||
env;
|
||||
@@ -219,22 +194,23 @@ COPY <<EOF uwu.sh
|
||||
COMPLEMENT_BASE_IMAGE="\${1:-$complement_base_image}"
|
||||
go test
|
||||
-json
|
||||
-shuffle="${complement_shuffle}"
|
||||
-parallel="${complement_parallel}"
|
||||
-timeout="${complement_timeout}"
|
||||
-count="${complement_count}"
|
||||
-tags="${complement_tags}"
|
||||
-skip="${complement_skip}"
|
||||
-run="${complement_run}"
|
||||
"${complement_tests}"
|
||||
| jq --unbuffered -c "${jq_sel} | ${jq_res}"
|
||||
"-shuffle=\$complement_shuffle"
|
||||
"-parallel=\$complement_parallel"
|
||||
"-timeout=\$complement_timeout"
|
||||
"-count=\$complement_count"
|
||||
"-tags=\$complement_tags"
|
||||
"-skip=\$complement_skip"
|
||||
"-run=\$complement_run"
|
||||
"\$complement_tests"
|
||||
| jq --unbuffered -c "."
|
||||
| tee output.jsonl
|
||||
| jq --unbuffered -c "$jq_sel | $jq_res"
|
||||
| tee results.jsonl
|
||||
| jq --unbuffered -r "${jq_tab}"
|
||||
| jq --unbuffered -r "$jq_tab"
|
||||
;
|
||||
|
||||
jq -s -c "sort_by(.Test)[]" < results.jsonl | uniq > new_results.jsonl;
|
||||
wc -l old_results.jsonl new_results.jsonl;
|
||||
diff -w -y -t --width=275 --suppress-common-lines old_results.jsonl new_results.jsonl;
|
||||
jq -s -c "sort_by(.Test, .Timestamp)[] | $jq_out" < output.jsonl > full_output.jsonl;
|
||||
EOF
|
||||
RUN echo $(tr -d '\n' < uwu.sh) > uwu.sh
|
||||
RUN echo $(tr -d '\n' < uwu.sh) > uwu.sh && chmod a+x uwu.sh
|
||||
ENTRYPOINT ["/bin/bash", "/usr/src/complement/uwu.sh"]
|
||||
|
||||
@@ -5,6 +5,7 @@ ARG rust_target
|
||||
ARG CARGO_TARGET_DIR
|
||||
ARG cargo_target_profile
|
||||
ARG install_prefix
|
||||
ARG assert_linkage=""
|
||||
|
||||
WORKDIR /
|
||||
COPY --link --from=input . .
|
||||
@@ -14,6 +15,13 @@ ENV src_path="${CARGO_TARGET_DIR}/${rust_target}/${cargo_target_profile}/tuwunel
|
||||
ENV dst_path="${install_prefix}/bin/tuwunel"
|
||||
COPY --from=bins $src_path $dst_path
|
||||
RUN <<EOF
|
||||
ret=$(ldd "${dst_path}")
|
||||
if [ "$ret" = "0" ] && [ "$assert_linkage" = "static" ]; then
|
||||
exit 1
|
||||
elif [ "$ret" != "0" ] && [ "$assert_linkage" = "dynamic" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
set -eux
|
||||
ldd -v ${dst_path} || true
|
||||
du -h ${dst_path}
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
# 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=""
|
||||
#TODO!!!
|
||||
ARG mrsdk_skip_list="--skip test_history_share_on_invite_pin_violation --skip test_room_notification_count"
|
||||
|
||||
WORKDIR /
|
||||
COPY --link --from=input . .
|
||||
|
||||
WORKDIR /usr/src
|
||||
ADD --link https://github.com/matrix-construct/matrix-rust-sdk.git 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
|
||||
@@ -1,6 +1,9 @@
|
||||
# syntax = docker/dockerfile:1.11-labs
|
||||
|
||||
FROM input AS rocksdb-fetch
|
||||
ARG rocksdb_branch="tuwunel-changes"
|
||||
ARG rust_rocksdb_branch="conduwuit-changes"
|
||||
ARG librocksdb_submodule="librocksdb-sys/rocksdb"
|
||||
|
||||
WORKDIR /
|
||||
COPY --link --from=input . .
|
||||
@@ -10,19 +13,20 @@ COPY --link --from=recipe rocksdb.url .
|
||||
|
||||
RUN <<EOF
|
||||
set -eux
|
||||
submodule="librocksdb-sys/rocksdb"
|
||||
url="$(cat rocksdb.url)"
|
||||
rocksdb_url="$(cat rocksdb.url)"
|
||||
git clone \
|
||||
--depth 1 \
|
||||
--recurse-submodules="${submodule}" \
|
||||
"${url}" \
|
||||
--single-branch \
|
||||
--branch "${rust_rocksdb_branch}" \
|
||||
"--recurse-submodules=${librocksdb_submodule}" \
|
||||
"${rocksdb_url}" \
|
||||
/usr/src/rocksdb
|
||||
EOF
|
||||
|
||||
|
||||
FROM input AS rocksdb-build
|
||||
ARG rocksdb_shared=0
|
||||
ARG rocksdb_portable=1
|
||||
ARG rocksdb_portable="1"
|
||||
ARG rocksdb_opt_level="3"
|
||||
ARG rocksdb_build_type="Release"
|
||||
ARG rocksdb_cxx_flags="-ftls-model=initial-exec"
|
||||
@@ -81,7 +85,7 @@ RUN <<EOF
|
||||
cmake \
|
||||
--build build \
|
||||
--target install \
|
||||
--parallel ${nprocs}
|
||||
--parallel "${nprocs}"
|
||||
EOF
|
||||
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
FROM input AS rustup
|
||||
ARG RUST_HOME
|
||||
ARG rust_target
|
||||
ARG rustup_version="1.28.1"
|
||||
ARG rustup_version="1.28.2"
|
||||
ARG rustup_profile="minimal"
|
||||
|
||||
WORKDIR /
|
||||
@@ -13,9 +13,10 @@ WORKDIR ${RUST_HOME}
|
||||
RUN <<EOF
|
||||
set -eux
|
||||
|
||||
url="https://static.rust-lang.org/rustup/archive/${rustup_version}/${rust_target}/rustup-init"
|
||||
var="${rustup_version}/${rust_target}"
|
||||
url="https://static.rust-lang.org/rustup/archive/$var/rustup-init"
|
||||
curl -S -O -s "$url"
|
||||
chmod o+x rustup-init
|
||||
chmod a+rx rustup-init
|
||||
EOF
|
||||
|
||||
FROM input AS rust
|
||||
@@ -53,7 +54,7 @@ RUN \
|
||||
--no-update-default-toolchain \
|
||||
--no-modify-path
|
||||
|
||||
chmod -R go+rw $CARGO_HOME $RUSTUP_HOME
|
||||
chmod -R a+rw $CARGO_HOME $RUSTUP_HOME
|
||||
rm rustup-init
|
||||
|
||||
rustup component add \
|
||||
|
||||
@@ -18,7 +18,6 @@ EOF
|
||||
|
||||
|
||||
FROM input AS ingredients
|
||||
ARG sys_target
|
||||
ARG rust_target
|
||||
ARG rust_toolchain
|
||||
ARG RUST_HOME
|
||||
@@ -28,23 +27,18 @@ ARG CARGO_TARGET
|
||||
ARG CARGO_TERM_VERBOSE=0
|
||||
ARG RUST_BACKTRACE
|
||||
ARG JEMALLOC_OVERRIDE
|
||||
#ARG ZSTD_SYS_USE_PKG_CONFIG=0
|
||||
ARG ROCKSDB_LIB_DIR
|
||||
|
||||
WORKDIR /
|
||||
COPY --link --from=input . .
|
||||
|
||||
WORKDIR /
|
||||
COPY --link --from=rust ${RUST_HOME} ${RUST_HOME}
|
||||
COPY --link --from=source /usr/src/tuwunel /usr/src/tuwunel
|
||||
|
||||
WORKDIR /usr/src/tuwunel
|
||||
COPY --link --from=source /usr/src/tuwunel .
|
||||
|
||||
ENV PATH="${CARGO_HOME}/bin:$PATH"
|
||||
ENV CARGO_TERM_VERBOSE="${CARGO_TERM_VERBOSE}"
|
||||
ENV RUST_BACKTRACE="${RUST_BACKTRACE}"
|
||||
ENV JEMALLOC_OVERRIDE="${JEMALLOC_OVERRIDE}"
|
||||
#ENV ZSTD_SYS_USE_PKG_CONFIG="${ZSTD_SYS_USE_PKG_CONFIG}"
|
||||
ENV ROCKSDB_LIB_DIR="${ROCKSDB_LIB_DIR}"
|
||||
RUN \
|
||||
--mount=type=cache,dst=${RUSTUP_HOME}/downloads,sharing=locked \
|
||||
@@ -91,6 +85,7 @@ RUN \
|
||||
package="rust-librocksdb-sys"
|
||||
cmd="cargo tree --manifest-path ${manifest} -f {r} -p ${package}"
|
||||
url="$(rustup run ${rust_toolchain} ${cmd} | head -n 1)"
|
||||
url="https://github.com/matrix-construct/rust-rocksdb" #TODO fixme
|
||||
echo "$url" > rocksdb.url
|
||||
|
||||
sha1sum recipe.json rocksdb.url
|
||||
|
||||
@@ -17,8 +17,8 @@ COPY --link --from=input . .
|
||||
|
||||
ENV DEBIAN_FRONTEND="${DEBIAN_FRONTEND}"
|
||||
RUN \
|
||||
--mount=type=cache,dst=${var_cache},sharing=locked \
|
||||
--mount=type=cache,dst=${var_lib_apt},sharing=locked \
|
||||
--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
|
||||
echo $(uname -a) $0 $-
|
||||
set -eux
|
||||
|
||||
+10
-5
@@ -1,5 +1,10 @@
|
||||
# Docker Builder
|
||||
|
||||
> [!NOTE]
|
||||
> This directory is for building docker images. For [deploying](https://github.com/matrix-construct/tuwunel/tree/main/docs/deploying)
|
||||
> [docker](https://github.com/matrix-construct/tuwunel/blob/main/docs/deploying/docker.md) visit
|
||||
> the appropriate documentation instead.
|
||||
|
||||
All Docker images for the project are built here. All images are
|
||||
[Docker Bake](https://docs.docker.com/build/bake/) targets. All targets are leaves and
|
||||
branches of a unified tree leading to a single root. It is a combinatorial matrix
|
||||
@@ -14,7 +19,7 @@ ### Layout
|
||||
This directory is made up of three types of files:
|
||||
|
||||
- Shell scripts are the user interface. Use this system through one of the shell scripts. The
|
||||
bake files can still be docker'ed directly but it's recomended to run the script.
|
||||
bake files can still be docker'ed directly but it's recommended to run the script.
|
||||
|
||||
- The `.hcl` files specify the targets of the tree. This is all standard
|
||||
[docker bake](https://docs.docker.com/build/bake/reference/). All targets are ordered where each
|
||||
@@ -30,8 +35,8 @@ ### Layout
|
||||
### Getting started
|
||||
|
||||
1. You will need to install docker buildx/buildkit and maybe a couple other related things.
|
||||
I would appreciate if you could contribute exact commands for your platform when you perform
|
||||
this step.
|
||||
|
||||
`apt-get install docker-buildx`
|
||||
|
||||
2. You will need to create a builder. There are a few complications that must be explained here
|
||||
so please be patient.
|
||||
@@ -62,7 +67,7 @@ ### Getting started
|
||||
all = true
|
||||
EOF
|
||||
|
||||
BKD_FLAGS="--allow-insecure-entitlement netwok.host"
|
||||
BKD_FLAGS="--allow-insecure-entitlement network.host"
|
||||
docker buildx create \
|
||||
--name owo \
|
||||
--bootstrap \
|
||||
@@ -84,7 +89,7 @@ ### Getting started
|
||||
5. Defeat the final boss by building and running complement to completion. This will involve
|
||||
building the targets for `complement-tester` and `complement-testee` using `bake.sh` and then
|
||||
invoking `complement.sh`. You can take cues again from another user of this in the
|
||||
[GitHub CI](https://github.com/matrix-construct/tuwunel/blob/main/.github/workflows/test.yml#L79).
|
||||
[GitHub CI](https://github.com/matrix-construct/tuwunel/blob/7e8c4c8883ca17de91c3323d329541342366ee0c/.github/workflows/test.yml#L140).
|
||||
|
||||
## Notes
|
||||
|
||||
|
||||
+530
-203
File diff suppressed because it is too large
Load Diff
+11
-22
@@ -14,8 +14,8 @@ default_feat_sets='["all"]'
|
||||
default_rust_toolchains='["nightly"]'
|
||||
default_rust_targets='["x86_64-unknown-linux-gnu"]'
|
||||
default_sys_names='["debian"]'
|
||||
default_sys_targets='["x86_64-linux-gnu"]'
|
||||
default_sys_versions='["testing-slim"]'
|
||||
default_sys_targets='["x86_64-v1-linux-gnu"]'
|
||||
|
||||
if test ! -z "$cargo_profile"; then
|
||||
env_cargo_profiles="[\"${cargo_profile}\"]"
|
||||
@@ -34,15 +34,15 @@ if test ! -z "$rust_toolchain"; then
|
||||
fi
|
||||
|
||||
if test ! -z "$sys_name"; then
|
||||
env_sys_name="[\"${sys_name}\"]"
|
||||
env_sys_names="[\"${sys_name}\"]"
|
||||
fi
|
||||
|
||||
if test ! -z "$sys_target"; then
|
||||
env_sys_target="[\"${sys_target}\"]"
|
||||
env_sys_targets="[\"${sys_target}\"]"
|
||||
fi
|
||||
|
||||
if test ! -z "$sys_version"; then
|
||||
env_sys_version="[\"${sys_version}\"]"
|
||||
env_sys_versions="[\"${sys_version}\"]"
|
||||
fi
|
||||
|
||||
set -a
|
||||
@@ -56,23 +56,13 @@ sys_targets="${env_sys_targets:-$default_sys_targets}"
|
||||
sys_versions="${env_sys_versions:-$default_sys_versions}"
|
||||
|
||||
docker_dir="$PWD/$BASEDIR"
|
||||
runner_name=$(echo $RUNNER_NAME | cut -d"." -f1)
|
||||
runner_num=$(echo $RUNNER_NAME | cut -d"." -f2)
|
||||
builder_name="${GITHUB_ACTOR:-owo}"
|
||||
toolchain_toml="$docker_dir/../rust-toolchain.toml"
|
||||
rust_msrv=$(grep "channel = " "$toolchain_toml" | cut -d'=' -f2 | sed 's/\s"\|"$//g')
|
||||
rocksdb_opt_level=3
|
||||
rocksdb_portable=1
|
||||
git_checkout="HEAD"
|
||||
git_checkout="${git_checkout:-HEAD}"
|
||||
use_chef="true"
|
||||
complement_count=1
|
||||
complement_skip="TestPartialStateJoin.*"
|
||||
complement_skip="${complement_skip}|TestRoomDeleteAlias/Pa.*/Can_delete_canonical_alias"
|
||||
complement_skip="${complement_skip}|TestUnbanViaInvite.*"
|
||||
complement_skip="${complement_skip}|TestRoomDeleteAlias/Pa.*/Regular_users_can_add_and_delete_aliases_when.*"
|
||||
complement_skip="${complement_skip}|TestToDeviceMessagesOverFederation/stopped_server"
|
||||
complement_skip="${complement_skip}|TestLogin/parallel/POST_/login_as_non-existing_user_is_rejected"
|
||||
complement_skip="${complement_skip}|TestRoomState/Parallel/GET_/publicRooms_lists_newly-created_room"
|
||||
complement_skip="${complement_skip}|TestThreadReceiptsInSyncMSC4102"
|
||||
complement_run=".*"
|
||||
set +a
|
||||
|
||||
###############################################################################
|
||||
@@ -83,6 +73,7 @@ if test "$CI" = "true"; then
|
||||
fi
|
||||
|
||||
args=""
|
||||
args="$args --provenance=false"
|
||||
args="$args --builder ${builder_name}"
|
||||
#args="$args --set *.platform=${sys_platform}"
|
||||
|
||||
@@ -90,11 +81,9 @@ if test "$CI" = "true"; then
|
||||
args="$args --allow=network.host"
|
||||
fi
|
||||
|
||||
if test ! -z "$runner_num"; then
|
||||
#cpu_num=$(expr $runner_num % $(nproc))
|
||||
#args="$args --cpuset-cpus=${cpu_num}"
|
||||
#args="$args --set *.args.nprocs=1"
|
||||
# https://github.com/moby/buildkit/issues/1276
|
||||
if test "$(uname)" = "Darwin"; then
|
||||
nprocs=$(sysctl -n hw.logicalcpu)
|
||||
args="$args --set *.args.nprocs=${nprocs}"
|
||||
:
|
||||
else
|
||||
nprocs=$(nproc)
|
||||
|
||||
+52
-12
@@ -12,9 +12,29 @@ default_feat_set="all"
|
||||
default_rust_toolchain="nightly"
|
||||
default_rust_target="x86_64-unknown-linux-gnu"
|
||||
default_sys_name="debian"
|
||||
default_sys_target="x86_64-linux-gnu"
|
||||
default_sys_target="x86_64-v1-linux-gnu"
|
||||
default_sys_version="testing-slim"
|
||||
|
||||
default_complement_verbose=0
|
||||
default_complement_count=1
|
||||
default_complement_parallel=1
|
||||
default_complement_shuffle=0
|
||||
default_complement_timeout="1h"
|
||||
default_complement_run=".*"
|
||||
|
||||
run="${1:-$default_complement_run}"
|
||||
skip=""
|
||||
skip="${skip}TestToDeviceMessagesOverFederation/stopped_server"
|
||||
skip="${skip}|TestToDeviceMessagesOverFederation/interrupted_connectivity"
|
||||
skip="${skip}|TestRoomCreate/Parallel/POST_/createRoom_makes_a_room_with_a_topic_and_writes_rich_topic_representation"
|
||||
skip="${skip}|TestRoomCreate/Parallel/POST_/createRoom_makes_a_room_with_a_topic_via_initial_state_overwritten_by_topic"
|
||||
skip="${skip}|TestLogin/parallel/POST_/"
|
||||
skip="${skip}|TestUnbanViaInvite"
|
||||
skip="${skip}|TestRoomState/Parallel/GET_/publicRooms_lists_newly-created_room"
|
||||
# flakes due to timeout in debug-mode
|
||||
skip="${skip}|TestMSC4297StateResolutionV2_1_starts_from_empty_set"
|
||||
skip="${skip}|TestMSC4297StateResolutionV2_1_includes_conflicted_subgraph"
|
||||
|
||||
set -a
|
||||
cargo_profile="${cargo_profile:-$default_cargo_profile}"
|
||||
feat_set="${feat_set:-$default_feat_set}"
|
||||
@@ -30,12 +50,21 @@ set +a
|
||||
|
||||
###############################################################################
|
||||
|
||||
envs=""
|
||||
envs="$envs -e complement_verbose=${complement_verbose:-$default_complement_verbose}"
|
||||
envs="$envs -e complement_count=${complement_count:-$default_complement_count}"
|
||||
envs="$envs -e complement_parallel=${complement_parallel:-$default_complement_parallel}"
|
||||
envs="$envs -e complement_shuffle=${complement_shuffle:-$default_complement_shuffle}"
|
||||
envs="$envs -e complement_timeout=${complement_timeout:-$default_complement_timeout}"
|
||||
envs="$envs -e complement_skip=${complement_skip:-$skip}"
|
||||
envs="$envs -e complement_run=${1:-$default_complement_run}"
|
||||
|
||||
set -x
|
||||
tester_image="complement-tester--${sys_name}--${sys_version}--${sys_target}"
|
||||
testee_image="complement-testee--${cargo_profile}--${rust_toolchain}--${rust_target}--${feat_set}--${sys_name}--${sys_version}--${sys_target}"
|
||||
name="complement_tester__${sys_name}__${sys_version}__${sys_target}"
|
||||
sock="/var/run/docker.sock"
|
||||
arg="--name $name -v $sock:$sock --network=host $tester_image ${testee_image}"
|
||||
arg="--name $name -v $sock:$sock --network=host $envs $tester_image ${testee_image}"
|
||||
set +x
|
||||
|
||||
if test "$CI_VERBOSE_ENV" = "true"; then
|
||||
@@ -45,21 +74,32 @@ fi
|
||||
|
||||
docker rm -f "$name" 2>/dev/null
|
||||
|
||||
trap 'set +x; date; echo -e "\033[1;41;37mFAIL\033[0m"' ERR
|
||||
arg="-d $arg"
|
||||
cid=$(docker run $arg)
|
||||
|
||||
if test "$CI" = "true"; then
|
||||
arg="-d $arg"
|
||||
cid=$(docker run $arg)
|
||||
else
|
||||
arg="--rm -a stdout -a stderr $arg"
|
||||
docker run $arg
|
||||
exit $?
|
||||
echo -n "$cid" > "$name"
|
||||
fi
|
||||
|
||||
echo -n "$cid" > "$name"
|
||||
output_src="$cid:/usr/src/complement/full_output.jsonl"
|
||||
output_dst="complement.jsonl"
|
||||
extract_output() {
|
||||
docker cp "$output_src" "$output_dst"
|
||||
}
|
||||
|
||||
trap 'docker container stop $cid; set +x; date; echo -e "\033[1;41;37mFAIL\033[0m"' INT
|
||||
result_src="$cid:/usr/src/complement/new_results.jsonl"
|
||||
result_dst="tests/complement/results.jsonl"
|
||||
extract_results() {
|
||||
docker cp "$result_src" "$result_dst"
|
||||
}
|
||||
|
||||
trap 'extract_output; set +x; date; echo -e "\033[1;41;37mERROR\033[0m"' ERR
|
||||
trap 'docker container stop $cid; extract_output' INT
|
||||
docker logs -f "$cid"
|
||||
docker wait "$cid" 2>/dev/null
|
||||
|
||||
echo -e "\033[1;42;30mPASS\033[0m"
|
||||
extract_results
|
||||
git diff -U0 --color --shortstat "$result_dst" | (grep "$run" || true)
|
||||
|
||||
git diff --quiet --exit-code "$result_dst"
|
||||
echo -e "\033[1;42;30mACCEPT\033[0m"
|
||||
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../CODE_OF_CONDUCT.md
|
||||
+2
-1
@@ -10,6 +10,7 @@ # Summary
|
||||
- [Kubernetes](deploying/kubernetes.md)
|
||||
- [Arch Linux](deploying/arch-linux.md)
|
||||
- [Debian](deploying/debian.md)
|
||||
- [Red Hat](deploying/redhat.md)
|
||||
- [FreeBSD](deploying/freebsd.md)
|
||||
- [TURN](turn.md)
|
||||
- [Appservices](appservices.md)
|
||||
@@ -19,4 +20,4 @@ # Summary
|
||||
- [Contributing](contributing.md)
|
||||
- [Testing](development/testing.md)
|
||||
- [Hot Reloading ("Live" Development)](development/hot_reload.md)
|
||||
- [Tuwunel Community Code of Conduct](../CODE_OF_CONDUCT.md)
|
||||
- [Tuwunel Community Code of Conduct](CODE_OF_CONDUCT.md)
|
||||
|
||||
+26
-6
@@ -7,14 +7,20 @@ ## Basics
|
||||
Tuwunel uses a config file for the majority of the settings, but also supports
|
||||
setting individual config options via commandline.
|
||||
|
||||
Please refer to the [example config
|
||||
file](./configuration/examples.md#example-configuration) for all of those
|
||||
settings.
|
||||
Please refer to the [example config file](./configuration/examples.md#example-configuration) for all of those settings.
|
||||
|
||||
The config file to use can be specified on the commandline when running
|
||||
Tuwunel by specifying the `-c`, `--config` flag. Alternatively, you can use
|
||||
the environment variable `TUWUNEL_CONFIG` to specify the config file to used.
|
||||
Conduit's environment variables are supported for backwards compatibility.
|
||||
Tuwunel by specifying the `-c`, `--config` flag. It is also possible to specify
|
||||
more than one config file.
|
||||
|
||||
Alternatively, you can use the environment variable `TUWUNEL_CONFIG` to specify
|
||||
the config file to used. Conduit's environment variables are supported for
|
||||
backwards compatibility.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> It is bad practice to uncomment default options without changing them. Many
|
||||
> defaults are updated by developers as features evolve and can be essential
|
||||
> to expected server function.
|
||||
|
||||
## Option commandline flag
|
||||
|
||||
@@ -31,6 +37,20 @@ ## Option commandline flag
|
||||
- `--option log=\"debug\"` works ✅
|
||||
- `--option server_name='"example.com'"` works ✅
|
||||
|
||||
## Relevance of configuration settings
|
||||
|
||||
There is a specific sequence for reading and overwriting the settings. The
|
||||
latest setting takes precedence and defines the configuration.
|
||||
|
||||
1. Set in `CONDUIT_CONFIG`.
|
||||
2. Set in `CONDUWUIT_CONFIG`.
|
||||
3. Set in `TUWUNEL_CONFIG`.
|
||||
4. Set in the first config file on the command line (e.g. `-c config_file_1.toml`).
|
||||
5. Set in the second config file on the command line (e.g. `-c config_file_2.toml`).
|
||||
6. Set in any additional config file on the command line (e.g. `-c config_file_n.toml`).
|
||||
7. Set within the options (again, the latest option in the list overrides).
|
||||
|
||||
|
||||
## Execute commandline flag
|
||||
|
||||
Tuwunel supports running admin commands on startup using the commandline
|
||||
|
||||
@@ -6,5 +6,6 @@ # Deployment Guides
|
||||
- [Kubernetes](kubernetes.md)
|
||||
- [Arch Linux](arch-linux.md)
|
||||
- [Debian](debian.md)
|
||||
- [Red Hat](redhat.md)
|
||||
- [FreeBSD](freebsd.md)
|
||||
- [Podman systemd](podman-systemd.md)
|
||||
|
||||
+18
-19
@@ -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
|
||||
|
||||
@@ -153,7 +152,7 @@ ### Caddy
|
||||
```caddyfile
|
||||
your.server.name, your.server.name:8448 {
|
||||
# TCP reverse_proxy
|
||||
reverse_proxy 127.0.0.1:6167
|
||||
reverse_proxy localhost:8008
|
||||
# UNIX socket
|
||||
#reverse_proxy unix//run/tuwunel/tuwunel.sock
|
||||
}
|
||||
@@ -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.
|
||||
As we would prefer our users to use Caddy, we will not provide configuration files for other proxies.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
@@ -2,7 +2,8 @@ # Tuwunel for Kubernetes
|
||||
|
||||
Tuwunel doesn't support horizontal scalability or distributed loading
|
||||
natively, however a community maintained Helm Chart is available here to run
|
||||
Tuwunel on Kubernetes: <https://gitlab.cronce.io/charts/conduwuit>
|
||||
Tuwunel on Kubernetes: <https://github.com/AreYouLoco/tuwunel-helm> and the
|
||||
legacy conduwuit version: <https://gitlab.cronce.io/charts/conduwuit>.
|
||||
|
||||
Should changes need to be made, please reach out to the maintainer in our
|
||||
Matrix room as this is not maintained/controlled by the Tuwunel maintainers.
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{{#include ../../rpm/README.md}}
|
||||
+100
-46
@@ -1,17 +1,11 @@
|
||||
# Troubleshooting Tuwunel
|
||||
|
||||
> ## Docker users ⚠️
|
||||
>
|
||||
> Docker is extremely UX unfriendly. Because of this, a ton of issues or support
|
||||
> is actually Docker support, not tuwunel support. We also cannot document the
|
||||
> ever-growing list of Docker issues here.
|
||||
>
|
||||
> [!IMPORTANT]
|
||||
> If you intend on asking for support and you are using Docker, **PLEASE**
|
||||
> triple validate your issues are **NOT** because you have a misconfiguration in
|
||||
> your Docker setup.
|
||||
>
|
||||
> If there are things like Compose file issues or Dockerhub image issues, those
|
||||
> can still be mentioned as long as they're something we can fix.
|
||||
> your Docker setup. We must remain focused on supporting Tuwunel issues and
|
||||
> cannot budget our time for generic Docker support. Compose file issues or
|
||||
> Dockerhub image issues are okay if they are something we can fix.
|
||||
|
||||
## Tuwunel and Matrix issues
|
||||
|
||||
@@ -93,48 +87,107 @@ ## RocksDB / database issues
|
||||
|
||||
#### Database corruption
|
||||
|
||||
If your database is corrupted *and* is failing to start (e.g. checksum
|
||||
mismatch), it may be recoverable but careful steps must be taken, and there is
|
||||
no guarantee it may be recoverable.
|
||||
There are many causes and varieties of database corruption. There are several
|
||||
methods for mitigation, each with outcomes ranging from a recovered state down
|
||||
to a savage state. This guide has been simplified into a set of universal steps
|
||||
which everyone can follow from the top until they have recovered or reach the
|
||||
end. The details and implications will be explained within each step.
|
||||
|
||||
The first thing that can be done is launching Tuwunel with the
|
||||
`rocksdb_repair` config option set to true. This will tell RocksDB to attempt to
|
||||
repair itself at launch. If this does not work, disable the option and continue
|
||||
reading.
|
||||
> [!TIP]
|
||||
> All command-line `-O` options can be expressed as environment variables or in
|
||||
> the config file based on your deployment's requirements. Note that
|
||||
> `--maintenance` is equivalent to configuring `startup_netburst = false` and
|
||||
> `listening = false`.
|
||||
|
||||
RocksDB has the following recovery modes:
|
||||
> [!IMPORTANT]
|
||||
> Always create a backup of the database before running any operation. This is
|
||||
> critical for steps 3 and above.
|
||||
|
||||
- `TolerateCorruptedTailRecords`
|
||||
- `AbsoluteConsistency`
|
||||
- `PointInTime`
|
||||
- `SkipAnyCorruptedRecord`
|
||||
**0. Start the server with the following options:**
|
||||
|
||||
By default, Tuwunel uses `TolerateCorruptedTailRecords` as generally these may
|
||||
be due to bad federation and we can re-fetch the correct data over federation.
|
||||
The RocksDB default is `PointInTime` which will attempt to restore a "snapshot"
|
||||
of the data when it was last known to be good. This data can be either a few
|
||||
seconds old, or multiple minutes prior. `PointInTime` may not be suitable for
|
||||
default usage due to clients and servers possibly not being able to handle
|
||||
sudden "backwards time travels", and `AbsoluteConsistency` may be too strict.
|
||||
`tuwunel --maintenance -O rocksdb_recovery_mode=0`
|
||||
|
||||
`AbsoluteConsistency` will fail to start the database if any sign of corruption
|
||||
is detected. `SkipAnyCorruptedRecord` will skip all forms of corruption unless
|
||||
it forbids the database from opening (e.g. too severe). Usage of
|
||||
`SkipAnyCorruptedRecord` voids any support as this may cause more damage and/or
|
||||
leave your database in a permanently inconsistent state, but it may do something
|
||||
if `PointInTime` does not work as a last ditch effort.
|
||||
This is actually a "control" and not a method of recovery. If the server starts
|
||||
you either do not have corruption or have deep corruption indicated by very
|
||||
specific errors from rocksdb citing corruption during runtime. If you are
|
||||
certain there is deep corruption skip to step 4, otherwise you are finished
|
||||
without any modifications.
|
||||
|
||||
With this in mind:
|
||||
**1. Start the server in Tolerate-Corrupted-Tail-Records mode:**
|
||||
|
||||
- First start Tuwunel with the `PointInTime` recovery method. See the [example
|
||||
config](configuration/examples.md) for how to do this using
|
||||
`rocksdb_recovery_mode`
|
||||
- If your database successfully opens, clients are recommended to clear their
|
||||
client cache to account for the rollback
|
||||
- Leave your Tuwunel running in `PointInTime` for at least 30-60 minutes so as
|
||||
much possible corruption is restored
|
||||
- If all goes will, you should be able to restore back to using
|
||||
`TolerateCorruptedTailRecords` and you have successfully recovered your database
|
||||
`tuwunel --maintenance -O rocksdb_recovery_mode=1`
|
||||
|
||||
The most common corruption scenario is from a loss of power to the hardware
|
||||
(not an application crash, though it is still possible). This is remediated
|
||||
by dropping the most recently written record. It is highly unlikely there will
|
||||
be any impact on the application from this loss. In the best-case the same data
|
||||
is often re-requested over the federation or replaced by a client. In the
|
||||
worst-case clients may need to clear-cache & reload to guarantee correctness.
|
||||
If the server starts you are finished.
|
||||
|
||||
**2. Start the server in Point-In-Time mode:**
|
||||
|
||||
`tuwunel --maintenance -O rocksdb_recovery_mode=2`
|
||||
|
||||
Similar to the corruption scenario above but for more severe cases. The most
|
||||
recent records are discarded back to the point where there is no corruption.
|
||||
It is highly unlikely there will be any impact on the application from this
|
||||
loss, but it is more likely than above that clients may need to clear-cache
|
||||
& reload to correctly resynchronize with the server.
|
||||
|
||||
**3. Start the server in Skip-Any-Corrupted-Record mode:**
|
||||
|
||||
> [!WARNING]
|
||||
> Salvage mode potentially impacting the application's ability to function.
|
||||
> We cannot provide support for users who have entered this mode.
|
||||
|
||||
`tuwunel --maintenance -O rocksdb_recovery_mode=3`
|
||||
|
||||
Similar to the prior corruption scenarios but for the most severe cases.
|
||||
The database will be inconsistent. It is theoretically possible for the
|
||||
server to continue functioning without notable issue in the best case, but
|
||||
it is completely uncertain what the effect of this operation will be. If
|
||||
the server starts you should immediately export your messages, encryption
|
||||
keys, etc, in a salvage effort and prepare to reinstall.
|
||||
|
||||
**4. Start the server in repair mode.**
|
||||
|
||||
> [!WARNING]
|
||||
> Salvage mode potentially impacting the application's ability to function.
|
||||
> We cannot provide support for users who have entered this mode.
|
||||
|
||||
> [!CAUTION]
|
||||
> Always create a backup of the database before entering this mode. The repair
|
||||
> is not configurable and not interactive. It may automatically remove more
|
||||
> data than anticipated, preventing further salvage efforts.
|
||||
|
||||
`tuwunel --maintenance -O rocksdb_repair=true`
|
||||
|
||||
For corruption affecting the bulk database tables not covered by any journal.
|
||||
This will leave the database in an inconsistent and unpredictable state. It
|
||||
is theoretically possible to continue operating the server depending on which
|
||||
records were dropped, such as some historical records which are no longer
|
||||
essential. Nevertheless the impact of this operation is impossible to assess
|
||||
and a successful recovery should be used to salvage data prior to reinstall.
|
||||
|
||||
Once finished, restart the server without `rocksdb_repair`. If no errors
|
||||
persist, restart the server again without maintenance mode.
|
||||
|
||||
**5. Utilize an external repair tool.**
|
||||
|
||||
> [!WARNING]
|
||||
> Salvage mode potentially impacting the application's ability to function.
|
||||
> We cannot provide support for users who have entered this mode.
|
||||
|
||||
```
|
||||
git clone https://github.com/facebook/rocksdb
|
||||
cd rocksdb
|
||||
make -j$(nproc) ldb
|
||||
./ldb repair --db=/var/lib/tuwunel/ 2>./repair-log.txt
|
||||
```
|
||||
|
||||
For situations when the repair mode in step 4 failed or produced unexpected
|
||||
results.
|
||||
|
||||
## Debugging
|
||||
|
||||
@@ -147,7 +200,8 @@ #### Debug/Trace log level
|
||||
Tuwunel builds without debug or trace log levels at compile time by default
|
||||
for substantial performance gains in CPU usage and improved compile times. If
|
||||
you need to access debug/trace log levels, you will need to build without the
|
||||
`release_max_log_level` feature or use our provided static debug binaries.
|
||||
`release_max_log_level` feature or use our provided release-logging binaries
|
||||
and images.
|
||||
|
||||
#### Changing log level dynamically
|
||||
|
||||
|
||||
Generated
+103
-164
@@ -10,11 +10,11 @@
|
||||
"nixpkgs-stable": "nixpkgs-stable"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1738524606,
|
||||
"narHash": "sha256-hPYEJ4juK3ph7kbjbvv7PlU1D9pAkkhl+pwx8fZY53U=",
|
||||
"lastModified": 1758711588,
|
||||
"narHash": "sha256-0nZlCCDC5PfndsQJXXtcyrtrfW49I3KadGMDlutzaGU=",
|
||||
"owner": "zhaofengli",
|
||||
"repo": "attic",
|
||||
"rev": "ff8a897d1f4408ebbf4d45fa9049c06b3e1e3f4e",
|
||||
"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": 1737621947,
|
||||
"narHash": "sha256-8HFvG7fvIFbgtaYAY2628Tb89fA55nPm2jSiNs0/Cws=",
|
||||
"lastModified": 1756385612,
|
||||
"narHash": "sha256-+NU5MMhuPHHRyvZZWNFG7zt+leRSPsJu1MwhOUzkPUk=",
|
||||
"owner": "cachix",
|
||||
"repo": "cachix",
|
||||
"rev": "f65a3cd5e339c223471e64c051434616e18cc4f5",
|
||||
"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": 1728672398,
|
||||
"narHash": "sha256-KxuGSoVUFnQLB2ZcYODW7AVPAh9JqRlD5BrfsC/Q4qs=",
|
||||
"lastModified": 1748883665,
|
||||
"narHash": "sha256-R0W7uAg+BLoHjMRMQ8+oiSbTq8nkGz5RDpQ+ZfxxP3A=",
|
||||
"owner": "cachix",
|
||||
"repo": "cachix",
|
||||
"rev": "aac51f698309fd0f381149214b7eee213c66ef0a",
|
||||
"rev": "f707778d902af4d62d8dd92c269f8e70de09acbe",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -80,33 +85,27 @@
|
||||
"complement": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1741891349,
|
||||
"narHash": "sha256-YvrzOWcX7DH1drp5SGa+E/fc7wN3hqFtPbqPjZpOu1Q=",
|
||||
"owner": "girlbossceo",
|
||||
"lastModified": 1756358878,
|
||||
"narHash": "sha256-H/4TkbFcGsMRDZ7gNaXZ7NxeO3b1YgkVt9zPb+3ATSg=",
|
||||
"owner": "matrix-construct",
|
||||
"repo": "complement",
|
||||
"rev": "e587b3df569cba411aeac7c20b6366d03c143745",
|
||||
"rev": "403840348f6bcc9cc8ed1671dc2f638c2b1ce4ac",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "girlbossceo",
|
||||
"owner": "matrix-construct",
|
||||
"ref": "main",
|
||||
"repo": "complement",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"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": 1739936662,
|
||||
"narHash": "sha256-x4syUjNUuRblR07nDPeLDP7DpphaBVbUaSoeZkFbGSk=",
|
||||
"lastModified": 1758758545,
|
||||
"narHash": "sha256-NU5WaEdfwF6i8faJ2Yh+jcK9vVFrofLcwlD/mP65JrI=",
|
||||
"owner": "ipetkov",
|
||||
"repo": "crane",
|
||||
"rev": "19de14aaeb869287647d9461cbd389187d8ecdb7",
|
||||
"rev": "95d528a5f54eaba0d12102249ce42f4d01f4e364",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -149,11 +148,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1733323168,
|
||||
"narHash": "sha256-d5DwB4MZvlaQpN6OQ4SLYxb5jA4UH5EtV5t5WOtjLPU=",
|
||||
"lastModified": 1754404745,
|
||||
"narHash": "sha256-BdbW/iTImczgcuATgQIa9sPGuYIBxVq2xqcvICsa2AQ=",
|
||||
"owner": "cachix",
|
||||
"repo": "devenv",
|
||||
"rev": "efa9010b8b1cfd5dd3c7ed1e172a470c3b84a064",
|
||||
"rev": "6563b21105168f90394dfaf58284b078af2d7275",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -170,11 +169,11 @@
|
||||
"rust-analyzer-src": "rust-analyzer-src"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1740724364,
|
||||
"narHash": "sha256-D1jLIueJx1dPrP09ZZwTrPf4cubV+TsFMYbpYYTVj6A=",
|
||||
"lastModified": 1759251341,
|
||||
"narHash": "sha256-0vt4IQnTLyAhaeom3h9GOCpO2+av+wK4zP7O8BSKr6I=",
|
||||
"owner": "nix-community",
|
||||
"repo": "fenix",
|
||||
"rev": "edf7d9e431cda8782e729253835f178a356d3aab",
|
||||
"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": {
|
||||
@@ -203,11 +202,11 @@
|
||||
"flake-compat_2": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1733328505,
|
||||
"narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=",
|
||||
"lastModified": 1747046372,
|
||||
"narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec",
|
||||
"rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -219,11 +218,11 @@
|
||||
"flake-compat_3": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1733328505,
|
||||
"narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=",
|
||||
"lastModified": 1747046372,
|
||||
"narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec",
|
||||
"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": {
|
||||
@@ -306,15 +305,14 @@
|
||||
"nixpkgs": [
|
||||
"cachix",
|
||||
"nixpkgs"
|
||||
],
|
||||
"nixpkgs-stable": "nixpkgs-stable_2"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1733318908,
|
||||
"narHash": "sha256-SVQVsbafSM1dJ4fpgyBqLZ+Lft+jcQuMtEL3lQWx2Sk=",
|
||||
"lastModified": 1750779888,
|
||||
"narHash": "sha256-wibppH3g/E2lxU43ZQHC5yA/7kIKLGxVEnsnVK1BtRg=",
|
||||
"owner": "cachix",
|
||||
"repo": "git-hooks.nix",
|
||||
"rev": "6f4e2a2112050951a314d2733a994fbab94864c6",
|
||||
"rev": "16ec914f6fb6f599ce988427d9d94efddf25fe6d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -345,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": 1740613216,
|
||||
"narHash": "sha256-NpPOBqNND3Qe9IwqYs0mJLGTmIx7e6FgUEBAnJ+1ZLA=",
|
||||
"lastModified": 1759268303,
|
||||
"narHash": "sha256-nT3JWey0P2lPWlnwN57XHmuzkeW8Mo795+Ahf8S5TMI=",
|
||||
"owner": "axboe",
|
||||
"repo": "liburing",
|
||||
"rev": "e1003e496e66f9b0ae06674869795edf772d5500",
|
||||
"rev": "1671cb952a8f680214b04ae9546d4f649f1b367b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -382,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"
|
||||
@@ -394,34 +385,30 @@
|
||||
"nixpkgs-regression": [
|
||||
"cachix",
|
||||
"devenv"
|
||||
],
|
||||
"pre-commit-hooks": [
|
||||
"cachix",
|
||||
"devenv"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1727438425,
|
||||
"narHash": "sha256-X8ES7I1cfNhR9oKp06F6ir4Np70WGZU5sfCOuNBEwMg=",
|
||||
"owner": "domenkozar",
|
||||
"lastModified": 1752773918,
|
||||
"narHash": "sha256-dOi/M6yNeuJlj88exI+7k154z+hAhFcuB8tZktiW7rg=",
|
||||
"owner": "cachix",
|
||||
"repo": "nix",
|
||||
"rev": "f6c5ae4c1b2e411e6b1e6a8181cc84363d6a7546",
|
||||
"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": {
|
||||
@@ -439,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": {
|
||||
@@ -454,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": {
|
||||
@@ -470,43 +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",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-stable_2": {
|
||||
"locked": {
|
||||
"lastModified": 1730741070,
|
||||
"narHash": "sha256-edm8WG19kWozJ/GqyYx2VjW99EdhjKwbY3ZwdlPAAlo=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "d063c1dd113c91ab27959ba540c0d9753409edf3",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-24.05",
|
||||
"ref": "nixos-25.05",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1730531603,
|
||||
"narHash": "sha256-Dqg6si5CqIzm87sp57j5nTaeBbWhHFaVyG7V6L8k3lY=",
|
||||
"lastModified": 1754214453,
|
||||
"narHash": "sha256-Q/I2xJn/j1wpkGhWkQnm20nShYnG7TI99foDBpXm1SY=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "7ffd9ae656aec493492b44d0ddfb28e79a1ea25d",
|
||||
"rev": "5b09dc45f24cf32316283e62aec81ffee3c3e376",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -518,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": 1733212471,
|
||||
"narHash": "sha256-M1+uCoV5igihRfcUKrr1riygbe73/dzNnzPsmaLCmpo=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "55d15ad12a74eb7d4646254e13638ad0c4128776",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_5": {
|
||||
"locked": {
|
||||
"lastModified": 1740547748,
|
||||
"narHash": "sha256-Ly2fBL1LscV+KyCqPRufUBuiw+zmWrlJzpWOWbahplg=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "3a05eebede89661660945da1f151959900903b6a",
|
||||
"rev": "647e5c14cbd5067f44ac86b74f014962df460840",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -567,16 +506,16 @@
|
||||
"rocksdb": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1741308171,
|
||||
"narHash": "sha256-YdBvdQ75UJg5ffwNjxizpviCVwVDJnBkM8ZtGIduMgY=",
|
||||
"owner": "girlbossceo",
|
||||
"lastModified": 1749358049,
|
||||
"narHash": "sha256-ZSjvAZBfZkJrBIpw8ANZMbJVb8AeuogvuAipGVE4Qe4=",
|
||||
"owner": "matrix-construct",
|
||||
"repo": "rocksdb",
|
||||
"rev": "3ce04794bcfbbb0d2e6f81ae35fc4acf688b6986",
|
||||
"rev": "cf7f65d0b377af019661c240f9165b3ef60640c3",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "girlbossceo",
|
||||
"ref": "v9.11.1",
|
||||
"owner": "matrix-construct",
|
||||
"ref": "tuwunel-changes",
|
||||
"repo": "rocksdb",
|
||||
"type": "github"
|
||||
}
|
||||
@@ -592,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": 1740691488,
|
||||
"narHash": "sha256-Fs6vBrByuiOf2WO77qeMDMTXcTGzrIMqLBv+lNeywwM=",
|
||||
"lastModified": 1759134797,
|
||||
"narHash": "sha256-YPi+jL3tx/yC5J5l7/OB7Lnlr9BMTzYnZtm7tRJzUNg=",
|
||||
"owner": "rust-lang",
|
||||
"repo": "rust-analyzer",
|
||||
"rev": "fe3eda77d3a7ce212388bda7b6cec8bffcc077e5",
|
||||
"rev": "062ac7a5451e8e92a32e22a60d86882d6a034f3f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
inputs = {
|
||||
attic.url = "github:zhaofengli/attic?ref=main";
|
||||
cachix.url = "github:cachix/cachix?ref=master";
|
||||
complement = { url = "github:girlbossceo/complement?ref=main"; flake = false; };
|
||||
complement = { url = "github:matrix-construct/complement?ref=main"; flake = false; };
|
||||
crane = { url = "github:ipetkov/crane?ref=master"; };
|
||||
fenix = { url = "github:nix-community/fenix?ref=main"; inputs.nixpkgs.follows = "nixpkgs"; };
|
||||
flake-compat = { url = "github:edolstra/flake-compat?ref=master"; flake = false; };
|
||||
flake-utils.url = "github:numtide/flake-utils?ref=main";
|
||||
nix-filter.url = "github:numtide/nix-filter?ref=main";
|
||||
nixpkgs.url = "github:NixOS/nixpkgs?ref=nixpkgs-unstable";
|
||||
rocksdb = { url = "github:girlbossceo/rocksdb?ref=v9.11.1"; flake = false; };
|
||||
rocksdb = { url = "github:matrix-construct/rocksdb?ref=tuwunel-changes"; flake = false; };
|
||||
liburing = { url = "github:axboe/liburing?ref=master"; flake = false; };
|
||||
};
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
file = ./rust-toolchain.toml;
|
||||
|
||||
# See also `rust-toolchain.toml`
|
||||
sha256 = "sha256-X/4ZBHO3iW0fOenQ3foEvscgAPJYl2abspaBThDOukI=";
|
||||
sha256 = "sha256-Qxt8XAuaUR2OMdKbN4u8dBJOhSHxS+uS06Wl9+flVEk=";
|
||||
};
|
||||
|
||||
mkScope = pkgs: pkgs.lib.makeScope pkgs.newScope (self: {
|
||||
@@ -53,7 +53,7 @@
|
||||
};
|
||||
rocksdb = (pkgs.rocksdb.override {
|
||||
liburing = self.liburing;
|
||||
}).overrideAttrs (old: {
|
||||
}).overrideAttrs (final: old: {
|
||||
src = inputs.rocksdb;
|
||||
version = pkgs.lib.removePrefix
|
||||
"v"
|
||||
@@ -62,6 +62,15 @@
|
||||
# we have this already at https://github.com/girlbossceo/rocksdb/commit/a935c0273e1ba44eacf88ce3685a9b9831486155
|
||||
# unsetting this so i don't have to revert it and make this nix exclusive
|
||||
patches = [];
|
||||
postPatch =
|
||||
pkgs.lib.optionalString (pkgs.lib.versionOlder final.version "8") ''
|
||||
# Fix gcc-13 build failures due to missing <cstdint> and
|
||||
# <system_error> includes, fixed upstream since 8.x
|
||||
sed -e '1i #include <cstdint>' -i db/compaction/compaction_iteration_stats.h
|
||||
sed -e '1i #include <cstdint>' -i table/block_based/data_block_hash_index.h
|
||||
sed -e '1i #include <cstdint>' -i util/string_util.h
|
||||
sed -e '1i #include <cstdint>' -i include/rocksdb/utilities/checkpoint.h
|
||||
'';
|
||||
cmakeFlags = pkgs.lib.subtractLists
|
||||
[
|
||||
# no real reason to have snappy or zlib, no one uses this
|
||||
@@ -195,10 +204,8 @@
|
||||
# be expected on non-debug builds.
|
||||
"jemalloc_prof"
|
||||
"jemalloc_stats"
|
||||
# this is non-functional on nix for some reason
|
||||
"hardened_malloc"
|
||||
# conduwuit_mods is a development-only hot reload feature
|
||||
"conduwuit_mods"
|
||||
# tuwunel_mods is a development-only hot reload feature
|
||||
"tuwunel_mods"
|
||||
];
|
||||
};
|
||||
default-debug = scopeHost.main.override {
|
||||
@@ -209,9 +216,7 @@
|
||||
# dont include experimental features
|
||||
"experimental"
|
||||
# this is non-functional on nix for some reason
|
||||
"hardened_malloc"
|
||||
# conduwuit_mods is a development-only hot reload feature
|
||||
"conduwuit_mods"
|
||||
"tuwunel_mods"
|
||||
];
|
||||
};
|
||||
# just a test profile used for things like CI and complement
|
||||
@@ -222,9 +227,7 @@
|
||||
# dont include experimental features
|
||||
"experimental"
|
||||
# this is non-functional on nix for some reason
|
||||
"hardened_malloc"
|
||||
# conduwuit_mods is a development-only hot reload feature
|
||||
"conduwuit_mods"
|
||||
"tuwunel_mods"
|
||||
];
|
||||
};
|
||||
all-features = scopeHost.main.override {
|
||||
@@ -237,9 +240,7 @@
|
||||
"jemalloc_prof"
|
||||
"jemalloc_stats"
|
||||
# this is non-functional on nix for some reason
|
||||
"hardened_malloc"
|
||||
# conduwuit_mods is a development-only hot reload feature
|
||||
"conduwuit_mods"
|
||||
"tuwunel_mods"
|
||||
];
|
||||
};
|
||||
all-features-debug = scopeHost.main.override {
|
||||
@@ -251,12 +252,9 @@
|
||||
# dont include experimental features
|
||||
"experimental"
|
||||
# this is non-functional on nix for some reason
|
||||
"hardened_malloc"
|
||||
# conduwuit_mods is a development-only hot reload feature
|
||||
"conduwuit_mods"
|
||||
"tuwunel_mods"
|
||||
];
|
||||
};
|
||||
hmalloc = scopeHost.main.override { features = ["hardened_malloc"]; };
|
||||
|
||||
oci-image = scopeHost.oci-image;
|
||||
oci-image-all-features = scopeHost.oci-image.override {
|
||||
@@ -269,10 +267,8 @@
|
||||
# be expected on non-debug builds.
|
||||
"jemalloc_prof"
|
||||
"jemalloc_stats"
|
||||
# this is non-functional on nix for some reason
|
||||
"hardened_malloc"
|
||||
# conduwuit_mods is a development-only hot reload feature
|
||||
"conduwuit_mods"
|
||||
# tuwunel_mods is a development-only hot reload feature
|
||||
"tuwunel_mods"
|
||||
];
|
||||
};
|
||||
};
|
||||
@@ -285,18 +281,11 @@
|
||||
disable_features = [
|
||||
# dont include experimental features
|
||||
"experimental"
|
||||
# this is non-functional on nix for some reason
|
||||
"hardened_malloc"
|
||||
# conduwuit_mods is a development-only hot reload feature
|
||||
"conduwuit_mods"
|
||||
# tuwunel_mods is a development-only hot reload feature
|
||||
"tuwunel_mods"
|
||||
];
|
||||
};
|
||||
};
|
||||
oci-image-hmalloc = scopeHost.oci-image.override {
|
||||
main = scopeHost.main.override {
|
||||
features = ["hardened_malloc"];
|
||||
};
|
||||
};
|
||||
|
||||
book = scopeHost.book;
|
||||
|
||||
@@ -350,10 +339,8 @@
|
||||
disable_features = [
|
||||
# dont include experimental features
|
||||
"experimental"
|
||||
# this is non-functional on nix for some reason
|
||||
"hardened_malloc"
|
||||
# conduwuit_mods is a development-only hot reload feature
|
||||
"conduwuit_mods"
|
||||
# tuwunel_mods is a development-only hot reload feature
|
||||
"tuwunel_mods"
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -370,10 +357,8 @@
|
||||
# be expected on non-debug builds.
|
||||
"jemalloc_prof"
|
||||
"jemalloc_stats"
|
||||
# this is non-functional on nix for some reason
|
||||
"hardened_malloc"
|
||||
# conduwuit_mods is a development-only hot reload feature
|
||||
"conduwuit_mods"
|
||||
# tuwunel_mods is a development-only hot reload feature
|
||||
"tuwunel_mods"
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -391,10 +376,8 @@
|
||||
# be expected on non-debug builds.
|
||||
"jemalloc_prof"
|
||||
"jemalloc_stats"
|
||||
# this is non-functional on nix for some reason
|
||||
"hardened_malloc"
|
||||
# conduwuit_mods is a development-only hot reload feature
|
||||
"conduwuit_mods"
|
||||
# tuwunel_mods is a development-only hot reload feature
|
||||
"tuwunel_mods"
|
||||
];
|
||||
x86_64_haswell_target_optimised = (if (crossSystem == "x86_64-linux-gnu" || crossSystem == "x86_64-linux-musl") then true else false);
|
||||
};
|
||||
@@ -411,22 +394,12 @@
|
||||
disable_features = [
|
||||
# dont include experimental features
|
||||
"experimental"
|
||||
# this is non-functional on nix for some reason
|
||||
"hardened_malloc"
|
||||
# conduwuit_mods is a development-only hot reload feature
|
||||
"conduwuit_mods"
|
||||
# tuwunel_mods is a development-only hot reload feature
|
||||
"tuwunel_mods"
|
||||
];
|
||||
};
|
||||
}
|
||||
|
||||
# An output for a statically-linked binary with hardened_malloc
|
||||
{
|
||||
name = "${binaryName}-hmalloc";
|
||||
value = scopeCrossStatic.main.override {
|
||||
features = ["hardened_malloc"];
|
||||
};
|
||||
}
|
||||
|
||||
# An output for an OCI image based on that binary
|
||||
{
|
||||
name = "oci-image-${crossSystem}";
|
||||
@@ -469,10 +442,8 @@
|
||||
# be expected on non-debug builds.
|
||||
"jemalloc_prof"
|
||||
"jemalloc_stats"
|
||||
# this is non-functional on nix for some reason
|
||||
"hardened_malloc"
|
||||
# conduwuit_mods is a development-only hot reload feature
|
||||
"conduwuit_mods"
|
||||
# tuwunel_mods is a development-only hot reload feature
|
||||
"tuwunel_mods"
|
||||
];
|
||||
};
|
||||
};
|
||||
@@ -492,10 +463,8 @@
|
||||
# be expected on non-debug builds.
|
||||
"jemalloc_prof"
|
||||
"jemalloc_stats"
|
||||
# this is non-functional on nix for some reason
|
||||
"hardened_malloc"
|
||||
# conduwuit_mods is a development-only hot reload feature
|
||||
"conduwuit_mods"
|
||||
# tuwunel_mods is a development-only hot reload feature
|
||||
"tuwunel_mods"
|
||||
];
|
||||
x86_64_haswell_target_optimised = (if (crossSystem == "x86_64-linux-gnu" || crossSystem == "x86_64-linux-musl") then true else false);
|
||||
};
|
||||
@@ -514,25 +483,13 @@
|
||||
disable_features = [
|
||||
# dont include experimental features
|
||||
"experimental"
|
||||
# this is non-functional on nix for some reason
|
||||
"hardened_malloc"
|
||||
# conduwuit_mods is a development-only hot reload feature
|
||||
"conduwuit_mods"
|
||||
# tuwunel_mods is a development-only hot reload feature
|
||||
"tuwunel_mods"
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
# An output for an OCI image based on that binary with hardened_malloc
|
||||
{
|
||||
name = "oci-image-${crossSystem}-hmalloc";
|
||||
value = scopeCrossStatic.oci-image.override {
|
||||
main = scopeCrossStatic.main.override {
|
||||
features = ["hardened_malloc"];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
# An output for a complement OCI image for the specified platform
|
||||
{
|
||||
name = "complement-${crossSystem}";
|
||||
@@ -562,10 +519,8 @@
|
||||
# be expected on non-debug builds.
|
||||
"jemalloc_prof"
|
||||
"jemalloc_stats"
|
||||
# this is non-functional on nix for some reason
|
||||
"hardened_malloc"
|
||||
# conduwuit_mods is a development-only hot reload feature
|
||||
"conduwuit_mods"
|
||||
# tuwunel_mods is a development-only hot reload feature
|
||||
"tuwunel_mods"
|
||||
];
|
||||
};
|
||||
}));
|
||||
|
||||
@@ -13,14 +13,16 @@ stdenv.mkDerivation {
|
||||
root = inputs.self;
|
||||
include = [
|
||||
"book.toml"
|
||||
"conduwuit-example.toml"
|
||||
"tuwunel-example.toml"
|
||||
"CODE_OF_CONDUCT.md"
|
||||
"CONTRIBUTING.md"
|
||||
"README.md"
|
||||
"development.md"
|
||||
"debian/conduwuit.service"
|
||||
"debian/tuwunel.service"
|
||||
"debian/README.md"
|
||||
"arch/conduwuit.service"
|
||||
"arch/tuwunel.service"
|
||||
"rpm/tuwunel.service"
|
||||
"rpm/README.md"
|
||||
"docs"
|
||||
"theme"
|
||||
];
|
||||
|
||||
@@ -15,7 +15,7 @@ LOG_FILE="${2:-complement_test_logs.jsonl}"
|
||||
# A `.jsonl` file to write test results to
|
||||
RESULTS_FILE="${3:-complement_test_results.jsonl}"
|
||||
|
||||
COMPLEMENT_BASE_IMAGE="${COMPLEMENT_BASE_IMAGE:-complement-conduwuit:main}"
|
||||
COMPLEMENT_BASE_IMAGE="${COMPLEMENT_BASE_IMAGE:-complement-tuwunel:main}"
|
||||
|
||||
# Complement tests that are skipped due to flakiness/reliability issues or we don't implement such features and won't for a long time
|
||||
SKIPPED_COMPLEMENT_TESTS='TestPartialStateJoin.*|TestRoomDeleteAlias/Parallel/Regular_users_can_add_and_delete_aliases_when_m.*|TestRoomDeleteAlias/Parallel/Can_delete_canonical_alias|TestUnbanViaInvite.*|TestRoomState/Parallel/GET_/publicRooms_lists.*"|TestRoomDeleteAlias/Parallel/Users_with_sufficient_power-level_can_delete_other.*'
|
||||
@@ -35,20 +35,20 @@ toplevel="$(git rev-parse --show-toplevel)"
|
||||
pushd "$toplevel" > /dev/null
|
||||
|
||||
if [ ! -f "complement_oci_image.tar.gz" ]; then
|
||||
echo "building complement conduwuit image"
|
||||
echo "building complement tuwunel image"
|
||||
|
||||
# if using macOS, use linux-complement
|
||||
#bin/nix-build-and-cache just .#linux-complement
|
||||
bin/nix-build-and-cache just .#complement
|
||||
#nix build -L .#complement
|
||||
|
||||
echo "complement conduwuit image tar.gz built at \"result\""
|
||||
echo "complement tuwunel image tar.gz built at \"result\""
|
||||
|
||||
echo "loading into docker"
|
||||
docker load < result
|
||||
popd > /dev/null
|
||||
else
|
||||
echo "skipping building a complement conduwuit image as complement_oci_image.tar.gz was already found, loading this"
|
||||
echo "skipping building a complement tuwunel image as complement_oci_image.tar.gz was already found, loading this"
|
||||
|
||||
docker load < complement_oci_image.tar.gz
|
||||
popd > /dev/null
|
||||
|
||||
@@ -18,7 +18,7 @@ just() {
|
||||
return
|
||||
fi
|
||||
|
||||
# historical "conduit" store for compatibility purposes, same as conduwuit
|
||||
# historical "conduit" store for compatibility purposes, same as tuwunel
|
||||
nix run --inputs-from "$toplevel" attic -- \
|
||||
login \
|
||||
conduit \
|
||||
@@ -52,26 +52,26 @@ just() {
|
||||
withattic conduit || withattic conduit || withattic conduit || true
|
||||
)
|
||||
|
||||
# main "conduwuit" store
|
||||
# main "tuwunel" store
|
||||
nix run --inputs-from "$toplevel" attic -- \
|
||||
login \
|
||||
conduwuit \
|
||||
"${ATTIC_ENDPOINT:-https://attic.kennel.juneis.dog/conduwuit}" \
|
||||
tuwunel \
|
||||
"${ATTIC_ENDPOINT:-https://attic.kennel.juneis.dog/tuwunel}" \
|
||||
"$ATTIC_TOKEN"
|
||||
|
||||
# Upload them to Attic (conduwuit store) and Cachix
|
||||
# Upload them to Attic (tuwunel store) and Cachix
|
||||
#
|
||||
# Use `xargs` and a here-string because something would probably explode if
|
||||
# several thousand arguments got passed to a command at once. Hopefully no
|
||||
# store paths include a newline in them.
|
||||
(
|
||||
IFS=$'\n'
|
||||
withattic conduwuit || withattic conduwuit || withattic conduwuit || true
|
||||
withattic tuwunel || withattic tuwunel || withattic tuwunel || true
|
||||
|
||||
# push to cachix if available
|
||||
if [ "$CACHIX_AUTH_TOKEN" ]; then
|
||||
nix shell --inputs-from "$toplevel" cachix -c xargs \
|
||||
cachix push conduwuit <<< "${cache[*]}"
|
||||
cachix push tuwunel <<< "${cache[*]}"
|
||||
fi
|
||||
)
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ let
|
||||
"gzip_compression"
|
||||
"zstd_compression"
|
||||
# complement doesn't need hot reloading
|
||||
"conduwuit_mods"
|
||||
"tuwunel_mods"
|
||||
# complement doesn't have URL preview media tests
|
||||
"url_preview"
|
||||
];
|
||||
@@ -41,13 +41,13 @@ let
|
||||
set -euxo pipefail
|
||||
|
||||
${lib.getExe' coreutils "env"} \
|
||||
CONDUWUIT_SERVER_NAME="$SERVER_NAME" \
|
||||
TUWUNEL_SERVER_NAME="$SERVER_NAME" \
|
||||
${lib.getExe main'}
|
||||
'';
|
||||
in
|
||||
|
||||
dockerTools.buildImage {
|
||||
name = "complement-conduwuit";
|
||||
name = "complement-tuwunel";
|
||||
tag = "main";
|
||||
|
||||
copyToRoot = buildEnv {
|
||||
@@ -75,9 +75,9 @@ dockerTools.buildImage {
|
||||
else [];
|
||||
|
||||
Env = [
|
||||
"CONDUWUIT_TLS__KEY=${./private_key.key}"
|
||||
"CONDUWUIT_TLS__CERTS=${./certificate.crt}"
|
||||
"CONDUWUIT_CONFIG=${./config.toml}"
|
||||
"TUWUNEL_TLS__KEY=${./private_key.key}"
|
||||
"TUWUNEL_TLS__CERTS=${./certificate.crt}"
|
||||
"TUWUNEL_CONFIG=${./config.toml}"
|
||||
"RUST_BACKTRACE=full"
|
||||
];
|
||||
|
||||
|
||||
@@ -23,10 +23,8 @@
|
||||
# be expected on non-debug builds.
|
||||
"jemalloc_prof"
|
||||
"jemalloc_stats"
|
||||
# this is non-functional on nix for some reason
|
||||
"hardened_malloc"
|
||||
# conduwuit_mods is a development-only hot reload feature
|
||||
"conduwuit_mods"
|
||||
# tuwunel_mods is a development-only hot reload feature
|
||||
"tuwunel_mods"
|
||||
]
|
||||
, disable_release_max_log_level ? false
|
||||
, features ? []
|
||||
@@ -130,7 +128,8 @@ buildDepsOnlyEnv =
|
||||
});
|
||||
|
||||
buildPackageEnv = {
|
||||
CONDUWUIT_VERSION_EXTRA = inputs.self.shortRev or inputs.self.dirtyShortRev or "";
|
||||
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
|
||||
@@ -203,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
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
# Tuwunel for Red Hat
|
||||
|
||||
Information about downloading and deploying the Red Hat package. This may also be
|
||||
referenced for other `rpm`-based distros such as CentOS.
|
||||
|
||||
### Installation
|
||||
|
||||
It is recommended to see the [generic deployment guide](../deploying/generic.md)
|
||||
for further information if needed as usage of the RPM package is generally
|
||||
related.
|
||||
|
||||
No `rpm` repository is currently offered yet, it is in the works/development.
|
||||
|
||||
### Configuration
|
||||
|
||||
When installed, the example config is placed at `/etc/tuwunel/tuwunel.toml`
|
||||
as the default config. The config mentions things required to be changed before
|
||||
starting.
|
||||
|
||||
You can tweak more detailed settings by uncommenting and setting the config
|
||||
options in `/etc/tuwunel/tuwunel.toml`.
|
||||
|
||||
### Running
|
||||
|
||||
The package uses the [`tuwunel.service`](../configuration/examples.md#example-systemd-unit-file)
|
||||
systemd unit file to start and stop Tuwunel. The binary is installed at `/usr/sbin/tuwunel`.
|
||||
|
||||
This package assumes by default that Tuwunel will be placed behind a reverse
|
||||
proxy. The default config options apply (listening on `localhost` and TCP port
|
||||
`8008`). Matrix federation requires a valid domain name and TLS, so you will
|
||||
need to set up TLS certificates and renewal for it to work properly if you
|
||||
intend to federate.
|
||||
|
||||
Consult various online documentation and guides on setting up a reverse proxy
|
||||
and TLS. Caddy is documented at the [generic deployment guide](../deploying/generic.md#setting-up-the-reverse-proxy)
|
||||
as it's the easiest and most user friendly.
|
||||
@@ -0,0 +1,41 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
TUWUNEL_DATABASE_PATH=/var/lib/tuwunel
|
||||
TUWUNEL_CONFIG_PATH=/etc/tuwunel
|
||||
|
||||
case "$1" in
|
||||
configure)
|
||||
# Create the `tuwunel` user if it does not exist yet.
|
||||
if ! getent passwd tuwunel > /dev/null ; then
|
||||
echo 'Adding system user for the tuwunel Matrix homeserver' 1>&2
|
||||
useradd --system --user-group \
|
||||
--create-home --home-dir "$TUWUNEL_DATABASE_PATH" \
|
||||
--shell "/usr/sbin/nologin" \
|
||||
tuwunel
|
||||
fi
|
||||
|
||||
# Create the database path if it does not exist yet and fix up ownership
|
||||
# and permissions for the config.
|
||||
mkdir -v -p "$TUWUNEL_DATABASE_PATH"
|
||||
|
||||
# symlink the previous location for compatibility if it does not exist yet.
|
||||
if ! test -L "/var/lib/matrix-conduit" ; then
|
||||
ln -s -v "$TUWUNEL_DATABASE_PATH" "/var/lib/matrix-conduit"
|
||||
fi
|
||||
if ! test -L "/var/lib/conduwuit" ; then
|
||||
ln -s -v "$TUWUNEL_DATABASE_PATH" "/var/lib/conduwuit"
|
||||
fi
|
||||
|
||||
chown -v tuwunel:tuwunel -R "$TUWUNEL_DATABASE_PATH"
|
||||
chown -v tuwunel:tuwunel -R "$TUWUNEL_CONFIG_PATH"
|
||||
|
||||
chmod -v 740 "$TUWUNEL_DATABASE_PATH"
|
||||
|
||||
echo ''
|
||||
echo 'Make sure you edit the example config at /etc/tuwunel/tuwunel.toml before starting!'
|
||||
echo 'To start the server, run: systemctl start tuwunel.service'
|
||||
echo ''
|
||||
|
||||
;;
|
||||
esac
|
||||
@@ -0,0 +1,65 @@
|
||||
[Unit]
|
||||
Description=Tuwunel Matrix homeserver
|
||||
Wants=network-online.target
|
||||
After=network-online.target
|
||||
Documentation=https://tuwunel.chat/
|
||||
|
||||
[Service]
|
||||
DynamicUser=yes
|
||||
User=tuwunel
|
||||
Group=tuwunel
|
||||
Type=notify
|
||||
|
||||
Environment="TUWUNEL_CONFIG=/etc/tuwunel/tuwunel.toml"
|
||||
|
||||
ExecStart=/usr/sbin/tuwunel
|
||||
|
||||
ReadWritePaths=/var/lib/tuwunel /etc/tuwunel
|
||||
|
||||
AmbientCapabilities=
|
||||
CapabilityBoundingSet=
|
||||
|
||||
DevicePolicy=closed
|
||||
LockPersonality=yes
|
||||
MemoryDenyWriteExecute=yes
|
||||
NoNewPrivileges=yes
|
||||
#ProcSubset=pid
|
||||
ProtectClock=yes
|
||||
ProtectControlGroups=yes
|
||||
ProtectHome=yes
|
||||
ProtectHostname=yes
|
||||
ProtectKernelLogs=yes
|
||||
ProtectKernelModules=yes
|
||||
ProtectKernelTunables=yes
|
||||
ProtectProc=invisible
|
||||
ProtectSystem=strict
|
||||
PrivateDevices=yes
|
||||
PrivateMounts=yes
|
||||
PrivateTmp=yes
|
||||
PrivateUsers=yes
|
||||
PrivateIPC=yes
|
||||
RemoveIPC=yes
|
||||
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
|
||||
RestrictNamespaces=yes
|
||||
RestrictRealtime=yes
|
||||
RestrictSUIDSGID=yes
|
||||
SystemCallArchitectures=native
|
||||
SystemCallFilter=@system-service @resources
|
||||
SystemCallFilter=~@clock @debug @module @mount @reboot @swap @cpu-emulation @obsolete @timer @chown @setuid @privileged @keyring @ipc
|
||||
SystemCallErrorNumber=EPERM
|
||||
|
||||
RuntimeDirectory=tuwunel
|
||||
RuntimeDirectoryMode=0750
|
||||
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
|
||||
TimeoutStopSec=2m
|
||||
TimeoutStartSec=2m
|
||||
|
||||
StartLimitInterval=1m
|
||||
StartLimitBurst=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Alias=matrix-tuwunel.service
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
# If you're having trouble making the relevant changes, bug a maintainer.
|
||||
|
||||
[toolchain]
|
||||
channel = "1.86.0"
|
||||
channel = "1.88.0"
|
||||
profile = "minimal"
|
||||
components = [
|
||||
# For rust-analyzer
|
||||
|
||||
+12
-10
@@ -11,6 +11,7 @@ version.workspace = true
|
||||
|
||||
[lib]
|
||||
path = "mod.rs"
|
||||
bench = false
|
||||
crate-type = [
|
||||
"rlib",
|
||||
# "dylib",
|
||||
@@ -18,46 +19,46 @@ crate-type = [
|
||||
|
||||
[features]
|
||||
brotli_compression = [
|
||||
"tuwunel-api/brotli_compression",
|
||||
"tuwunel-core/brotli_compression",
|
||||
"tuwunel-service/brotli_compression",
|
||||
]
|
||||
bzip2_compression = [
|
||||
"tuwunel-database/bzip2_compression",
|
||||
"tuwunel-service/bzip2_compression",
|
||||
]
|
||||
gzip_compression = [
|
||||
"tuwunel-api/gzip_compression",
|
||||
"tuwunel-core/gzip_compression",
|
||||
"tuwunel-service/gzip_compression",
|
||||
]
|
||||
io_uring = [
|
||||
"tuwunel-api/io_uring",
|
||||
"tuwunel-database/io_uring",
|
||||
"tuwunel-service/io_uring",
|
||||
]
|
||||
jemalloc = [
|
||||
"tuwunel-api/jemalloc",
|
||||
"tuwunel-core/jemalloc",
|
||||
"tuwunel-database/jemalloc",
|
||||
"tuwunel-service/jemalloc",
|
||||
]
|
||||
jemalloc_conf = [
|
||||
"tuwunel-api/jemalloc_conf",
|
||||
"tuwunel-core/jemalloc_conf",
|
||||
"tuwunel-database/jemalloc_conf",
|
||||
"tuwunel-service/jemalloc_conf",
|
||||
]
|
||||
jemalloc_prof = [
|
||||
"tuwunel-api/jemalloc_prof",
|
||||
"tuwunel-core/jemalloc_prof",
|
||||
"tuwunel-database/jemalloc_prof",
|
||||
"tuwunel-service/jemalloc_prof",
|
||||
]
|
||||
jemalloc_stats = [
|
||||
"tuwunel-api/jemalloc_stats",
|
||||
"tuwunel-core/jemalloc_stats",
|
||||
"tuwunel-database/jemalloc_stats",
|
||||
"tuwunel-service/jemalloc_stats",
|
||||
]
|
||||
lz4_compression = [
|
||||
"tuwunel-database/lz4_compression",
|
||||
"tuwunel-service/lz4_compression",
|
||||
]
|
||||
release_max_log_level = [
|
||||
"tuwunel-api/release_max_log_level",
|
||||
"tuwunel-core/release_max_log_level",
|
||||
"tuwunel-database/release_max_log_level",
|
||||
"tuwunel-service/release_max_log_level",
|
||||
@@ -67,23 +68,24 @@ release_max_log_level = [
|
||||
"log/release_max_level_info",
|
||||
]
|
||||
zstd_compression = [
|
||||
"tuwunel-api/zstd_compression",
|
||||
"tuwunel-core/zstd_compression",
|
||||
"tuwunel-database/zstd_compression",
|
||||
"tuwunel-service/zstd_compression",
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
base64.workspace = true
|
||||
clap.workspace = true
|
||||
tuwunel-api.workspace = true
|
||||
tuwunel-core.workspace = true
|
||||
tuwunel-database.workspace = true
|
||||
tuwunel-macros.workspace = true
|
||||
tuwunel-service.workspace = true
|
||||
const-str.workspace = true
|
||||
ctor.workspace = true
|
||||
futures.workspace = true
|
||||
log.workspace = true
|
||||
ruma.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
serde_yaml.workspace = true
|
||||
tokio.workspace = true
|
||||
|
||||
+102
-61
@@ -2,6 +2,7 @@
|
||||
collections::HashMap,
|
||||
fmt::Write,
|
||||
iter::once,
|
||||
str::FromStr,
|
||||
time::{Instant, SystemTime},
|
||||
};
|
||||
|
||||
@@ -11,9 +12,10 @@
|
||||
OwnedRoomOrAliasId, OwnedServerName, RoomId, RoomVersionId,
|
||||
api::federation::event::get_room_state, events::AnyStateEvent, serde::Raw,
|
||||
};
|
||||
use serde::Serialize;
|
||||
use tracing_subscriber::EnvFilter;
|
||||
use tuwunel_core::{
|
||||
Err, Result, debug_error, err, info,
|
||||
Err, Result, debug_error, err, info, jwt,
|
||||
matrix::{
|
||||
Event,
|
||||
pdu::{PduEvent, PduId, RawPduId},
|
||||
@@ -22,13 +24,11 @@
|
||||
utils::{
|
||||
stream::{IterStream, ReadyExt},
|
||||
string::EMPTY,
|
||||
time::now_secs,
|
||||
},
|
||||
warn,
|
||||
};
|
||||
use tuwunel_service::rooms::{
|
||||
short::{ShortEventId, ShortRoomId},
|
||||
state_compressor::HashSetCompressStateEvent,
|
||||
};
|
||||
use tuwunel_service::rooms::{short::ShortRoomId, state_compressor::HashSetCompressStateEvent};
|
||||
|
||||
use crate::admin_command;
|
||||
|
||||
@@ -42,7 +42,6 @@ pub(super) async fn echo(&self, message: Vec<String>) -> Result {
|
||||
pub(super) async fn get_auth_chain(&self, event_id: OwnedEventId) -> Result {
|
||||
let Ok(event) = self
|
||||
.services
|
||||
.rooms
|
||||
.timeline
|
||||
.get_pdu_json(&event_id)
|
||||
.await
|
||||
@@ -61,7 +60,6 @@ pub(super) async fn get_auth_chain(&self, event_id: OwnedEventId) -> Result {
|
||||
let start = Instant::now();
|
||||
let count = self
|
||||
.services
|
||||
.rooms
|
||||
.auth_chain
|
||||
.event_ids_iter(room_id, once(event_id.as_ref()))
|
||||
.ready_filter_map(Result::ok)
|
||||
@@ -84,9 +82,12 @@ pub(super) async fn parse_pdu(&self) -> Result {
|
||||
}
|
||||
|
||||
let string = self.body[1..self.body.len().saturating_sub(1)].join("\n");
|
||||
let rules = RoomVersionId::V6
|
||||
.rules()
|
||||
.expect("rules for V6 rooms");
|
||||
match serde_json::from_str(&string) {
|
||||
| Err(e) => return Err!("Invalid json in command body: {e}"),
|
||||
| Ok(value) => match ruma::signatures::reference_hash(&value, &RoomVersionId::V6) {
|
||||
| Ok(value) => match ruma::signatures::reference_hash(&value, &rules) {
|
||||
| Err(e) => return Err!("Could not parse PDU JSON: {e:?}"),
|
||||
| Ok(hash) => {
|
||||
let event_id = OwnedEventId::parse(format!("${hash}"));
|
||||
@@ -105,7 +106,6 @@ pub(super) async fn get_pdu(&self, event_id: OwnedEventId) -> Result {
|
||||
let mut outlier = false;
|
||||
let mut pdu_json = self
|
||||
.services
|
||||
.rooms
|
||||
.timeline
|
||||
.get_non_outlier_pdu_json(&event_id)
|
||||
.await;
|
||||
@@ -114,7 +114,6 @@ pub(super) async fn get_pdu(&self, event_id: OwnedEventId) -> Result {
|
||||
outlier = true;
|
||||
pdu_json = self
|
||||
.services
|
||||
.rooms
|
||||
.timeline
|
||||
.get_pdu_json(&event_id)
|
||||
.await;
|
||||
@@ -136,20 +135,11 @@ 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
|
||||
.rooms
|
||||
.timeline
|
||||
.get_pdu_json_from_id(&pdu_id)
|
||||
.await;
|
||||
@@ -249,7 +239,6 @@ pub(super) async fn get_remote_pdu(
|
||||
.sending
|
||||
.send_federation_request(&server, ruma::api::federation::event::get_event::v1::Request {
|
||||
event_id: event_id.clone(),
|
||||
include_unredacted_content: None,
|
||||
})
|
||||
.await
|
||||
{
|
||||
@@ -270,32 +259,28 @@ pub(super) async fn get_remote_pdu(
|
||||
})?;
|
||||
|
||||
trace!("Attempting to parse PDU: {:?}", &response.pdu);
|
||||
let _parsed_pdu = {
|
||||
let (room_id, ..) = {
|
||||
let parsed_result = self
|
||||
.services
|
||||
.rooms
|
||||
.event_handler
|
||||
.parse_incoming_pdu(&response.pdu)
|
||||
.boxed()
|
||||
.await;
|
||||
|
||||
let (event_id, value, room_id) = match parsed_result {
|
||||
match parsed_result {
|
||||
| Ok(t) => t,
|
||||
| Err(e) => {
|
||||
warn!("Failed to parse PDU: {e}");
|
||||
info!("Full PDU: {:?}", &response.pdu);
|
||||
return Err!("Failed to parse PDU remote server {server} sent us: {e}");
|
||||
},
|
||||
};
|
||||
|
||||
vec![(event_id, value, room_id)]
|
||||
}
|
||||
};
|
||||
|
||||
info!("Attempting to handle event ID {event_id} as backfilled PDU");
|
||||
self.services
|
||||
.rooms
|
||||
.timeline
|
||||
.backfill_pdu(&server, response.pdu)
|
||||
.backfill_pdu(&room_id, &server, response.pdu)
|
||||
.await?;
|
||||
|
||||
let text = serde_json::to_string_pretty(&json)?;
|
||||
@@ -308,10 +293,9 @@ 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.rooms.alias.resolve(&room).await?;
|
||||
let room_id = self.services.alias.maybe_resolve(&room).await?;
|
||||
let room_state: Vec<Raw<AnyStateEvent>> = self
|
||||
.services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_full_pdus(&room_id)
|
||||
.map_ok(Event::into_format)
|
||||
@@ -489,7 +473,6 @@ pub(super) async fn verify_pdu(&self, event_id: OwnedEventId) -> Result {
|
||||
|
||||
let mut event = self
|
||||
.services
|
||||
.rooms
|
||||
.timeline
|
||||
.get_pdu_json(&event_id)
|
||||
.await?;
|
||||
@@ -514,7 +497,6 @@ pub(super) async fn verify_pdu(&self, event_id: OwnedEventId) -> Result {
|
||||
pub(super) async fn first_pdu_in_room(&self, room_id: OwnedRoomId) -> Result {
|
||||
if !self
|
||||
.services
|
||||
.rooms
|
||||
.state_cache
|
||||
.server_in_room(&self.services.server.name, &room_id)
|
||||
.await
|
||||
@@ -524,7 +506,6 @@ pub(super) async fn first_pdu_in_room(&self, room_id: OwnedRoomId) -> Result {
|
||||
|
||||
let first_pdu = self
|
||||
.services
|
||||
.rooms
|
||||
.timeline
|
||||
.first_pdu_in_room(&room_id)
|
||||
.await
|
||||
@@ -539,7 +520,6 @@ pub(super) async fn first_pdu_in_room(&self, room_id: OwnedRoomId) -> Result {
|
||||
pub(super) async fn latest_pdu_in_room(&self, room_id: OwnedRoomId) -> Result {
|
||||
if !self
|
||||
.services
|
||||
.rooms
|
||||
.state_cache
|
||||
.server_in_room(&self.services.server.name, &room_id)
|
||||
.await
|
||||
@@ -549,7 +529,6 @@ pub(super) async fn latest_pdu_in_room(&self, room_id: OwnedRoomId) -> Result {
|
||||
|
||||
let latest_pdu = self
|
||||
.services
|
||||
.rooms
|
||||
.timeline
|
||||
.latest_pdu_in_room(&room_id)
|
||||
.await
|
||||
@@ -568,7 +547,6 @@ pub(super) async fn force_set_room_state_from_server(
|
||||
) -> Result {
|
||||
if !self
|
||||
.services
|
||||
.rooms
|
||||
.state_cache
|
||||
.server_in_room(&self.services.server.name, &room_id)
|
||||
.await
|
||||
@@ -578,7 +556,6 @@ pub(super) async fn force_set_room_state_from_server(
|
||||
|
||||
let first_pdu = self
|
||||
.services
|
||||
.rooms
|
||||
.timeline
|
||||
.latest_pdu_in_room(&room_id)
|
||||
.await
|
||||
@@ -586,7 +563,6 @@ pub(super) async fn force_set_room_state_from_server(
|
||||
|
||||
let room_version = self
|
||||
.services
|
||||
.rooms
|
||||
.state
|
||||
.get_room_version(&room_id)
|
||||
.await?;
|
||||
@@ -605,7 +581,6 @@ pub(super) async fn force_set_room_state_from_server(
|
||||
for pdu in remote_state_response.pdus.clone() {
|
||||
match self
|
||||
.services
|
||||
.rooms
|
||||
.event_handler
|
||||
.parse_incoming_pdu(&pdu)
|
||||
.await
|
||||
@@ -624,24 +599,33 @@ pub(super) async fn force_set_room_state_from_server(
|
||||
.server_keys
|
||||
.validate_and_add_event_id(pdu, &room_version)
|
||||
}) {
|
||||
let Ok((event_id, value)) = result.await else {
|
||||
let Ok((event_id, mut value)) = result.await else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let pdu = PduEvent::from_id_val(&event_id, value.clone()).map_err(|e| {
|
||||
let invalid_pdu_err = |e| {
|
||||
debug_error!("Invalid PDU in fetching remote room state PDUs response: {value:#?}");
|
||||
err!(BadServerResponse(debug_error!("Invalid PDU in send_join response: {e:?}")))
|
||||
})?;
|
||||
};
|
||||
|
||||
let pdu = if value["type"] == "m.room.create" {
|
||||
PduEvent::from_rid_val(&room_id, &event_id, value.clone()).map_err(invalid_pdu_err)?
|
||||
} else {
|
||||
PduEvent::from_id_val(&event_id, value.clone()).map_err(invalid_pdu_err)?
|
||||
};
|
||||
|
||||
if !value.contains_key("room_id") {
|
||||
let room_id = CanonicalJsonValue::String(room_id.as_str().into());
|
||||
value.insert("room_id".into(), room_id);
|
||||
}
|
||||
|
||||
self.services
|
||||
.rooms
|
||||
.outlier
|
||||
.timeline
|
||||
.add_pdu_outlier(&event_id, &value);
|
||||
|
||||
if let Some(state_key) = &pdu.state_key {
|
||||
let shortstatekey = self
|
||||
.services
|
||||
.rooms
|
||||
.short
|
||||
.get_or_create_shortstatekey(&pdu.kind.to_string().into(), state_key)
|
||||
.await;
|
||||
@@ -664,14 +648,12 @@ pub(super) async fn force_set_room_state_from_server(
|
||||
};
|
||||
|
||||
self.services
|
||||
.rooms
|
||||
.outlier
|
||||
.timeline
|
||||
.add_pdu_outlier(&event_id, &value);
|
||||
}
|
||||
|
||||
let new_room_state = self
|
||||
.services
|
||||
.rooms
|
||||
.event_handler
|
||||
.resolve_state(&room_id, &room_version, state)
|
||||
.await?;
|
||||
@@ -683,21 +665,13 @@ pub(super) async fn force_set_room_state_from_server(
|
||||
removed,
|
||||
} = self
|
||||
.services
|
||||
.rooms
|
||||
.state_compressor
|
||||
.save_state(room_id.clone().as_ref(), new_room_state)
|
||||
.await?;
|
||||
|
||||
let state_lock = self
|
||||
.services
|
||||
.rooms
|
||||
.state
|
||||
.mutex
|
||||
.lock(&*room_id)
|
||||
.await;
|
||||
let state_lock = self.services.state.mutex.lock(&*room_id).await;
|
||||
|
||||
self.services
|
||||
.rooms
|
||||
.state
|
||||
.force_state(room_id.clone().as_ref(), short_state_hash, added, removed, &state_lock)
|
||||
.await?;
|
||||
@@ -707,7 +681,6 @@ pub(super) async fn force_set_room_state_from_server(
|
||||
the room's m.room.member state"
|
||||
);
|
||||
self.services
|
||||
.rooms
|
||||
.state_cache
|
||||
.update_joined_count(&room_id)
|
||||
.await;
|
||||
@@ -934,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<_>>()?;
|
||||
|
||||
@@ -975,3 +948,71 @@ pub(super) async fn trim_memory(&self) -> Result {
|
||||
|
||||
writeln!(self, "done").await
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn create_jwt(
|
||||
&self,
|
||||
user: String,
|
||||
exp_from_now: Option<u64>,
|
||||
nbf_from_now: Option<u64>,
|
||||
issuer: Option<String>,
|
||||
audience: Option<String>,
|
||||
) -> Result {
|
||||
use jwt::{Algorithm, EncodingKey, Header, encode};
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Claim {
|
||||
sub: String,
|
||||
iss: Option<String>,
|
||||
aud: Option<String>,
|
||||
exp: Option<usize>,
|
||||
nbf: Option<usize>,
|
||||
}
|
||||
|
||||
let config = &self.services.config.jwt;
|
||||
if config.format.as_str() != "HMAC" {
|
||||
return Err!("This command only supports HMAC key format, not {}.", config.format);
|
||||
}
|
||||
|
||||
let key = EncodingKey::from_secret(config.key.as_ref());
|
||||
let alg = Algorithm::from_str(config.algorithm.as_str()).map_err(|e| {
|
||||
err!(Config("jwt.algorithm", "JWT algorithm is not recognized or configured {e}"))
|
||||
})?;
|
||||
|
||||
let header = Header { alg, ..Default::default() };
|
||||
let claim = Claim {
|
||||
sub: user,
|
||||
|
||||
iss: issuer,
|
||||
|
||||
aud: audience,
|
||||
|
||||
exp: exp_from_now
|
||||
.and_then(|val| now_secs().checked_add(val))
|
||||
.map(TryInto::try_into)
|
||||
.and_then(Result::ok),
|
||||
|
||||
nbf: nbf_from_now
|
||||
.and_then(|val| now_secs().checked_add(val))
|
||||
.map(TryInto::try_into)
|
||||
.and_then(Result::ok),
|
||||
};
|
||||
|
||||
encode(&header, &claim, &key)
|
||||
.map_err(|e| err!("Failed to encode JWT: {e}"))
|
||||
.map(async |token| self.write_str(&token).await)?
|
||||
.await
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn resync_database(&self) -> Result {
|
||||
if !self.services.db.is_secondary() {
|
||||
return Err!("Not a secondary instance.");
|
||||
}
|
||||
|
||||
self.services
|
||||
.db
|
||||
.engine
|
||||
.update()
|
||||
.map_err(|e| err!("Failed to update from primary: {e:?}"))
|
||||
}
|
||||
|
||||
+30
-14
@@ -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
|
||||
@@ -72,15 +72,6 @@ pub(super) enum DebugCommand {
|
||||
},
|
||||
|
||||
/// - Gets all the room state events for the specified room.
|
||||
///
|
||||
/// This is functionally equivalent to `GET
|
||||
/// /_matrix/client/v3/rooms/{roomid}/state`, except the admin command does
|
||||
/// *not* check if the sender user is allowed to see state events. This is
|
||||
/// done because it's implied that server admins here have database access
|
||||
/// and can see/get room info themselves anyways if they were malicious
|
||||
/// admins.
|
||||
///
|
||||
/// Of course the check is still done on the actual client API.
|
||||
GetRoomState {
|
||||
/// Room ID
|
||||
room_id: OwnedRoomOrAliasId,
|
||||
@@ -125,13 +116,13 @@ pub(super) enum DebugCommand {
|
||||
reset: bool,
|
||||
},
|
||||
|
||||
/// - Verify json signatures
|
||||
/// - Sign JSON blob
|
||||
///
|
||||
/// This command needs a JSON blob provided in a Markdown code block below
|
||||
/// the command.
|
||||
SignJson,
|
||||
|
||||
/// - Verify json signatures
|
||||
/// - Verify JSON signatures
|
||||
///
|
||||
/// This command needs a JSON blob provided in a Markdown code block below
|
||||
/// the command.
|
||||
@@ -234,6 +225,31 @@ pub(super) enum DebugCommand {
|
||||
level: Option<i32>,
|
||||
},
|
||||
|
||||
/// - Create a JWT token for login
|
||||
CreateJwt {
|
||||
/// Localpart of the user's MXID
|
||||
user: String,
|
||||
|
||||
/// Set expiration time in seconds from now.
|
||||
#[arg(long)]
|
||||
exp_from_now: Option<u64>,
|
||||
|
||||
/// Set not-before time in seconds from now.
|
||||
#[arg(long)]
|
||||
nbf_from_now: Option<u64>,
|
||||
|
||||
/// Claim an issuer.
|
||||
#[arg(long)]
|
||||
issuer: Option<String>,
|
||||
|
||||
/// Claim an audience.
|
||||
#[arg(long)]
|
||||
audience: Option<String>,
|
||||
},
|
||||
|
||||
/// - Synchronize database with primary (secondary only)
|
||||
ResyncDatabase,
|
||||
|
||||
/// - Developer test stubs
|
||||
#[command(subcommand)]
|
||||
#[allow(non_snake_case)]
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use std::fmt::Write;
|
||||
|
||||
use futures::StreamExt;
|
||||
use ruma::{OwnedRoomId, OwnedServerName, OwnedUserId};
|
||||
use tuwunel_core::{Err, Result};
|
||||
@@ -8,43 +6,19 @@
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn disable_room(&self, room_id: OwnedRoomId) -> Result {
|
||||
self.services
|
||||
.rooms
|
||||
.metadata
|
||||
.disable_room(&room_id, true);
|
||||
self.services.metadata.disable_room(&room_id);
|
||||
self.write_str("Room disabled.").await
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn enable_room(&self, room_id: OwnedRoomId) -> Result {
|
||||
self.services
|
||||
.rooms
|
||||
.metadata
|
||||
.disable_room(&room_id, false);
|
||||
self.services.metadata.enable_room(&room_id);
|
||||
self.write_str("Room enabled.").await
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn incoming_federation(&self) -> Result {
|
||||
let msg = {
|
||||
let map = self
|
||||
.services
|
||||
.rooms
|
||||
.event_handler
|
||||
.federation_handletime
|
||||
.read()
|
||||
.expect("locked");
|
||||
|
||||
let mut msg = format!("Handling {} incoming pdus:\n", map.len());
|
||||
for (r, (e, i)) in map.iter() {
|
||||
let elapsed = i.elapsed();
|
||||
writeln!(msg, "{} {}: {}m{}s", r, e, elapsed.as_secs() / 60, elapsed.as_secs() % 60)?;
|
||||
}
|
||||
|
||||
msg
|
||||
};
|
||||
|
||||
self.write_str(&msg).await
|
||||
Err!("This command is temporarily disabled")
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
@@ -102,7 +76,6 @@ pub(super) async fn remote_user_in_rooms(&self, user_id: OwnedUserId) -> Result
|
||||
|
||||
let mut rooms: Vec<(OwnedRoomId, u64, String)> = self
|
||||
.services
|
||||
.rooms
|
||||
.state_cache
|
||||
.rooms_joined(&user_id)
|
||||
.then(|room_id| get_room_info(self.services, room_id))
|
||||
|
||||
@@ -37,7 +37,6 @@ pub(super) async fn delete(
|
||||
// parsing the PDU for any MXC URLs begins here
|
||||
match self
|
||||
.services
|
||||
.rooms
|
||||
.timeline
|
||||
.get_pdu_json(&event_id)
|
||||
.await
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#![recursion_limit = "192"]
|
||||
#![allow(clippy::wildcard_imports)]
|
||||
#![allow(clippy::enum_glob_use)]
|
||||
#![allow(clippy::too_many_arguments)]
|
||||
|
||||
@@ -32,7 +32,7 @@ pub(super) async fn process(subcommand: GlobalsCommand, context: &Context<'_>) -
|
||||
},
|
||||
| GlobalsCommand::CurrentCount => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = services.globals.db.current_count();
|
||||
let results = services.globals.current_count();
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
write!(context, "Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```")
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -19,6 +19,9 @@ pub(crate) enum PresenceCommand {
|
||||
PresenceSince {
|
||||
/// UNIX timestamp since (u64)
|
||||
since: u64,
|
||||
|
||||
/// Upper-bound of since
|
||||
to: Option<u64>,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -34,11 +37,11 @@ pub(super) async fn process(subcommand: PresenceCommand, context: &Context<'_>)
|
||||
|
||||
write!(context, "Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```")
|
||||
},
|
||||
| PresenceCommand::PresenceSince { since } => {
|
||||
| PresenceCommand::PresenceSince { since, to } => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results: Vec<(_, _, _)> = services
|
||||
.presence
|
||||
.presence_since(since)
|
||||
.presence_since(since, to)
|
||||
.map(|(user_id, count, bytes)| (user_id.to_owned(), count, bytes.to_vec()))
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
+27
-16
@@ -1,5 +1,6 @@
|
||||
use std::{borrow::Cow, collections::BTreeMap, ops::Deref, sync::Arc};
|
||||
|
||||
use base64::prelude::*;
|
||||
use clap::Subcommand;
|
||||
use futures::{FutureExt, Stream, StreamExt, TryStreamExt};
|
||||
use tokio::time::Instant;
|
||||
@@ -15,25 +16,29 @@
|
||||
|
||||
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,
|
||||
|
||||
/// Key
|
||||
key: String,
|
||||
|
||||
/// Encode as base64
|
||||
#[arg(long, short)]
|
||||
base64: bool,
|
||||
},
|
||||
|
||||
/// - Raw database delete (for string keys)
|
||||
RawDel {
|
||||
Del {
|
||||
/// Map name
|
||||
map: String,
|
||||
|
||||
@@ -42,7 +47,7 @@ pub(crate) enum RawCommand {
|
||||
},
|
||||
|
||||
/// - Raw database keys iteration
|
||||
RawKeys {
|
||||
Keys {
|
||||
/// Map name
|
||||
map: String,
|
||||
|
||||
@@ -51,7 +56,7 @@ pub(crate) enum RawCommand {
|
||||
},
|
||||
|
||||
/// - Raw database key size breakdown
|
||||
RawKeysSizes {
|
||||
KeysSizes {
|
||||
/// Map name
|
||||
map: Option<String>,
|
||||
|
||||
@@ -60,7 +65,7 @@ pub(crate) enum RawCommand {
|
||||
},
|
||||
|
||||
/// - Raw database keys total bytes
|
||||
RawKeysTotal {
|
||||
KeysTotal {
|
||||
/// Map name
|
||||
map: Option<String>,
|
||||
|
||||
@@ -69,7 +74,7 @@ pub(crate) enum RawCommand {
|
||||
},
|
||||
|
||||
/// - Raw database values size breakdown
|
||||
RawValsSizes {
|
||||
ValsSizes {
|
||||
/// Map name
|
||||
map: Option<String>,
|
||||
|
||||
@@ -78,7 +83,7 @@ pub(crate) enum RawCommand {
|
||||
},
|
||||
|
||||
/// - Raw database values total bytes
|
||||
RawValsTotal {
|
||||
ValsTotal {
|
||||
/// Map name
|
||||
map: Option<String>,
|
||||
|
||||
@@ -87,7 +92,7 @@ pub(crate) enum RawCommand {
|
||||
},
|
||||
|
||||
/// - Raw database items iteration
|
||||
RawIter {
|
||||
Iter {
|
||||
/// Map name
|
||||
map: String,
|
||||
|
||||
@@ -96,7 +101,7 @@ pub(crate) enum RawCommand {
|
||||
},
|
||||
|
||||
/// - Raw database keys iteration
|
||||
RawKeysFrom {
|
||||
KeysFrom {
|
||||
/// Map name
|
||||
map: String,
|
||||
|
||||
@@ -109,7 +114,7 @@ pub(crate) enum RawCommand {
|
||||
},
|
||||
|
||||
/// - Raw database items iteration
|
||||
RawIterFrom {
|
||||
IterFrom {
|
||||
/// Map name
|
||||
map: String,
|
||||
|
||||
@@ -122,7 +127,7 @@ pub(crate) enum RawCommand {
|
||||
},
|
||||
|
||||
/// - Raw database record count
|
||||
RawCount {
|
||||
Count {
|
||||
/// Map name
|
||||
map: Option<String>,
|
||||
|
||||
@@ -160,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>,
|
||||
@@ -421,13 +426,19 @@ pub(super) async fn raw_del(&self, map: String, key: String) -> Result {
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn raw_get(&self, map: String, key: String) -> Result {
|
||||
pub(super) async fn raw_get(&self, map: String, key: String, base64: bool) -> Result {
|
||||
let map = self.services.db.get(&map)?;
|
||||
let timer = Instant::now();
|
||||
let handle = map.get(&key).await?;
|
||||
|
||||
let query_time = timer.elapsed();
|
||||
let result = String::from_utf8_lossy(&handle);
|
||||
|
||||
let result = if base64 {
|
||||
BASE64_STANDARD.encode(&handle)
|
||||
} else {
|
||||
String::from_utf8_lossy(&handle).to_string()
|
||||
};
|
||||
|
||||
self.write_str(&format!("Query completed in {query_time:?}:\n\n```rs\n{result:?}\n```"))
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -30,11 +30,7 @@ pub(super) async fn process(subcommand: RoomAliasCommand, context: &Context<'_>)
|
||||
match subcommand {
|
||||
| RoomAliasCommand::ResolveLocalAlias { alias } => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = services
|
||||
.rooms
|
||||
.alias
|
||||
.resolve_local_alias(&alias)
|
||||
.await;
|
||||
let results = services.alias.resolve_local_alias(&alias).await;
|
||||
let query_time = timer.elapsed();
|
||||
|
||||
write!(context, "Query completed in {query_time:?}:\n\n```rs\n{results:#?}\n```")
|
||||
@@ -42,7 +38,6 @@ pub(super) async fn process(subcommand: RoomAliasCommand, context: &Context<'_>)
|
||||
| RoomAliasCommand::LocalAliasesForRoom { room_id } => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let aliases: Vec<_> = services
|
||||
.rooms
|
||||
.alias
|
||||
.local_aliases_for_room(&room_id)
|
||||
.map(ToOwned::to_owned)
|
||||
@@ -55,7 +50,6 @@ pub(super) async fn process(subcommand: RoomAliasCommand, context: &Context<'_>)
|
||||
| RoomAliasCommand::AllLocalAliases => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let aliases = services
|
||||
.rooms
|
||||
.alias
|
||||
.all_local_aliases()
|
||||
.map(|(room_id, alias)| (room_id.to_owned(), alias.to_owned()))
|
||||
|
||||
@@ -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 {
|
||||
@@ -83,7 +87,6 @@ pub(super) async fn process(subcommand: RoomStateCacheCommand, context: &Context
|
||||
| RoomStateCacheCommand::ServerInRoom { server, room_id } => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let result = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.server_in_room(&server, &room_id)
|
||||
.await;
|
||||
@@ -98,7 +101,6 @@ pub(super) async fn process(subcommand: RoomStateCacheCommand, context: &Context
|
||||
| RoomStateCacheCommand::RoomServers { room_id } => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results: Vec<_> = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_servers(&room_id)
|
||||
.map(ToOwned::to_owned)
|
||||
@@ -115,7 +117,6 @@ pub(super) async fn process(subcommand: RoomStateCacheCommand, context: &Context
|
||||
| RoomStateCacheCommand::ServerRooms { server } => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results: Vec<_> = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.server_rooms(&server)
|
||||
.map(ToOwned::to_owned)
|
||||
@@ -132,7 +133,6 @@ pub(super) async fn process(subcommand: RoomStateCacheCommand, context: &Context
|
||||
| RoomStateCacheCommand::RoomMembers { room_id } => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results: Vec<_> = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_members(&room_id)
|
||||
.map(ToOwned::to_owned)
|
||||
@@ -149,7 +149,6 @@ pub(super) async fn process(subcommand: RoomStateCacheCommand, context: &Context
|
||||
| RoomStateCacheCommand::LocalUsersInRoom { room_id } => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results: Vec<_> = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.local_users_in_room(&room_id)
|
||||
.map(ToOwned::to_owned)
|
||||
@@ -166,7 +165,6 @@ pub(super) async fn process(subcommand: RoomStateCacheCommand, context: &Context
|
||||
| RoomStateCacheCommand::ActiveLocalUsersInRoom { room_id } => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results: Vec<_> = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.active_local_users_in_room(&room_id)
|
||||
.map(ToOwned::to_owned)
|
||||
@@ -183,7 +181,6 @@ pub(super) async fn process(subcommand: RoomStateCacheCommand, context: &Context
|
||||
| RoomStateCacheCommand::RoomJoinedCount { room_id } => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_joined_count(&room_id)
|
||||
.await;
|
||||
@@ -198,7 +195,6 @@ pub(super) async fn process(subcommand: RoomStateCacheCommand, context: &Context
|
||||
| RoomStateCacheCommand::RoomInvitedCount { room_id } => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_invited_count(&room_id)
|
||||
.await;
|
||||
@@ -213,7 +209,6 @@ pub(super) async fn process(subcommand: RoomStateCacheCommand, context: &Context
|
||||
| RoomStateCacheCommand::RoomUserOnceJoined { room_id } => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results: Vec<_> = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_useroncejoined(&room_id)
|
||||
.map(ToOwned::to_owned)
|
||||
@@ -230,7 +225,6 @@ pub(super) async fn process(subcommand: RoomStateCacheCommand, context: &Context
|
||||
| RoomStateCacheCommand::RoomMembersInvited { room_id } => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results: Vec<_> = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_members_invited(&room_id)
|
||||
.map(ToOwned::to_owned)
|
||||
@@ -247,7 +241,6 @@ pub(super) async fn process(subcommand: RoomStateCacheCommand, context: &Context
|
||||
| RoomStateCacheCommand::GetInviteCount { room_id, user_id } => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.get_invite_count(&room_id, &user_id)
|
||||
.await;
|
||||
@@ -262,7 +255,6 @@ pub(super) async fn process(subcommand: RoomStateCacheCommand, context: &Context
|
||||
| RoomStateCacheCommand::GetLeftCount { room_id, user_id } => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.get_left_count(&room_id, &user_id)
|
||||
.await;
|
||||
@@ -277,7 +269,6 @@ pub(super) async fn process(subcommand: RoomStateCacheCommand, context: &Context
|
||||
| RoomStateCacheCommand::RoomsJoined { user_id } => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results: Vec<_> = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.rooms_joined(&user_id)
|
||||
.map(ToOwned::to_owned)
|
||||
@@ -294,9 +285,8 @@ pub(super) async fn process(subcommand: RoomStateCacheCommand, context: &Context
|
||||
| RoomStateCacheCommand::RoomsInvited { user_id } => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results: Vec<_> = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.rooms_invited(&user_id)
|
||||
.rooms_invited_state(&user_id)
|
||||
.collect()
|
||||
.await;
|
||||
let query_time = timer.elapsed();
|
||||
@@ -310,9 +300,8 @@ pub(super) async fn process(subcommand: RoomStateCacheCommand, context: &Context
|
||||
| RoomStateCacheCommand::RoomsLeft { user_id } => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results: Vec<_> = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.rooms_left(&user_id)
|
||||
.rooms_left_state(&user_id)
|
||||
.collect()
|
||||
.await;
|
||||
let query_time = timer.elapsed();
|
||||
@@ -326,12 +315,27 @@ pub(super) async fn process(subcommand: RoomStateCacheCommand, context: &Context
|
||||
| RoomStateCacheCommand::InviteState { user_id, room_id } => {
|
||||
let timer = tokio::time::Instant::now();
|
||||
let results = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.invite_state(&user_id, &room_id)
|
||||
.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```"
|
||||
|
||||
@@ -27,16 +27,14 @@ pub(crate) enum RoomTimelineCommand {
|
||||
pub(super) async fn last(&self, room_id: OwnedRoomOrAliasId) -> Result {
|
||||
let room_id = self
|
||||
.services
|
||||
.rooms
|
||||
.alias
|
||||
.resolve(&room_id)
|
||||
.maybe_resolve(&room_id)
|
||||
.await?;
|
||||
|
||||
let result = self
|
||||
.services
|
||||
.rooms
|
||||
.timeline
|
||||
.last_timeline_count(None, &room_id)
|
||||
.last_timeline_count(None, &room_id, None)
|
||||
.await?;
|
||||
|
||||
self.write_str(&format!("{result:#?}")).await
|
||||
@@ -51,16 +49,14 @@ pub(super) async fn pdus(
|
||||
) -> Result {
|
||||
let room_id = self
|
||||
.services
|
||||
.rooms
|
||||
.alias
|
||||
.resolve(&room_id)
|
||||
.maybe_resolve(&room_id)
|
||||
.await?;
|
||||
|
||||
let from: Option<PduCount> = from.as_deref().map(str::parse).transpose()?;
|
||||
|
||||
let result: Vec<_> = self
|
||||
.services
|
||||
.rooms
|
||||
.timeline
|
||||
.pdus_rev(None, &room_id, from)
|
||||
.try_take(limit.unwrap_or(3))
|
||||
|
||||
@@ -21,7 +21,6 @@ pub(crate) enum ShortCommand {
|
||||
pub(super) async fn short_event_id(&self, event_id: OwnedEventId) -> Result {
|
||||
let shortid = self
|
||||
.services
|
||||
.rooms
|
||||
.short
|
||||
.get_shorteventid(&event_id)
|
||||
.await?;
|
||||
@@ -33,14 +32,12 @@ pub(super) async fn short_event_id(&self, event_id: OwnedEventId) -> Result {
|
||||
pub(super) async fn short_room_id(&self, room_id: OwnedRoomOrAliasId) -> Result {
|
||||
let room_id = self
|
||||
.services
|
||||
.rooms
|
||||
.alias
|
||||
.resolve(&room_id)
|
||||
.maybe_resolve(&room_id)
|
||||
.await?;
|
||||
|
||||
let shortid = self
|
||||
.services
|
||||
.rooms
|
||||
.short
|
||||
.get_shortroomid(&room_id)
|
||||
.await?;
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
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_connections();
|
||||
|
||||
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.find_connection(&key)?;
|
||||
|
||||
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(),
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -134,7 +134,6 @@ async fn get_shared_rooms(&self, user_a: OwnedUserId, user_b: OwnedUserId) -> Re
|
||||
let timer = tokio::time::Instant::now();
|
||||
let result: Vec<_> = self
|
||||
.services
|
||||
.rooms
|
||||
.state_cache
|
||||
.get_shared_rooms(&user_a, &user_b)
|
||||
.map(ToOwned::to_owned)
|
||||
|
||||
+8
-16
@@ -63,17 +63,15 @@ pub(super) async fn process(command: RoomAliasCommand, context: &Context<'_>) ->
|
||||
match (
|
||||
force,
|
||||
services
|
||||
.rooms
|
||||
.alias
|
||||
.resolve_local_alias(&room_alias)
|
||||
.await,
|
||||
) {
|
||||
| (true, Ok(id)) => {
|
||||
match services.rooms.alias.set_alias(
|
||||
&room_alias,
|
||||
&room_id,
|
||||
server_user,
|
||||
) {
|
||||
match services
|
||||
.alias
|
||||
.set_alias(&room_alias, &room_id, server_user)
|
||||
{
|
||||
| Err(err) => Err!("Failed to remove alias: {err}"),
|
||||
| Ok(()) =>
|
||||
context
|
||||
@@ -88,11 +86,10 @@ pub(super) async fn process(command: RoomAliasCommand, context: &Context<'_>) ->
|
||||
overwrite"
|
||||
),
|
||||
| (_, Err(_)) => {
|
||||
match services.rooms.alias.set_alias(
|
||||
&room_alias,
|
||||
&room_id,
|
||||
server_user,
|
||||
) {
|
||||
match services
|
||||
.alias
|
||||
.set_alias(&room_alias, &room_id, server_user)
|
||||
{
|
||||
| Err(err) => Err!("Failed to remove alias: {err}"),
|
||||
| Ok(()) => context.write_str("Successfully set alias").await,
|
||||
}
|
||||
@@ -101,14 +98,12 @@ pub(super) async fn process(command: RoomAliasCommand, context: &Context<'_>) ->
|
||||
},
|
||||
| RoomAliasCommand::Remove { .. } => {
|
||||
match services
|
||||
.rooms
|
||||
.alias
|
||||
.resolve_local_alias(&room_alias)
|
||||
.await
|
||||
{
|
||||
| Err(_) => Err!("Alias isn't in use."),
|
||||
| Ok(id) => match services
|
||||
.rooms
|
||||
.alias
|
||||
.remove_alias(&room_alias, server_user)
|
||||
.await
|
||||
@@ -123,7 +118,6 @@ pub(super) async fn process(command: RoomAliasCommand, context: &Context<'_>) ->
|
||||
},
|
||||
| RoomAliasCommand::Which { .. } => {
|
||||
match services
|
||||
.rooms
|
||||
.alias
|
||||
.resolve_local_alias(&room_alias)
|
||||
.await
|
||||
@@ -141,7 +135,6 @@ pub(super) async fn process(command: RoomAliasCommand, context: &Context<'_>) ->
|
||||
| RoomAliasCommand::List { room_id } =>
|
||||
if let Some(room_id) = room_id {
|
||||
let aliases: Vec<OwnedRoomAliasId> = services
|
||||
.rooms
|
||||
.alias
|
||||
.local_aliases_for_room(&room_id)
|
||||
.map(Into::into)
|
||||
@@ -160,7 +153,6 @@ pub(super) async fn process(command: RoomAliasCommand, context: &Context<'_>) ->
|
||||
context.write_str(&plain).await
|
||||
} else {
|
||||
let aliases = services
|
||||
.rooms
|
||||
.alias
|
||||
.all_local_aliases()
|
||||
.map(|(room_id, localpart)| (room_id.into(), localpart.into()))
|
||||
|
||||
+24
-23
@@ -16,27 +16,14 @@ pub(super) async fn list_rooms(
|
||||
let page = page.unwrap_or(1);
|
||||
let mut rooms = self
|
||||
.services
|
||||
.rooms
|
||||
.metadata
|
||||
.iter_ids()
|
||||
.filter_map(|room_id| async move {
|
||||
(!exclude_disabled
|
||||
|| !self
|
||||
.services
|
||||
.rooms
|
||||
.metadata
|
||||
.is_disabled(room_id)
|
||||
.await)
|
||||
.filter_map(async |room_id| {
|
||||
(!exclude_disabled || !self.services.metadata.is_disabled(room_id).await)
|
||||
.then_some(room_id)
|
||||
})
|
||||
.filter_map(|room_id| async move {
|
||||
(!exclude_banned
|
||||
|| !self
|
||||
.services
|
||||
.rooms
|
||||
.metadata
|
||||
.is_banned(room_id)
|
||||
.await)
|
||||
.filter_map(async |room_id| {
|
||||
(!exclude_banned || !self.services.metadata.is_banned(room_id).await)
|
||||
.then_some(room_id)
|
||||
})
|
||||
.then(|room_id| get_room_info(self.services, room_id))
|
||||
@@ -74,12 +61,26 @@ pub(super) async fn list_rooms(
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn exists(&self, room_id: OwnedRoomId) -> Result {
|
||||
let result = self
|
||||
.services
|
||||
.rooms
|
||||
.metadata
|
||||
.exists(&room_id)
|
||||
.await;
|
||||
let result = self.services.metadata.exists(&room_id).await;
|
||||
|
||||
self.write_str(&format!("{result}")).await
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn delete_room(&self, room_id: OwnedRoomId, force: bool) -> Result {
|
||||
if self.services.admin.is_admin_room(&room_id).await {
|
||||
return Err!("Cannot delete admin room");
|
||||
}
|
||||
|
||||
let state_lock = self.services.state.mutex.lock(&room_id).await;
|
||||
|
||||
self.services
|
||||
.delete
|
||||
.delete_room(&room_id, force, state_lock)
|
||||
.await?;
|
||||
|
||||
self.write_str("Successfully deleted the room from our database.")
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use clap::Subcommand;
|
||||
use futures::StreamExt;
|
||||
use ruma::OwnedRoomId;
|
||||
use tuwunel_core::{Err, Result};
|
||||
use tuwunel_core::Result;
|
||||
|
||||
use crate::{Context, PAGE_SIZE, get_room_info};
|
||||
|
||||
@@ -29,18 +29,17 @@ pub(super) async fn process(command: RoomDirectoryCommand, context: &Context<'_>
|
||||
let services = context.services;
|
||||
match command {
|
||||
| RoomDirectoryCommand::Publish { room_id } => {
|
||||
services.rooms.directory.set_public(&room_id);
|
||||
services.directory.set_public(&room_id);
|
||||
context.write_str("Room published").await
|
||||
},
|
||||
| RoomDirectoryCommand::Unpublish { room_id } => {
|
||||
services.rooms.directory.set_not_public(&room_id);
|
||||
services.directory.set_not_public(&room_id);
|
||||
context.write_str("Room unpublished").await
|
||||
},
|
||||
| RoomDirectoryCommand::List { page } => {
|
||||
// TODO: i know there's a way to do this with clap, but i can't seem to find it
|
||||
let page = page.unwrap_or(1);
|
||||
let mut rooms: Vec<_> = services
|
||||
.rooms
|
||||
.directory
|
||||
.public_rooms()
|
||||
.then(|room_id| get_room_info(services, room_id))
|
||||
@@ -57,7 +56,11 @@ pub(super) async fn process(command: RoomDirectoryCommand, context: &Context<'_>
|
||||
.collect();
|
||||
|
||||
if rooms.is_empty() {
|
||||
return Err!("No more rooms.");
|
||||
context
|
||||
.write_str("No rooms are published.")
|
||||
.await?;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let body = rooms
|
||||
|
||||
@@ -30,7 +30,6 @@ pub(crate) enum RoomInfoCommand {
|
||||
async fn list_joined_members(&self, room_id: OwnedRoomId, local_only: bool) -> Result {
|
||||
let room_name = self
|
||||
.services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.get_name(&room_id)
|
||||
.await
|
||||
@@ -38,7 +37,6 @@ async fn list_joined_members(&self, room_id: OwnedRoomId, local_only: bool) -> R
|
||||
|
||||
let member_info: Vec<_> = self
|
||||
.services
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_members(&room_id)
|
||||
.ready_filter(|user_id| {
|
||||
@@ -47,7 +45,7 @@ async fn list_joined_members(&self, room_id: OwnedRoomId, local_only: bool) -> R
|
||||
.unwrap_or(true)
|
||||
})
|
||||
.map(ToOwned::to_owned)
|
||||
.filter_map(|user_id| async move {
|
||||
.filter_map(async |user_id| {
|
||||
Some((
|
||||
self.services
|
||||
.users
|
||||
@@ -75,7 +73,6 @@ async fn list_joined_members(&self, room_id: OwnedRoomId, local_only: bool) -> R
|
||||
async fn view_room_topic(&self, room_id: OwnedRoomId) -> Result {
|
||||
let Ok(room_topic) = self
|
||||
.services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.get_room_topic(&room_id)
|
||||
.await
|
||||
|
||||
@@ -56,4 +56,12 @@ pub(super) enum RoomCommand {
|
||||
Exists {
|
||||
room_id: OwnedRoomId,
|
||||
},
|
||||
|
||||
/// - Delete room
|
||||
DeleteRoom {
|
||||
room_id: OwnedRoomId,
|
||||
|
||||
#[arg(short, long)]
|
||||
force: bool,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use clap::Subcommand;
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use ruma::{OwnedRoomId, OwnedRoomOrAliasId, RoomAliasId, RoomId, RoomOrAliasId};
|
||||
use tuwunel_api::client::leave_room;
|
||||
use tuwunel_core::{
|
||||
Err, Result, debug,
|
||||
utils::{IterStream, ReadyExt},
|
||||
@@ -49,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) {
|
||||
@@ -70,10 +69,7 @@ async fn ban_room(&self, room: OwnedRoomOrAliasId) -> Result {
|
||||
};
|
||||
|
||||
debug!("Room specified is a room ID, banning room ID");
|
||||
self.services
|
||||
.rooms
|
||||
.metadata
|
||||
.ban_room(room_id, true);
|
||||
self.services.metadata.ban_room(room_id);
|
||||
|
||||
room_id.to_owned()
|
||||
} else if room.is_room_alias_id() {
|
||||
@@ -95,7 +91,6 @@ async fn ban_room(&self, room: OwnedRoomOrAliasId) -> Result {
|
||||
|
||||
let room_id = match self
|
||||
.services
|
||||
.rooms
|
||||
.alias
|
||||
.resolve_local_alias(room_alias)
|
||||
.await
|
||||
@@ -109,9 +104,8 @@ async fn ban_room(&self, room: OwnedRoomOrAliasId) -> Result {
|
||||
|
||||
match self
|
||||
.services
|
||||
.rooms
|
||||
.alias
|
||||
.resolve_alias(room_alias, None)
|
||||
.resolve_alias(room_alias)
|
||||
.await
|
||||
{
|
||||
| Ok((room_id, servers)) => {
|
||||
@@ -131,10 +125,7 @@ async fn ban_room(&self, room: OwnedRoomOrAliasId) -> Result {
|
||||
},
|
||||
};
|
||||
|
||||
self.services
|
||||
.rooms
|
||||
.metadata
|
||||
.ban_room(&room_id, true);
|
||||
self.services.metadata.ban_room(&room_id);
|
||||
|
||||
room_id
|
||||
} else {
|
||||
@@ -148,7 +139,6 @@ async fn ban_room(&self, room: OwnedRoomOrAliasId) -> Result {
|
||||
debug!("Making all users leave the room {room_id} and forgetting it");
|
||||
let mut users = self
|
||||
.services
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_members(&room_id)
|
||||
.map(ToOwned::to_owned)
|
||||
@@ -161,27 +151,31 @@ async fn ban_room(&self, room: OwnedRoomOrAliasId) -> Result {
|
||||
evicting admins too)",
|
||||
);
|
||||
|
||||
if let Err(e) = leave_room(self.services, user_id, &room_id, None)
|
||||
let state_lock = self.services.state.mutex.lock(&room_id).await;
|
||||
|
||||
if let Err(e) = self
|
||||
.services
|
||||
.membership
|
||||
.leave(user_id, &room_id, None, false, &state_lock)
|
||||
.boxed()
|
||||
.await
|
||||
{
|
||||
warn!("Failed to leave room: {e}");
|
||||
}
|
||||
|
||||
drop(state_lock);
|
||||
|
||||
self.services
|
||||
.rooms
|
||||
.state_cache
|
||||
.forget(&room_id, user_id);
|
||||
}
|
||||
|
||||
self.services
|
||||
.rooms
|
||||
.alias
|
||||
.local_aliases_for_room(&room_id)
|
||||
.map(ToOwned::to_owned)
|
||||
.for_each(|local_alias| async move {
|
||||
.for_each(async |local_alias| {
|
||||
self.services
|
||||
.rooms
|
||||
.alias
|
||||
.remove_alias(&local_alias, &self.services.globals.server_user)
|
||||
.await
|
||||
@@ -190,15 +184,9 @@ async fn ban_room(&self, room: OwnedRoomOrAliasId) -> Result {
|
||||
.await;
|
||||
|
||||
// unpublish from room directory
|
||||
self.services
|
||||
.rooms
|
||||
.directory
|
||||
.set_not_public(&room_id);
|
||||
self.services.directory.set_not_public(&room_id);
|
||||
|
||||
self.services
|
||||
.rooms
|
||||
.metadata
|
||||
.disable_room(&room_id, true);
|
||||
self.services.metadata.disable_room(&room_id);
|
||||
|
||||
self.write_str(
|
||||
"Room banned, removed all our local users, and disabled incoming federation with room.",
|
||||
@@ -221,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();
|
||||
@@ -258,7 +246,6 @@ async fn ban_list_of_rooms(&self) -> Result {
|
||||
| Ok(room_alias) => {
|
||||
let room_id = match self
|
||||
.services
|
||||
.rooms
|
||||
.alias
|
||||
.resolve_local_alias(room_alias)
|
||||
.await
|
||||
@@ -272,9 +259,8 @@ async fn ban_list_of_rooms(&self) -> Result {
|
||||
|
||||
match self
|
||||
.services
|
||||
.rooms
|
||||
.alias
|
||||
.resolve_alias(room_alias, None)
|
||||
.resolve_alias(room_alias)
|
||||
.await
|
||||
{
|
||||
| Ok((room_id, servers)) => {
|
||||
@@ -320,10 +306,7 @@ async fn ban_list_of_rooms(&self) -> Result {
|
||||
}
|
||||
|
||||
for room_id in room_ids {
|
||||
self.services
|
||||
.rooms
|
||||
.metadata
|
||||
.ban_room(&room_id, true);
|
||||
self.services.metadata.ban_room(&room_id);
|
||||
|
||||
debug!("Banned {room_id} successfully");
|
||||
room_ban_count = room_ban_count.saturating_add(1);
|
||||
@@ -331,7 +314,6 @@ async fn ban_list_of_rooms(&self) -> Result {
|
||||
debug!("Making all users leave the room {room_id} and forgetting it");
|
||||
let mut users = self
|
||||
.services
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_members(&room_id)
|
||||
.map(ToOwned::to_owned)
|
||||
@@ -344,28 +326,32 @@ async fn ban_list_of_rooms(&self) -> Result {
|
||||
evicting admins too)",
|
||||
);
|
||||
|
||||
if let Err(e) = leave_room(self.services, user_id, &room_id, None)
|
||||
let state_lock = self.services.state.mutex.lock(&room_id).await;
|
||||
|
||||
if let Err(e) = self
|
||||
.services
|
||||
.membership
|
||||
.leave(user_id, &room_id, None, false, &state_lock)
|
||||
.boxed()
|
||||
.await
|
||||
{
|
||||
warn!("Failed to leave room: {e}");
|
||||
}
|
||||
|
||||
drop(state_lock);
|
||||
|
||||
self.services
|
||||
.rooms
|
||||
.state_cache
|
||||
.forget(&room_id, user_id);
|
||||
}
|
||||
|
||||
// remove any local aliases, ignore errors
|
||||
self.services
|
||||
.rooms
|
||||
.alias
|
||||
.local_aliases_for_room(&room_id)
|
||||
.map(ToOwned::to_owned)
|
||||
.for_each(|local_alias| async move {
|
||||
.for_each(async |local_alias| {
|
||||
self.services
|
||||
.rooms
|
||||
.alias
|
||||
.remove_alias(&local_alias, &self.services.globals.server_user)
|
||||
.await
|
||||
@@ -374,15 +360,9 @@ async fn ban_list_of_rooms(&self) -> Result {
|
||||
.await;
|
||||
|
||||
// unpublish from room directory, ignore errors
|
||||
self.services
|
||||
.rooms
|
||||
.directory
|
||||
.set_not_public(&room_id);
|
||||
self.services.directory.set_not_public(&room_id);
|
||||
|
||||
self.services
|
||||
.rooms
|
||||
.metadata
|
||||
.disable_room(&room_id, true);
|
||||
self.services.metadata.disable_room(&room_id);
|
||||
}
|
||||
|
||||
self.write_str(&format!(
|
||||
@@ -407,10 +387,7 @@ async fn unban_room(&self, room: OwnedRoomOrAliasId) -> Result {
|
||||
};
|
||||
|
||||
debug!("Room specified is a room ID, unbanning room ID");
|
||||
self.services
|
||||
.rooms
|
||||
.metadata
|
||||
.ban_room(room_id, false);
|
||||
self.services.metadata.unban_room(room_id);
|
||||
|
||||
room_id.to_owned()
|
||||
} else if room.is_room_alias_id() {
|
||||
@@ -432,7 +409,6 @@ async fn unban_room(&self, room: OwnedRoomOrAliasId) -> Result {
|
||||
|
||||
let room_id = match self
|
||||
.services
|
||||
.rooms
|
||||
.alias
|
||||
.resolve_local_alias(room_alias)
|
||||
.await
|
||||
@@ -446,9 +422,8 @@ async fn unban_room(&self, room: OwnedRoomOrAliasId) -> Result {
|
||||
|
||||
match self
|
||||
.services
|
||||
.rooms
|
||||
.alias
|
||||
.resolve_alias(room_alias, None)
|
||||
.resolve_alias(room_alias)
|
||||
.await
|
||||
{
|
||||
| Ok((room_id, servers)) => {
|
||||
@@ -466,10 +441,7 @@ async fn unban_room(&self, room: OwnedRoomOrAliasId) -> Result {
|
||||
},
|
||||
};
|
||||
|
||||
self.services
|
||||
.rooms
|
||||
.metadata
|
||||
.ban_room(&room_id, false);
|
||||
self.services.metadata.unban_room(&room_id);
|
||||
|
||||
room_id
|
||||
} else {
|
||||
@@ -480,10 +452,7 @@ async fn unban_room(&self, room: OwnedRoomOrAliasId) -> Result {
|
||||
);
|
||||
};
|
||||
|
||||
self.services
|
||||
.rooms
|
||||
.metadata
|
||||
.disable_room(&room_id, false);
|
||||
self.services.metadata.enable_room(&room_id);
|
||||
self.write_str("Room unbanned and federation re-enabled.")
|
||||
.await
|
||||
}
|
||||
@@ -492,7 +461,6 @@ async fn unban_room(&self, room: OwnedRoomOrAliasId) -> Result {
|
||||
async fn list_banned_rooms(&self, no_details: bool) -> Result {
|
||||
let room_ids: Vec<OwnedRoomId> = self
|
||||
.services
|
||||
.rooms
|
||||
.metadata
|
||||
.list_banned_rooms()
|
||||
.map(Into::into)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
+201
-141
@@ -2,30 +2,27 @@
|
||||
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use ruma::{
|
||||
OwnedEventId, OwnedRoomId, OwnedRoomOrAliasId, OwnedUserId, UserId,
|
||||
Int, OwnedEventId, OwnedRoomId, OwnedRoomOrAliasId, OwnedUserId, UserId,
|
||||
events::{
|
||||
RoomAccountDataEventType, StateEventType,
|
||||
room::{
|
||||
power_levels::{RoomPowerLevels, RoomPowerLevelsEventContent},
|
||||
power_levels::{RoomPowerLevels, RoomPowerLevelsEventContent, UserPowerLevel},
|
||||
redaction::RoomRedactionEventContent,
|
||||
},
|
||||
tag::{TagEvent, TagEventContent, TagInfo},
|
||||
},
|
||||
};
|
||||
use tuwunel_api::client::{
|
||||
full_user_deactivate, join_room_by_id_helper, leave_all_rooms, leave_room, update_avatar_url,
|
||||
update_displayname,
|
||||
};
|
||||
use tuwunel_core::{
|
||||
Err, Result, debug, debug_warn, error, info, is_equal_to,
|
||||
matrix::{Event, pdu::PduBuilder},
|
||||
utils::{self, ReadyExt},
|
||||
warn,
|
||||
};
|
||||
use tuwunel_service::Services;
|
||||
|
||||
use crate::{
|
||||
admin_command, get_room_info,
|
||||
utils::{parse_active_local_user_id, parse_local_user_id},
|
||||
utils::{parse_active_local_user_id, parse_local_user_id, parse_user_id},
|
||||
};
|
||||
|
||||
const AUTO_GEN_PASSWORD_LENGTH: usize = 25;
|
||||
@@ -122,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.rooms.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"
|
||||
@@ -132,7 +129,6 @@ pub(super) async fn create_user(&self, username: String, password: Option<String
|
||||
|
||||
if !self
|
||||
.services
|
||||
.rooms
|
||||
.state_cache
|
||||
.server_in_room(self.services.globals.server_name(), &room_id)
|
||||
.await
|
||||
@@ -143,20 +139,24 @@ pub(super) async fn create_user(&self, username: String, password: Option<String
|
||||
continue;
|
||||
}
|
||||
|
||||
let state_lock = self.services.state.mutex.lock(&room_id).await;
|
||||
|
||||
if let Some(room_server_name) = room.server_name() {
|
||||
match join_room_by_id_helper(
|
||||
self.services,
|
||||
&user_id,
|
||||
&room_id,
|
||||
Some("Automatically joining this room upon registration".to_owned()),
|
||||
&[
|
||||
self.services.globals.server_name().to_owned(),
|
||||
room_server_name.to_owned(),
|
||||
],
|
||||
None,
|
||||
&None,
|
||||
)
|
||||
.await
|
||||
match self
|
||||
.services
|
||||
.membership
|
||||
.join(
|
||||
&user_id,
|
||||
&room_id,
|
||||
Some("Automatically joining this room upon registration".to_owned()),
|
||||
&[
|
||||
self.services.globals.server_name().to_owned(),
|
||||
room_server_name.to_owned(),
|
||||
],
|
||||
&None,
|
||||
&state_lock,
|
||||
)
|
||||
.await
|
||||
{
|
||||
| Ok(_response) => {
|
||||
info!("Automatically joined room {room} for user {user_id}");
|
||||
@@ -175,6 +175,8 @@ pub(super) async fn create_user(&self, username: String, password: Option<String
|
||||
.await;
|
||||
},
|
||||
}
|
||||
|
||||
drop(state_lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -186,7 +188,6 @@ pub(super) async fn create_user(&self, username: String, password: Option<String
|
||||
if let Ok(admin_room) = self.services.admin.get_admin_room().await {
|
||||
if self
|
||||
.services
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_joined_count(&admin_room)
|
||||
.await
|
||||
@@ -195,6 +196,7 @@ pub(super) async fn create_user(&self, username: String, password: Option<String
|
||||
self.services
|
||||
.admin
|
||||
.make_user_admin(&user_id)
|
||||
.boxed()
|
||||
.await?;
|
||||
warn!("Granting {user_id} admin privileges as the first user");
|
||||
}
|
||||
@@ -216,31 +218,7 @@ pub(super) async fn deactivate(&self, no_leave_rooms: bool, user_id: String) ->
|
||||
return Err!("Not allowed to deactivate the server service account.",);
|
||||
}
|
||||
|
||||
self.services
|
||||
.users
|
||||
.deactivate_account(&user_id)
|
||||
.await?;
|
||||
|
||||
if !no_leave_rooms {
|
||||
self.services
|
||||
.admin
|
||||
.send_text(&format!("Making {user_id} leave all rooms after deactivation..."))
|
||||
.await;
|
||||
|
||||
let all_joined_rooms: Vec<OwnedRoomId> = self
|
||||
.services
|
||||
.rooms
|
||||
.state_cache
|
||||
.rooms_joined(&user_id)
|
||||
.map(Into::into)
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
full_user_deactivate(self.services, &user_id, &all_joined_rooms).await?;
|
||||
update_displayname(self.services, &user_id, None, &all_joined_rooms).await;
|
||||
update_avatar_url(self.services, &user_id, None, None, &all_joined_rooms).await;
|
||||
leave_all_rooms(self.services, &user_id).await;
|
||||
}
|
||||
deactivate_user(self.services, &user_id, no_leave_rooms).await?;
|
||||
|
||||
self.write_str(&format!("User {user_id} has been deactivated"))
|
||||
.await
|
||||
@@ -333,38 +311,16 @@ pub(super) async fn deactivate_all(&self, no_leave_rooms: bool, force: bool) ->
|
||||
let mut deactivation_count: usize = 0;
|
||||
|
||||
for user_id in user_ids {
|
||||
match self
|
||||
.services
|
||||
.users
|
||||
.deactivate_account(&user_id)
|
||||
.await
|
||||
{
|
||||
match deactivate_user(self.services, &user_id, no_leave_rooms).await {
|
||||
| Ok(()) => {
|
||||
deactivation_count = deactivation_count.saturating_add(1);
|
||||
},
|
||||
| Err(e) => {
|
||||
self.services
|
||||
.admin
|
||||
.send_text(&format!("Failed deactivating user: {e}"))
|
||||
.await;
|
||||
},
|
||||
| Ok(()) => {
|
||||
deactivation_count = deactivation_count.saturating_add(1);
|
||||
if !no_leave_rooms {
|
||||
info!("Forcing user {user_id} to leave all rooms apart of deactivate-all");
|
||||
let all_joined_rooms: Vec<OwnedRoomId> = self
|
||||
.services
|
||||
.rooms
|
||||
.state_cache
|
||||
.rooms_joined(&user_id)
|
||||
.map(Into::into)
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
full_user_deactivate(self.services, &user_id, &all_joined_rooms).await?;
|
||||
update_displayname(self.services, &user_id, None, &all_joined_rooms).await;
|
||||
update_avatar_url(self.services, &user_id, None, None, &all_joined_rooms)
|
||||
.await;
|
||||
leave_all_rooms(self.services, &user_id).await;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -381,6 +337,20 @@ pub(super) async fn deactivate_all(&self, no_leave_rooms: bool, force: bool) ->
|
||||
.await
|
||||
}
|
||||
|
||||
async fn deactivate_user(services: &Services, user_id: &UserId, no_leave_rooms: bool) -> Result {
|
||||
if !no_leave_rooms {
|
||||
services
|
||||
.deactivate
|
||||
.full_deactivate(user_id)
|
||||
.boxed()
|
||||
.await?;
|
||||
} else {
|
||||
services.users.deactivate_account(user_id).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn list_joined_rooms(&self, user_id: String) -> Result {
|
||||
// Validate user id
|
||||
@@ -388,7 +358,6 @@ pub(super) async fn list_joined_rooms(&self, user_id: String) -> Result {
|
||||
|
||||
let mut rooms: Vec<(OwnedRoomId, u64, String)> = self
|
||||
.services
|
||||
.rooms
|
||||
.state_cache
|
||||
.rooms_joined(&user_id)
|
||||
.then(|room_id| get_room_info(self.services, room_id))
|
||||
@@ -438,14 +407,12 @@ pub(super) async fn force_join_list_of_local_users(
|
||||
|
||||
let (room_id, servers) = self
|
||||
.services
|
||||
.rooms
|
||||
.alias
|
||||
.resolve_with_servers(&room_id, None)
|
||||
.maybe_resolve_with_servers(&room_id, None)
|
||||
.await?;
|
||||
|
||||
if !self
|
||||
.services
|
||||
.rooms
|
||||
.state_cache
|
||||
.server_in_room(self.services.globals.server_name(), &room_id)
|
||||
.await
|
||||
@@ -455,7 +422,6 @@ pub(super) async fn force_join_list_of_local_users(
|
||||
|
||||
let server_admins: Vec<_> = self
|
||||
.services
|
||||
.rooms
|
||||
.state_cache
|
||||
.active_local_users_in_room(&admin_room)
|
||||
.map(ToOwned::to_owned)
|
||||
@@ -464,7 +430,6 @@ pub(super) async fn force_join_list_of_local_users(
|
||||
|
||||
if !self
|
||||
.services
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_members(&room_id)
|
||||
.ready_any(|user_id| server_admins.contains(&user_id.to_owned()))
|
||||
@@ -512,17 +477,21 @@ pub(super) async fn force_join_list_of_local_users(
|
||||
let mut failed_joins: usize = 0;
|
||||
let mut successful_joins: usize = 0;
|
||||
|
||||
let state_lock = self.services.state.mutex.lock(&room_id).await;
|
||||
|
||||
for user_id in user_ids {
|
||||
match join_room_by_id_helper(
|
||||
self.services,
|
||||
&user_id,
|
||||
&room_id,
|
||||
Some(String::from(BULK_JOIN_REASON)),
|
||||
&servers,
|
||||
None,
|
||||
&None,
|
||||
)
|
||||
.await
|
||||
match self
|
||||
.services
|
||||
.membership
|
||||
.join(
|
||||
&user_id,
|
||||
&room_id,
|
||||
Some(String::from(BULK_JOIN_REASON)),
|
||||
&servers,
|
||||
&None,
|
||||
&state_lock,
|
||||
)
|
||||
.await
|
||||
{
|
||||
| Ok(_res) => {
|
||||
successful_joins = successful_joins.saturating_add(1);
|
||||
@@ -534,6 +503,8 @@ pub(super) async fn force_join_list_of_local_users(
|
||||
}
|
||||
}
|
||||
|
||||
drop(state_lock);
|
||||
|
||||
self.write_str(&format!(
|
||||
"{successful_joins} local users have been joined to {room_id}. {failed_joins} joins \
|
||||
failed.",
|
||||
@@ -560,14 +531,12 @@ pub(super) async fn force_join_all_local_users(
|
||||
|
||||
let (room_id, servers) = self
|
||||
.services
|
||||
.rooms
|
||||
.alias
|
||||
.resolve_with_servers(&room_id, None)
|
||||
.maybe_resolve_with_servers(&room_id, None)
|
||||
.await?;
|
||||
|
||||
if !self
|
||||
.services
|
||||
.rooms
|
||||
.state_cache
|
||||
.server_in_room(self.services.globals.server_name(), &room_id)
|
||||
.await
|
||||
@@ -577,7 +546,6 @@ pub(super) async fn force_join_all_local_users(
|
||||
|
||||
let server_admins: Vec<_> = self
|
||||
.services
|
||||
.rooms
|
||||
.state_cache
|
||||
.active_local_users_in_room(&admin_room)
|
||||
.map(ToOwned::to_owned)
|
||||
@@ -586,7 +554,6 @@ pub(super) async fn force_join_all_local_users(
|
||||
|
||||
if !self
|
||||
.services
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_members(&room_id)
|
||||
.ready_any(|user_id| server_admins.contains(&user_id.to_owned()))
|
||||
@@ -598,6 +565,8 @@ pub(super) async fn force_join_all_local_users(
|
||||
let mut failed_joins: usize = 0;
|
||||
let mut successful_joins: usize = 0;
|
||||
|
||||
let state_lock = self.services.state.mutex.lock(&room_id).await;
|
||||
|
||||
for user_id in &self
|
||||
.services
|
||||
.users
|
||||
@@ -606,16 +575,18 @@ pub(super) async fn force_join_all_local_users(
|
||||
.collect::<Vec<_>>()
|
||||
.await
|
||||
{
|
||||
match join_room_by_id_helper(
|
||||
self.services,
|
||||
user_id,
|
||||
&room_id,
|
||||
Some(String::from(BULK_JOIN_REASON)),
|
||||
&servers,
|
||||
None,
|
||||
&None,
|
||||
)
|
||||
.await
|
||||
match self
|
||||
.services
|
||||
.membership
|
||||
.join(
|
||||
user_id,
|
||||
&room_id,
|
||||
Some(String::from(BULK_JOIN_REASON)),
|
||||
&servers,
|
||||
&None,
|
||||
&state_lock,
|
||||
)
|
||||
.await
|
||||
{
|
||||
| Ok(_res) => {
|
||||
successful_joins = successful_joins.saturating_add(1);
|
||||
@@ -627,6 +598,8 @@ pub(super) async fn force_join_all_local_users(
|
||||
}
|
||||
}
|
||||
|
||||
drop(state_lock);
|
||||
|
||||
self.write_str(&format!(
|
||||
"{successful_joins} local users have been joined to {room_id}. {failed_joins} joins \
|
||||
failed.",
|
||||
@@ -643,18 +616,24 @@ pub(super) async fn force_join_room(
|
||||
let user_id = parse_local_user_id(self.services, &user_id)?;
|
||||
let (room_id, servers) = self
|
||||
.services
|
||||
.rooms
|
||||
.alias
|
||||
.resolve_with_servers(&room_id, None)
|
||||
.maybe_resolve_with_servers(&room_id, None)
|
||||
.await?;
|
||||
|
||||
assert!(
|
||||
self.services.globals.user_is_local(&user_id),
|
||||
"Parsed user_id must be a local user"
|
||||
);
|
||||
join_room_by_id_helper(self.services, &user_id, &room_id, None, &servers, None, &None)
|
||||
|
||||
let state_lock = self.services.state.mutex.lock(&room_id).await;
|
||||
|
||||
self.services
|
||||
.membership
|
||||
.join(&user_id, &room_id, None, &servers, &None, &state_lock)
|
||||
.await?;
|
||||
|
||||
drop(state_lock);
|
||||
|
||||
self.write_str(&format!("{user_id} has been joined to {room_id}.",))
|
||||
.await
|
||||
}
|
||||
@@ -668,9 +647,8 @@ pub(super) async fn force_leave_room(
|
||||
let user_id = parse_local_user_id(self.services, &user_id)?;
|
||||
let room_id = self
|
||||
.services
|
||||
.rooms
|
||||
.alias
|
||||
.resolve(&room_id)
|
||||
.maybe_resolve(&room_id)
|
||||
.await?;
|
||||
|
||||
assert!(
|
||||
@@ -680,7 +658,6 @@ pub(super) async fn force_leave_room(
|
||||
|
||||
if !self
|
||||
.services
|
||||
.rooms
|
||||
.state_cache
|
||||
.is_joined(&user_id, &room_id)
|
||||
.await
|
||||
@@ -688,10 +665,16 @@ pub(super) async fn force_leave_room(
|
||||
return Err!("{user_id} is not joined in the room");
|
||||
}
|
||||
|
||||
leave_room(self.services, &user_id, &room_id, None)
|
||||
let state_lock = self.services.state.mutex.lock(&room_id).await;
|
||||
|
||||
self.services
|
||||
.membership
|
||||
.leave(&user_id, &room_id, None, false, &state_lock)
|
||||
.boxed()
|
||||
.await?;
|
||||
|
||||
drop(state_lock);
|
||||
|
||||
self.write_str(&format!("{user_id} has left {room_id}.",))
|
||||
.await
|
||||
}
|
||||
@@ -701,9 +684,8 @@ pub(super) async fn force_demote(&self, user_id: String, room_id: OwnedRoomOrAli
|
||||
let user_id = parse_local_user_id(self.services, &user_id)?;
|
||||
let room_id = self
|
||||
.services
|
||||
.rooms
|
||||
.alias
|
||||
.resolve(&room_id)
|
||||
.maybe_resolve(&room_id)
|
||||
.await?;
|
||||
|
||||
assert!(
|
||||
@@ -711,45 +693,42 @@ pub(super) async fn force_demote(&self, user_id: String, room_id: OwnedRoomOrAli
|
||||
"Parsed user_id must be a local user"
|
||||
);
|
||||
|
||||
let state_lock = self
|
||||
.services
|
||||
.rooms
|
||||
.state
|
||||
.mutex
|
||||
.lock(&room_id)
|
||||
.await;
|
||||
let state_lock = self.services.state.mutex.lock(&room_id).await;
|
||||
|
||||
let room_power_levels: Option<RoomPowerLevelsEventContent> = self
|
||||
let room_power_levels: Option<RoomPowerLevels> = self
|
||||
.services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get_content(&room_id, &StateEventType::RoomPowerLevels, "")
|
||||
.get_power_levels(&room_id)
|
||||
.await
|
||||
.ok();
|
||||
|
||||
let user_can_demote_self = room_power_levels
|
||||
let user_can_change_self = room_power_levels
|
||||
.as_ref()
|
||||
.is_some_and(|power_levels_content| {
|
||||
RoomPowerLevels::from(power_levels_content.clone())
|
||||
.user_can_change_user_power_level(&user_id, &user_id)
|
||||
}) || self
|
||||
.services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get(&room_id, &StateEventType::RoomCreate, "")
|
||||
.await
|
||||
.is_ok_and(|event| event.sender() == user_id);
|
||||
.is_some_and(|power_levels| {
|
||||
power_levels.user_can_change_user_power_level(&user_id, &user_id)
|
||||
});
|
||||
|
||||
let user_can_demote_self = user_can_change_self
|
||||
|| self
|
||||
.services
|
||||
.state_accessor
|
||||
.room_state_get(&room_id, &StateEventType::RoomCreate, "")
|
||||
.await
|
||||
.is_ok_and(|event| event.sender() == user_id);
|
||||
|
||||
if !user_can_demote_self {
|
||||
return Err!("User is not allowed to modify their own power levels in the room.",);
|
||||
return Err!("User is not allowed to modify their own power levels in the room.");
|
||||
}
|
||||
|
||||
let mut power_levels_content = room_power_levels.unwrap_or_default();
|
||||
let mut power_levels_content: RoomPowerLevelsEventContent = room_power_levels
|
||||
.map(TryInto::try_into)
|
||||
.transpose()?
|
||||
.unwrap_or_default();
|
||||
|
||||
power_levels_content.users.remove(&user_id);
|
||||
|
||||
let event_id = self
|
||||
.services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(String::new(), &power_levels_content),
|
||||
@@ -766,6 +745,89 @@ pub(super) async fn force_demote(&self, user_id: String, room_id: OwnedRoomOrAli
|
||||
.await
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn force_promote(
|
||||
&self,
|
||||
target_id: String,
|
||||
room_id: OwnedRoomOrAliasId,
|
||||
) -> Result {
|
||||
let target_id = parse_user_id(self.services, &target_id)?;
|
||||
let room_id = self
|
||||
.services
|
||||
.alias
|
||||
.maybe_resolve(&room_id)
|
||||
.await?;
|
||||
|
||||
let state_lock = self.services.state.mutex.lock(&room_id).await;
|
||||
|
||||
let room_power_levels = self
|
||||
.services
|
||||
.state_accessor
|
||||
.get_power_levels(&room_id)
|
||||
.await?;
|
||||
|
||||
let privileged_member = self
|
||||
.services
|
||||
.state_cache
|
||||
.room_members(&room_id)
|
||||
.ready_filter(|member_id| {
|
||||
self.services.globals.user_is_local(member_id)
|
||||
&& room_power_levels.user_can_change_user_power_level(member_id, &target_id)
|
||||
})
|
||||
.map(ToOwned::to_owned)
|
||||
.ready_fold_default(|selected_user, member_id| match selected_user {
|
||||
| None => Some(member_id),
|
||||
| Some(selected_user) => Some(
|
||||
if room_power_levels.for_user(&selected_user)
|
||||
> room_power_levels.for_user(&member_id)
|
||||
{
|
||||
selected_user
|
||||
} else {
|
||||
member_id
|
||||
},
|
||||
),
|
||||
})
|
||||
.await;
|
||||
|
||||
let Some(privileged_member) = privileged_member else {
|
||||
return Err!("No privileged user exists in room, cannot promote.");
|
||||
};
|
||||
|
||||
info!("Selected privileged member {privileged_member}");
|
||||
|
||||
let power_level: Int = match room_power_levels.for_user(&privileged_member) {
|
||||
| UserPowerLevel::Infinite => Int::MAX,
|
||||
| UserPowerLevel::Int(x) => x,
|
||||
};
|
||||
|
||||
let mut power_levels_content: RoomPowerLevelsEventContent = room_power_levels.try_into()?;
|
||||
|
||||
power_levels_content
|
||||
.users
|
||||
.insert(target_id.clone(), power_level);
|
||||
|
||||
let event_id = self
|
||||
.services
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(String::new(), &power_levels_content),
|
||||
&privileged_member,
|
||||
&room_id,
|
||||
&state_lock,
|
||||
)
|
||||
.await?;
|
||||
|
||||
drop(state_lock);
|
||||
|
||||
self.write_str(&format!(
|
||||
"User {privileged_member} promoted {target_id} to {power_level} power level in \
|
||||
{room_id} - {event_id}"
|
||||
))
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn make_user_admin(&self, user_id: String) -> Result {
|
||||
let user_id = parse_local_user_id(self.services, &user_id)?;
|
||||
@@ -777,6 +839,7 @@ pub(super) async fn make_user_admin(&self, user_id: String) -> Result {
|
||||
self.services
|
||||
.admin
|
||||
.make_user_admin(&user_id)
|
||||
.boxed()
|
||||
.await?;
|
||||
|
||||
self.write_str(&format!("{user_id} has been granted admin privileges.",))
|
||||
@@ -883,7 +946,6 @@ pub(super) async fn get_room_tags(&self, user_id: String, room_id: OwnedRoomId)
|
||||
pub(super) async fn redact_event(&self, event_id: OwnedEventId) -> Result {
|
||||
let Ok(event) = self
|
||||
.services
|
||||
.rooms
|
||||
.timeline
|
||||
.get_non_outlier_pdu(&event_id)
|
||||
.await
|
||||
@@ -911,14 +973,12 @@ pub(super) async fn redact_event(&self, event_id: OwnedEventId) -> Result {
|
||||
let redaction_event_id = {
|
||||
let state_lock = self
|
||||
.services
|
||||
.rooms
|
||||
.state
|
||||
.mutex
|
||||
.lock(event.room_id())
|
||||
.await;
|
||||
|
||||
self.services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder {
|
||||
|
||||
@@ -88,6 +88,12 @@ pub(super) enum UserCommand {
|
||||
room_id: OwnedRoomOrAliasId,
|
||||
},
|
||||
|
||||
/// - Force promote
|
||||
ForcePromote {
|
||||
user_id: String,
|
||||
room_id: OwnedRoomOrAliasId,
|
||||
},
|
||||
|
||||
/// - Grant server-admin privileges to a user.
|
||||
MakeUserAdmin {
|
||||
user_id: String,
|
||||
|
||||
@@ -17,13 +17,11 @@ pub(crate) async fn get_room_info(
|
||||
(
|
||||
room_id.into(),
|
||||
services
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_joined_count(room_id)
|
||||
.await
|
||||
.unwrap_or(0),
|
||||
services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.get_name(room_id)
|
||||
.await
|
||||
|
||||
+10
-2
@@ -11,6 +11,7 @@ version.workspace = true
|
||||
|
||||
[lib]
|
||||
path = "mod.rs"
|
||||
bench = false
|
||||
crate-type = [
|
||||
"rlib",
|
||||
# "dylib",
|
||||
@@ -22,6 +23,9 @@ brotli_compression = [
|
||||
"tuwunel-service/brotli_compression",
|
||||
"reqwest/brotli",
|
||||
]
|
||||
bzip2_compression = [
|
||||
"tuwunel-service/bzip2_compression",
|
||||
]
|
||||
element_hacks = [
|
||||
"tuwunel-service/element_hacks",
|
||||
]
|
||||
@@ -52,6 +56,9 @@ jemalloc_stats = [
|
||||
ldap = [
|
||||
"tuwunel-service/ldap",
|
||||
]
|
||||
lz4_compression = [
|
||||
"tuwunel-service/lz4_compression",
|
||||
]
|
||||
release_max_log_level = [
|
||||
"tuwunel-core/release_max_log_level",
|
||||
"tuwunel-service/release_max_log_level",
|
||||
@@ -73,9 +80,8 @@ axum-extra.workspace = true
|
||||
axum.workspace = true
|
||||
base64.workspace = true
|
||||
bytes.workspace = true
|
||||
tuwunel-core.workspace = true
|
||||
tuwunel-service.workspace = true
|
||||
const-str.workspace = true
|
||||
ctor.workspace = true
|
||||
futures.workspace = true
|
||||
hmac.workspace = true
|
||||
http.workspace = true
|
||||
@@ -93,6 +99,8 @@ serde.workspace = true
|
||||
sha1.workspace = true
|
||||
tokio.workspace = true
|
||||
tracing.workspace = true
|
||||
tuwunel-core.workspace = true
|
||||
tuwunel-service.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
+11
-820
@@ -1,599 +1,13 @@
|
||||
use std::fmt::Write;
|
||||
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::InsecureClientIp;
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use register::RegistrationKind;
|
||||
use ruma::{
|
||||
OwnedRoomId, UserId,
|
||||
api::client::{
|
||||
account::{
|
||||
ThirdPartyIdRemovalStatus, change_password, check_registration_token_validity,
|
||||
deactivate, get_3pids, get_username_availability,
|
||||
register::{self, LoginType},
|
||||
request_3pid_management_token_via_email, request_3pid_management_token_via_msisdn,
|
||||
whoami,
|
||||
},
|
||||
uiaa::{AuthFlow, AuthType, UiaaInfo},
|
||||
},
|
||||
events::{
|
||||
GlobalAccountDataEventType, StateEventType,
|
||||
room::power_levels::{RoomPowerLevels, RoomPowerLevelsEventContent},
|
||||
},
|
||||
push,
|
||||
use ruma::api::client::account::{
|
||||
ThirdPartyIdRemovalStatus, change_password, deactivate, get_3pids,
|
||||
request_3pid_management_token_via_email, request_3pid_management_token_via_msisdn, whoami,
|
||||
};
|
||||
use tuwunel_core::{
|
||||
Err, Error, Result, debug_info, err, error, info, is_equal_to,
|
||||
matrix::{Event, pdu::PduBuilder},
|
||||
utils,
|
||||
utils::{ReadyExt, stream::BroadbandExt},
|
||||
warn,
|
||||
};
|
||||
use tuwunel_service::Services;
|
||||
use tuwunel_core::{Err, Result, info, utils::ReadyExt};
|
||||
|
||||
use super::{DEVICE_ID_LENGTH, SESSION_ID_LENGTH, TOKEN_LENGTH, join_room_by_id_helper};
|
||||
use crate::Ruma;
|
||||
|
||||
const RANDOM_USER_ID_LENGTH: usize = 10;
|
||||
|
||||
/// # `GET /_matrix/client/v3/register/available`
|
||||
///
|
||||
/// Checks if a username is valid and available on this server.
|
||||
///
|
||||
/// Conditions for returning true:
|
||||
/// - The user id is not historical
|
||||
/// - The server name of the user id matches this server
|
||||
/// - No user or appservice on this server already claimed this username
|
||||
///
|
||||
/// Note: This will not reserve the username, so the username might become
|
||||
/// invalid when trying to register
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "register_available")]
|
||||
pub(crate) async fn get_register_available_route(
|
||||
State(services): State<crate::State>,
|
||||
InsecureClientIp(client): InsecureClientIp,
|
||||
body: Ruma<get_username_availability::v3::Request>,
|
||||
) -> Result<get_username_availability::v3::Response> {
|
||||
// workaround for https://github.com/matrix-org/matrix-appservice-irc/issues/1780 due to inactivity of fixing the issue
|
||||
let is_matrix_appservice_irc = body
|
||||
.appservice_info
|
||||
.as_ref()
|
||||
.is_some_and(|appservice| {
|
||||
appservice.registration.id == "irc"
|
||||
|| appservice
|
||||
.registration
|
||||
.id
|
||||
.contains("matrix-appservice-irc")
|
||||
|| appservice
|
||||
.registration
|
||||
.id
|
||||
.contains("matrix_appservice_irc")
|
||||
});
|
||||
|
||||
if services
|
||||
.globals
|
||||
.forbidden_usernames()
|
||||
.is_match(&body.username)
|
||||
{
|
||||
return Err!(Request(Forbidden("Username is forbidden")));
|
||||
}
|
||||
|
||||
// don't force the username lowercase if it's from matrix-appservice-irc
|
||||
let body_username = if is_matrix_appservice_irc {
|
||||
body.username.clone()
|
||||
} else {
|
||||
body.username.to_lowercase()
|
||||
};
|
||||
|
||||
// Validate user id
|
||||
let user_id =
|
||||
match UserId::parse_with_server_name(&body_username, services.globals.server_name()) {
|
||||
| Ok(user_id) => {
|
||||
if let Err(e) = user_id.validate_strict() {
|
||||
// unless the username is from the broken matrix appservice IRC bridge, we
|
||||
// should follow synapse's behaviour on not allowing things like spaces
|
||||
// and UTF-8 characters in usernames
|
||||
if !is_matrix_appservice_irc {
|
||||
return Err!(Request(InvalidUsername(debug_warn!(
|
||||
"Username {body_username} contains disallowed characters or spaces: \
|
||||
{e}"
|
||||
))));
|
||||
}
|
||||
}
|
||||
|
||||
user_id
|
||||
},
|
||||
| Err(e) => {
|
||||
return Err!(Request(InvalidUsername(debug_warn!(
|
||||
"Username {body_username} is not valid: {e}"
|
||||
))));
|
||||
},
|
||||
};
|
||||
|
||||
// Check if username is creative enough
|
||||
if services.users.exists(&user_id).await {
|
||||
return Err!(Request(UserInUse("User ID is not available.")));
|
||||
}
|
||||
|
||||
if let Some(ref info) = body.appservice_info {
|
||||
if !info.is_user_match(&user_id) {
|
||||
return Err!(Request(Exclusive("Username is not in an appservice namespace.")));
|
||||
}
|
||||
}
|
||||
|
||||
if services
|
||||
.appservice
|
||||
.is_exclusive_user_id(&user_id)
|
||||
.await
|
||||
{
|
||||
return Err!(Request(Exclusive("Username is reserved by an appservice.")));
|
||||
}
|
||||
|
||||
Ok(get_username_availability::v3::Response { available: true })
|
||||
}
|
||||
|
||||
/// # `POST /_matrix/client/v3/register`
|
||||
///
|
||||
/// Register an account on this homeserver.
|
||||
///
|
||||
/// You can use [`GET
|
||||
/// /_matrix/client/v3/register/available`](fn.get_register_available_route.
|
||||
/// html) to check if the user id is valid and available.
|
||||
///
|
||||
/// - Only works if registration is enabled
|
||||
/// - If type is guest: ignores all parameters except
|
||||
/// initial_device_display_name
|
||||
/// - If sender is not appservice: Requires UIAA (but we only use a dummy stage)
|
||||
/// - If type is not guest and no username is given: Always fails after UIAA
|
||||
/// check
|
||||
/// - Creates a new account and populates it with default account data
|
||||
/// - If `inhibit_login` is false: Creates a device and returns device id and
|
||||
/// access_token
|
||||
#[allow(clippy::doc_markdown)]
|
||||
#[tracing::instrument(skip_all, fields(%client), name = "register")]
|
||||
pub(crate) async fn register_route(
|
||||
State(services): State<crate::State>,
|
||||
InsecureClientIp(client): InsecureClientIp,
|
||||
body: Ruma<register::v3::Request>,
|
||||
) -> Result<register::v3::Response> {
|
||||
let is_guest = body.kind == RegistrationKind::Guest;
|
||||
let emergency_mode_enabled = services.config.emergency_password.is_some();
|
||||
|
||||
if !services.config.allow_registration && body.appservice_info.is_none() {
|
||||
match (body.username.as_ref(), body.initial_device_display_name.as_ref()) {
|
||||
| (Some(username), Some(device_display_name)) => {
|
||||
info!(
|
||||
%is_guest,
|
||||
user = %username,
|
||||
device_name = %device_display_name,
|
||||
"Rejecting registration attempt as registration is disabled"
|
||||
);
|
||||
},
|
||||
| (Some(username), _) => {
|
||||
info!(
|
||||
%is_guest,
|
||||
user = %username,
|
||||
"Rejecting registration attempt as registration is disabled"
|
||||
);
|
||||
},
|
||||
| (_, Some(device_display_name)) => {
|
||||
info!(
|
||||
%is_guest,
|
||||
device_name = %device_display_name,
|
||||
"Rejecting registration attempt as registration is disabled"
|
||||
);
|
||||
},
|
||||
| (None, _) => {
|
||||
info!(
|
||||
%is_guest,
|
||||
"Rejecting registration attempt as registration is disabled"
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
return Err!(Request(Forbidden("Registration has been disabled.")));
|
||||
}
|
||||
|
||||
if is_guest
|
||||
&& (!services.config.allow_guest_registration
|
||||
|| (services.config.allow_registration
|
||||
&& services.globals.registration_token.is_some()))
|
||||
{
|
||||
info!(
|
||||
"Guest registration disabled / registration enabled with token configured, \
|
||||
rejecting guest registration attempt, initial device name: \"{}\"",
|
||||
body.initial_device_display_name
|
||||
.as_deref()
|
||||
.unwrap_or("")
|
||||
);
|
||||
return Err!(Request(GuestAccessForbidden("Guest registration is disabled.")));
|
||||
}
|
||||
|
||||
// forbid guests from registering if there is not a real admin user yet. give
|
||||
// generic user error.
|
||||
if is_guest && services.users.count().await < 2 {
|
||||
warn!(
|
||||
"Guest account attempted to register before a real admin user has been registered, \
|
||||
rejecting registration. Guest's initial device name: \"{}\"",
|
||||
body.initial_device_display_name
|
||||
.as_deref()
|
||||
.unwrap_or("")
|
||||
);
|
||||
return Err!(Request(Forbidden("Registration is temporarily disabled.")));
|
||||
}
|
||||
|
||||
let user_id = match (body.username.as_ref(), is_guest) {
|
||||
| (Some(username), false) => {
|
||||
// workaround for https://github.com/matrix-org/matrix-appservice-irc/issues/1780 due to inactivity of fixing the issue
|
||||
let is_matrix_appservice_irc =
|
||||
body.appservice_info
|
||||
.as_ref()
|
||||
.is_some_and(|appservice| {
|
||||
appservice.registration.id == "irc"
|
||||
|| appservice
|
||||
.registration
|
||||
.id
|
||||
.contains("matrix-appservice-irc")
|
||||
|| appservice
|
||||
.registration
|
||||
.id
|
||||
.contains("matrix_appservice_irc")
|
||||
});
|
||||
|
||||
if services
|
||||
.globals
|
||||
.forbidden_usernames()
|
||||
.is_match(username)
|
||||
&& !emergency_mode_enabled
|
||||
{
|
||||
return Err!(Request(Forbidden("Username is forbidden")));
|
||||
}
|
||||
|
||||
// don't force the username lowercase if it's from matrix-appservice-irc
|
||||
let body_username = if is_matrix_appservice_irc {
|
||||
username.clone()
|
||||
} else {
|
||||
username.to_lowercase()
|
||||
};
|
||||
|
||||
let proposed_user_id = match UserId::parse_with_server_name(
|
||||
&body_username,
|
||||
services.globals.server_name(),
|
||||
) {
|
||||
| Ok(user_id) => {
|
||||
if let Err(e) = user_id.validate_strict() {
|
||||
// unless the username is from the broken matrix appservice IRC bridge, or
|
||||
// we are in emergency mode, we should follow synapse's behaviour on
|
||||
// not allowing things like spaces and UTF-8 characters in usernames
|
||||
if !is_matrix_appservice_irc && !emergency_mode_enabled {
|
||||
return Err!(Request(InvalidUsername(debug_warn!(
|
||||
"Username {body_username} contains disallowed characters or \
|
||||
spaces: {e}"
|
||||
))));
|
||||
}
|
||||
}
|
||||
|
||||
user_id
|
||||
},
|
||||
| Err(e) => {
|
||||
return Err!(Request(InvalidUsername(debug_warn!(
|
||||
"Username {body_username} is not valid: {e}"
|
||||
))));
|
||||
},
|
||||
};
|
||||
|
||||
if services.users.exists(&proposed_user_id).await {
|
||||
return Err!(Request(UserInUse("User ID is not available.")));
|
||||
}
|
||||
|
||||
proposed_user_id
|
||||
},
|
||||
| _ => loop {
|
||||
let proposed_user_id = UserId::parse_with_server_name(
|
||||
utils::random_string(RANDOM_USER_ID_LENGTH).to_lowercase(),
|
||||
services.globals.server_name(),
|
||||
)
|
||||
.unwrap();
|
||||
if !services.users.exists(&proposed_user_id).await {
|
||||
break proposed_user_id;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
if body.body.login_type == Some(LoginType::ApplicationService) {
|
||||
match body.appservice_info {
|
||||
| Some(ref info) =>
|
||||
if !info.is_user_match(&user_id) && !emergency_mode_enabled {
|
||||
return Err!(Request(Exclusive(
|
||||
"Username is not in an appservice namespace."
|
||||
)));
|
||||
},
|
||||
| _ => {
|
||||
return Err!(Request(MissingToken("Missing appservice token.")));
|
||||
},
|
||||
}
|
||||
} else if services
|
||||
.appservice
|
||||
.is_exclusive_user_id(&user_id)
|
||||
.await && !emergency_mode_enabled
|
||||
{
|
||||
return Err!(Request(Exclusive("Username is reserved by an appservice.")));
|
||||
}
|
||||
|
||||
// UIAA
|
||||
let mut uiaainfo;
|
||||
let skip_auth = if services.globals.registration_token.is_some() {
|
||||
// Registration token required
|
||||
uiaainfo = UiaaInfo {
|
||||
flows: vec![AuthFlow {
|
||||
stages: vec![AuthType::RegistrationToken],
|
||||
}],
|
||||
completed: Vec::new(),
|
||||
params: Box::default(),
|
||||
session: None,
|
||||
auth_error: None,
|
||||
};
|
||||
body.appservice_info.is_some()
|
||||
} else {
|
||||
// No registration token necessary, but clients must still go through the flow
|
||||
uiaainfo = UiaaInfo {
|
||||
flows: vec![AuthFlow { stages: vec![AuthType::Dummy] }],
|
||||
completed: Vec::new(),
|
||||
params: Box::default(),
|
||||
session: None,
|
||||
auth_error: None,
|
||||
};
|
||||
body.appservice_info.is_some() || is_guest
|
||||
};
|
||||
|
||||
if !skip_auth {
|
||||
match &body.auth {
|
||||
| Some(auth) => {
|
||||
let (worked, uiaainfo) = services
|
||||
.uiaa
|
||||
.try_auth(
|
||||
&UserId::parse_with_server_name("", services.globals.server_name())
|
||||
.unwrap(),
|
||||
"".into(),
|
||||
auth,
|
||||
&uiaainfo,
|
||||
)
|
||||
.await?;
|
||||
if !worked {
|
||||
return Err(Error::Uiaa(uiaainfo));
|
||||
}
|
||||
// Success!
|
||||
},
|
||||
| _ => match body.json_body {
|
||||
| Some(ref json) => {
|
||||
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
||||
services.uiaa.create(
|
||||
&UserId::parse_with_server_name("", services.globals.server_name())
|
||||
.unwrap(),
|
||||
"".into(),
|
||||
&uiaainfo,
|
||||
json,
|
||||
);
|
||||
return Err(Error::Uiaa(uiaainfo));
|
||||
},
|
||||
| _ => {
|
||||
return Err!(Request(NotJson("JSON body is not valid")));
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
let password = if is_guest { None } else { body.password.as_deref() };
|
||||
|
||||
// Create user
|
||||
services
|
||||
.users
|
||||
.create(&user_id, password, None)
|
||||
.await?;
|
||||
|
||||
// Default to pretty displayname
|
||||
let mut displayname = user_id.localpart().to_owned();
|
||||
|
||||
// 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()
|
||||
.is_empty()
|
||||
&& body.appservice_info.is_none()
|
||||
{
|
||||
write!(displayname, " {}", services.server.config.new_user_displayname_suffix)?;
|
||||
}
|
||||
|
||||
services
|
||||
.users
|
||||
.set_displayname(&user_id, Some(displayname.clone()));
|
||||
|
||||
// Initial account data
|
||||
services
|
||||
.account_data
|
||||
.update(
|
||||
None,
|
||||
&user_id,
|
||||
GlobalAccountDataEventType::PushRules
|
||||
.to_string()
|
||||
.into(),
|
||||
&serde_json::to_value(ruma::events::push_rules::PushRulesEvent {
|
||||
content: ruma::events::push_rules::PushRulesEventContent {
|
||||
global: push::Ruleset::server_default(&user_id),
|
||||
},
|
||||
})?,
|
||||
)
|
||||
.await?;
|
||||
|
||||
if (!is_guest && body.inhibit_login)
|
||||
|| body
|
||||
.appservice_info
|
||||
.as_ref()
|
||||
.is_some_and(|appservice| appservice.registration.device_management)
|
||||
{
|
||||
return Ok(register::v3::Response {
|
||||
access_token: None,
|
||||
user_id,
|
||||
device_id: None,
|
||||
refresh_token: None,
|
||||
expires_in: None,
|
||||
});
|
||||
}
|
||||
|
||||
// Generate new device id if the user didn't specify one
|
||||
let device_id = if is_guest { None } else { body.device_id.clone() }
|
||||
.unwrap_or_else(|| utils::random_string(DEVICE_ID_LENGTH).into());
|
||||
|
||||
// Generate new token for the device
|
||||
let token = utils::random_string(TOKEN_LENGTH);
|
||||
|
||||
// Create device for this account
|
||||
services
|
||||
.users
|
||||
.create_device(
|
||||
&user_id,
|
||||
&device_id,
|
||||
&token,
|
||||
body.initial_device_display_name.clone(),
|
||||
Some(client.to_string()),
|
||||
)
|
||||
.await?;
|
||||
|
||||
debug_info!(%user_id, %device_id, "User account was created");
|
||||
|
||||
let device_display_name = body
|
||||
.initial_device_display_name
|
||||
.as_deref()
|
||||
.unwrap_or("");
|
||||
|
||||
// log in conduit admin channel if a non-guest user registered
|
||||
if body.appservice_info.is_none() && !is_guest {
|
||||
if !device_display_name.is_empty() {
|
||||
let notice = format!(
|
||||
"New user \"{user_id}\" registered on this server from IP {client} and device \
|
||||
display name \"{device_display_name}\""
|
||||
);
|
||||
|
||||
info!("{notice}");
|
||||
if services.server.config.admin_room_notices {
|
||||
services.admin.notice(¬ice).await;
|
||||
}
|
||||
} else {
|
||||
let notice = format!("New user \"{user_id}\" registered on this server.");
|
||||
|
||||
info!("{notice}");
|
||||
if services.server.config.admin_room_notices {
|
||||
services.admin.notice(¬ice).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// log in conduit admin channel if a guest registered
|
||||
if body.appservice_info.is_none() && is_guest && services.config.log_guest_registrations {
|
||||
debug_info!("New guest user \"{user_id}\" registered on this server.");
|
||||
|
||||
if !device_display_name.is_empty() {
|
||||
if services.server.config.admin_room_notices {
|
||||
services
|
||||
.admin
|
||||
.notice(&format!(
|
||||
"Guest user \"{user_id}\" with device display name \
|
||||
\"{device_display_name}\" registered on this server from IP {client}"
|
||||
))
|
||||
.await;
|
||||
}
|
||||
} else {
|
||||
#[allow(clippy::collapsible_else_if)]
|
||||
if services.server.config.admin_room_notices {
|
||||
services
|
||||
.admin
|
||||
.notice(&format!(
|
||||
"Guest user \"{user_id}\" with no device display name registered on \
|
||||
this server from IP {client}",
|
||||
))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If this is the first real user, grant them admin privileges except for guest
|
||||
// users
|
||||
// Note: the server user is generated first
|
||||
if !is_guest {
|
||||
if let Ok(admin_room) = services.admin.get_admin_room().await {
|
||||
if services
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_joined_count(&admin_room)
|
||||
.await
|
||||
.is_ok_and(is_equal_to!(1))
|
||||
{
|
||||
services.admin.make_user_admin(&user_id).await?;
|
||||
warn!("Granting {user_id} admin privileges as the first user");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if body.appservice_info.is_none()
|
||||
&& !services.server.config.auto_join_rooms.is_empty()
|
||||
&& (services.config.allow_guests_auto_join_rooms || !is_guest)
|
||||
{
|
||||
for room in &services.server.config.auto_join_rooms {
|
||||
let Ok(room_id) = services.rooms.alias.resolve(room).await else {
|
||||
error!(
|
||||
"Failed to resolve room alias to room ID when attempting to auto join \
|
||||
{room}, skipping"
|
||||
);
|
||||
continue;
|
||||
};
|
||||
|
||||
if !services
|
||||
.rooms
|
||||
.state_cache
|
||||
.server_in_room(services.globals.server_name(), &room_id)
|
||||
.await
|
||||
{
|
||||
warn!(
|
||||
"Skipping room {room} to automatically join as we have never joined before."
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(room_server_name) = room.server_name() {
|
||||
match join_room_by_id_helper(
|
||||
&services,
|
||||
&user_id,
|
||||
&room_id,
|
||||
Some("Automatically joining this room upon registration".to_owned()),
|
||||
&[services.globals.server_name().to_owned(), room_server_name.to_owned()],
|
||||
None,
|
||||
&body.appservice_info,
|
||||
)
|
||||
.boxed()
|
||||
.await
|
||||
{
|
||||
| Err(e) => {
|
||||
// don't return this error so we don't fail registrations
|
||||
error!(
|
||||
"Failed to automatically join room {room} for user {user_id}: {e}"
|
||||
);
|
||||
},
|
||||
| _ => {
|
||||
info!("Automatically joined room {room} for user {user_id}");
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(register::v3::Response {
|
||||
access_token: Some(token),
|
||||
user_id,
|
||||
device_id: Some(device_id),
|
||||
refresh_token: None,
|
||||
expires_in: None,
|
||||
})
|
||||
}
|
||||
use crate::{Ruma, router::auth_uiaa};
|
||||
|
||||
/// # `POST /_matrix/client/r0/account/password`
|
||||
///
|
||||
@@ -618,48 +32,7 @@ pub(crate) async fn change_password_route(
|
||||
InsecureClientIp(client): InsecureClientIp,
|
||||
body: Ruma<change_password::v3::Request>,
|
||||
) -> Result<change_password::v3::Response> {
|
||||
// Authentication for this endpoint was made optional, but we need
|
||||
// authentication currently
|
||||
let sender_user = body
|
||||
.sender_user
|
||||
.as_ref()
|
||||
.ok_or_else(|| err!(Request(MissingToken("Missing access token."))))?;
|
||||
|
||||
let mut uiaainfo = UiaaInfo {
|
||||
flows: vec![AuthFlow { stages: vec![AuthType::Password] }],
|
||||
completed: Vec::new(),
|
||||
params: Box::default(),
|
||||
session: None,
|
||||
auth_error: None,
|
||||
};
|
||||
|
||||
match &body.auth {
|
||||
| Some(auth) => {
|
||||
let (worked, uiaainfo) = services
|
||||
.uiaa
|
||||
.try_auth(sender_user, body.sender_device(), auth, &uiaainfo)
|
||||
.await?;
|
||||
|
||||
if !worked {
|
||||
return Err(Error::Uiaa(uiaainfo));
|
||||
}
|
||||
|
||||
// Success!
|
||||
},
|
||||
| _ => match body.json_body {
|
||||
| Some(ref json) => {
|
||||
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
||||
services
|
||||
.uiaa
|
||||
.create(sender_user, body.sender_device(), &uiaainfo, json);
|
||||
|
||||
return Err(Error::Uiaa(uiaainfo));
|
||||
},
|
||||
| _ => {
|
||||
return Err!(Request(NotJson("JSON body is not valid")));
|
||||
},
|
||||
},
|
||||
}
|
||||
let ref sender_user = auth_uiaa(&services, &body).await?;
|
||||
|
||||
services
|
||||
.users
|
||||
@@ -671,32 +44,9 @@ pub(crate) async fn change_password_route(
|
||||
services
|
||||
.users
|
||||
.all_device_ids(sender_user)
|
||||
.ready_filter(|id| *id != body.sender_device())
|
||||
.ready_filter(|&id| Some(id) != body.sender_device.as_deref())
|
||||
.for_each(|id| services.users.remove_device(sender_user, id))
|
||||
.await;
|
||||
|
||||
// Remove all pushers except the ones associated with this session
|
||||
services
|
||||
.pusher
|
||||
.get_pushkeys(sender_user)
|
||||
.map(ToOwned::to_owned)
|
||||
.broad_filter_map(async |pushkey| {
|
||||
services
|
||||
.pusher
|
||||
.get_pusher_device(&pushkey)
|
||||
.await
|
||||
.ok()
|
||||
.filter(|pusher_device| pusher_device != body.sender_device())
|
||||
.is_some()
|
||||
.then_some(pushkey)
|
||||
})
|
||||
.for_each(async |pushkey| {
|
||||
services
|
||||
.pusher
|
||||
.delete_pusher(sender_user, &pushkey)
|
||||
.await;
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
info!("User {sender_user} changed their password.");
|
||||
@@ -747,66 +97,15 @@ pub(crate) async fn deactivate_route(
|
||||
InsecureClientIp(client): InsecureClientIp,
|
||||
body: Ruma<deactivate::v3::Request>,
|
||||
) -> Result<deactivate::v3::Response> {
|
||||
// Authentication for this endpoint was made optional, but we need
|
||||
// authentication currently
|
||||
let sender_user = body
|
||||
.sender_user
|
||||
.as_ref()
|
||||
.ok_or_else(|| err!(Request(MissingToken("Missing access token."))))?;
|
||||
let ref sender_user = auth_uiaa(&services, &body).await?;
|
||||
|
||||
let mut uiaainfo = UiaaInfo {
|
||||
flows: vec![AuthFlow { stages: vec![AuthType::Password] }],
|
||||
completed: Vec::new(),
|
||||
params: Box::default(),
|
||||
session: None,
|
||||
auth_error: None,
|
||||
};
|
||||
|
||||
match &body.auth {
|
||||
| Some(auth) => {
|
||||
let (worked, uiaainfo) = services
|
||||
.uiaa
|
||||
.try_auth(sender_user, body.sender_device(), auth, &uiaainfo)
|
||||
.await?;
|
||||
|
||||
if !worked {
|
||||
return Err(Error::Uiaa(uiaainfo));
|
||||
}
|
||||
// Success!
|
||||
},
|
||||
| _ => match body.json_body {
|
||||
| Some(ref json) => {
|
||||
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
||||
services
|
||||
.uiaa
|
||||
.create(sender_user, body.sender_device(), &uiaainfo, json);
|
||||
|
||||
return Err(Error::Uiaa(uiaainfo));
|
||||
},
|
||||
| _ => {
|
||||
return Err!(Request(NotJson("JSON body is not valid")));
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Remove profile pictures and display name
|
||||
let all_joined_rooms: Vec<OwnedRoomId> = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.rooms_joined(sender_user)
|
||||
.map(Into::into)
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
super::update_displayname(&services, sender_user, None, &all_joined_rooms).await;
|
||||
super::update_avatar_url(&services, sender_user, None, None, &all_joined_rooms).await;
|
||||
|
||||
full_user_deactivate(&services, sender_user, &all_joined_rooms)
|
||||
services
|
||||
.deactivate
|
||||
.full_deactivate(sender_user)
|
||||
.boxed()
|
||||
.await?;
|
||||
|
||||
info!("User {sender_user} deactivated their account.");
|
||||
|
||||
if services.server.config.admin_room_notices {
|
||||
services
|
||||
.admin
|
||||
@@ -860,111 +159,3 @@ pub(crate) async fn request_3pid_management_token_via_msisdn_route(
|
||||
) -> Result<request_3pid_management_token_via_msisdn::v3::Response> {
|
||||
Err!(Request(ThreepidDenied("Third party identifiers are not implemented")))
|
||||
}
|
||||
|
||||
/// # `GET /_matrix/client/v1/register/m.login.registration_token/validity`
|
||||
///
|
||||
/// Checks if the provided registration token is valid at the time of checking
|
||||
///
|
||||
/// Currently does not have any ratelimiting, and this isn't very practical as
|
||||
/// there is only one registration token allowed.
|
||||
pub(crate) async fn check_registration_token_validity(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<check_registration_token_validity::v1::Request>,
|
||||
) -> Result<check_registration_token_validity::v1::Response> {
|
||||
let Some(reg_token) = services.globals.registration_token.clone() else {
|
||||
return Err!(Request(Forbidden("Server does not allow token registration")));
|
||||
};
|
||||
|
||||
Ok(check_registration_token_validity::v1::Response { valid: reg_token == body.token })
|
||||
}
|
||||
|
||||
/// Runs through all the deactivation steps:
|
||||
///
|
||||
/// - Mark as deactivated
|
||||
/// - Removing display name
|
||||
/// - Removing avatar URL and blurhash
|
||||
/// - Removing all profile data
|
||||
/// - Leaving all rooms (and forgets all of them)
|
||||
pub async fn full_user_deactivate(
|
||||
services: &Services,
|
||||
user_id: &UserId,
|
||||
all_joined_rooms: &[OwnedRoomId],
|
||||
) -> Result<()> {
|
||||
services
|
||||
.users
|
||||
.deactivate_account(user_id)
|
||||
.await
|
||||
.ok();
|
||||
|
||||
super::update_displayname(services, user_id, None, all_joined_rooms).await;
|
||||
super::update_avatar_url(services, user_id, None, None, all_joined_rooms).await;
|
||||
|
||||
services
|
||||
.users
|
||||
.all_profile_keys(user_id)
|
||||
.ready_for_each(|(profile_key, _)| {
|
||||
services
|
||||
.users
|
||||
.set_profile_key(user_id, &profile_key, None);
|
||||
})
|
||||
.await;
|
||||
|
||||
for room_id in all_joined_rooms {
|
||||
let state_lock = services.rooms.state.mutex.lock(room_id).await;
|
||||
|
||||
let room_power_levels = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get_content::<RoomPowerLevelsEventContent>(
|
||||
room_id,
|
||||
&StateEventType::RoomPowerLevels,
|
||||
"",
|
||||
)
|
||||
.await
|
||||
.ok();
|
||||
|
||||
let user_can_demote_self =
|
||||
room_power_levels
|
||||
.as_ref()
|
||||
.is_some_and(|power_levels_content| {
|
||||
RoomPowerLevels::from(power_levels_content.clone())
|
||||
.user_can_change_user_power_level(user_id, user_id)
|
||||
}) || services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get(room_id, &StateEventType::RoomCreate, "")
|
||||
.await
|
||||
.is_ok_and(|event| event.sender() == user_id);
|
||||
|
||||
if user_can_demote_self {
|
||||
let mut power_levels_content = room_power_levels.unwrap_or_default();
|
||||
power_levels_content.users.remove(user_id);
|
||||
|
||||
// ignore errors so deactivation doesn't fail
|
||||
match services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(String::new(), &power_levels_content),
|
||||
user_id,
|
||||
room_id,
|
||||
&state_lock,
|
||||
)
|
||||
.await
|
||||
{
|
||||
| Err(e) => {
|
||||
warn!(%room_id, %user_id, "Failed to demote user's own power level: {e}");
|
||||
},
|
||||
| _ => {
|
||||
info!("Demoted {user_id} in {room_id} as part of account deactivation");
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
super::leave_all_rooms(services, user_id)
|
||||
.boxed()
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+6
-15
@@ -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;
|
||||
@@ -19,7 +19,6 @@ pub(crate) async fn create_alias_route(
|
||||
) -> Result<create_alias::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
services
|
||||
.rooms
|
||||
.alias
|
||||
.appservice_checks(&body.room_alias, &body.appservice_info)
|
||||
.await?;
|
||||
@@ -27,15 +26,14 @@ 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.")));
|
||||
}
|
||||
|
||||
if services
|
||||
.rooms
|
||||
.alias
|
||||
.resolve_local_alias(&body.room_alias)
|
||||
.await
|
||||
@@ -45,7 +43,6 @@ pub(crate) async fn create_alias_route(
|
||||
}
|
||||
|
||||
services
|
||||
.rooms
|
||||
.alias
|
||||
.set_alias(&body.room_alias, &body.room_id, sender_user)?;
|
||||
|
||||
@@ -63,13 +60,11 @@ pub(crate) async fn delete_alias_route(
|
||||
) -> Result<delete_alias::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
services
|
||||
.rooms
|
||||
.alias
|
||||
.appservice_checks(&body.room_alias, &body.appservice_info)
|
||||
.await?;
|
||||
|
||||
services
|
||||
.rooms
|
||||
.alias
|
||||
.remove_alias(&body.room_alias, sender_user)
|
||||
.await?;
|
||||
@@ -88,14 +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
|
||||
.rooms
|
||||
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:?}");
|
||||
@@ -111,7 +103,6 @@ async fn room_available_servers(
|
||||
) -> Vec<OwnedServerName> {
|
||||
// find active servers in room state cache to suggest
|
||||
let mut servers: Vec<OwnedServerName> = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_servers(room_id)
|
||||
.map(ToOwned::to_owned)
|
||||
|
||||
@@ -3,9 +3,12 @@
|
||||
use axum::extract::State;
|
||||
use ruma::{
|
||||
RoomVersionId,
|
||||
api::client::discovery::get_capabilities::{
|
||||
self, Capabilities, GetLoginTokenCapability, RoomVersionStability,
|
||||
RoomVersionsCapability, ThirdPartyIdChangesCapability,
|
||||
api::client::discovery::{
|
||||
get_capabilities,
|
||||
get_capabilities::v3::{
|
||||
Capabilities, GetLoginTokenCapability, ProfileFieldsCapability, RoomVersionStability,
|
||||
RoomVersionsCapability, ThirdPartyIdChangesCapability,
|
||||
},
|
||||
},
|
||||
};
|
||||
use serde_json::json;
|
||||
@@ -41,8 +44,7 @@ pub(crate) async fn get_capabilities_route(
|
||||
enabled: services.server.config.login_via_existing_session,
|
||||
};
|
||||
|
||||
// MSC4133 capability
|
||||
capabilities.set("uk.tcpip.msc4133.profile_fields", json!({"enabled": true}))?;
|
||||
capabilities.profile_fields = ProfileFieldsCapability::new(true).into();
|
||||
|
||||
capabilities.set(
|
||||
"org.matrix.msc4267.forget_forced_upon_leave",
|
||||
|
||||
+11
-24
@@ -38,7 +38,7 @@ pub(crate) async fn get_context_route(
|
||||
let event_id = &body.event_id;
|
||||
let filter = &body.filter;
|
||||
|
||||
if !services.rooms.metadata.exists(room_id).await {
|
||||
if !services.metadata.exists(room_id).await {
|
||||
return Err!(Request(Forbidden("Room does not exist to this server")));
|
||||
}
|
||||
|
||||
@@ -50,19 +50,16 @@ pub(crate) async fn get_context_route(
|
||||
.min(LIMIT_MAX);
|
||||
|
||||
let base_id = services
|
||||
.rooms
|
||||
.timeline
|
||||
.get_pdu_id(event_id)
|
||||
.map_err(|_| err!(Request(NotFound("Event not found."))));
|
||||
|
||||
let base_pdu = services
|
||||
.rooms
|
||||
.timeline
|
||||
.get_pdu(event_id)
|
||||
.map_err(|_| err!(Request(NotFound("Base event not found."))));
|
||||
|
||||
let visible = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.user_can_see_event(sender_user, room_id, event_id)
|
||||
.map(Ok);
|
||||
@@ -74,7 +71,11 @@ pub(crate) async fn get_context_route(
|
||||
}
|
||||
|
||||
if !visible {
|
||||
debug_warn!(req_evt = ?event_id, ?base_id, ?room_id, "Event requested by {sender_user} but is not allowed to see it, returning 404");
|
||||
debug_warn!(
|
||||
req_evt = ?event_id, ?base_id, ?room_id,
|
||||
"Event requested by {sender_user} but is not allowed to see it."
|
||||
);
|
||||
|
||||
return Err!(Request(NotFound("Event not found.")));
|
||||
}
|
||||
|
||||
@@ -83,7 +84,6 @@ pub(crate) async fn get_context_route(
|
||||
let base_event = ignored_filter(&services, (base_count, base_pdu), sender_user);
|
||||
|
||||
let events_before = services
|
||||
.rooms
|
||||
.timeline
|
||||
.pdus_rev(Some(sender_user), room_id, Some(base_count))
|
||||
.ignore_err()
|
||||
@@ -94,14 +94,13 @@ pub(crate) async fn get_context_route(
|
||||
.collect();
|
||||
|
||||
let events_after = services
|
||||
.rooms
|
||||
.timeline
|
||||
.pdus(Some(sender_user), room_id, Some(base_count))
|
||||
.ignore_err()
|
||||
.ready_filter_map(|item| event_filter(item, filter))
|
||||
.wide_filter_map(|item| ignored_filter(&services, item, sender_user))
|
||||
.wide_filter_map(|item| visibility_filter(&services, item, sender_user))
|
||||
.take(limit / 2)
|
||||
.take(limit.div_ceil(2))
|
||||
.collect();
|
||||
|
||||
let (base_event, events_before, events_after): (_, Vec<_>, Vec<_>) =
|
||||
@@ -132,21 +131,14 @@ pub(crate) async fn get_context_route(
|
||||
let state_at = events_after
|
||||
.last()
|
||||
.map(ref_at!(1))
|
||||
.map_or(body.event_id.as_ref(), |pdu| pdu.event_id.as_ref());
|
||||
.map_or_else(|| body.event_id.as_ref(), |pdu| pdu.event_id.as_ref());
|
||||
|
||||
let state_ids = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.state
|
||||
.pdu_shortstatehash(state_at)
|
||||
.or_else(|_| {
|
||||
services
|
||||
.rooms
|
||||
.state
|
||||
.get_room_shortstatehash(room_id)
|
||||
})
|
||||
.or_else(|_| services.state.get_room_shortstatehash(room_id))
|
||||
.map_ok(|shortstatehash| {
|
||||
services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.state_full_ids(shortstatehash)
|
||||
.map(Ok)
|
||||
@@ -163,7 +155,6 @@ pub(crate) async fn get_context_route(
|
||||
let shorteventids = state_ids.iter().map(ref_at!(1)).stream();
|
||||
let lazy_loading_witnessed = lazy_loading_witnessed.unwrap_or_default();
|
||||
let state: Vec<_> = services
|
||||
.rooms
|
||||
.short
|
||||
.multi_get_statekey_from_short(shortstatekeys)
|
||||
.zip(shorteventids)
|
||||
@@ -182,11 +173,7 @@ pub(crate) async fn get_context_route(
|
||||
Some(event_id)
|
||||
})
|
||||
.broad_filter_map(|event_id: &OwnedEventId| {
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.get_pdu(event_id.as_ref())
|
||||
.ok()
|
||||
services.timeline.get_pdu(event_id.as_ref()).ok()
|
||||
})
|
||||
.map(Event::into_format)
|
||||
.collect()
|
||||
|
||||
+10
-80
@@ -3,16 +3,13 @@
|
||||
use futures::StreamExt;
|
||||
use ruma::{
|
||||
MilliSecondsSinceUnixEpoch, OwnedDeviceId,
|
||||
api::client::{
|
||||
device::{self, delete_device, delete_devices, get_device, get_devices, update_device},
|
||||
error::ErrorKind,
|
||||
uiaa::{AuthFlow, AuthType, UiaaInfo},
|
||||
api::client::device::{
|
||||
self, delete_device, delete_devices, get_device, get_devices, update_device,
|
||||
},
|
||||
};
|
||||
use tuwunel_core::{Err, Error, Result, debug, err, utils};
|
||||
use tuwunel_core::{Err, Result, debug, err, utils};
|
||||
|
||||
use super::SESSION_ID_LENGTH;
|
||||
use crate::{Ruma, client::DEVICE_ID_LENGTH};
|
||||
use crate::{Ruma, client::DEVICE_ID_LENGTH, router::auth_uiaa};
|
||||
|
||||
/// # `GET /_matrix/client/r0/devices`
|
||||
///
|
||||
@@ -100,7 +97,8 @@ pub(crate) async fn update_device_route(
|
||||
.create_device(
|
||||
sender_user,
|
||||
&device_id,
|
||||
&appservice.registration.as_token,
|
||||
(&appservice.registration.as_token, None),
|
||||
None,
|
||||
None,
|
||||
Some(client.to_string()),
|
||||
)
|
||||
@@ -125,10 +123,10 @@ pub(crate) async fn delete_device_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<delete_device::v3::Request>,
|
||||
) -> Result<delete_device::v3::Response> {
|
||||
let (sender_user, sender_device) = body.sender();
|
||||
let appservice = body.appservice_info.as_ref();
|
||||
|
||||
if appservice.is_some_and(|appservice| appservice.registration.device_management) {
|
||||
let sender_user = body.sender_user();
|
||||
debug!(
|
||||
"Skipping UIAA for {sender_user} as this is from an appservice and MSC4190 is \
|
||||
enabled"
|
||||
@@ -141,41 +139,7 @@ pub(crate) async fn delete_device_route(
|
||||
return Ok(delete_device::v3::Response {});
|
||||
}
|
||||
|
||||
// UIAA
|
||||
let mut uiaainfo = UiaaInfo {
|
||||
flows: vec![AuthFlow { stages: vec![AuthType::Password] }],
|
||||
completed: Vec::new(),
|
||||
params: Box::default(),
|
||||
session: None,
|
||||
auth_error: None,
|
||||
};
|
||||
|
||||
match &body.auth {
|
||||
| Some(auth) => {
|
||||
let (worked, uiaainfo) = services
|
||||
.uiaa
|
||||
.try_auth(sender_user, sender_device, auth, &uiaainfo)
|
||||
.await?;
|
||||
|
||||
if !worked {
|
||||
return Err!(Uiaa(uiaainfo));
|
||||
}
|
||||
// Success!
|
||||
},
|
||||
| _ => match body.json_body {
|
||||
| Some(ref json) => {
|
||||
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
|
||||
services
|
||||
.uiaa
|
||||
.create(sender_user, sender_device, &uiaainfo, json);
|
||||
|
||||
return Err!(Uiaa(uiaainfo));
|
||||
},
|
||||
| _ => {
|
||||
return Err!(Request(NotJson("Not json.")));
|
||||
},
|
||||
},
|
||||
}
|
||||
let ref sender_user = auth_uiaa(&services, &body).await?;
|
||||
|
||||
services
|
||||
.users
|
||||
@@ -202,10 +166,10 @@ pub(crate) async fn delete_devices_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<delete_devices::v3::Request>,
|
||||
) -> Result<delete_devices::v3::Response> {
|
||||
let (sender_user, sender_device) = body.sender();
|
||||
let appservice = body.appservice_info.as_ref();
|
||||
|
||||
if appservice.is_some_and(|appservice| appservice.registration.device_management) {
|
||||
let sender_user = body.sender_user();
|
||||
debug!(
|
||||
"Skipping UIAA for {sender_user} as this is from an appservice and MSC4190 is \
|
||||
enabled"
|
||||
@@ -220,41 +184,7 @@ pub(crate) async fn delete_devices_route(
|
||||
return Ok(delete_devices::v3::Response {});
|
||||
}
|
||||
|
||||
// UIAA
|
||||
let mut uiaainfo = UiaaInfo {
|
||||
flows: vec![AuthFlow { stages: vec![AuthType::Password] }],
|
||||
completed: Vec::new(),
|
||||
params: Box::default(),
|
||||
session: None,
|
||||
auth_error: None,
|
||||
};
|
||||
|
||||
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 {
|
||||
| Some(ref 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 ref sender_user = auth_uiaa(&services, &body).await?;
|
||||
|
||||
for device_id in &body.devices {
|
||||
services
|
||||
|
||||
+93
-88
@@ -16,24 +16,21 @@
|
||||
},
|
||||
federation,
|
||||
},
|
||||
directory::{Filter, PublicRoomJoinRule, PublicRoomsChunk, RoomNetwork, RoomTypeFilter},
|
||||
directory::{Filter, PublicRoomsChunk, RoomNetwork, RoomTypeFilter},
|
||||
events::{
|
||||
StateEventType,
|
||||
room::{
|
||||
join_rules::{JoinRule, RoomJoinRulesEventContent},
|
||||
power_levels::{RoomPowerLevels, RoomPowerLevelsEventContent},
|
||||
},
|
||||
room::join_rules::{JoinRule, RoomJoinRulesEventContent},
|
||||
},
|
||||
uint,
|
||||
};
|
||||
use tuwunel_core::{
|
||||
Err, Result, err, info,
|
||||
Err, Result, err, info, is_true,
|
||||
matrix::Event,
|
||||
utils::{
|
||||
TryFutureExtExt,
|
||||
math::Expected,
|
||||
result::FlatOk,
|
||||
stream::{ReadyExt, WidebandExt},
|
||||
stream::{IterStream, ReadyExt, WidebandExt},
|
||||
},
|
||||
};
|
||||
use tuwunel_service::Services;
|
||||
@@ -51,19 +48,7 @@ pub(crate) async fn get_public_rooms_filtered_route(
|
||||
InsecureClientIp(client): InsecureClientIp,
|
||||
body: Ruma<get_public_rooms_filtered::v3::Request>,
|
||||
) -> Result<get_public_rooms_filtered::v3::Response> {
|
||||
if let Some(server) = &body.server {
|
||||
if services
|
||||
.config
|
||||
.forbidden_remote_room_directory_server_names
|
||||
.is_match(server.host())
|
||||
|| services
|
||||
.config
|
||||
.forbidden_remote_server_names
|
||||
.is_match(server.host())
|
||||
{
|
||||
return Err!(Request(Forbidden("Server is banned on this homeserver.")));
|
||||
}
|
||||
}
|
||||
check_server_banned(&services, body.server.as_deref())?;
|
||||
|
||||
let response = get_public_rooms_filtered_helper(
|
||||
&services,
|
||||
@@ -92,19 +77,7 @@ pub(crate) async fn get_public_rooms_route(
|
||||
InsecureClientIp(client): InsecureClientIp,
|
||||
body: Ruma<get_public_rooms::v3::Request>,
|
||||
) -> Result<get_public_rooms::v3::Response> {
|
||||
if let Some(server) = &body.server {
|
||||
if services
|
||||
.config
|
||||
.forbidden_remote_room_directory_server_names
|
||||
.is_match(server.host())
|
||||
|| services
|
||||
.config
|
||||
.forbidden_remote_server_names
|
||||
.is_match(server.host())
|
||||
{
|
||||
return Err!(Request(Forbidden("Server is banned on this homeserver.")));
|
||||
}
|
||||
}
|
||||
check_server_banned(&services, body.server.as_deref())?;
|
||||
|
||||
let response = get_public_rooms_filtered_helper(
|
||||
&services,
|
||||
@@ -138,12 +111,7 @@ pub(crate) async fn set_room_visibility_route(
|
||||
) -> Result<set_room_visibility::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
|
||||
if !services
|
||||
.rooms
|
||||
.metadata
|
||||
.exists(&body.room_id)
|
||||
.await
|
||||
{
|
||||
if !services.metadata.exists(&body.room_id).await {
|
||||
// Return 404 if the room doesn't exist
|
||||
return Err!(Request(NotFound("Room not found")));
|
||||
}
|
||||
@@ -193,7 +161,7 @@ pub(crate) async fn set_room_visibility_route(
|
||||
)));
|
||||
}
|
||||
|
||||
services.rooms.directory.set_public(&body.room_id);
|
||||
services.directory.set_public(&body.room_id);
|
||||
|
||||
if services.server.config.admin_room_notices {
|
||||
services
|
||||
@@ -206,10 +174,7 @@ pub(crate) async fn set_room_visibility_route(
|
||||
}
|
||||
info!("{sender_user} made {0} public to the room directory", body.room_id);
|
||||
},
|
||||
| room::Visibility::Private => services
|
||||
.rooms
|
||||
.directory
|
||||
.set_not_public(&body.room_id),
|
||||
| room::Visibility::Private => services.directory.set_not_public(&body.room_id),
|
||||
| _ => {
|
||||
return Err!(Request(InvalidParam("Room visibility type is not supported.",)));
|
||||
},
|
||||
@@ -225,19 +190,13 @@ pub(crate) async fn get_room_visibility_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<get_room_visibility::v3::Request>,
|
||||
) -> Result<get_room_visibility::v3::Response> {
|
||||
if !services
|
||||
.rooms
|
||||
.metadata
|
||||
.exists(&body.room_id)
|
||||
.await
|
||||
{
|
||||
if !services.metadata.exists(&body.room_id).await {
|
||||
// Return 404 if the room doesn't exist
|
||||
return Err!(Request(NotFound("Room not found")));
|
||||
}
|
||||
|
||||
Ok(get_room_visibility::v3::Response {
|
||||
visibility: if services
|
||||
.rooms
|
||||
.directory
|
||||
.is_public_room(&body.room_id)
|
||||
.await
|
||||
@@ -308,32 +267,61 @@ pub(crate) async fn get_public_rooms_filtered_helper(
|
||||
}
|
||||
}
|
||||
|
||||
let search_term = filter
|
||||
.generic_search_term
|
||||
.as_deref()
|
||||
.map(str::to_lowercase);
|
||||
|
||||
let search_room_id = filter
|
||||
.generic_search_term
|
||||
.as_deref()
|
||||
.filter(|_| services.config.allow_public_room_search_by_id)
|
||||
.filter(|s| s.starts_with('!'))
|
||||
.filter(|s| s.len() > 5); // require some characters to limit scope.
|
||||
|
||||
let meta_public_rooms = search_room_id
|
||||
.filter(|_| services.config.allow_unlisted_room_search_by_id)
|
||||
.map(|prefix| services.metadata.public_ids_prefix(prefix))
|
||||
.into_iter()
|
||||
.stream()
|
||||
.flatten();
|
||||
|
||||
let mut all_rooms: Vec<PublicRoomsChunk> = services
|
||||
.rooms
|
||||
.directory
|
||||
.public_rooms()
|
||||
.map(ToOwned::to_owned)
|
||||
.chain(meta_public_rooms)
|
||||
.wide_then(|room_id| public_rooms_chunk(services, room_id))
|
||||
.ready_filter_map(|chunk| {
|
||||
if !filter.room_types.is_empty() && !filter.room_types.contains(&RoomTypeFilter::from(chunk.room_type.clone())) {
|
||||
if !filter.room_types.is_empty()
|
||||
&& !filter
|
||||
.room_types
|
||||
.contains(&RoomTypeFilter::from(chunk.room_type.clone()))
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
if let Some(query) = filter.generic_search_term.as_ref().map(|q| q.to_lowercase()) {
|
||||
if let Some(query) = search_room_id {
|
||||
if chunk.room_id.as_str().contains(query) {
|
||||
return Some(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(query) = search_term.as_deref() {
|
||||
if let Some(name) = &chunk.name {
|
||||
if name.as_str().to_lowercase().contains(&query) {
|
||||
if name.as_str().to_lowercase().contains(query) {
|
||||
return Some(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(topic) = &chunk.topic {
|
||||
if topic.to_lowercase().contains(&query) {
|
||||
if topic.to_lowercase().contains(query) {
|
||||
return Some(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(canonical_alias) = &chunk.canonical_alias {
|
||||
if canonical_alias.as_str().to_lowercase().contains(&query) {
|
||||
if canonical_alias.as_str().to_lowercase().contains(query) {
|
||||
return Some(chunk);
|
||||
}
|
||||
}
|
||||
@@ -385,20 +373,14 @@ async fn user_can_publish_room(
|
||||
room_id: &RoomId,
|
||||
) -> Result<bool> {
|
||||
match services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get(room_id, &StateEventType::RoomPowerLevels, "")
|
||||
.get_power_levels(room_id)
|
||||
.await
|
||||
{
|
||||
| Ok(event) => serde_json::from_str(event.content().get())
|
||||
.map_err(|_| err!(Database("Invalid event content for m.room.power_levels")))
|
||||
.map(|content: RoomPowerLevelsEventContent| {
|
||||
RoomPowerLevels::from(content)
|
||||
.user_can_send_state(user_id, StateEventType::RoomHistoryVisibility)
|
||||
}),
|
||||
| Ok(power_levels) =>
|
||||
Ok(power_levels.user_can_send_state(user_id, StateEventType::RoomHistoryVisibility)),
|
||||
| _ => {
|
||||
match services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get(room_id, &StateEventType::RoomCreate, "")
|
||||
.await
|
||||
@@ -411,57 +393,57 @@ async fn user_can_publish_room(
|
||||
}
|
||||
|
||||
async fn public_rooms_chunk(services: &Services, room_id: OwnedRoomId) -> PublicRoomsChunk {
|
||||
let name = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.get_name(&room_id)
|
||||
.ok();
|
||||
let name = services.state_accessor.get_name(&room_id).ok();
|
||||
|
||||
let room_type = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.get_room_type(&room_id)
|
||||
.ok();
|
||||
|
||||
let canonical_alias = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.get_canonical_alias(&room_id)
|
||||
.ok()
|
||||
.then(async |alias| {
|
||||
if let Some(alias) = alias
|
||||
&& services.globals.alias_is_local(&alias)
|
||||
&& let Ok(alias_room_id) = services.alias.resolve_local_alias(&alias).await
|
||||
&& alias_room_id == room_id
|
||||
{
|
||||
Some(alias)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
let avatar_url = services
|
||||
.state_accessor
|
||||
.get_avatar(&room_id)
|
||||
.map_ok(|content| content.url)
|
||||
.ok();
|
||||
|
||||
let avatar_url = services.rooms.state_accessor.get_avatar(&room_id);
|
||||
|
||||
let topic = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.get_room_topic(&room_id)
|
||||
.ok();
|
||||
|
||||
let world_readable = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.is_world_readable(&room_id);
|
||||
|
||||
let join_rule = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get_content(&room_id, &StateEventType::RoomJoinRules, "")
|
||||
.map_ok(|c: RoomJoinRulesEventContent| match c.join_rule {
|
||||
| JoinRule::Public => PublicRoomJoinRule::Public,
|
||||
| JoinRule::Public => "public".into(),
|
||||
| JoinRule::Knock => "knock".into(),
|
||||
| JoinRule::KnockRestricted(_) => "knock_restricted".into(),
|
||||
| _ => "invite".into(),
|
||||
});
|
||||
|
||||
let guest_can_join = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.guest_can_join(&room_id);
|
||||
let guest_can_join = services.state_accessor.guest_can_join(&room_id);
|
||||
|
||||
let num_joined_members = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_joined_count(&room_id);
|
||||
let num_joined_members = services.state_cache.room_joined_count(&room_id);
|
||||
|
||||
let (
|
||||
(avatar_url, canonical_alias, guest_can_join, join_rule, name),
|
||||
@@ -474,7 +456,7 @@ async fn public_rooms_chunk(services: &Services, room_id: OwnedRoomId) -> Public
|
||||
.await;
|
||||
|
||||
PublicRoomsChunk {
|
||||
avatar_url: avatar_url.into_option().unwrap_or_default().url,
|
||||
avatar_url: avatar_url.flatten(),
|
||||
canonical_alias,
|
||||
guest_can_join,
|
||||
join_rule: join_rule.unwrap_or_default(),
|
||||
@@ -490,3 +472,26 @@ async fn public_rooms_chunk(services: &Services, room_id: OwnedRoomId) -> Public
|
||||
world_readable,
|
||||
}
|
||||
}
|
||||
|
||||
fn check_server_banned(services: &Services, server: Option<&ServerName>) -> Result {
|
||||
let Some(server) = server else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let conditions = [
|
||||
services
|
||||
.config
|
||||
.forbidden_remote_room_directory_server_names
|
||||
.is_match(server.host()),
|
||||
services
|
||||
.config
|
||||
.forbidden_remote_server_names
|
||||
.is_match(server.host()),
|
||||
];
|
||||
|
||||
if conditions.iter().any(is_true!()) {
|
||||
return Err!(Request(Forbidden("Server is banned on this homeserver.")));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+60
-82
@@ -3,16 +3,13 @@
|
||||
use axum::extract::State;
|
||||
use futures::{StreamExt, stream::FuturesUnordered};
|
||||
use ruma::{
|
||||
OneTimeKeyAlgorithm, OwnedDeviceId, OwnedUserId, UserId,
|
||||
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,
|
||||
},
|
||||
@@ -20,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`
|
||||
///
|
||||
@@ -39,7 +37,11 @@ pub(crate) async fn upload_keys_route(
|
||||
) -> Result<upload_keys::v3::Response> {
|
||||
let (sender_user, sender_device) = body.sender();
|
||||
|
||||
for (key_id, one_time_key) in &body.one_time_keys {
|
||||
for (key_id, one_time_key) in body
|
||||
.one_time_keys
|
||||
.iter()
|
||||
.take(services.config.one_time_key_limit)
|
||||
{
|
||||
if one_time_key
|
||||
.deserialize()
|
||||
.inspect_err(|e| {
|
||||
@@ -157,26 +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] }],
|
||||
completed: Vec::new(),
|
||||
params: Box::default(),
|
||||
session: None,
|
||||
auth_error: None,
|
||||
};
|
||||
|
||||
// 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 {
|
||||
@@ -184,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,
|
||||
@@ -248,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 {
|
||||
@@ -260,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 {
|
||||
@@ -267,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()? {
|
||||
@@ -276,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()? {
|
||||
@@ -296,6 +275,7 @@ async fn check_for_new_keys(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if empty {
|
||||
return Ok(None);
|
||||
}
|
||||
@@ -379,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
|
||||
@@ -396,7 +376,6 @@ pub(crate) async fn get_key_changes_route(
|
||||
);
|
||||
|
||||
let mut rooms_joined = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.rooms_joined(sender_user)
|
||||
.boxed();
|
||||
@@ -526,7 +505,7 @@ pub(crate) async fn get_keys_helper<F>(
|
||||
|
||||
let mut futures: FuturesUnordered<_> = get_over_federation
|
||||
.into_iter()
|
||||
.map(|(server, vec)| async move {
|
||||
.map(async |(server, vec)| {
|
||||
let mut device_keys_input_fed = BTreeMap::new();
|
||||
for (user_id, keys) in vec {
|
||||
device_keys_input_fed.insert(user_id.to_owned(), keys.clone());
|
||||
@@ -560,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);
|
||||
}
|
||||
@@ -597,22 +576,21 @@ fn add_unsigned_device_display_name(
|
||||
keys: &mut Raw<ruma::encryption::DeviceKeys>,
|
||||
metadata: ruma::api::client::device::Device,
|
||||
include_display_names: bool,
|
||||
) -> serde_json::Result<()> {
|
||||
) -> Result {
|
||||
if let Some(display_name) = metadata.display_name {
|
||||
let mut object = keys.deserialize_as::<serde_json::Map<String, serde_json::Value>>()?;
|
||||
let mut object = keys.deserialize_as_unchecked::<CanonicalJsonObject>()?;
|
||||
|
||||
let unsigned = object
|
||||
.entry("unsigned")
|
||||
.or_insert_with(|| json!({}));
|
||||
if let serde_json::Value::Object(unsigned_object) = unsigned {
|
||||
if include_display_names {
|
||||
unsigned_object.insert("device_display_name".to_owned(), display_name.into());
|
||||
if let CanonicalJsonValue::Object(unsigned) = object
|
||||
.entry("unsigned".into())
|
||||
.or_insert_with(|| CanonicalJsonObject::default().into())
|
||||
{
|
||||
let display_name = if include_display_names {
|
||||
CanonicalJsonValue::String(display_name)
|
||||
} else {
|
||||
unsigned_object.insert(
|
||||
"device_display_name".to_owned(),
|
||||
Some(metadata.device_id.as_str().to_owned()).into(),
|
||||
);
|
||||
}
|
||||
CanonicalJsonValue::String(metadata.device_id.into())
|
||||
};
|
||||
|
||||
unsigned.insert("device_display_name".into(), display_name);
|
||||
}
|
||||
|
||||
*keys = Raw::from_json(serde_json::value::to_raw_value(&object)?);
|
||||
@@ -656,7 +634,7 @@ pub(crate) async fn claim_keys_helper(
|
||||
|
||||
let mut futures: FuturesUnordered<_> = get_over_federation
|
||||
.into_iter()
|
||||
.map(|(server, vec)| async move {
|
||||
.map(async |(server, vec)| {
|
||||
let mut one_time_keys_input_fed = BTreeMap::new();
|
||||
for (user_id, keys) in vec {
|
||||
one_time_keys_input_fed.insert(user_id.clone(), keys.clone());
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
use axum::extract::State;
|
||||
use ruma::{
|
||||
api::client::membership::ban_user,
|
||||
events::room::member::{MembershipState, RoomMemberEventContent},
|
||||
};
|
||||
use tuwunel_core::{Err, Result, matrix::pdu::PduBuilder};
|
||||
use futures::FutureExt;
|
||||
use ruma::api::client::membership::ban_user;
|
||||
use tuwunel_core::{Err, Result};
|
||||
|
||||
use crate::Ruma;
|
||||
|
||||
@@ -20,38 +18,12 @@ pub(crate) async fn ban_user_route(
|
||||
return Err!(Request(Forbidden("You cannot ban yourself.")));
|
||||
}
|
||||
|
||||
let state_lock = services
|
||||
.rooms
|
||||
.state
|
||||
.mutex
|
||||
.lock(&body.room_id)
|
||||
.await;
|
||||
|
||||
let current_member_content = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.get_member(&body.room_id, &body.user_id)
|
||||
.await
|
||||
.unwrap_or_else(|_| RoomMemberEventContent::new(MembershipState::Ban));
|
||||
let state_lock = services.state.mutex.lock(&body.room_id).await;
|
||||
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(body.user_id.to_string(), &RoomMemberEventContent {
|
||||
membership: MembershipState::Ban,
|
||||
reason: body.reason.clone(),
|
||||
displayname: None, // display name may be offensive
|
||||
avatar_url: None, // avatar may be offensive
|
||||
is_direct: None,
|
||||
join_authorized_via_users_server: None,
|
||||
third_party_invite: None,
|
||||
..current_member_content
|
||||
}),
|
||||
sender_user,
|
||||
&body.room_id,
|
||||
&state_lock,
|
||||
)
|
||||
.membership
|
||||
.ban(&body.room_id, &body.user_id, body.reason.as_ref(), sender_user, &state_lock)
|
||||
.boxed()
|
||||
.await?;
|
||||
|
||||
drop(state_lock);
|
||||
|
||||
@@ -21,18 +21,9 @@ pub(crate) async fn forget_room_route(
|
||||
let user_id = body.sender_user();
|
||||
let room_id = &body.room_id;
|
||||
|
||||
let joined = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.is_joined(user_id, room_id);
|
||||
let knocked = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.is_knocked(user_id, room_id);
|
||||
let invited = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.is_invited(user_id, room_id);
|
||||
let joined = services.state_cache.is_joined(user_id, room_id);
|
||||
let knocked = services.state_cache.is_knocked(user_id, room_id);
|
||||
let invited = services.state_cache.is_invited(user_id, room_id);
|
||||
|
||||
pin_mut!(joined, knocked, invited);
|
||||
if joined.or(knocked).or(invited).await {
|
||||
@@ -40,7 +31,6 @@ pub(crate) async fn forget_room_route(
|
||||
}
|
||||
|
||||
let membership = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.get_member(room_id, user_id)
|
||||
.await;
|
||||
@@ -55,15 +45,11 @@ pub(crate) async fn forget_room_route(
|
||||
|
||||
if non_membership
|
||||
|| services
|
||||
.rooms
|
||||
.state_cache
|
||||
.is_left(user_id, room_id)
|
||||
.await
|
||||
{
|
||||
services
|
||||
.rooms
|
||||
.state_cache
|
||||
.forget(room_id, user_id);
|
||||
services.state_cache.forget(room_id, user_id);
|
||||
}
|
||||
|
||||
Ok(forget_room::v3::Response::new())
|
||||
|
||||
@@ -1,19 +1,11 @@
|
||||
use axum::extract::State;
|
||||
use axum_client_ip::InsecureClientIp;
|
||||
use futures::{FutureExt, join};
|
||||
use ruma::{
|
||||
OwnedServerName, RoomId, UserId,
|
||||
api::{client::membership::invite_user, federation::membership::create_invite},
|
||||
events::room::member::{MembershipState, RoomMemberEventContent},
|
||||
};
|
||||
use tuwunel_core::{
|
||||
Err, Result, debug_error, err, info,
|
||||
matrix::{event::gen_event_id_canonical_json, pdu::PduBuilder},
|
||||
};
|
||||
use tuwunel_service::Services;
|
||||
use ruma::{api::client::membership::invite_user, events::room::member::MembershipState};
|
||||
use tuwunel_core::{Err, Result};
|
||||
|
||||
use super::banned_room_check;
|
||||
use crate::Ruma;
|
||||
use crate::{Ruma, client::utils::invite_check};
|
||||
|
||||
/// # `POST /_matrix/client/r0/rooms/{roomId}/invite`
|
||||
///
|
||||
@@ -26,220 +18,56 @@ pub(crate) async fn invite_user_route(
|
||||
) -> Result<invite_user::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
|
||||
if !services.users.is_admin(sender_user).await && services.config.block_non_admin_invites {
|
||||
debug_error!(
|
||||
"User {sender_user} is not an admin and attempted to send an invite to room {}",
|
||||
&body.room_id
|
||||
);
|
||||
return Err!(Request(Forbidden("Invites are not allowed on this server.")));
|
||||
}
|
||||
let room_id = &body.room_id;
|
||||
|
||||
banned_room_check(
|
||||
&services,
|
||||
sender_user,
|
||||
Some(&body.room_id),
|
||||
body.room_id.server_name(),
|
||||
client,
|
||||
)
|
||||
.await?;
|
||||
invite_check(&services, sender_user, room_id).await?;
|
||||
|
||||
match &body.recipient {
|
||||
| invite_user::v3::InvitationRecipient::UserId { user_id } => {
|
||||
let sender_ignored_recipient = services
|
||||
.users
|
||||
.user_is_ignored(sender_user, user_id);
|
||||
let recipient_ignored_by_sender = services
|
||||
.users
|
||||
.user_is_ignored(user_id, sender_user);
|
||||
|
||||
let (sender_ignored_recipient, recipient_ignored_by_sender) =
|
||||
join!(sender_ignored_recipient, recipient_ignored_by_sender);
|
||||
|
||||
if sender_ignored_recipient {
|
||||
return Ok(invite_user::v3::Response {});
|
||||
}
|
||||
|
||||
if let Ok(target_user_membership) = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.get_member(&body.room_id, user_id)
|
||||
.await
|
||||
{
|
||||
if target_user_membership.membership == MembershipState::Ban {
|
||||
return Err!(Request(Forbidden("User is banned from this room.")));
|
||||
}
|
||||
}
|
||||
|
||||
if recipient_ignored_by_sender {
|
||||
// silently drop the invite to the recipient if they've been ignored by the
|
||||
// sender, pretend it worked
|
||||
return Ok(invite_user::v3::Response {});
|
||||
}
|
||||
|
||||
invite_helper(
|
||||
&services,
|
||||
sender_user,
|
||||
user_id,
|
||||
&body.room_id,
|
||||
body.reason.clone(),
|
||||
false,
|
||||
)
|
||||
.boxed()
|
||||
.await?;
|
||||
|
||||
Ok(invite_user::v3::Response {})
|
||||
},
|
||||
| _ => {
|
||||
Err!(Request(NotFound("User not found.")))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn invite_helper(
|
||||
services: &Services,
|
||||
sender_user: &UserId,
|
||||
user_id: &UserId,
|
||||
room_id: &RoomId,
|
||||
reason: Option<String>,
|
||||
is_direct: bool,
|
||||
) -> Result {
|
||||
if !services.users.is_admin(sender_user).await && services.config.block_non_admin_invites {
|
||||
info!(
|
||||
"User {sender_user} is not an admin and attempted to send an invite to room \
|
||||
{room_id}"
|
||||
);
|
||||
return Err!(Request(Forbidden("Invites are not allowed on this server.")));
|
||||
}
|
||||
|
||||
if !services.globals.user_is_local(user_id) {
|
||||
let (pdu, pdu_json, invite_room_state) = {
|
||||
let state_lock = services.rooms.state.mutex.lock(room_id).await;
|
||||
|
||||
let content = RoomMemberEventContent {
|
||||
avatar_url: services.users.avatar_url(user_id).await.ok(),
|
||||
is_direct: Some(is_direct),
|
||||
reason,
|
||||
..RoomMemberEventContent::new(MembershipState::Invite)
|
||||
};
|
||||
|
||||
let (pdu, pdu_json) = services
|
||||
.rooms
|
||||
.timeline
|
||||
.create_hash_and_sign_event(
|
||||
PduBuilder::state(user_id.to_string(), &content),
|
||||
sender_user,
|
||||
room_id,
|
||||
&state_lock,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let invite_room_state = services.rooms.state.summary_stripped(&pdu).await;
|
||||
|
||||
drop(state_lock);
|
||||
|
||||
(pdu, pdu_json, invite_room_state)
|
||||
};
|
||||
|
||||
let room_version_id = services
|
||||
.rooms
|
||||
.state
|
||||
.get_room_version(room_id)
|
||||
.await?;
|
||||
|
||||
let response = services
|
||||
.sending
|
||||
.send_federation_request(user_id.server_name(), create_invite::v2::Request {
|
||||
room_id: room_id.to_owned(),
|
||||
event_id: (*pdu.event_id).to_owned(),
|
||||
room_version: room_version_id.clone(),
|
||||
event: services
|
||||
.sending
|
||||
.convert_to_outgoing_federation_event(pdu_json.clone())
|
||||
.await,
|
||||
invite_room_state,
|
||||
via: services
|
||||
.rooms
|
||||
.state_cache
|
||||
.servers_route_via(room_id)
|
||||
.await
|
||||
.ok(),
|
||||
})
|
||||
.await?;
|
||||
|
||||
// We do not add the event_id field to the pdu here because of signature and
|
||||
// hashes checks
|
||||
let (event_id, value) = gen_event_id_canonical_json(&response.event, &room_version_id)
|
||||
.map_err(|e| {
|
||||
err!(Request(BadJson(warn!("Could not convert event to canonical JSON: {e}"))))
|
||||
})?;
|
||||
|
||||
if pdu.event_id != event_id {
|
||||
return Err!(Request(BadJson(warn!(
|
||||
%pdu.event_id, %event_id,
|
||||
"Server {} sent event with wrong event ID",
|
||||
user_id.server_name()
|
||||
))));
|
||||
}
|
||||
|
||||
let origin: OwnedServerName = serde_json::from_value(serde_json::to_value(
|
||||
value
|
||||
.get("origin")
|
||||
.ok_or_else(|| err!(Request(BadJson("Event missing origin field."))))?,
|
||||
)?)
|
||||
.map_err(|e| {
|
||||
err!(Request(BadJson(warn!("Origin field in event is not a valid server name: {e}"))))
|
||||
})?;
|
||||
|
||||
let pdu_id = services
|
||||
.rooms
|
||||
.event_handler
|
||||
.handle_incoming_pdu(&origin, room_id, &event_id, value, true)
|
||||
.boxed()
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
err!(Request(InvalidParam("Could not accept incoming PDU as timeline event.")))
|
||||
})?;
|
||||
|
||||
return services
|
||||
.sending
|
||||
.send_pdu_room(room_id, &pdu_id)
|
||||
.await;
|
||||
}
|
||||
|
||||
if !services
|
||||
.rooms
|
||||
.state_cache
|
||||
.is_joined(sender_user, room_id)
|
||||
.await
|
||||
{
|
||||
return Err!(Request(Forbidden(
|
||||
"You must be joined in the room you are trying to invite from."
|
||||
)));
|
||||
}
|
||||
|
||||
let state_lock = services.rooms.state.mutex.lock(room_id).await;
|
||||
|
||||
let content = RoomMemberEventContent {
|
||||
displayname: services.users.displayname(user_id).await.ok(),
|
||||
avatar_url: services.users.avatar_url(user_id).await.ok(),
|
||||
blurhash: services.users.blurhash(user_id).await.ok(),
|
||||
is_direct: Some(is_direct),
|
||||
reason,
|
||||
..RoomMemberEventContent::new(MembershipState::Invite)
|
||||
};
|
||||
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(user_id.to_string(), &content),
|
||||
sender_user,
|
||||
room_id,
|
||||
&state_lock,
|
||||
)
|
||||
banned_room_check(&services, sender_user, Some(room_id), room_id.server_name(), client)
|
||||
.await?;
|
||||
|
||||
drop(state_lock);
|
||||
let invite_user::v3::InvitationRecipient::UserId { user_id } = &body.recipient else {
|
||||
return Err!(Request(ThreepidDenied("Third party identifiers are not implemented")));
|
||||
};
|
||||
|
||||
Ok(())
|
||||
let sender_ignored_recipient = services
|
||||
.users
|
||||
.user_is_ignored(sender_user, user_id);
|
||||
|
||||
let recipient_ignored_by_sender = services
|
||||
.users
|
||||
.user_is_ignored(user_id, sender_user);
|
||||
|
||||
let (sender_ignored_recipient, recipient_ignored_by_sender) =
|
||||
join!(sender_ignored_recipient, recipient_ignored_by_sender);
|
||||
|
||||
if sender_ignored_recipient {
|
||||
return Ok(invite_user::v3::Response {});
|
||||
}
|
||||
|
||||
// TODO: this should be in the service, but moving it from here would
|
||||
// trigger the recipient_ignored_by_sender check before the banned check,
|
||||
// revealing the ignore state to the sending user if the recipient is banned
|
||||
if let Ok(target_user_membership) = services
|
||||
.state_accessor
|
||||
.get_member(room_id, user_id)
|
||||
.await
|
||||
{
|
||||
if target_user_membership.membership == MembershipState::Ban {
|
||||
return Err!(Request(Forbidden("User is banned from this room.")));
|
||||
}
|
||||
}
|
||||
|
||||
if recipient_ignored_by_sender {
|
||||
// silently drop the invite to the recipient if they've been ignored by the
|
||||
// sender, pretend it worked
|
||||
return Ok(invite_user::v3::Response {});
|
||||
}
|
||||
|
||||
services
|
||||
.membership
|
||||
.invite(sender_user, user_id, room_id, body.reason.as_ref(), false)
|
||||
.boxed()
|
||||
.await?;
|
||||
|
||||
Ok(invite_user::v3::Response {})
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,7 @@
|
||||
use axum::extract::State;
|
||||
use ruma::{
|
||||
api::client::membership::kick_user,
|
||||
events::room::member::{MembershipState, RoomMemberEventContent},
|
||||
};
|
||||
use tuwunel_core::{Err, Result, matrix::pdu::PduBuilder};
|
||||
use futures::FutureExt;
|
||||
use ruma::api::client::membership::kick_user;
|
||||
use tuwunel_core::{Err, Result};
|
||||
|
||||
use crate::Ruma;
|
||||
|
||||
@@ -14,50 +12,18 @@ pub(crate) async fn kick_user_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<kick_user::v3::Request>,
|
||||
) -> Result<kick_user::v3::Response> {
|
||||
let state_lock = services
|
||||
.rooms
|
||||
.state
|
||||
.mutex
|
||||
.lock(&body.room_id)
|
||||
.await;
|
||||
let sender_user = body.sender_user();
|
||||
|
||||
let Ok(event) = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.get_member(&body.room_id, &body.user_id)
|
||||
.await
|
||||
else {
|
||||
// copy synapse's behaviour of returning 200 without any change to the state
|
||||
// instead of erroring on left users
|
||||
return Ok(kick_user::v3::Response::new());
|
||||
};
|
||||
|
||||
if !matches!(
|
||||
event.membership,
|
||||
MembershipState::Invite | MembershipState::Knock | MembershipState::Join,
|
||||
) {
|
||||
return Err!(Request(Forbidden(
|
||||
"Cannot kick a user who is not apart of the room (current membership: {})",
|
||||
event.membership
|
||||
)));
|
||||
if sender_user == body.user_id {
|
||||
return Err!(Request(Forbidden("You cannot kick yourself.")));
|
||||
}
|
||||
|
||||
let state_lock = services.state.mutex.lock(&body.room_id).await;
|
||||
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(body.user_id.to_string(), &RoomMemberEventContent {
|
||||
membership: MembershipState::Leave,
|
||||
reason: body.reason.clone(),
|
||||
is_direct: None,
|
||||
join_authorized_via_users_server: None,
|
||||
third_party_invite: None,
|
||||
..event
|
||||
}),
|
||||
body.sender_user(),
|
||||
&body.room_id,
|
||||
&state_lock,
|
||||
)
|
||||
.membership
|
||||
.kick(&body.room_id, &body.user_id, body.reason.as_ref(), sender_user, &state_lock)
|
||||
.boxed()
|
||||
.await?;
|
||||
|
||||
drop(state_lock);
|
||||
|
||||
@@ -4,11 +4,14 @@
|
||||
use axum_client_ip::InsecureClientIp;
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use ruma::{
|
||||
CanonicalJsonObject, CanonicalJsonValue, OwnedEventId, OwnedRoomId, OwnedServerName, RoomId,
|
||||
CanonicalJsonObject, CanonicalJsonValue, OwnedEventId, OwnedServerName, RoomId,
|
||||
RoomVersionId, UserId,
|
||||
api::{
|
||||
client::knock::knock_room,
|
||||
federation::{self},
|
||||
federation::{
|
||||
membership::RawStrippedState,
|
||||
{self},
|
||||
},
|
||||
},
|
||||
canonical_json::to_canonical_value,
|
||||
events::{
|
||||
@@ -17,14 +20,14 @@
|
||||
},
|
||||
};
|
||||
use tuwunel_core::{
|
||||
Err, Result, debug, debug_info, debug_warn, err, info,
|
||||
Err, Result, debug, debug_info, debug_warn, err, extract_variant, info,
|
||||
matrix::{
|
||||
PduCount,
|
||||
event::{Event, gen_event_id},
|
||||
pdu::{PduBuilder, PduEvent},
|
||||
},
|
||||
result::FlatOk,
|
||||
trace,
|
||||
utils::{self, shuffle, stream::IterStream},
|
||||
utils::{self},
|
||||
warn,
|
||||
};
|
||||
use tuwunel_service::{
|
||||
@@ -36,7 +39,7 @@
|
||||
};
|
||||
|
||||
use super::banned_room_check;
|
||||
use crate::Ruma;
|
||||
use crate::{Ruma, client::membership::get_join_params};
|
||||
|
||||
/// # `POST /_matrix/client/*/knock/{roomIdOrAlias}`
|
||||
///
|
||||
@@ -50,102 +53,13 @@ pub(crate) async fn knock_room_route(
|
||||
let sender_user = body.sender_user();
|
||||
let body = &body.body;
|
||||
|
||||
let (servers, room_id) = match OwnedRoomId::try_from(body.room_id_or_alias.clone()) {
|
||||
| Ok(room_id) => {
|
||||
banned_room_check(
|
||||
&services,
|
||||
sender_user,
|
||||
Some(&room_id),
|
||||
room_id.server_name(),
|
||||
client,
|
||||
)
|
||||
.await?;
|
||||
let (room_id, servers) =
|
||||
get_join_params(&services, sender_user, &body.room_id_or_alias, &body.via).await?;
|
||||
|
||||
let mut servers = body.via.clone();
|
||||
servers.extend(
|
||||
services
|
||||
.rooms
|
||||
.state_cache
|
||||
.servers_invite_via(&room_id)
|
||||
.map(ToOwned::to_owned)
|
||||
.collect::<Vec<_>>()
|
||||
.await,
|
||||
);
|
||||
banned_room_check(&services, sender_user, Some(&room_id), room_id.server_name(), client)
|
||||
.await?;
|
||||
|
||||
servers.extend(
|
||||
services
|
||||
.rooms
|
||||
.state_cache
|
||||
.invite_state(sender_user, &room_id)
|
||||
.await
|
||||
.unwrap_or_default()
|
||||
.iter()
|
||||
.filter_map(|event| event.get_field("sender").ok().flatten())
|
||||
.filter_map(|sender: &str| UserId::parse(sender).ok())
|
||||
.map(|user| user.server_name().to_owned()),
|
||||
);
|
||||
|
||||
if let Some(server) = room_id.server_name() {
|
||||
servers.push(server.to_owned());
|
||||
}
|
||||
|
||||
servers.sort_unstable();
|
||||
servers.dedup();
|
||||
shuffle(&mut servers);
|
||||
|
||||
(servers, room_id)
|
||||
},
|
||||
| Err(room_alias) => {
|
||||
let (room_id, mut servers) = services
|
||||
.rooms
|
||||
.alias
|
||||
.resolve_alias(&room_alias, Some(body.via.clone()))
|
||||
.await?;
|
||||
|
||||
banned_room_check(
|
||||
&services,
|
||||
sender_user,
|
||||
Some(&room_id),
|
||||
Some(room_alias.server_name()),
|
||||
client,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let addl_via_servers = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.servers_invite_via(&room_id)
|
||||
.map(ToOwned::to_owned);
|
||||
|
||||
let addl_state_servers = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.invite_state(sender_user, &room_id)
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
|
||||
let mut addl_servers: Vec<_> = addl_state_servers
|
||||
.iter()
|
||||
.map(|event| event.get_field("sender"))
|
||||
.filter_map(FlatOk::flat_ok)
|
||||
.map(|user: &UserId| user.server_name().to_owned())
|
||||
.stream()
|
||||
.chain(addl_via_servers)
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
addl_servers.sort_unstable();
|
||||
addl_servers.dedup();
|
||||
shuffle(&mut addl_servers);
|
||||
servers.append(&mut addl_servers);
|
||||
|
||||
(servers, room_id)
|
||||
},
|
||||
};
|
||||
|
||||
knock_room_by_id_helper(&services, sender_user, &room_id, body.reason.clone(), &servers)
|
||||
.boxed()
|
||||
.await
|
||||
knock_room_by_id_helper(&services, sender_user, &room_id, body.reason.clone(), &servers).await
|
||||
}
|
||||
|
||||
async fn knock_room_by_id_helper(
|
||||
@@ -155,10 +69,9 @@ async fn knock_room_by_id_helper(
|
||||
reason: Option<String>,
|
||||
servers: &[OwnedServerName],
|
||||
) -> Result<knock_room::v3::Response> {
|
||||
let state_lock = services.rooms.state.mutex.lock(room_id).await;
|
||||
let state_lock = services.state.mutex.lock(room_id).await;
|
||||
|
||||
if services
|
||||
.rooms
|
||||
.state_cache
|
||||
.is_invited(sender_user, room_id)
|
||||
.await
|
||||
@@ -170,7 +83,6 @@ async fn knock_room_by_id_helper(
|
||||
}
|
||||
|
||||
if services
|
||||
.rooms
|
||||
.state_cache
|
||||
.is_joined(sender_user, room_id)
|
||||
.await
|
||||
@@ -180,7 +92,6 @@ async fn knock_room_by_id_helper(
|
||||
}
|
||||
|
||||
if services
|
||||
.rooms
|
||||
.state_cache
|
||||
.is_knocked(sender_user, room_id)
|
||||
.await
|
||||
@@ -190,7 +101,6 @@ async fn knock_room_by_id_helper(
|
||||
}
|
||||
|
||||
if let Ok(membership) = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.get_member(room_id, sender_user)
|
||||
.await
|
||||
@@ -202,7 +112,6 @@ async fn knock_room_by_id_helper(
|
||||
}
|
||||
|
||||
let server_in_room = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.server_in_room(services.globals.server_name(), room_id)
|
||||
.await;
|
||||
@@ -234,11 +143,7 @@ async fn knock_room_helper_local(
|
||||
) -> Result {
|
||||
debug_info!("We can knock locally");
|
||||
|
||||
let room_version_id = services
|
||||
.rooms
|
||||
.state
|
||||
.get_room_version(room_id)
|
||||
.await?;
|
||||
let room_version_id = services.state.get_room_version(room_id).await?;
|
||||
|
||||
if matches!(
|
||||
room_version_id,
|
||||
@@ -262,7 +167,6 @@ async fn knock_room_helper_local(
|
||||
|
||||
// Try normal knock first
|
||||
let Err(error) = services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(sender_user.to_string(), &content),
|
||||
@@ -346,12 +250,12 @@ async fn knock_room_helper_local(
|
||||
let knock_event = knock_event_stub;
|
||||
|
||||
info!("Asking {remote_server} for send_knock in room {room_id}");
|
||||
let send_knock_request = federation::knock::send_knock::v1::Request {
|
||||
let send_knock_request = federation::membership::create_knock_event::v1::Request {
|
||||
room_id: room_id.to_owned(),
|
||||
event_id: event_id.clone(),
|
||||
pdu: services
|
||||
.sending
|
||||
.convert_to_outgoing_federation_event(knock_event.clone())
|
||||
.federation
|
||||
.format_pdu_into(knock_event.clone(), Some(&room_version_id))
|
||||
.await,
|
||||
};
|
||||
|
||||
@@ -363,7 +267,6 @@ async fn knock_room_helper_local(
|
||||
info!("send_knock finished");
|
||||
|
||||
services
|
||||
.rooms
|
||||
.short
|
||||
.get_or_create_shortroomid(room_id)
|
||||
.await;
|
||||
@@ -374,8 +277,8 @@ 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
|
||||
.rooms
|
||||
.state_cache
|
||||
.update_membership(
|
||||
room_id,
|
||||
@@ -384,15 +287,21 @@ async fn knock_room_helper_local(
|
||||
.get_content::<RoomMemberEventContent>()
|
||||
.expect("we just created this"),
|
||||
sender_user,
|
||||
Some(send_knock_response.knock_room_state),
|
||||
Some(
|
||||
send_knock_response
|
||||
.knock_room_state
|
||||
.into_iter()
|
||||
.filter_map(|s| extract_variant!(s, RawStrippedState::Stripped))
|
||||
.collect(),
|
||||
),
|
||||
None,
|
||||
false,
|
||||
PduCount::Normal(*count),
|
||||
)
|
||||
.await?;
|
||||
|
||||
info!("Appending room knock event locally");
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.append_pdu(
|
||||
&parsed_knock_pdu,
|
||||
@@ -477,12 +386,12 @@ async fn knock_room_helper_remote(
|
||||
let knock_event = knock_event_stub;
|
||||
|
||||
info!("Asking {remote_server} for send_knock in room {room_id}");
|
||||
let send_knock_request = federation::knock::send_knock::v1::Request {
|
||||
let send_knock_request = federation::membership::create_knock_event::v1::Request {
|
||||
room_id: room_id.to_owned(),
|
||||
event_id: event_id.clone(),
|
||||
pdu: services
|
||||
.sending
|
||||
.convert_to_outgoing_federation_event(knock_event.clone())
|
||||
.federation
|
||||
.format_pdu_into(knock_event.clone(), Some(&room_version_id))
|
||||
.await,
|
||||
};
|
||||
|
||||
@@ -494,7 +403,6 @@ async fn knock_room_helper_remote(
|
||||
info!("send_knock finished");
|
||||
|
||||
services
|
||||
.rooms
|
||||
.short
|
||||
.get_or_create_shortroomid(room_id)
|
||||
.await;
|
||||
@@ -507,7 +415,14 @@ async fn knock_room_helper_remote(
|
||||
let state = send_knock_response
|
||||
.knock_room_state
|
||||
.iter()
|
||||
.map(|event| serde_json::from_str::<CanonicalJsonObject>(event.clone().into_json().get()))
|
||||
.map(|event| {
|
||||
serde_json::from_str::<CanonicalJsonObject>(
|
||||
extract_variant!(event.clone(), RawStrippedState::Stripped)
|
||||
.expect("Raw<AnyStrippedStateEvent>")
|
||||
.json()
|
||||
.get(),
|
||||
)
|
||||
})
|
||||
.filter_map(Result::ok);
|
||||
|
||||
let mut state_map: HashMap<u64, OwnedEventId> = HashMap::new();
|
||||
@@ -534,21 +449,19 @@ async fn knock_room_helper_remote(
|
||||
|
||||
let event_id = gen_event_id(&event, &room_version_id)?;
|
||||
let shortstatekey = services
|
||||
.rooms
|
||||
.short
|
||||
.get_or_create_shortstatekey(&event_type, &state_key)
|
||||
.await;
|
||||
|
||||
services
|
||||
.rooms
|
||||
.outlier
|
||||
.timeline
|
||||
.add_pdu_outlier(&event_id, &event);
|
||||
|
||||
state_map.insert(shortstatekey, event_id.clone());
|
||||
}
|
||||
|
||||
info!("Compressing state from send_knock");
|
||||
let compressed: CompressedState = services
|
||||
.rooms
|
||||
.state_compressor
|
||||
.compress_state_events(
|
||||
state_map
|
||||
@@ -564,27 +477,24 @@ async fn knock_room_helper_remote(
|
||||
added,
|
||||
removed,
|
||||
} = services
|
||||
.rooms
|
||||
.state_compressor
|
||||
.save_state(room_id, Arc::new(compressed))
|
||||
.await?;
|
||||
|
||||
debug!("Forcing state for new room");
|
||||
services
|
||||
.rooms
|
||||
.state
|
||||
.force_state(room_id, statehash_before_knock, added, removed, &state_lock)
|
||||
.await?;
|
||||
|
||||
let statehash_after_knock = services
|
||||
.rooms
|
||||
.state
|
||||
.append_to_state(&parsed_knock_pdu)
|
||||
.await?;
|
||||
|
||||
info!("Updating membership locally to knock state with provided stripped state events");
|
||||
let count = services.globals.next_count();
|
||||
services
|
||||
.rooms
|
||||
.state_cache
|
||||
.update_membership(
|
||||
room_id,
|
||||
@@ -593,15 +503,21 @@ async fn knock_room_helper_remote(
|
||||
.get_content::<RoomMemberEventContent>()
|
||||
.expect("we just created this"),
|
||||
sender_user,
|
||||
Some(send_knock_response.knock_room_state),
|
||||
Some(
|
||||
send_knock_response
|
||||
.knock_room_state
|
||||
.into_iter()
|
||||
.filter_map(|s| extract_variant!(s, RawStrippedState::Stripped))
|
||||
.collect(),
|
||||
),
|
||||
None,
|
||||
false,
|
||||
PduCount::Normal(*count),
|
||||
)
|
||||
.await?;
|
||||
|
||||
info!("Appending room knock event locally");
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.append_pdu(
|
||||
&parsed_knock_pdu,
|
||||
@@ -615,7 +531,6 @@ async fn knock_room_helper_remote(
|
||||
// We set the room state after inserting the pdu, so that we never have a moment
|
||||
// in time where events in the current room state do not exist
|
||||
services
|
||||
.rooms
|
||||
.state
|
||||
.set_room_state(room_id, statehash_after_knock, &state_lock);
|
||||
|
||||
@@ -627,7 +542,7 @@ async fn make_knock_request(
|
||||
sender_user: &UserId,
|
||||
room_id: &RoomId,
|
||||
servers: &[OwnedServerName],
|
||||
) -> Result<(federation::knock::create_knock_event_template::v1::Response, OwnedServerName)> {
|
||||
) -> Result<(federation::membership::prepare_knock_event::v1::Response, OwnedServerName)> {
|
||||
let mut make_knock_response_and_server =
|
||||
Err!(BadServerResponse("No server available to assist in knocking."));
|
||||
|
||||
@@ -644,7 +559,7 @@ async fn make_knock_request(
|
||||
.sending
|
||||
.send_federation_request(
|
||||
remote_server,
|
||||
federation::knock::create_knock_event_template::v1::Request {
|
||||
federation::membership::prepare_knock_event::v1::Request {
|
||||
room_id: room_id.to_owned(),
|
||||
user_id: sender_user.to_owned(),
|
||||
ver: services
|
||||
|
||||
@@ -1,25 +1,7 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use axum::extract::State;
|
||||
use futures::{FutureExt, StreamExt, TryFutureExt, pin_mut};
|
||||
use ruma::{
|
||||
CanonicalJsonObject, CanonicalJsonValue, OwnedServerName, RoomId, RoomVersionId, UserId,
|
||||
api::{
|
||||
client::membership::leave_room,
|
||||
federation::{self},
|
||||
},
|
||||
events::{
|
||||
StateEventType,
|
||||
room::member::{MembershipState, RoomMemberEventContent},
|
||||
},
|
||||
};
|
||||
use tuwunel_core::{
|
||||
Err, Result, debug_info, debug_warn, err,
|
||||
matrix::{event::gen_event_id, pdu::PduBuilder},
|
||||
utils::{self, FutureBoolExt, future::ReadyEqExt},
|
||||
warn,
|
||||
};
|
||||
use tuwunel_service::Services;
|
||||
use futures::FutureExt;
|
||||
use ruma::api::client::membership::leave_room;
|
||||
use tuwunel_core::Result;
|
||||
|
||||
use crate::Ruma;
|
||||
|
||||
@@ -32,366 +14,21 @@ pub(crate) async fn leave_room_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<leave_room::v3::Request>,
|
||||
) -> Result<leave_room::v3::Response> {
|
||||
leave_room(&services, body.sender_user(), &body.room_id, body.reason.clone())
|
||||
let state_lock = services.state.mutex.lock(&body.room_id).await;
|
||||
|
||||
services
|
||||
.membership
|
||||
.leave(body.sender_user(), &body.room_id, body.reason.clone(), false, &state_lock)
|
||||
.boxed()
|
||||
.await
|
||||
.map(|()| leave_room::v3::Response::new())
|
||||
}
|
||||
|
||||
// Make a user leave all their joined rooms, rescinds knocks, forgets all rooms,
|
||||
// and ignores errors
|
||||
pub async fn leave_all_rooms(services: &Services, user_id: &UserId) {
|
||||
let rooms_joined = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.rooms_joined(user_id)
|
||||
.map(ToOwned::to_owned);
|
||||
|
||||
let rooms_invited = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.rooms_invited(user_id)
|
||||
.map(|(r, _)| r);
|
||||
|
||||
let rooms_knocked = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.rooms_knocked(user_id)
|
||||
.map(|(r, _)| r);
|
||||
|
||||
let all_rooms: Vec<_> = rooms_joined
|
||||
.chain(rooms_invited)
|
||||
.chain(rooms_knocked)
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
for room_id in all_rooms {
|
||||
// ignore errors
|
||||
if let Err(e) = leave_room(services, user_id, &room_id, None)
|
||||
.boxed()
|
||||
.await
|
||||
{
|
||||
warn!(%user_id, "Failed to leave {room_id} remotely: {e}");
|
||||
}
|
||||
|
||||
services
|
||||
.rooms
|
||||
.state_cache
|
||||
.forget(&room_id, user_id);
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn leave_room(
|
||||
services: &Services,
|
||||
user_id: &UserId,
|
||||
room_id: &RoomId,
|
||||
reason: Option<String>,
|
||||
) -> Result {
|
||||
let default_member_content = RoomMemberEventContent {
|
||||
membership: MembershipState::Leave,
|
||||
reason: reason.clone(),
|
||||
join_authorized_via_users_server: None,
|
||||
is_direct: None,
|
||||
avatar_url: None,
|
||||
displayname: None,
|
||||
third_party_invite: None,
|
||||
blurhash: None,
|
||||
};
|
||||
|
||||
let is_banned = services.rooms.metadata.is_banned(room_id);
|
||||
let is_disabled = services.rooms.metadata.is_disabled(room_id);
|
||||
|
||||
pin_mut!(is_banned, is_disabled);
|
||||
if is_banned.or(is_disabled).await {
|
||||
// the room is banned/disabled, the room must be rejected locally since we
|
||||
// cant/dont want to federate with this server
|
||||
services
|
||||
.rooms
|
||||
.state_cache
|
||||
.update_membership(
|
||||
room_id,
|
||||
user_id,
|
||||
default_member_content,
|
||||
user_id,
|
||||
None,
|
||||
None,
|
||||
true,
|
||||
)
|
||||
.await?;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let dont_have_room = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.server_in_room(services.globals.server_name(), room_id)
|
||||
.eq(&false);
|
||||
|
||||
let not_knocked = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.is_knocked(user_id, room_id)
|
||||
.eq(&false);
|
||||
|
||||
// Ask a remote server if we don't have this room and are not knocking on it
|
||||
if dont_have_room.and(not_knocked).await {
|
||||
if let Err(e) = remote_leave_room(services, user_id, room_id)
|
||||
.boxed()
|
||||
.await
|
||||
{
|
||||
warn!(%user_id, "Failed to leave room {room_id} remotely: {e}");
|
||||
// Don't tell the client about this error
|
||||
}
|
||||
|
||||
let last_state = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.invite_state(user_id, room_id)
|
||||
.or_else(|_| {
|
||||
services
|
||||
.rooms
|
||||
.state_cache
|
||||
.knock_state(user_id, room_id)
|
||||
})
|
||||
.or_else(|_| {
|
||||
services
|
||||
.rooms
|
||||
.state_cache
|
||||
.left_state(user_id, room_id)
|
||||
})
|
||||
.await
|
||||
.ok();
|
||||
|
||||
// We always drop the invite, we can't rely on other servers
|
||||
services
|
||||
.rooms
|
||||
.state_cache
|
||||
.update_membership(
|
||||
room_id,
|
||||
user_id,
|
||||
default_member_content,
|
||||
user_id,
|
||||
last_state,
|
||||
None,
|
||||
true,
|
||||
)
|
||||
.await?;
|
||||
} else {
|
||||
let state_lock = services.rooms.state.mutex.lock(room_id).await;
|
||||
|
||||
let Ok(event) = services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_get_content::<RoomMemberEventContent>(
|
||||
room_id,
|
||||
&StateEventType::RoomMember,
|
||||
user_id.as_str(),
|
||||
)
|
||||
.await
|
||||
else {
|
||||
debug_warn!(
|
||||
"Trying to leave a room you are not a member of, marking room as left locally."
|
||||
);
|
||||
|
||||
return services
|
||||
.rooms
|
||||
.state_cache
|
||||
.update_membership(
|
||||
room_id,
|
||||
user_id,
|
||||
default_member_content,
|
||||
user_id,
|
||||
None,
|
||||
None,
|
||||
true,
|
||||
)
|
||||
.await;
|
||||
};
|
||||
|
||||
services
|
||||
.rooms
|
||||
.timeline
|
||||
.build_and_append_pdu(
|
||||
PduBuilder::state(user_id.to_string(), &RoomMemberEventContent {
|
||||
membership: MembershipState::Leave,
|
||||
reason,
|
||||
join_authorized_via_users_server: None,
|
||||
is_direct: None,
|
||||
..event
|
||||
}),
|
||||
user_id,
|
||||
room_id,
|
||||
&state_lock,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn remote_leave_room(
|
||||
services: &Services,
|
||||
user_id: &UserId,
|
||||
room_id: &RoomId,
|
||||
) -> Result<()> {
|
||||
let mut make_leave_response_and_server =
|
||||
Err!(BadServerResponse("No remote server available to assist in leaving {room_id}."));
|
||||
|
||||
let mut servers: HashSet<OwnedServerName> = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.servers_invite_via(room_id)
|
||||
.map(ToOwned::to_owned)
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
match services
|
||||
.rooms
|
||||
.state_cache
|
||||
.invite_state(user_id, room_id)
|
||||
.await
|
||||
{
|
||||
| Ok(invite_state) => {
|
||||
servers.extend(
|
||||
invite_state
|
||||
.iter()
|
||||
.filter_map(|event| event.get_field("sender").ok().flatten())
|
||||
.filter_map(|sender: &str| UserId::parse(sender).ok())
|
||||
.map(|user| user.server_name().to_owned()),
|
||||
);
|
||||
},
|
||||
| _ => {
|
||||
match services
|
||||
.rooms
|
||||
.state_cache
|
||||
.knock_state(user_id, room_id)
|
||||
.await
|
||||
{
|
||||
| Ok(knock_state) => {
|
||||
servers.extend(
|
||||
knock_state
|
||||
.iter()
|
||||
.filter_map(|event| event.get_field("sender").ok().flatten())
|
||||
.filter_map(|sender: &str| UserId::parse(sender).ok())
|
||||
.filter_map(|sender| {
|
||||
if !services.globals.user_is_local(sender) {
|
||||
Some(sender.server_name().to_owned())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}),
|
||||
);
|
||||
},
|
||||
| _ => {},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
if let Some(room_id_server_name) = room_id.server_name() {
|
||||
servers.insert(room_id_server_name.to_owned());
|
||||
}
|
||||
|
||||
debug_info!("servers in remote_leave_room: {servers:?}");
|
||||
|
||||
for remote_server in servers {
|
||||
let make_leave_response = services
|
||||
.sending
|
||||
.send_federation_request(
|
||||
&remote_server,
|
||||
federation::membership::prepare_leave_event::v1::Request {
|
||||
room_id: room_id.to_owned(),
|
||||
user_id: user_id.to_owned(),
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
make_leave_response_and_server = make_leave_response.map(|r| (r, remote_server));
|
||||
|
||||
if make_leave_response_and_server.is_ok() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let (make_leave_response, remote_server) = make_leave_response_and_server?;
|
||||
|
||||
let Some(room_version_id) = make_leave_response.room_version else {
|
||||
return Err!(BadServerResponse(warn!(
|
||||
"No room version was returned by {remote_server} for {room_id}, room version is \
|
||||
likely not supported by tuwunel"
|
||||
)));
|
||||
};
|
||||
|
||||
if !services
|
||||
.server
|
||||
.supported_room_version(&room_version_id)
|
||||
{
|
||||
return Err!(BadServerResponse(warn!(
|
||||
"Remote room version {room_version_id} for {room_id} is not supported by conduwuit",
|
||||
)));
|
||||
}
|
||||
|
||||
let mut leave_event_stub = serde_json::from_str::<CanonicalJsonObject>(
|
||||
make_leave_response.event.get(),
|
||||
)
|
||||
.map_err(|e| {
|
||||
err!(BadServerResponse(warn!(
|
||||
"Invalid make_leave event json received from {remote_server} for {room_id}: {e:?}"
|
||||
)))
|
||||
})?;
|
||||
|
||||
// TODO: Is origin needed?
|
||||
leave_event_stub.insert(
|
||||
"origin".to_owned(),
|
||||
CanonicalJsonValue::String(services.globals.server_name().as_str().to_owned()),
|
||||
);
|
||||
leave_event_stub.insert(
|
||||
"origin_server_ts".to_owned(),
|
||||
CanonicalJsonValue::Integer(
|
||||
utils::millis_since_unix_epoch()
|
||||
.try_into()
|
||||
.expect("Timestamp is valid js_int value"),
|
||||
),
|
||||
);
|
||||
|
||||
// room v3 and above removed the "event_id" field from remote PDU format
|
||||
match room_version_id {
|
||||
| RoomVersionId::V1 | RoomVersionId::V2 => {},
|
||||
| _ => {
|
||||
leave_event_stub.remove("event_id");
|
||||
},
|
||||
}
|
||||
|
||||
// In order to create a compatible ref hash (EventID) the `hashes` field needs
|
||||
// to be present
|
||||
services
|
||||
.server_keys
|
||||
.hash_and_sign_event(&mut leave_event_stub, &room_version_id)?;
|
||||
|
||||
// Generate event id
|
||||
let event_id = gen_event_id(&leave_event_stub, &room_version_id)?;
|
||||
|
||||
// Add event_id back
|
||||
leave_event_stub
|
||||
.insert("event_id".to_owned(), CanonicalJsonValue::String(event_id.clone().into()));
|
||||
|
||||
// It has enough fields to be called a proper event now
|
||||
let leave_event = leave_event_stub;
|
||||
|
||||
services
|
||||
.sending
|
||||
.send_federation_request(
|
||||
&remote_server,
|
||||
federation::membership::create_leave_event::v2::Request {
|
||||
room_id: room_id.to_owned(),
|
||||
event_id,
|
||||
pdu: services
|
||||
.sending
|
||||
.convert_to_outgoing_federation_event(leave_event.clone())
|
||||
.await,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
if services.config.delete_rooms_after_leave {
|
||||
services
|
||||
.delete
|
||||
.delete_if_empty_local(&body.room_id, state_lock)
|
||||
.boxed()
|
||||
.await;
|
||||
}
|
||||
|
||||
Ok(leave_room::v3::Response {})
|
||||
}
|
||||
|
||||
@@ -1,21 +1,24 @@
|
||||
use axum::extract::State;
|
||||
use futures::{FutureExt, StreamExt, future::join};
|
||||
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::{
|
||||
StateEventType,
|
||||
room::member::{MembershipState, RoomMemberEventContent},
|
||||
room::{
|
||||
history_visibility::{HistoryVisibility, RoomHistoryVisibilityEventContent},
|
||||
member::{MembershipState, RoomMemberEventContent},
|
||||
},
|
||||
},
|
||||
};
|
||||
use tuwunel_core::{
|
||||
Err, Result, at,
|
||||
matrix::Event,
|
||||
utils::{
|
||||
future::TryExtExt,
|
||||
stream::{BroadbandExt, ReadyExt},
|
||||
future::{BoolExt, TryExtExt},
|
||||
stream::ReadyExt,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -31,22 +34,20 @@ pub(crate) async fn get_member_events_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<get_member_events::v3::Request>,
|
||||
) -> Result<get_member_events::v3::Response> {
|
||||
let sender_user = body.sender_user();
|
||||
let membership = body.membership.as_ref();
|
||||
let not_membership = body.not_membership.as_ref();
|
||||
|
||||
if !services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.user_can_see_state_events(sender_user, &body.room_id)
|
||||
.user_can_see_state_events(body.sender_user(), &body.room_id)
|
||||
.await
|
||||
{
|
||||
return Err!(Request(Forbidden("You don't have permission to view this room.")));
|
||||
return Err!(Request(Forbidden(
|
||||
"You aren't a member of the room and weren't previously a member of the room."
|
||||
)));
|
||||
}
|
||||
|
||||
let membership = body.membership.as_ref();
|
||||
let not_membership = body.not_membership.as_ref();
|
||||
Ok(get_member_events::v3::Response {
|
||||
chunk: services
|
||||
.rooms
|
||||
.state_accessor
|
||||
.room_state_full(&body.room_id)
|
||||
.ready_filter_map(Result::ok)
|
||||
@@ -70,53 +71,64 @@ pub(crate) async fn joined_members_route(
|
||||
State(services): State<crate::State>,
|
||||
body: Ruma<joined_members::v3::Request>,
|
||||
) -> Result<joined_members::v3::Response> {
|
||||
if !services
|
||||
.rooms
|
||||
let is_joined = services
|
||||
.state_cache
|
||||
.is_joined(body.sender_user(), &body.room_id);
|
||||
|
||||
let is_world_readable = services
|
||||
.state_accessor
|
||||
.user_can_see_state_events(body.sender_user(), &body.room_id)
|
||||
.await
|
||||
{
|
||||
return Err!(Request(Forbidden("You don't have permission to view this room.")));
|
||||
.room_state_get_content(&body.room_id, &StateEventType::RoomHistoryVisibility, "")
|
||||
.map_ok_or(false, |c: RoomHistoryVisibilityEventContent| {
|
||||
c.history_visibility == HistoryVisibility::WorldReadable
|
||||
});
|
||||
|
||||
pin_mut!(is_joined, is_world_readable);
|
||||
if !is_joined.or(is_world_readable).await {
|
||||
return Err!(Request(Forbidden("You aren't a member of the room.")));
|
||||
}
|
||||
|
||||
Ok(joined_members::v3::Response {
|
||||
joined: services
|
||||
.rooms
|
||||
.state_cache
|
||||
.room_members(&body.room_id)
|
||||
.map(ToOwned::to_owned)
|
||||
.broad_then(|user_id| async move {
|
||||
let (display_name, avatar_url) = join(
|
||||
services.users.displayname(&user_id).ok(),
|
||||
services.users.avatar_url(&user_id).ok(),
|
||||
)
|
||||
.await;
|
||||
.state_accessor
|
||||
.room_state_full(&body.room_id)
|
||||
.ready_filter_map(Result::ok)
|
||||
.ready_filter(|((ty, _), _)| *ty == StateEventType::RoomMember)
|
||||
.map(at!(1))
|
||||
.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();
|
||||
let member = RoomMember {
|
||||
display_name: content.displayname,
|
||||
avatar_url: content.avatar_url,
|
||||
};
|
||||
|
||||
(user_id, RoomMember { display_name, avatar_url })
|
||||
Some((sender, member))
|
||||
})
|
||||
.collect()
|
||||
.boxed()
|
||||
.await,
|
||||
})
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
|
||||
@@ -8,18 +8,21 @@
|
||||
mod members;
|
||||
mod unban;
|
||||
|
||||
use std::net::IpAddr;
|
||||
use std::{cmp::Ordering, net::IpAddr};
|
||||
|
||||
use axum::extract::State;
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use ruma::{OwnedRoomId, RoomId, ServerName, UserId, api::client::membership::joined_rooms};
|
||||
use tuwunel_core::{Err, Result, warn};
|
||||
use ruma::{
|
||||
OwnedRoomId, OwnedServerName, RoomId, RoomOrAliasId, ServerName, UserId,
|
||||
api::client::membership::joined_rooms,
|
||||
};
|
||||
use tuwunel_core::{Err, Result, result::LogErr, utils::shuffle, warn};
|
||||
use tuwunel_service::Services;
|
||||
|
||||
pub(crate) use self::{
|
||||
ban::ban_user_route,
|
||||
forget::forget_room_route,
|
||||
invite::{invite_helper, invite_user_route},
|
||||
invite::invite_user_route,
|
||||
join::{join_room_by_id_or_alias_route, join_room_by_id_route},
|
||||
kick::kick_user_route,
|
||||
knock::knock_room_route,
|
||||
@@ -27,11 +30,7 @@
|
||||
members::{get_member_events_route, joined_members_route},
|
||||
unban::unban_user_route,
|
||||
};
|
||||
pub use self::{
|
||||
join::join_room_by_id_helper,
|
||||
leave::{leave_all_rooms, leave_room},
|
||||
};
|
||||
use crate::{Ruma, client::full_user_deactivate};
|
||||
use crate::Ruma;
|
||||
|
||||
/// # `POST /_matrix/client/r0/joined_rooms`
|
||||
///
|
||||
@@ -42,7 +41,6 @@ pub(crate) async fn joined_rooms_route(
|
||||
) -> Result<joined_rooms::v3::Response> {
|
||||
Ok(joined_rooms::v3::Response {
|
||||
joined_rooms: services
|
||||
.rooms
|
||||
.state_cache
|
||||
.rooms_joined(body.sender_user())
|
||||
.map(ToOwned::to_owned)
|
||||
@@ -68,53 +66,28 @@ pub(crate) async fn banned_room_check(
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// TODO: weird condition
|
||||
if let Some(room_id) = room_id {
|
||||
if services.rooms.metadata.is_banned(room_id).await
|
||||
|| services
|
||||
.config
|
||||
.forbidden_remote_server_names
|
||||
.is_match(
|
||||
room_id
|
||||
.server_name()
|
||||
.expect("legacy room mxid")
|
||||
.host(),
|
||||
) {
|
||||
if services.metadata.is_banned(room_id).await
|
||||
|| (room_id.server_name().is_some()
|
||||
&& services
|
||||
.config
|
||||
.forbidden_remote_server_names
|
||||
.is_match(
|
||||
room_id
|
||||
.server_name()
|
||||
.expect("legacy room mxid")
|
||||
.host(),
|
||||
)) {
|
||||
warn!(
|
||||
"User {user_id} who is not an admin attempted to send an invite for or \
|
||||
attempted to join a banned room or banned room server name: {room_id}"
|
||||
);
|
||||
|
||||
if services
|
||||
.server
|
||||
.config
|
||||
.auto_deactivate_banned_room_attempts
|
||||
{
|
||||
warn!(
|
||||
"Automatically deactivating user {user_id} due to attempted banned room join"
|
||||
);
|
||||
|
||||
if services.server.config.admin_room_notices {
|
||||
services
|
||||
.admin
|
||||
.send_text(&format!(
|
||||
"Automatically deactivating user {user_id} due to attempted banned \
|
||||
room join from IP {client_ip}"
|
||||
))
|
||||
.await;
|
||||
}
|
||||
|
||||
let all_joined_rooms: Vec<OwnedRoomId> = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.rooms_joined(user_id)
|
||||
.map(Into::into)
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
full_user_deactivate(services, user_id, &all_joined_rooms)
|
||||
.boxed()
|
||||
.await?;
|
||||
}
|
||||
maybe_deactivate(services, user_id, client_ip)
|
||||
.await
|
||||
.log_err()
|
||||
.ok();
|
||||
|
||||
return Err!(Request(Forbidden("This room is banned on this homeserver.")));
|
||||
}
|
||||
@@ -129,37 +102,10 @@ pub(crate) async fn banned_room_check(
|
||||
name {server_name} that is globally forbidden. Rejecting.",
|
||||
);
|
||||
|
||||
if services
|
||||
.server
|
||||
.config
|
||||
.auto_deactivate_banned_room_attempts
|
||||
{
|
||||
warn!(
|
||||
"Automatically deactivating user {user_id} due to attempted banned room join"
|
||||
);
|
||||
|
||||
if services.server.config.admin_room_notices {
|
||||
services
|
||||
.admin
|
||||
.send_text(&format!(
|
||||
"Automatically deactivating user {user_id} due to attempted banned \
|
||||
room join from IP {client_ip}"
|
||||
))
|
||||
.await;
|
||||
}
|
||||
|
||||
let all_joined_rooms: Vec<OwnedRoomId> = services
|
||||
.rooms
|
||||
.state_cache
|
||||
.rooms_joined(user_id)
|
||||
.map(Into::into)
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
full_user_deactivate(services, user_id, &all_joined_rooms)
|
||||
.boxed()
|
||||
.await?;
|
||||
}
|
||||
maybe_deactivate(services, user_id, client_ip)
|
||||
.await
|
||||
.log_err()
|
||||
.ok();
|
||||
|
||||
return Err!(Request(Forbidden("This remote server is banned on this homeserver.")));
|
||||
}
|
||||
@@ -167,3 +113,116 @@ pub(crate) async fn banned_room_check(
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn maybe_deactivate(services: &Services, user_id: &UserId, client_ip: IpAddr) -> Result {
|
||||
if services
|
||||
.server
|
||||
.config
|
||||
.auto_deactivate_banned_room_attempts
|
||||
{
|
||||
warn!("Automatically deactivating user {user_id} due to attempted banned room join");
|
||||
|
||||
if services.server.config.admin_room_notices {
|
||||
services
|
||||
.admin
|
||||
.send_text(&format!(
|
||||
"Automatically deactivating user {user_id} due to attempted banned room \
|
||||
join from IP {client_ip}"
|
||||
))
|
||||
.await;
|
||||
}
|
||||
|
||||
services
|
||||
.deactivate
|
||||
.full_deactivate(user_id)
|
||||
.boxed()
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// TODO: should this be in services? banned check would have to resolve again if
|
||||
// room_id is not available at callsite
|
||||
async fn get_join_params(
|
||||
services: &Services,
|
||||
user_id: &UserId,
|
||||
room_id_or_alias: &RoomOrAliasId,
|
||||
via: &[OwnedServerName],
|
||||
) -> Result<(OwnedRoomId, Vec<OwnedServerName>)> {
|
||||
// servers tried first, additional_servers shuffled then tried after
|
||||
let (room_id, mut servers, mut additional_servers) =
|
||||
match OwnedRoomId::try_from(room_id_or_alias.to_owned()) {
|
||||
// if room id, shuffle via + room_id server_name ...
|
||||
| Ok(room_id) => {
|
||||
let mut additional_servers = via.to_vec();
|
||||
|
||||
if let Some(server) = room_id.server_name() {
|
||||
additional_servers.push(server.to_owned());
|
||||
}
|
||||
|
||||
(room_id, Vec::new(), additional_servers)
|
||||
},
|
||||
// ... if room alias, resolve and don't shuffle ...
|
||||
| Err(room_alias) => {
|
||||
let (room_id, servers) = services.alias.resolve_alias(&room_alias).await?;
|
||||
|
||||
(room_id, servers, Vec::new())
|
||||
},
|
||||
};
|
||||
|
||||
// either way, add invited vias
|
||||
additional_servers.extend(
|
||||
services
|
||||
.state_cache
|
||||
.servers_invite_via(&room_id)
|
||||
.map(ToOwned::to_owned)
|
||||
.collect::<Vec<_>>()
|
||||
.await,
|
||||
);
|
||||
|
||||
// either way, add invite senders' servers
|
||||
additional_servers.extend(
|
||||
services
|
||||
.state_cache
|
||||
.invite_state(user_id, &room_id)
|
||||
.await
|
||||
.unwrap_or_default()
|
||||
.iter()
|
||||
.filter_map(|event| event.get_field("sender").ok().flatten())
|
||||
.filter_map(|sender: &str| UserId::parse(sender).ok())
|
||||
.map(|user| user.server_name().to_owned()),
|
||||
);
|
||||
|
||||
// shuffle additionals, append to base servers
|
||||
additional_servers.sort_unstable();
|
||||
additional_servers.dedup();
|
||||
shuffle(&mut additional_servers);
|
||||
servers.sort_unstable();
|
||||
servers.dedup();
|
||||
servers.append(&mut additional_servers);
|
||||
|
||||
// sort deprioritized servers last
|
||||
servers.sort_by(|a, b| {
|
||||
let a_matches = services
|
||||
.server
|
||||
.config
|
||||
.deprioritize_joins_through_servers
|
||||
.is_match(a.host());
|
||||
let b_matches = services
|
||||
.server
|
||||
.config
|
||||
.deprioritize_joins_through_servers
|
||||
.is_match(b.host());
|
||||
|
||||
if a_matches && !b_matches {
|
||||
Ordering::Greater
|
||||
} else if !a_matches && b_matches {
|
||||
Ordering::Less
|
||||
} else {
|
||||
Ordering::Equal
|
||||
}
|
||||
});
|
||||
|
||||
Ok((room_id, servers))
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user