Merge tag 'v0.3.66' into max
This commit is contained in:
commit
112ed7cedd
6
.env
6
.env
@ -1,5 +1,11 @@
|
|||||||
HOSTNAME=localhost:8079
|
HOSTNAME=localhost:8079
|
||||||
PORT=8079
|
PORT=8079
|
||||||
|
HTTPS=false
|
||||||
|
DEBUG=true
|
||||||
RESTRICTED_MODE=true
|
RESTRICTED_MODE=true
|
||||||
|
VALIDATE_SIGNATURES=false
|
||||||
API_TOKEN=kjsdhfkwjenrkajhsdakjsnd
|
API_TOKEN=kjsdhfkwjenrkajhsdakjsnd
|
||||||
|
FOOTER_BLURB="Opéré par <a href=\"https://mastodon.xolus.net/@max\">@max</a>"
|
||||||
|
LOCAL_DOMAINS="xolus.net"
|
||||||
|
LOCAL_BLURB="<p>Relais ActivityPub francophone</p>"
|
||||||
# OPENTELEMETRY_URL=http://localhost:4317
|
# OPENTELEMETRY_URL=http://localhost:4317
|
||||||
|
361
Cargo.lock
generated
361
Cargo.lock
generated
@ -63,14 +63,17 @@ dependencies = [
|
|||||||
"actix-codec",
|
"actix-codec",
|
||||||
"actix-rt",
|
"actix-rt",
|
||||||
"actix-service",
|
"actix-service",
|
||||||
|
"actix-tls",
|
||||||
"actix-utils",
|
"actix-utils",
|
||||||
"ahash",
|
"ahash",
|
||||||
"base64",
|
"base64",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
|
"brotli",
|
||||||
"bytes",
|
"bytes",
|
||||||
"bytestring",
|
"bytestring",
|
||||||
"derive_more",
|
"derive_more",
|
||||||
"encoding_rs",
|
"encoding_rs",
|
||||||
|
"flate2",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"h2",
|
"h2",
|
||||||
"http",
|
"http",
|
||||||
@ -192,6 +195,7 @@ dependencies = [
|
|||||||
"actix-rt",
|
"actix-rt",
|
||||||
"actix-server",
|
"actix-server",
|
||||||
"actix-service",
|
"actix-service",
|
||||||
|
"actix-tls",
|
||||||
"actix-utils",
|
"actix-utils",
|
||||||
"ahash",
|
"ahash",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -251,13 +255,28 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
version = "0.7.19"
|
version = "0.7.20"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e"
|
checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "alloc-no-stdlib"
|
||||||
|
version = "2.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "alloc-stdlib"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece"
|
||||||
|
dependencies = [
|
||||||
|
"alloc-no-stdlib",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ammonia"
|
name = "ammonia"
|
||||||
version = "3.2.1"
|
version = "3.2.1"
|
||||||
@ -279,7 +298,7 @@ checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ap-relay"
|
name = "ap-relay"
|
||||||
version = "0.3.50"
|
version = "0.3.66"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"activitystreams",
|
"activitystreams",
|
||||||
"activitystreams-ext",
|
"activitystreams-ext",
|
||||||
@ -300,13 +319,20 @@ dependencies = [
|
|||||||
"futures-util",
|
"futures-util",
|
||||||
"http-signature-normalization-actix",
|
"http-signature-normalization-actix",
|
||||||
"lru",
|
"lru",
|
||||||
|
"metrics",
|
||||||
|
"metrics-util",
|
||||||
"mime",
|
"mime",
|
||||||
|
"minify-html",
|
||||||
"opentelemetry",
|
"opentelemetry",
|
||||||
"opentelemetry-otlp",
|
"opentelemetry-otlp",
|
||||||
|
"pin-project-lite",
|
||||||
|
"quanta",
|
||||||
"rand",
|
"rand",
|
||||||
"rsa",
|
"rsa",
|
||||||
"rsa-magic-public-key",
|
"rsa-magic-public-key",
|
||||||
"ructe",
|
"ructe",
|
||||||
|
"rustls",
|
||||||
|
"rustls-pemfile",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2",
|
"sha2",
|
||||||
@ -346,6 +372,12 @@ version = "1.5.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "983cd8b9d4b02a6dc6ffa557262eb5858a27a0038ffffe21a0f133eaa819a164"
|
checksum = "983cd8b9d4b02a6dc6ffa557262eb5858a27a0038ffffe21a0f133eaa819a164"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arrayvec"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-mutex"
|
name = "async-mutex"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
@ -485,9 +517,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "background-jobs"
|
name = "background-jobs"
|
||||||
version = "0.13.0"
|
version = "0.14.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "793a813f9145c5f3a27b8dcd834c0927de68bbd60d53a369e5894f3cc5759020"
|
checksum = "62dc7cfc967d6714768097a876ca2941a54a26976c6d3c95ea6da48974890970"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"background-jobs-actix",
|
"background-jobs-actix",
|
||||||
"background-jobs-core",
|
"background-jobs-core",
|
||||||
@ -495,15 +527,16 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "background-jobs-actix"
|
name = "background-jobs-actix"
|
||||||
version = "0.13.1"
|
version = "0.14.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "47263ad9c5679419347dae655c2fa2cba078b0eaa51ac758d4f0e9690c06910b"
|
checksum = "99f8bfe0a984c8d0bc7e67b376cc05e0b9015fdd3ee878900046120ef781c47e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"actix-rt",
|
"actix-rt",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-mutex",
|
"async-mutex",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"background-jobs-core",
|
"background-jobs-core",
|
||||||
|
"metrics",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
@ -515,14 +548,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "background-jobs-core"
|
name = "background-jobs-core"
|
||||||
version = "0.13.0"
|
version = "0.14.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "48e78e842fe2ae461319e3d1843c12e301630e65650332b02032ac70b0dfc66f"
|
checksum = "1274e49ae8eff1fc6b4943660e59ce2f2e13e65a23a707924a50a40c7b94fc4d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"actix-rt",
|
"actix-rt",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"event-listener",
|
"event-listener",
|
||||||
|
"metrics",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
@ -562,6 +596,18 @@ version = "1.3.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitvec"
|
||||||
|
version = "0.19.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "55f93d0ef3363c364d5976646a38f04cf67cfe1d4c8d160cdea02cab2c116b33"
|
||||||
|
dependencies = [
|
||||||
|
"funty",
|
||||||
|
"radium",
|
||||||
|
"tap",
|
||||||
|
"wyz",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "block-buffer"
|
name = "block-buffer"
|
||||||
version = "0.10.3"
|
version = "0.10.3"
|
||||||
@ -581,6 +627,27 @@ dependencies = [
|
|||||||
"cipher",
|
"cipher",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "brotli"
|
||||||
|
version = "3.3.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a1a0b1dbcc8ae29329621f8d4f0d835787c1c38bb1401979b49d13b0b305ff68"
|
||||||
|
dependencies = [
|
||||||
|
"alloc-no-stdlib",
|
||||||
|
"alloc-stdlib",
|
||||||
|
"brotli-decompressor",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "brotli-decompressor"
|
||||||
|
version = "2.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "59ad2d4653bf5ca36ae797b1f4bb4dbddb60ce49ca4aed8a2ce4829f60425b80"
|
||||||
|
dependencies = [
|
||||||
|
"alloc-no-stdlib",
|
||||||
|
"alloc-stdlib",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.11.1"
|
version = "3.11.1"
|
||||||
@ -601,9 +668,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
version = "1.2.1"
|
version = "1.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db"
|
checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytestring"
|
name = "bytestring"
|
||||||
@ -616,9 +683,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.76"
|
version = "1.0.77"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "76a284da2e6fe2092f2353e51713435363112dfd60030e22add80be333fb928f"
|
checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
@ -692,7 +759,7 @@ dependencies = [
|
|||||||
"async-trait",
|
"async-trait",
|
||||||
"json5",
|
"json5",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"nom",
|
"nom 7.1.1",
|
||||||
"pathdiff",
|
"pathdiff",
|
||||||
"ron",
|
"ron",
|
||||||
"rust-ini",
|
"rust-ini",
|
||||||
@ -780,9 +847,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-epoch"
|
name = "crossbeam-epoch"
|
||||||
version = "0.9.11"
|
version = "0.9.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f916dfc5d356b0ed9dae65f1db9fc9770aa2851d2662b988ccf4fe3516e86348"
|
checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
@ -793,9 +860,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-utils"
|
name = "crossbeam-utils"
|
||||||
version = "0.8.12"
|
version = "0.8.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac"
|
checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
@ -810,6 +877,17 @@ dependencies = [
|
|||||||
"typenum",
|
"typenum",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "css-minify"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "692b185e3b7c9af96b3195f3021f53a931d896968ed2ad3fb1cdb6558b30c9ab"
|
||||||
|
dependencies = [
|
||||||
|
"derive_more",
|
||||||
|
"indexmap",
|
||||||
|
"nom 6.1.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "darling"
|
name = "darling"
|
||||||
version = "0.13.4"
|
version = "0.13.4"
|
||||||
@ -929,6 +1007,12 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "endian-type"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "erasable"
|
name = "erasable"
|
||||||
version = "1.2.1"
|
version = "1.2.1"
|
||||||
@ -995,6 +1079,12 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "funty"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futf"
|
name = "futf"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
@ -1121,7 +1211,7 @@ checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"wasi",
|
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1161,7 +1251,7 @@ dependencies = [
|
|||||||
"base64",
|
"base64",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"flate2",
|
"flate2",
|
||||||
"nom",
|
"nom 7.1.1",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1233,9 +1323,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "http-signature-normalization-actix"
|
name = "http-signature-normalization-actix"
|
||||||
version = "0.6.1"
|
version = "0.6.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "86dfd54a1764ad79376b8dbf29e5bf918a463eb5ec66c90cd0388508289af6f0"
|
checksum = "7483d0ee4d093fa4bfe5956cd405492c07808a5064a29cfe3960d474f21f39c2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"actix-http",
|
"actix-http",
|
||||||
"actix-rt",
|
"actix-rt",
|
||||||
@ -1437,6 +1527,19 @@ dependencies = [
|
|||||||
"spin",
|
"spin",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lexical-core"
|
||||||
|
version = "0.7.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe"
|
||||||
|
dependencies = [
|
||||||
|
"arrayvec",
|
||||||
|
"bitflags",
|
||||||
|
"cfg-if",
|
||||||
|
"ryu",
|
||||||
|
"static_assertions",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.137"
|
version = "0.2.137"
|
||||||
@ -1507,6 +1610,15 @@ version = "0.1.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
|
checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mach"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "maplit"
|
name = "maplit"
|
||||||
version = "1.0.2"
|
version = "1.0.2"
|
||||||
@ -1556,13 +1668,56 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memoffset"
|
name = "memoffset"
|
||||||
version = "0.6.5"
|
version = "0.7.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
|
checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "metrics"
|
||||||
|
version = "0.20.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7b9b8653cec6897f73b519a43fba5ee3d50f62fe9af80b428accdcc093b4a849"
|
||||||
|
dependencies = [
|
||||||
|
"ahash",
|
||||||
|
"metrics-macros",
|
||||||
|
"portable-atomic",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "metrics-macros"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "731f8ecebd9f3a4aa847dfe75455e4757a45da40a7793d2f0b1f9b6ed18b23f3"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "metrics-util"
|
||||||
|
version = "0.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f7d24dc2dbae22bff6f1f9326ffce828c9f07ef9cc1e8002e5279f845432a30a"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"crossbeam-epoch",
|
||||||
|
"crossbeam-utils",
|
||||||
|
"hashbrown",
|
||||||
|
"indexmap",
|
||||||
|
"metrics",
|
||||||
|
"num_cpus",
|
||||||
|
"ordered-float",
|
||||||
|
"parking_lot 0.12.1",
|
||||||
|
"portable-atomic",
|
||||||
|
"quanta",
|
||||||
|
"radix_trie",
|
||||||
|
"sketches-ddsketch",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mime"
|
name = "mime"
|
||||||
version = "0.3.16"
|
version = "0.3.16"
|
||||||
@ -1579,6 +1734,29 @@ dependencies = [
|
|||||||
"unicase",
|
"unicase",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "minify-html"
|
||||||
|
version = "0.10.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "58f84854d62363972a73c3d8331b85a479366a0871a83f2a01ac11b9ba787c10"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"css-minify",
|
||||||
|
"lazy_static",
|
||||||
|
"memchr",
|
||||||
|
"minify-js",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "minify-js"
|
||||||
|
version = "0.2.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7fe033709f5a1159736cf7e22748518ffb75af26f3a6264d52ecc8bb38c68c36"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
|
"parse-js",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "minimal-lexical"
|
name = "minimal-lexical"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
@ -1602,7 +1780,7 @@ checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"wasi",
|
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1624,6 +1802,28 @@ version = "1.0.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
|
checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nibble_vec"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43"
|
||||||
|
dependencies = [
|
||||||
|
"smallvec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nom"
|
||||||
|
version = "6.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e7413f999671bd4745a7b624bd370a569fb6bc574b23c83a3c5ed2e453f3d5e2"
|
||||||
|
dependencies = [
|
||||||
|
"bitvec",
|
||||||
|
"funty",
|
||||||
|
"lexical-core",
|
||||||
|
"memchr",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nom"
|
name = "nom"
|
||||||
version = "7.1.1"
|
version = "7.1.1"
|
||||||
@ -1642,7 +1842,7 @@ checksum = "37794436ca3029a3089e0b95d42da1f0b565ad271e4d3bb4bad0c7bb70b10605"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"bytecount",
|
"bytecount",
|
||||||
"memchr",
|
"memchr",
|
||||||
"nom",
|
"nom 7.1.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1822,6 +2022,15 @@ dependencies = [
|
|||||||
"tokio-stream",
|
"tokio-stream",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ordered-float"
|
||||||
|
version = "2.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7940cf2ca942593318d07fcf2596cdca60a85c9e7fab408a5e21a4f9dcd40d87"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ordered-multimap"
|
name = "ordered-multimap"
|
||||||
version = "0.4.3"
|
version = "0.4.3"
|
||||||
@ -1834,9 +2043,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "os_str_bytes"
|
name = "os_str_bytes"
|
||||||
version = "6.4.0"
|
version = "6.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7b5bf27447411e9ee3ff51186bf7a08e16c341efdde93f4d823e8844429bed7e"
|
checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "overload"
|
name = "overload"
|
||||||
@ -1892,6 +2101,17 @@ dependencies = [
|
|||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parse-js"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "66bb85ec60d22b9e6d4adac1e3dbdaf3903a4485f476c5f4dd7ed1285cbf4dad"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"lazy_static",
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "paste"
|
name = "paste"
|
||||||
version = "1.0.9"
|
version = "1.0.9"
|
||||||
@ -2065,6 +2285,12 @@ dependencies = [
|
|||||||
"spki",
|
"spki",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "portable-atomic"
|
||||||
|
version = "0.3.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "15eb2c6e362923af47e13c23ca5afb859e83d54452c55b0b9ac763b8f7c1ac16"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppv-lite86"
|
name = "ppv-lite86"
|
||||||
version = "0.2.17"
|
version = "0.2.17"
|
||||||
@ -2175,6 +2401,22 @@ dependencies = [
|
|||||||
"prost",
|
"prost",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quanta"
|
||||||
|
version = "0.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b7e31331286705f455e56cca62e0e717158474ff02b7936c1fa596d983f4ae27"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
"libc",
|
||||||
|
"mach",
|
||||||
|
"once_cell",
|
||||||
|
"raw-cpuid",
|
||||||
|
"wasi 0.10.2+wasi-snapshot-preview1",
|
||||||
|
"web-sys",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.21"
|
version = "1.0.21"
|
||||||
@ -2184,6 +2426,22 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "radium"
|
||||||
|
version = "0.5.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "radix_trie"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd"
|
||||||
|
dependencies = [
|
||||||
|
"endian-type",
|
||||||
|
"nibble_vec",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.8.5"
|
version = "0.8.5"
|
||||||
@ -2214,6 +2472,15 @@ dependencies = [
|
|||||||
"getrandom",
|
"getrandom",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "raw-cpuid"
|
||||||
|
version = "10.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a6823ea29436221176fe662da99998ad3b4db2c7f31e7b6f5fe43adccd6320bb"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rc-box"
|
name = "rc-box"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
@ -2376,7 +2643,7 @@ dependencies = [
|
|||||||
"arc-swap",
|
"arc-swap",
|
||||||
"fastrand",
|
"fastrand",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"nom",
|
"nom 7.1.1",
|
||||||
"nom_locate",
|
"nom_locate",
|
||||||
"num-bigint",
|
"num-bigint",
|
||||||
"num-integer",
|
"num-integer",
|
||||||
@ -2396,7 +2663,7 @@ dependencies = [
|
|||||||
"itertools 0.10.5",
|
"itertools 0.10.5",
|
||||||
"md5",
|
"md5",
|
||||||
"mime",
|
"mime",
|
||||||
"nom",
|
"nom 7.1.1",
|
||||||
"rsass",
|
"rsass",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -2490,9 +2757,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.88"
|
version = "1.0.89"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8e8b3801309262e8184d9687fb697586833e939767aea0dda89f5a8e650e8bd7"
|
checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"ryu",
|
"ryu",
|
||||||
@ -2579,6 +2846,12 @@ version = "0.3.10"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
|
checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sketches-ddsketch"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ceb945e54128e09c43d8e4f1277851bd5044c6fc540bbaa2ad888f60b3da9ae7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slab"
|
name = "slab"
|
||||||
version = "0.4.7"
|
version = "0.4.7"
|
||||||
@ -2636,6 +2909,12 @@ dependencies = [
|
|||||||
"der",
|
"der",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "static_assertions"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "string_cache"
|
name = "string_cache"
|
||||||
version = "0.8.4"
|
version = "0.8.4"
|
||||||
@ -2703,6 +2982,12 @@ version = "0.1.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "20f34339676cdcab560c9a82300c4c2581f68b9369aedf0fae86f2ff9565ff3e"
|
checksum = "20f34339676cdcab560c9a82300c4c2581f68b9369aedf0fae86f2ff9565ff3e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tap"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "teloxide"
|
name = "teloxide"
|
||||||
version = "0.11.2"
|
version = "0.11.2"
|
||||||
@ -3287,6 +3572,12 @@ dependencies = [
|
|||||||
"try-lock",
|
"try-lock",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.10.2+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.11.0+wasi-snapshot-preview1"
|
version = "0.11.0+wasi-snapshot-preview1"
|
||||||
@ -3496,6 +3787,12 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wyz"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yaml-rust"
|
name = "yaml-rust"
|
||||||
version = "0.4.5"
|
version = "0.4.5"
|
||||||
|
17
Cargo.toml
17
Cargo.toml
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ap-relay"
|
name = "ap-relay"
|
||||||
description = "A simple activitypub relay"
|
description = "A simple activitypub relay"
|
||||||
version = "0.3.50"
|
version = "0.3.66"
|
||||||
authors = ["asonix <asonix@asonix.dog>"]
|
authors = ["asonix <asonix@asonix.dog>"]
|
||||||
license = "AGPL-3.0"
|
license = "AGPL-3.0"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
@ -23,7 +23,11 @@ default = []
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
actix-rt = "2.7.0"
|
actix-rt = "2.7.0"
|
||||||
actix-web = { version = "4.0.1", default-features = false }
|
actix-web = { version = "4.0.1", default-features = false, features = [
|
||||||
|
"rustls",
|
||||||
|
"compress-brotli",
|
||||||
|
"compress-gzip",
|
||||||
|
] }
|
||||||
actix-webfinger = "0.4.0"
|
actix-webfinger = "0.4.0"
|
||||||
activitystreams = "0.7.0-alpha.19"
|
activitystreams = "0.7.0-alpha.19"
|
||||||
activitystreams-ext = "0.1.0-alpha.2"
|
activitystreams-ext = "0.1.0-alpha.2"
|
||||||
@ -38,12 +42,19 @@ dashmap = "5.1.0"
|
|||||||
dotenv = "0.15.0"
|
dotenv = "0.15.0"
|
||||||
futures-util = "0.3.17"
|
futures-util = "0.3.17"
|
||||||
lru = "0.8.0"
|
lru = "0.8.0"
|
||||||
|
metrics = "0.20.1"
|
||||||
|
metrics-util = "0.14.0"
|
||||||
mime = "0.3.16"
|
mime = "0.3.16"
|
||||||
|
minify-html = "0.10.0"
|
||||||
opentelemetry = { version = "0.18", features = ["rt-tokio"] }
|
opentelemetry = { version = "0.18", features = ["rt-tokio"] }
|
||||||
opentelemetry-otlp = "0.11"
|
opentelemetry-otlp = "0.11"
|
||||||
|
pin-project-lite = "0.2.9"
|
||||||
|
quanta = "0.10.1"
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
rsa = "0.7"
|
rsa = "0.7"
|
||||||
rsa-magic-public-key = "0.6.0"
|
rsa-magic-public-key = "0.6.0"
|
||||||
|
rustls = "0.20.7"
|
||||||
|
rustls-pemfile = "1.0.1"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
sha2 = { version = "0.10", features = ["oid"] }
|
sha2 = { version = "0.10", features = ["oid"] }
|
||||||
@ -70,7 +81,7 @@ tokio = { version = "1", features = ["macros", "sync"] }
|
|||||||
uuid = { version = "1", features = ["v4", "serde"] }
|
uuid = { version = "1", features = ["v4", "serde"] }
|
||||||
|
|
||||||
[dependencies.background-jobs]
|
[dependencies.background-jobs]
|
||||||
version = "0.13.0"
|
version = "0.14.0"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["background-jobs-actix", "error-logging"]
|
features = ["background-jobs-actix", "error-logging"]
|
||||||
|
|
||||||
|
23
README.md
23
README.md
@ -10,7 +10,7 @@ $ sudo docker run --rm -it \
|
|||||||
-e ADDR=0.0.0.0 \
|
-e ADDR=0.0.0.0 \
|
||||||
-e SLED_PATH=/mnt/sled/db-0.34 \
|
-e SLED_PATH=/mnt/sled/db-0.34 \
|
||||||
-p 8080:8080 \
|
-p 8080:8080 \
|
||||||
asonix/relay:0.3.23
|
asonix/relay:0.3.52
|
||||||
```
|
```
|
||||||
This will launch the relay with the database stored in "./sled/db-0.34" and listening on port 8080
|
This will launch the relay with the database stored in "./sled/db-0.34" and listening on port 8080
|
||||||
#### Cargo
|
#### Cargo
|
||||||
@ -98,6 +98,11 @@ API_TOKEN=somepasswordishtoken
|
|||||||
OPENTELEMETRY_URL=localhost:4317
|
OPENTELEMETRY_URL=localhost:4317
|
||||||
TELEGRAM_TOKEN=secret
|
TELEGRAM_TOKEN=secret
|
||||||
TELEGRAM_ADMIN_HANDLE=your_handle
|
TELEGRAM_ADMIN_HANDLE=your_handle
|
||||||
|
TLS_KEY=/path/to/key
|
||||||
|
TLS_CERT=/path/to/cert
|
||||||
|
FOOTER_BLURB="Contact <a href=\"https://masto.asonix.dog/@asonix\">@asonix</a> for inquiries"
|
||||||
|
LOCAL_DOMAINS=masto.asonix.dog
|
||||||
|
LOCAL_BLURB="<p>Welcome to my cool relay where I have cool relay things happening. I hope you enjoy your stay!</p>"
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Descriptions
|
#### Descriptions
|
||||||
@ -112,15 +117,15 @@ Whether to print incoming activities to the console when requests hit the /inbox
|
|||||||
##### `RESTRICTED_MODE`
|
##### `RESTRICTED_MODE`
|
||||||
This setting enables an 'allowlist' setup where only servers that have been explicitly enabled through the `relay -a` command can join the relay. This is `false` by default. If `RESTRICTED_MODE` is not enabled, then manually allowing domains with `relay -a` has no effect.
|
This setting enables an 'allowlist' setup where only servers that have been explicitly enabled through the `relay -a` command can join the relay. This is `false` by default. If `RESTRICTED_MODE` is not enabled, then manually allowing domains with `relay -a` has no effect.
|
||||||
##### `VALIDATE_SIGNATURES`
|
##### `VALIDATE_SIGNATURES`
|
||||||
This setting enforces checking HTTP signatures on incoming activities. It defaults to `false` but should be set to `true` in production scenarios
|
This setting enforces checking HTTP signatures on incoming activities. It defaults to `true`
|
||||||
##### `HTTPS`
|
##### `HTTPS`
|
||||||
Whether the current server is running on an HTTPS port or not. This is used for generating URLs to the current running relay. By default it is set to `false`, but should be `true` in production scenarios.
|
Whether the current server is running on an HTTPS port or not. This is used for generating URLs to the current running relay. By default it is set to `true`
|
||||||
##### `PUBLISH_BLOCKS`
|
##### `PUBLISH_BLOCKS`
|
||||||
Whether or not to publish a list of blocked domains in the `nodeinfo` metadata for the server. It defaults to `false`.
|
Whether or not to publish a list of blocked domains in the `nodeinfo` metadata for the server. It defaults to `false`.
|
||||||
##### `SLED_PATH`
|
##### `SLED_PATH`
|
||||||
Where to store the on-disk database of connected servers. This defaults to `./sled/db-0.34`.
|
Where to store the on-disk database of connected servers. This defaults to `./sled/db-0.34`.
|
||||||
##### `RUST_LOG`
|
##### `RUST_LOG`
|
||||||
The log level to print. Available levels are `ERROR`, `WARN`, `INFO`, `DEBUG`, and `TRACE`. You can also specify module paths to enable some logs but not others, such as `RUST_LOG=warn,tracing_actix_web=info,relay=info`
|
The log level to print. Available levels are `ERROR`, `WARN`, `INFO`, `DEBUG`, and `TRACE`. You can also specify module paths to enable some logs but not others, such as `RUST_LOG=warn,tracing_actix_web=info,relay=info`. This defaults to `warn`
|
||||||
##### `SOURCE_REPO`
|
##### `SOURCE_REPO`
|
||||||
The URL to the source code for the relay. This defaults to `https://git.asonix.dog/asonix/relay`, but should be changed if you're running a fork hosted elsewhere.
|
The URL to the source code for the relay. This defaults to `https://git.asonix.dog/asonix/relay`, but should be changed if you're running a fork hosted elsewhere.
|
||||||
##### `API_TOKEN`
|
##### `API_TOKEN`
|
||||||
@ -131,6 +136,16 @@ A URL for exporting opentelemetry spans. This is mostly useful for debugging. Th
|
|||||||
A Telegram Bot Token for running the relay administration bot. There is no default.
|
A Telegram Bot Token for running the relay administration bot. There is no default.
|
||||||
##### `TELEGRAM_ADMIN_HANDLE`
|
##### `TELEGRAM_ADMIN_HANDLE`
|
||||||
The handle of the telegram user allowed to administer the relay. There is no default.
|
The handle of the telegram user allowed to administer the relay. There is no default.
|
||||||
|
##### `TLS_KEY`
|
||||||
|
Optional - This is specified if you are running the relay directly on the internet and have a TLS key to provide HTTPS for your relay
|
||||||
|
##### `TLS_CERT`
|
||||||
|
Optional - This is specified if you are running the relay directly on the internet and have a TLS certificate chain to provide HTTPS for your relay
|
||||||
|
##### `FOOTER_BLURB`
|
||||||
|
Optional - Add custom notes in the footer of the page
|
||||||
|
##### `LOCAL_DOMAINS`
|
||||||
|
Optional - domains of mastodon servers run by the same admin as the relay
|
||||||
|
##### `LOCAL_BLURB`
|
||||||
|
Optional - description for the relay
|
||||||
|
|
||||||
### Subscribing
|
### Subscribing
|
||||||
Mastodon admins can subscribe to this relay by adding the `/inbox` route to their relay settings.
|
Mastodon admins can subscribe to this relay by adding the `/inbox` route to their relay settings.
|
||||||
|
@ -41,7 +41,7 @@ header {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
section {
|
article {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
color: #333;
|
color: #333;
|
||||||
border: 1px solid #e5e5e5;
|
border: 1px solid #e5e5e5;
|
||||||
@ -51,8 +51,16 @@ section {
|
|||||||
max-width: 700px;
|
max-width: 700px;
|
||||||
padding-bottom: 32px;
|
padding-bottom: 32px;
|
||||||
|
|
||||||
> p:first-child {
|
section {
|
||||||
margin-top: 0;
|
border-bottom: 1px solid #e5e5e5;
|
||||||
|
|
||||||
|
> h4:first-child,
|
||||||
|
> p:first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
> p:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
@ -67,13 +75,13 @@ section {
|
|||||||
|
|
||||||
li {
|
li {
|
||||||
padding-top: 36px;
|
padding-top: 36px;
|
||||||
border-bottom: 1px solid #e5e5e5;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.padded {
|
.padded {
|
||||||
padding: 0 24px;
|
padding: 0 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.local-explainer,
|
||||||
.joining {
|
.joining {
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
}
|
}
|
||||||
@ -174,9 +182,11 @@ footer {
|
|||||||
|
|
||||||
li {
|
li {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
border-bottom: none;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
article section {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,7 +251,7 @@ footer {
|
|||||||
padding: 24px;
|
padding: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
section {
|
article {
|
||||||
border-left: none;
|
border-left: none;
|
||||||
border-right: none;
|
border-right: none;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
admin::{AllowedDomains, BlockedDomains, ConnectedActors, Domains},
|
admin::{AllowedDomains, BlockedDomains, ConnectedActors, Domains},
|
||||||
|
collector::Snapshot,
|
||||||
config::{AdminUrlKind, Config},
|
config::{AdminUrlKind, Config},
|
||||||
error::{Error, ErrorKind},
|
error::{Error, ErrorKind},
|
||||||
};
|
};
|
||||||
@ -50,6 +51,10 @@ pub(crate) async fn connected(client: &Client, config: &Config) -> Result<Connec
|
|||||||
get_results(client, config, AdminUrlKind::Connected).await
|
get_results(client, config, AdminUrlKind::Connected).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn stats(client: &Client, config: &Config) -> Result<Snapshot, Error> {
|
||||||
|
get_results(client, config, AdminUrlKind::Stats).await
|
||||||
|
}
|
||||||
|
|
||||||
async fn get_results<T: DeserializeOwned>(
|
async fn get_results<T: DeserializeOwned>(
|
||||||
client: &Client,
|
client: &Client,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
admin::{AllowedDomains, BlockedDomains, ConnectedActors, Domains},
|
admin::{AllowedDomains, BlockedDomains, ConnectedActors, Domains},
|
||||||
|
collector::{MemoryCollector, Snapshot},
|
||||||
error::Error,
|
error::Error,
|
||||||
extractors::Admin,
|
extractors::Admin,
|
||||||
};
|
};
|
||||||
use actix_web::{web::Json, HttpResponse};
|
use actix_web::{
|
||||||
|
web::{Data, Json},
|
||||||
|
HttpResponse,
|
||||||
|
};
|
||||||
|
|
||||||
pub(crate) async fn allow(
|
pub(crate) async fn allow(
|
||||||
admin: Admin,
|
admin: Admin,
|
||||||
@ -58,3 +62,10 @@ pub(crate) async fn connected(admin: Admin) -> Result<Json<ConnectedActors>, Err
|
|||||||
|
|
||||||
Ok(Json(ConnectedActors { connected_actors }))
|
Ok(Json(ConnectedActors { connected_actors }))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn stats(
|
||||||
|
_admin: Admin,
|
||||||
|
collector: Data<MemoryCollector>,
|
||||||
|
) -> Result<Json<Snapshot>, Error> {
|
||||||
|
Ok(Json(collector.snapshot()))
|
||||||
|
}
|
||||||
|
@ -14,11 +14,14 @@ pub(crate) struct Args {
|
|||||||
|
|
||||||
#[arg(short, long, help = "List allowed and blocked domains")]
|
#[arg(short, long, help = "List allowed and blocked domains")]
|
||||||
list: bool,
|
list: bool,
|
||||||
|
|
||||||
|
#[arg(short, long, help = "Get statistics from the server")]
|
||||||
|
stats: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Args {
|
impl Args {
|
||||||
pub(crate) fn any(&self) -> bool {
|
pub(crate) fn any(&self) -> bool {
|
||||||
!self.blocks.is_empty() || !self.allowed.is_empty() || self.list
|
!self.blocks.is_empty() || !self.allowed.is_empty() || self.list || self.stats
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new() -> Self {
|
pub(crate) fn new() -> Self {
|
||||||
@ -40,4 +43,8 @@ impl Args {
|
|||||||
pub(crate) fn list(&self) -> bool {
|
pub(crate) fn list(&self) -> bool {
|
||||||
self.list
|
self.list
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn stats(&self) -> bool {
|
||||||
|
self.stats
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
414
src/collector.rs
Normal file
414
src/collector.rs
Normal file
@ -0,0 +1,414 @@
|
|||||||
|
use metrics::{Key, Recorder, SetRecorderError};
|
||||||
|
use metrics_util::{
|
||||||
|
registry::{AtomicStorage, GenerationalStorage, Recency, Registry},
|
||||||
|
MetricKindMask, Summary,
|
||||||
|
};
|
||||||
|
use quanta::Clock;
|
||||||
|
use std::{
|
||||||
|
collections::{BTreeMap, HashMap},
|
||||||
|
sync::{atomic::Ordering, Arc, RwLock},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
|
const SECONDS: u64 = 1;
|
||||||
|
const MINUTES: u64 = 60 * SECONDS;
|
||||||
|
const HOURS: u64 = 60 * MINUTES;
|
||||||
|
const DAYS: u64 = 24 * HOURS;
|
||||||
|
|
||||||
|
type DistributionMap = BTreeMap<Vec<(String, String)>, Summary>;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct MemoryCollector {
|
||||||
|
inner: Arc<Inner>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Inner {
|
||||||
|
descriptions: RwLock<HashMap<String, metrics::SharedString>>,
|
||||||
|
distributions: RwLock<HashMap<String, DistributionMap>>,
|
||||||
|
recency: Recency<Key>,
|
||||||
|
registry: Registry<Key, GenerationalStorage<AtomicStorage>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||||
|
struct Counter {
|
||||||
|
labels: BTreeMap<String, String>,
|
||||||
|
value: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Counter {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let labels = self
|
||||||
|
.labels
|
||||||
|
.iter()
|
||||||
|
.map(|(k, v)| format!("{}: {}", k, v))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", ");
|
||||||
|
|
||||||
|
write!(f, "{} - {}", labels, self.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||||
|
struct Gauge {
|
||||||
|
labels: BTreeMap<String, String>,
|
||||||
|
value: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Gauge {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let labels = self
|
||||||
|
.labels
|
||||||
|
.iter()
|
||||||
|
.map(|(k, v)| format!("{}: {}", k, v))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", ");
|
||||||
|
|
||||||
|
write!(f, "{} - {}", labels, self.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||||
|
struct Histogram {
|
||||||
|
labels: BTreeMap<String, String>,
|
||||||
|
value: Vec<(f64, Option<f64>)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Histogram {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let labels = self
|
||||||
|
.labels
|
||||||
|
.iter()
|
||||||
|
.map(|(k, v)| format!("{}: {}", k, v))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", ");
|
||||||
|
|
||||||
|
let value = self
|
||||||
|
.value
|
||||||
|
.iter()
|
||||||
|
.map(|(k, v)| {
|
||||||
|
if let Some(v) = v {
|
||||||
|
format!("{}: {:.6}", k, v)
|
||||||
|
} else {
|
||||||
|
format!("{}: None,", k)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", ");
|
||||||
|
|
||||||
|
write!(f, "{} - {}", labels, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Deserialize, serde::Serialize)]
|
||||||
|
pub(crate) struct Snapshot {
|
||||||
|
counters: HashMap<String, Vec<Counter>>,
|
||||||
|
gauges: HashMap<String, Vec<Gauge>>,
|
||||||
|
histograms: HashMap<String, Vec<Histogram>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
const PAIRS: [((&str, &str), &str); 2] = [
|
||||||
|
(
|
||||||
|
(
|
||||||
|
"background-jobs.worker.started",
|
||||||
|
"background-jobs.worker.finished",
|
||||||
|
),
|
||||||
|
"background-jobs.worker.running",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
(
|
||||||
|
"background-jobs.job.started",
|
||||||
|
"background-jobs.job.finished",
|
||||||
|
),
|
||||||
|
"background-jobs.job.running",
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct MergeCounter {
|
||||||
|
start: Option<Counter>,
|
||||||
|
finish: Option<Counter>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MergeCounter {
|
||||||
|
fn merge(self) -> Option<Counter> {
|
||||||
|
match (self.start, self.finish) {
|
||||||
|
(Some(start), Some(end)) => Some(Counter {
|
||||||
|
labels: start.labels,
|
||||||
|
value: start.value.saturating_sub(end.value),
|
||||||
|
}),
|
||||||
|
(Some(only), None) => Some(only),
|
||||||
|
(None, Some(only)) => Some(Counter {
|
||||||
|
labels: only.labels,
|
||||||
|
value: 0,
|
||||||
|
}),
|
||||||
|
(None, None) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Snapshot {
|
||||||
|
pub(crate) fn present(self) {
|
||||||
|
if !self.counters.is_empty() {
|
||||||
|
println!("Counters");
|
||||||
|
let mut merging = HashMap::new();
|
||||||
|
for (key, counters) in self.counters {
|
||||||
|
if let Some(((start, _), name)) = PAIRS
|
||||||
|
.iter()
|
||||||
|
.find(|((start, finish), _)| *start == key || *finish == key)
|
||||||
|
{
|
||||||
|
let entry = merging.entry(name).or_insert_with(HashMap::new);
|
||||||
|
|
||||||
|
for counter in counters {
|
||||||
|
let mut merge_counter = entry
|
||||||
|
.entry(counter.labels.clone())
|
||||||
|
.or_insert_with(MergeCounter::default);
|
||||||
|
if key == *start {
|
||||||
|
merge_counter.start = Some(counter);
|
||||||
|
} else {
|
||||||
|
merge_counter.finish = Some(counter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("\t{}", key);
|
||||||
|
for counter in counters {
|
||||||
|
println!("\t\t{}", counter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (key, counters) in merging {
|
||||||
|
println!("\t{}", key);
|
||||||
|
|
||||||
|
for (_, counter) in counters {
|
||||||
|
if let Some(counter) = counter.merge() {
|
||||||
|
println!("\t\t{}", counter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.gauges.is_empty() {
|
||||||
|
println!("Gauges");
|
||||||
|
for (key, gauges) in self.gauges {
|
||||||
|
println!("\t{}", key);
|
||||||
|
|
||||||
|
for gauge in gauges {
|
||||||
|
println!("\t\t{}", gauge);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.histograms.is_empty() {
|
||||||
|
println!("Histograms");
|
||||||
|
for (key, histograms) in self.histograms {
|
||||||
|
println!("\t{}", key);
|
||||||
|
|
||||||
|
for histogram in histograms {
|
||||||
|
println!("\t\t{}", histogram);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn key_to_parts(key: &Key) -> (String, Vec<(String, String)>) {
|
||||||
|
let labels = key
|
||||||
|
.labels()
|
||||||
|
.into_iter()
|
||||||
|
.map(|label| (label.key().to_string(), label.value().to_string()))
|
||||||
|
.collect();
|
||||||
|
let name = key.name().to_string();
|
||||||
|
(name, labels)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Inner {
|
||||||
|
fn snapshot_counters(&self) -> HashMap<String, Vec<Counter>> {
|
||||||
|
let mut counters = HashMap::new();
|
||||||
|
|
||||||
|
for (key, counter) in self.registry.get_counter_handles() {
|
||||||
|
let gen = counter.get_generation();
|
||||||
|
if !self.recency.should_store_counter(&key, gen, &self.registry) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (name, labels) = key_to_parts(&key);
|
||||||
|
let value = counter.get_inner().load(Ordering::Acquire);
|
||||||
|
counters.entry(name).or_insert_with(Vec::new).push(Counter {
|
||||||
|
labels: labels.into_iter().collect(),
|
||||||
|
value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
counters
|
||||||
|
}
|
||||||
|
|
||||||
|
fn snapshot_gauges(&self) -> HashMap<String, Vec<Gauge>> {
|
||||||
|
let mut gauges = HashMap::new();
|
||||||
|
|
||||||
|
for (key, gauge) in self.registry.get_gauge_handles() {
|
||||||
|
let gen = gauge.get_generation();
|
||||||
|
if !self.recency.should_store_gauge(&key, gen, &self.registry) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (name, labels) = key_to_parts(&key);
|
||||||
|
let value = f64::from_bits(gauge.get_inner().load(Ordering::Acquire));
|
||||||
|
gauges.entry(name).or_insert_with(Vec::new).push(Gauge {
|
||||||
|
labels: labels.into_iter().collect(),
|
||||||
|
value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
gauges
|
||||||
|
}
|
||||||
|
|
||||||
|
fn snapshot_histograms(&self) -> HashMap<String, Vec<Histogram>> {
|
||||||
|
for (key, histogram) in self.registry.get_histogram_handles() {
|
||||||
|
let gen = histogram.get_generation();
|
||||||
|
let (name, labels) = key_to_parts(&key);
|
||||||
|
|
||||||
|
if !self
|
||||||
|
.recency
|
||||||
|
.should_store_histogram(&key, gen, &self.registry)
|
||||||
|
{
|
||||||
|
let mut d = self.distributions.write().unwrap();
|
||||||
|
let delete_by_name = if let Some(by_name) = d.get_mut(&name) {
|
||||||
|
by_name.remove(&labels);
|
||||||
|
by_name.is_empty()
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
drop(d);
|
||||||
|
|
||||||
|
if delete_by_name {
|
||||||
|
self.descriptions.write().unwrap().remove(&name);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut d = self.distributions.write().unwrap();
|
||||||
|
let outer_entry = d.entry(name.clone()).or_insert_with(BTreeMap::new);
|
||||||
|
|
||||||
|
let entry = outer_entry
|
||||||
|
.entry(labels)
|
||||||
|
.or_insert_with(Summary::with_defaults);
|
||||||
|
|
||||||
|
histogram.get_inner().clear_with(|samples| {
|
||||||
|
for sample in samples {
|
||||||
|
entry.add(*sample);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let d = self.distributions.read().unwrap().clone();
|
||||||
|
d.into_iter()
|
||||||
|
.map(|(key, value)| {
|
||||||
|
(
|
||||||
|
key,
|
||||||
|
value
|
||||||
|
.into_iter()
|
||||||
|
.map(|(labels, summary)| Histogram {
|
||||||
|
labels: labels.into_iter().collect(),
|
||||||
|
value: [0.001, 0.01, 0.05, 0.1, 0.5, 0.9, 0.99, 1.0]
|
||||||
|
.into_iter()
|
||||||
|
.map(|q| (q, summary.quantile(q)))
|
||||||
|
.collect(),
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn snapshot(&self) -> Snapshot {
|
||||||
|
Snapshot {
|
||||||
|
counters: self.snapshot_counters(),
|
||||||
|
gauges: self.snapshot_gauges(),
|
||||||
|
histograms: self.snapshot_histograms(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MemoryCollector {
|
||||||
|
pub(crate) fn new() -> Self {
|
||||||
|
MemoryCollector {
|
||||||
|
inner: Arc::new(Inner {
|
||||||
|
descriptions: Default::default(),
|
||||||
|
distributions: Default::default(),
|
||||||
|
recency: Recency::new(
|
||||||
|
Clock::new(),
|
||||||
|
MetricKindMask::ALL,
|
||||||
|
Some(Duration::from_secs(5 * DAYS)),
|
||||||
|
),
|
||||||
|
registry: Registry::new(GenerationalStorage::atomic()),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn install(&self) -> Result<(), SetRecorderError> {
|
||||||
|
metrics::set_boxed_recorder(Box::new(self.clone()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn snapshot(&self) -> Snapshot {
|
||||||
|
self.inner.snapshot()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_description_if_missing(
|
||||||
|
&self,
|
||||||
|
key: &metrics::KeyName,
|
||||||
|
description: metrics::SharedString,
|
||||||
|
) {
|
||||||
|
let mut d = self.inner.descriptions.write().unwrap();
|
||||||
|
d.entry(key.as_str().to_owned()).or_insert(description);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Recorder for MemoryCollector {
|
||||||
|
fn describe_counter(
|
||||||
|
&self,
|
||||||
|
key: metrics::KeyName,
|
||||||
|
_: Option<metrics::Unit>,
|
||||||
|
description: metrics::SharedString,
|
||||||
|
) {
|
||||||
|
self.add_description_if_missing(&key, description)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn describe_gauge(
|
||||||
|
&self,
|
||||||
|
key: metrics::KeyName,
|
||||||
|
_: Option<metrics::Unit>,
|
||||||
|
description: metrics::SharedString,
|
||||||
|
) {
|
||||||
|
self.add_description_if_missing(&key, description)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn describe_histogram(
|
||||||
|
&self,
|
||||||
|
key: metrics::KeyName,
|
||||||
|
_: Option<metrics::Unit>,
|
||||||
|
description: metrics::SharedString,
|
||||||
|
) {
|
||||||
|
self.add_description_if_missing(&key, description)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register_counter(&self, key: &Key) -> metrics::Counter {
|
||||||
|
self.inner
|
||||||
|
.registry
|
||||||
|
.get_or_create_counter(key, |c| c.clone().into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register_gauge(&self, key: &Key) -> metrics::Gauge {
|
||||||
|
self.inner
|
||||||
|
.registry
|
||||||
|
.get_or_create_gauge(key, |c| c.clone().into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register_histogram(&self, key: &Key) -> metrics::Histogram {
|
||||||
|
self.inner
|
||||||
|
.registry
|
||||||
|
.get_or_create_histogram(key, |c| c.clone().into())
|
||||||
|
}
|
||||||
|
}
|
122
src/config.rs
122
src/config.rs
@ -14,8 +14,9 @@ use activitystreams::{
|
|||||||
};
|
};
|
||||||
use config::Environment;
|
use config::Environment;
|
||||||
use http_signature_normalization_actix::prelude::{VerifyDigest, VerifySignature};
|
use http_signature_normalization_actix::prelude::{VerifyDigest, VerifySignature};
|
||||||
|
use rustls::{Certificate, PrivateKey};
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
use std::{net::IpAddr, path::PathBuf};
|
use std::{io::BufReader, net::IpAddr, path::PathBuf};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize)]
|
#[derive(Clone, Debug, serde::Deserialize)]
|
||||||
@ -34,6 +35,11 @@ pub(crate) struct ParsedConfig {
|
|||||||
telegram_token: Option<String>,
|
telegram_token: Option<String>,
|
||||||
telegram_admin_handle: Option<String>,
|
telegram_admin_handle: Option<String>,
|
||||||
api_token: Option<String>,
|
api_token: Option<String>,
|
||||||
|
tls_key: Option<PathBuf>,
|
||||||
|
tls_cert: Option<PathBuf>,
|
||||||
|
footer_blurb: Option<String>,
|
||||||
|
local_domains: Option<String>,
|
||||||
|
local_blurb: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -52,6 +58,16 @@ pub struct Config {
|
|||||||
telegram_token: Option<String>,
|
telegram_token: Option<String>,
|
||||||
telegram_admin_handle: Option<String>,
|
telegram_admin_handle: Option<String>,
|
||||||
api_token: Option<String>,
|
api_token: Option<String>,
|
||||||
|
tls: Option<TlsConfig>,
|
||||||
|
footer_blurb: Option<String>,
|
||||||
|
local_domains: Vec<String>,
|
||||||
|
local_blurb: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct TlsConfig {
|
||||||
|
key: PathBuf,
|
||||||
|
cert: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -77,6 +93,7 @@ pub enum AdminUrlKind {
|
|||||||
Allowed,
|
Allowed,
|
||||||
Blocked,
|
Blocked,
|
||||||
Connected,
|
Connected,
|
||||||
|
Stats,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for Config {
|
impl std::fmt::Debug for Config {
|
||||||
@ -99,6 +116,11 @@ impl std::fmt::Debug for Config {
|
|||||||
.field("telegram_token", &"[redacted]")
|
.field("telegram_token", &"[redacted]")
|
||||||
.field("telegram_admin_handle", &self.telegram_admin_handle)
|
.field("telegram_admin_handle", &self.telegram_admin_handle)
|
||||||
.field("api_token", &"[redacted]")
|
.field("api_token", &"[redacted]")
|
||||||
|
.field("tls_key", &"[redacted]")
|
||||||
|
.field("tls_cert", &"[redacted]")
|
||||||
|
.field("footer_blurb", &self.footer_blurb)
|
||||||
|
.field("local_domains", &self.local_domains)
|
||||||
|
.field("local_blurb", &self.local_blurb)
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -111,8 +133,8 @@ impl Config {
|
|||||||
.set_default("port", 8080u64)?
|
.set_default("port", 8080u64)?
|
||||||
.set_default("debug", true)?
|
.set_default("debug", true)?
|
||||||
.set_default("restricted_mode", false)?
|
.set_default("restricted_mode", false)?
|
||||||
.set_default("validate_signatures", false)?
|
.set_default("validate_signatures", true)?
|
||||||
.set_default("https", false)?
|
.set_default("https", true)?
|
||||||
.set_default("publish_blocks", false)?
|
.set_default("publish_blocks", false)?
|
||||||
.set_default("sled_path", "./sled/db-0-34")?
|
.set_default("sled_path", "./sled/db-0-34")?
|
||||||
.set_default("source_repo", "https://git.asonix.dog/asonix/relay")?
|
.set_default("source_repo", "https://git.asonix.dog/asonix/relay")?
|
||||||
@ -120,6 +142,11 @@ impl Config {
|
|||||||
.set_default("telegram_token", None as Option<&str>)?
|
.set_default("telegram_token", None as Option<&str>)?
|
||||||
.set_default("telegram_admin_handle", None as Option<&str>)?
|
.set_default("telegram_admin_handle", None as Option<&str>)?
|
||||||
.set_default("api_token", None as Option<&str>)?
|
.set_default("api_token", None as Option<&str>)?
|
||||||
|
.set_default("tls_key", None as Option<&str>)?
|
||||||
|
.set_default("tls_cert", None as Option<&str>)?
|
||||||
|
.set_default("footer_blurb", None as Option<&str>)?
|
||||||
|
.set_default("local_domains", None as Option<&str>)?
|
||||||
|
.set_default("local_blurb", None as Option<&str>)?
|
||||||
.add_source(Environment::default())
|
.add_source(Environment::default())
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
@ -128,6 +155,26 @@ impl Config {
|
|||||||
let scheme = if config.https { "https" } else { "http" };
|
let scheme = if config.https { "https" } else { "http" };
|
||||||
let base_uri = iri!(format!("{}://{}", scheme, config.hostname)).into_absolute();
|
let base_uri = iri!(format!("{}://{}", scheme, config.hostname)).into_absolute();
|
||||||
|
|
||||||
|
let tls = match (config.tls_key, config.tls_cert) {
|
||||||
|
(Some(key), Some(cert)) => Some(TlsConfig { key, cert }),
|
||||||
|
(Some(_), None) => {
|
||||||
|
tracing::warn!("TLS_KEY is set but TLS_CERT isn't , not building TLS config");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
(None, Some(_)) => {
|
||||||
|
tracing::warn!("TLS_CERT is set but TLS_KEY isn't , not building TLS config");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
(None, None) => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let local_domains = config
|
||||||
|
.local_domains
|
||||||
|
.iter()
|
||||||
|
.flat_map(|s| s.split(','))
|
||||||
|
.map(|d| d.to_string())
|
||||||
|
.collect();
|
||||||
|
|
||||||
Ok(Config {
|
Ok(Config {
|
||||||
hostname: config.hostname,
|
hostname: config.hostname,
|
||||||
addr: config.addr,
|
addr: config.addr,
|
||||||
@ -143,9 +190,76 @@ impl Config {
|
|||||||
telegram_token: config.telegram_token,
|
telegram_token: config.telegram_token,
|
||||||
telegram_admin_handle: config.telegram_admin_handle,
|
telegram_admin_handle: config.telegram_admin_handle,
|
||||||
api_token: config.api_token,
|
api_token: config.api_token,
|
||||||
|
tls,
|
||||||
|
footer_blurb: config.footer_blurb,
|
||||||
|
local_domains,
|
||||||
|
local_blurb: config.local_blurb,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn open_keys(&self) -> Result<Option<(Vec<Certificate>, PrivateKey)>, Error> {
|
||||||
|
let tls = if let Some(tls) = &self.tls {
|
||||||
|
tls
|
||||||
|
} else {
|
||||||
|
tracing::warn!("No TLS config present");
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut certs_reader = BufReader::new(std::fs::File::open(&tls.cert)?);
|
||||||
|
let certs = rustls_pemfile::certs(&mut certs_reader)?;
|
||||||
|
|
||||||
|
if certs.is_empty() {
|
||||||
|
tracing::warn!("No certs read from certificate file");
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut key_reader = BufReader::new(std::fs::File::open(&tls.key)?);
|
||||||
|
let key = rustls_pemfile::read_one(&mut key_reader)?;
|
||||||
|
|
||||||
|
let certs = certs.into_iter().map(Certificate).collect();
|
||||||
|
|
||||||
|
let key = if let Some(key) = key {
|
||||||
|
match key {
|
||||||
|
rustls_pemfile::Item::RSAKey(der) => PrivateKey(der),
|
||||||
|
rustls_pemfile::Item::PKCS8Key(der) => PrivateKey(der),
|
||||||
|
rustls_pemfile::Item::ECKey(der) => PrivateKey(der),
|
||||||
|
_ => {
|
||||||
|
tracing::warn!("Unknown key format: {:?}", key);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tracing::warn!("Failed to read private key");
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Some((certs, key)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn footer_blurb(&self) -> Option<crate::templates::Html<String>> {
|
||||||
|
if let Some(blurb) = &self.footer_blurb {
|
||||||
|
if !blurb.is_empty() {
|
||||||
|
return Some(crate::templates::Html(ammonia::clean(blurb)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn local_blurb(&self) -> Option<crate::templates::Html<String>> {
|
||||||
|
if let Some(blurb) = &self.local_blurb {
|
||||||
|
if !blurb.is_empty() {
|
||||||
|
return Some(crate::templates::Html(ammonia::clean(blurb)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn local_domains(&self) -> &[String] {
|
||||||
|
&self.local_domains
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn sled_path(&self) -> &PathBuf {
|
pub(crate) fn sled_path(&self) -> &PathBuf {
|
||||||
&self.sled_path
|
&self.sled_path
|
||||||
}
|
}
|
||||||
@ -338,6 +452,8 @@ impl Config {
|
|||||||
.try_resolve(IriRelativeStr::new("api/v1/admin/blocked")?.as_ref())?,
|
.try_resolve(IriRelativeStr::new("api/v1/admin/blocked")?.as_ref())?,
|
||||||
AdminUrlKind::Connected => FixedBaseResolver::new(self.base_uri.as_ref())
|
AdminUrlKind::Connected => FixedBaseResolver::new(self.base_uri.as_ref())
|
||||||
.try_resolve(IriRelativeStr::new("api/v1/admin/connected")?.as_ref())?,
|
.try_resolve(IriRelativeStr::new("api/v1/admin/connected")?.as_ref())?,
|
||||||
|
AdminUrlKind::Stats => FixedBaseResolver::new(self.base_uri.as_ref())
|
||||||
|
.try_resolve(IriRelativeStr::new("api/v1/admin/stats")?.as_ref())?,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(iri)
|
Ok(iri)
|
||||||
|
@ -100,6 +100,12 @@ pub(crate) enum ErrorKind {
|
|||||||
#[error("Couldn't sign digest")]
|
#[error("Couldn't sign digest")]
|
||||||
Signature(#[from] signature::Error),
|
Signature(#[from] signature::Error),
|
||||||
|
|
||||||
|
#[error("Couldn't read signature")]
|
||||||
|
ReadSignature(signature::Error),
|
||||||
|
|
||||||
|
#[error("Couldn't verify signature")]
|
||||||
|
VerifySignature(signature::Error),
|
||||||
|
|
||||||
#[error("Couldn't parse the signature header")]
|
#[error("Couldn't parse the signature header")]
|
||||||
HeaderValidation(#[from] actix_web::http::header::InvalidHeaderValue),
|
HeaderValidation(#[from] actix_web::http::header::InvalidHeaderValue),
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ use actix_web::{
|
|||||||
use bcrypt::{BcryptError, DEFAULT_COST};
|
use bcrypt::{BcryptError, DEFAULT_COST};
|
||||||
use futures_util::future::LocalBoxFuture;
|
use futures_util::future::LocalBoxFuture;
|
||||||
use http_signature_normalization_actix::prelude::InvalidHeaderValue;
|
use http_signature_normalization_actix::prelude::InvalidHeaderValue;
|
||||||
use std::{convert::Infallible, str::FromStr};
|
use std::{convert::Infallible, str::FromStr, time::Instant};
|
||||||
use tracing_error::SpanTrace;
|
use tracing_error::SpanTrace;
|
||||||
|
|
||||||
use crate::db::Db;
|
use crate::db::Db;
|
||||||
@ -61,7 +61,8 @@ impl Admin {
|
|||||||
hashed_api_token: Data<AdminConfig>,
|
hashed_api_token: Data<AdminConfig>,
|
||||||
x_api_token: XApiToken,
|
x_api_token: XApiToken,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
if actix_web::web::block(move || hashed_api_token.verify(x_api_token))
|
let span = tracing::Span::current();
|
||||||
|
if actix_web::web::block(move || span.in_scope(|| hashed_api_token.verify(x_api_token)))
|
||||||
.await
|
.await
|
||||||
.map_err(Error::canceled)??
|
.map_err(Error::canceled)??
|
||||||
{
|
{
|
||||||
@ -178,10 +179,15 @@ impl FromRequest for Admin {
|
|||||||
type Future = LocalBoxFuture<'static, Result<Self, Self::Error>>;
|
type Future = LocalBoxFuture<'static, Result<Self, Self::Error>>;
|
||||||
|
|
||||||
fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
|
fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
|
||||||
|
let now = Instant::now();
|
||||||
let res = Self::prepare_verify(req);
|
let res = Self::prepare_verify(req);
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let (db, c, t) = res?;
|
let (db, c, t) = res?;
|
||||||
Self::verify(c, t).await?;
|
Self::verify(c, t).await?;
|
||||||
|
metrics::histogram!(
|
||||||
|
"relay.admin.verify",
|
||||||
|
now.elapsed().as_micros() as f64 / 1_000_000_f64
|
||||||
|
);
|
||||||
Ok(Admin { db })
|
Ok(Admin { db })
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,8 @@ mod nodeinfo;
|
|||||||
mod process_listeners;
|
mod process_listeners;
|
||||||
|
|
||||||
pub(crate) use self::{
|
pub(crate) use self::{
|
||||||
contact::QueryContact, deliver::Deliver, deliver_many::DeliverMany,
|
contact::QueryContact, deliver::Deliver, deliver_many::DeliverMany, instance::QueryInstance,
|
||||||
instance::QueryInstance, nodeinfo::QueryNodeinfo,
|
nodeinfo::QueryNodeinfo,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -22,7 +22,7 @@ use background_jobs::{
|
|||||||
memory_storage::{ActixTimer, Storage},
|
memory_storage::{ActixTimer, Storage},
|
||||||
Job, Manager, QueueHandle, WorkerConfig,
|
Job, Manager, QueueHandle, WorkerConfig,
|
||||||
};
|
};
|
||||||
use std::time::Duration;
|
use std::{convert::TryFrom, num::NonZeroUsize, time::Duration};
|
||||||
|
|
||||||
fn debug_object(activity: &serde_json::Value) -> &serde_json::Value {
|
fn debug_object(activity: &serde_json::Value) -> &serde_json::Value {
|
||||||
let mut object = &activity["object"]["type"];
|
let mut object = &activity["object"]["type"];
|
||||||
@ -44,6 +44,9 @@ pub(crate) fn create_workers(
|
|||||||
media: MediaCache,
|
media: MediaCache,
|
||||||
config: Config,
|
config: Config,
|
||||||
) -> (Manager, JobServer) {
|
) -> (Manager, JobServer) {
|
||||||
|
let parallelism = std::thread::available_parallelism()
|
||||||
|
.unwrap_or_else(|_| NonZeroUsize::try_from(1).expect("nonzero"));
|
||||||
|
|
||||||
let shared = WorkerConfig::new_managed(Storage::new(ActixTimer), move |queue_handle| {
|
let shared = WorkerConfig::new_managed(Storage::new(ActixTimer), move |queue_handle| {
|
||||||
JobState::new(
|
JobState::new(
|
||||||
state.clone(),
|
state.clone(),
|
||||||
@ -64,8 +67,10 @@ pub(crate) fn create_workers(
|
|||||||
.register::<apub::Forward>()
|
.register::<apub::Forward>()
|
||||||
.register::<apub::Reject>()
|
.register::<apub::Reject>()
|
||||||
.register::<apub::Undo>()
|
.register::<apub::Undo>()
|
||||||
.set_worker_count("default", 16)
|
.set_worker_count("maintenance", 2)
|
||||||
.start();
|
.set_worker_count("apub", 2)
|
||||||
|
.set_worker_count("deliver", 8)
|
||||||
|
.start_with_threads(parallelism);
|
||||||
|
|
||||||
shared.every(Duration::from_secs(60 * 5), Listeners);
|
shared.every(Duration::from_secs(60 * 5), Listeners);
|
||||||
|
|
@ -67,6 +67,7 @@ impl ActixJob for Announce {
|
|||||||
type Future = Pin<Box<dyn Future<Output = Result<(), anyhow::Error>>>>;
|
type Future = Pin<Box<dyn Future<Output = Result<(), anyhow::Error>>>>;
|
||||||
|
|
||||||
const NAME: &'static str = "relay::jobs::apub::Announce";
|
const NAME: &'static str = "relay::jobs::apub::Announce";
|
||||||
|
const QUEUE: &'static str = "apub";
|
||||||
|
|
||||||
fn run(self, state: Self::State) -> Self::Future {
|
fn run(self, state: Self::State) -> Self::Future {
|
||||||
Box::pin(async move { self.perform(state).await.map_err(Into::into) })
|
Box::pin(async move { self.perform(state).await.map_err(Into::into) })
|
||||||
|
@ -116,6 +116,7 @@ impl ActixJob for Follow {
|
|||||||
type Future = Pin<Box<dyn Future<Output = Result<(), anyhow::Error>>>>;
|
type Future = Pin<Box<dyn Future<Output = Result<(), anyhow::Error>>>>;
|
||||||
|
|
||||||
const NAME: &'static str = "relay::jobs::apub::Follow";
|
const NAME: &'static str = "relay::jobs::apub::Follow";
|
||||||
|
const QUEUE: &'static str = "apub";
|
||||||
|
|
||||||
fn run(self, state: Self::State) -> Self::Future {
|
fn run(self, state: Self::State) -> Self::Future {
|
||||||
Box::pin(async move { self.perform(state).await.map_err(Into::into) })
|
Box::pin(async move { self.perform(state).await.map_err(Into::into) })
|
||||||
|
@ -52,6 +52,7 @@ impl ActixJob for Forward {
|
|||||||
type Future = Pin<Box<dyn Future<Output = Result<(), anyhow::Error>>>>;
|
type Future = Pin<Box<dyn Future<Output = Result<(), anyhow::Error>>>>;
|
||||||
|
|
||||||
const NAME: &'static str = "relay::jobs::apub::Forward";
|
const NAME: &'static str = "relay::jobs::apub::Forward";
|
||||||
|
const QUEUE: &'static str = "apub";
|
||||||
|
|
||||||
fn run(self, state: Self::State) -> Self::Future {
|
fn run(self, state: Self::State) -> Self::Future {
|
||||||
Box::pin(async move { self.perform(state).await.map_err(Into::into) })
|
Box::pin(async move { self.perform(state).await.map_err(Into::into) })
|
||||||
|
@ -38,6 +38,7 @@ impl ActixJob for Reject {
|
|||||||
type Future = Pin<Box<dyn Future<Output = Result<(), anyhow::Error>>>>;
|
type Future = Pin<Box<dyn Future<Output = Result<(), anyhow::Error>>>>;
|
||||||
|
|
||||||
const NAME: &'static str = "relay::jobs::apub::Reject";
|
const NAME: &'static str = "relay::jobs::apub::Reject";
|
||||||
|
const QUEUE: &'static str = "apub";
|
||||||
|
|
||||||
fn run(self, state: Self::State) -> Self::Future {
|
fn run(self, state: Self::State) -> Self::Future {
|
||||||
Box::pin(async move { self.perform(state).await.map_err(Into::into) })
|
Box::pin(async move { self.perform(state).await.map_err(Into::into) })
|
||||||
|
@ -53,6 +53,7 @@ impl ActixJob for Undo {
|
|||||||
type Future = Pin<Box<dyn Future<Output = Result<(), anyhow::Error>>>>;
|
type Future = Pin<Box<dyn Future<Output = Result<(), anyhow::Error>>>>;
|
||||||
|
|
||||||
const NAME: &'static str = "relay::jobs::apub::Undo";
|
const NAME: &'static str = "relay::jobs::apub::Undo";
|
||||||
|
const QUEUE: &'static str = "apub";
|
||||||
|
|
||||||
fn run(self, state: Self::State) -> Self::Future {
|
fn run(self, state: Self::State) -> Self::Future {
|
||||||
Box::pin(async move { self.perform(state).await.map_err(Into::into) })
|
Box::pin(async move { self.perform(state).await.map_err(Into::into) })
|
||||||
|
@ -86,6 +86,7 @@ impl ActixJob for QueryContact {
|
|||||||
type Future = Pin<Box<dyn Future<Output = Result<(), anyhow::Error>>>>;
|
type Future = Pin<Box<dyn Future<Output = Result<(), anyhow::Error>>>>;
|
||||||
|
|
||||||
const NAME: &'static str = "relay::jobs::QueryContact";
|
const NAME: &'static str = "relay::jobs::QueryContact";
|
||||||
|
const QUEUE: &'static str = "maintenance";
|
||||||
|
|
||||||
fn run(self, state: Self::State) -> Self::Future {
|
fn run(self, state: Self::State) -> Self::Future {
|
||||||
Box::pin(async move { self.perform(state).await.map_err(Into::into) })
|
Box::pin(async move { self.perform(state).await.map_err(Into::into) })
|
||||||
|
@ -55,6 +55,7 @@ impl ActixJob for Deliver {
|
|||||||
type Future = Pin<Box<dyn Future<Output = Result<(), anyhow::Error>>>>;
|
type Future = Pin<Box<dyn Future<Output = Result<(), anyhow::Error>>>>;
|
||||||
|
|
||||||
const NAME: &'static str = "relay::jobs::Deliver";
|
const NAME: &'static str = "relay::jobs::Deliver";
|
||||||
|
const QUEUE: &'static str = "deliver";
|
||||||
const BACKOFF: Backoff = Backoff::Exponential(8);
|
const BACKOFF: Backoff = Backoff::Exponential(8);
|
||||||
|
|
||||||
fn run(self, state: Self::State) -> Self::Future {
|
fn run(self, state: Self::State) -> Self::Future {
|
||||||
|
@ -50,6 +50,7 @@ impl ActixJob for DeliverMany {
|
|||||||
type Future = LocalBoxFuture<'static, Result<(), anyhow::Error>>;
|
type Future = LocalBoxFuture<'static, Result<(), anyhow::Error>>;
|
||||||
|
|
||||||
const NAME: &'static str = "relay::jobs::DeliverMany";
|
const NAME: &'static str = "relay::jobs::DeliverMany";
|
||||||
|
const QUEUE: &'static str = "deliver";
|
||||||
|
|
||||||
fn run(self, state: Self::State) -> Self::Future {
|
fn run(self, state: Self::State) -> Self::Future {
|
||||||
Box::pin(async move { self.perform(state).await.map_err(Into::into) })
|
Box::pin(async move { self.perform(state).await.map_err(Into::into) })
|
||||||
|
@ -110,6 +110,7 @@ impl ActixJob for QueryInstance {
|
|||||||
type Future = Pin<Box<dyn Future<Output = Result<(), anyhow::Error>>>>;
|
type Future = Pin<Box<dyn Future<Output = Result<(), anyhow::Error>>>>;
|
||||||
|
|
||||||
const NAME: &'static str = "relay::jobs::QueryInstance";
|
const NAME: &'static str = "relay::jobs::QueryInstance";
|
||||||
|
const QUEUE: &'static str = "maintenance";
|
||||||
|
|
||||||
fn run(self, state: Self::State) -> Self::Future {
|
fn run(self, state: Self::State) -> Self::Future {
|
||||||
Box::pin(async move { self.perform(state).await.map_err(Into::into) })
|
Box::pin(async move { self.perform(state).await.map_err(Into::into) })
|
||||||
|
@ -100,6 +100,7 @@ impl ActixJob for QueryNodeinfo {
|
|||||||
type Future = Pin<Box<dyn Future<Output = Result<(), anyhow::Error>>>>;
|
type Future = Pin<Box<dyn Future<Output = Result<(), anyhow::Error>>>>;
|
||||||
|
|
||||||
const NAME: &'static str = "relay::jobs::QueryNodeinfo";
|
const NAME: &'static str = "relay::jobs::QueryNodeinfo";
|
||||||
|
const QUEUE: &'static str = "maintenance";
|
||||||
|
|
||||||
fn run(self, state: Self::State) -> Self::Future {
|
fn run(self, state: Self::State) -> Self::Future {
|
||||||
Box::pin(async move { self.perform(state).await.map_err(Into::into) })
|
Box::pin(async move { self.perform(state).await.map_err(Into::into) })
|
||||||
|
@ -28,6 +28,7 @@ impl ActixJob for Listeners {
|
|||||||
type Future = Pin<Box<dyn Future<Output = Result<(), anyhow::Error>>>>;
|
type Future = Pin<Box<dyn Future<Output = Result<(), anyhow::Error>>>>;
|
||||||
|
|
||||||
const NAME: &'static str = "relay::jobs::Listeners";
|
const NAME: &'static str = "relay::jobs::Listeners";
|
||||||
|
const QUEUE: &'static str = "maintenance";
|
||||||
|
|
||||||
fn run(self, state: Self::State) -> Self::Future {
|
fn run(self, state: Self::State) -> Self::Future {
|
||||||
Box::pin(async move { self.perform(state).await.map_err(Into::into) })
|
Box::pin(async move { self.perform(state).await.map_err(Into::into) })
|
||||||
|
179
src/main.rs
179
src/main.rs
@ -2,11 +2,13 @@
|
|||||||
#![allow(clippy::needless_borrow)]
|
#![allow(clippy::needless_borrow)]
|
||||||
|
|
||||||
use activitystreams::iri_string::types::IriString;
|
use activitystreams::iri_string::types::IriString;
|
||||||
use actix_web::{web, App, HttpServer};
|
use actix_web::{middleware::Compress, web, App, HttpServer};
|
||||||
|
use collector::MemoryCollector;
|
||||||
#[cfg(feature = "console")]
|
#[cfg(feature = "console")]
|
||||||
use console_subscriber::ConsoleLayer;
|
use console_subscriber::ConsoleLayer;
|
||||||
use opentelemetry::{sdk::Resource, KeyValue};
|
use opentelemetry::{sdk::Resource, KeyValue};
|
||||||
use opentelemetry_otlp::WithExportConfig;
|
use opentelemetry_otlp::WithExportConfig;
|
||||||
|
use rustls::ServerConfig;
|
||||||
use tracing_actix_web::TracingLogger;
|
use tracing_actix_web::TracingLogger;
|
||||||
use tracing_error::ErrorLayer;
|
use tracing_error::ErrorLayer;
|
||||||
use tracing_log::LogTracer;
|
use tracing_log::LogTracer;
|
||||||
@ -15,6 +17,7 @@ use tracing_subscriber::{filter::Targets, fmt::format::FmtSpan, layer::Subscribe
|
|||||||
mod admin;
|
mod admin;
|
||||||
mod apub;
|
mod apub;
|
||||||
mod args;
|
mod args;
|
||||||
|
mod collector;
|
||||||
mod config;
|
mod config;
|
||||||
mod data;
|
mod data;
|
||||||
mod db;
|
mod db;
|
||||||
@ -32,7 +35,7 @@ use self::{
|
|||||||
data::{ActorCache, MediaCache, State},
|
data::{ActorCache, MediaCache, State},
|
||||||
db::Db,
|
db::Db,
|
||||||
jobs::create_workers,
|
jobs::create_workers,
|
||||||
middleware::{DebugPayload, RelayResolver},
|
middleware::{DebugPayload, RelayResolver, Timings},
|
||||||
routes::{actor, inbox, index, nodeinfo, nodeinfo_meta, statics},
|
routes::{actor, inbox, index, nodeinfo, nodeinfo_meta, statics},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -43,7 +46,7 @@ fn init_subscriber(
|
|||||||
LogTracer::init()?;
|
LogTracer::init()?;
|
||||||
|
|
||||||
let targets: Targets = std::env::var("RUST_LOG")
|
let targets: Targets = std::env::var("RUST_LOG")
|
||||||
.unwrap_or_else(|_| "info".into())
|
.unwrap_or_else(|_| "warn,actix_web=debug,actix_server=debug,tracing_actix_web=info".into())
|
||||||
.parse()?;
|
.parse()?;
|
||||||
|
|
||||||
let format_layer = tracing_subscriber::fmt::layer()
|
let format_layer = tracing_subscriber::fmt::layer()
|
||||||
@ -91,72 +94,120 @@ fn init_subscriber(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_rt::main]
|
fn main() -> Result<(), anyhow::Error> {
|
||||||
async fn main() -> Result<(), anyhow::Error> {
|
|
||||||
dotenv::dotenv().ok();
|
dotenv::dotenv().ok();
|
||||||
|
|
||||||
let config = Config::build()?;
|
let config = Config::build()?;
|
||||||
|
|
||||||
init_subscriber(Config::software_name(), config.opentelemetry_url())?;
|
init_subscriber(Config::software_name(), config.opentelemetry_url())?;
|
||||||
|
let collector = MemoryCollector::new();
|
||||||
|
collector.install()?;
|
||||||
|
|
||||||
let args = Args::new();
|
let args = Args::new();
|
||||||
|
|
||||||
if args.any() {
|
if args.any() {
|
||||||
let client = requests::build_client(&config.user_agent());
|
return client_main(config, args);
|
||||||
|
|
||||||
if !args.blocks().is_empty() || !args.allowed().is_empty() {
|
|
||||||
if args.undo() {
|
|
||||||
admin::client::unblock(&client, &config, args.blocks().to_vec()).await?;
|
|
||||||
admin::client::disallow(&client, &config, args.allowed().to_vec()).await?;
|
|
||||||
} else {
|
|
||||||
admin::client::block(&client, &config, args.blocks().to_vec()).await?;
|
|
||||||
admin::client::allow(&client, &config, args.allowed().to_vec()).await?;
|
|
||||||
}
|
|
||||||
println!("Updated lists");
|
|
||||||
}
|
|
||||||
|
|
||||||
if args.list() {
|
|
||||||
let (blocked, allowed, connected) = tokio::try_join!(
|
|
||||||
admin::client::blocked(&client, &config),
|
|
||||||
admin::client::allowed(&client, &config),
|
|
||||||
admin::client::connected(&client, &config)
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let mut report = String::from("Report:\n");
|
|
||||||
if !allowed.allowed_domains.is_empty() {
|
|
||||||
report += "\nAllowed\n\t";
|
|
||||||
report += &allowed.allowed_domains.join("\n\t");
|
|
||||||
}
|
|
||||||
if !blocked.blocked_domains.is_empty() {
|
|
||||||
report += "\n\nBlocked\n\t";
|
|
||||||
report += &blocked.blocked_domains.join("\n\t");
|
|
||||||
}
|
|
||||||
if !connected.connected_actors.is_empty() {
|
|
||||||
report += "\n\nConnected\n\t";
|
|
||||||
report += &connected.connected_actors.join("\n\t");
|
|
||||||
}
|
|
||||||
report += "\n";
|
|
||||||
println!("{report}");
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tracing::warn!("Opening DB");
|
||||||
let db = Db::build(&config)?;
|
let db = Db::build(&config)?;
|
||||||
|
|
||||||
let media = MediaCache::new(db.clone());
|
tracing::warn!("Building caches");
|
||||||
let state = State::build(db.clone()).await?;
|
|
||||||
let actors = ActorCache::new(db.clone());
|
let actors = ActorCache::new(db.clone());
|
||||||
|
let media = MediaCache::new(db.clone());
|
||||||
|
|
||||||
|
server_main(db, actors, media, collector, config)?;
|
||||||
|
|
||||||
|
tracing::warn!("Application exit");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_rt::main]
|
||||||
|
async fn client_main(config: Config, args: Args) -> Result<(), anyhow::Error> {
|
||||||
|
actix_rt::spawn(do_client_main(config, args)).await?
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn do_client_main(config: Config, args: Args) -> Result<(), anyhow::Error> {
|
||||||
|
let client = requests::build_client(&config.user_agent());
|
||||||
|
|
||||||
|
if !args.blocks().is_empty() || !args.allowed().is_empty() {
|
||||||
|
if args.undo() {
|
||||||
|
admin::client::unblock(&client, &config, args.blocks().to_vec()).await?;
|
||||||
|
admin::client::disallow(&client, &config, args.allowed().to_vec()).await?;
|
||||||
|
} else {
|
||||||
|
admin::client::block(&client, &config, args.blocks().to_vec()).await?;
|
||||||
|
admin::client::allow(&client, &config, args.allowed().to_vec()).await?;
|
||||||
|
}
|
||||||
|
println!("Updated lists");
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.list() {
|
||||||
|
let (blocked, allowed, connected) = tokio::try_join!(
|
||||||
|
admin::client::blocked(&client, &config),
|
||||||
|
admin::client::allowed(&client, &config),
|
||||||
|
admin::client::connected(&client, &config)
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let mut report = String::from("Report:\n");
|
||||||
|
if !allowed.allowed_domains.is_empty() {
|
||||||
|
report += "\nAllowed\n\t";
|
||||||
|
report += &allowed.allowed_domains.join("\n\t");
|
||||||
|
}
|
||||||
|
if !blocked.blocked_domains.is_empty() {
|
||||||
|
report += "\n\nBlocked\n\t";
|
||||||
|
report += &blocked.blocked_domains.join("\n\t");
|
||||||
|
}
|
||||||
|
if !connected.connected_actors.is_empty() {
|
||||||
|
report += "\n\nConnected\n\t";
|
||||||
|
report += &connected.connected_actors.join("\n\t");
|
||||||
|
}
|
||||||
|
report += "\n";
|
||||||
|
println!("{report}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.stats() {
|
||||||
|
let stats = admin::client::stats(&client, &config).await?;
|
||||||
|
stats.present();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_rt::main]
|
||||||
|
async fn server_main(
|
||||||
|
db: Db,
|
||||||
|
actors: ActorCache,
|
||||||
|
media: MediaCache,
|
||||||
|
collector: MemoryCollector,
|
||||||
|
config: Config,
|
||||||
|
) -> Result<(), anyhow::Error> {
|
||||||
|
actix_rt::spawn(do_server_main(db, actors, media, collector, config)).await?
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn do_server_main(
|
||||||
|
db: Db,
|
||||||
|
actors: ActorCache,
|
||||||
|
media: MediaCache,
|
||||||
|
collector: MemoryCollector,
|
||||||
|
config: Config,
|
||||||
|
) -> Result<(), anyhow::Error> {
|
||||||
|
tracing::warn!("Creating state");
|
||||||
|
let state = State::build(db.clone()).await?;
|
||||||
|
|
||||||
|
tracing::warn!("Creating workers");
|
||||||
let (manager, job_server) =
|
let (manager, job_server) =
|
||||||
create_workers(state.clone(), actors.clone(), media.clone(), config.clone());
|
create_workers(state.clone(), actors.clone(), media.clone(), config.clone());
|
||||||
|
|
||||||
if let Some((token, admin_handle)) = config.telegram_info() {
|
if let Some((token, admin_handle)) = config.telegram_info() {
|
||||||
|
tracing::warn!("Creating telegram handler");
|
||||||
telegram::start(admin_handle.to_owned(), db.clone(), token);
|
telegram::start(admin_handle.to_owned(), db.clone(), token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let keys = config.open_keys()?;
|
||||||
|
|
||||||
let bind_address = config.bind_address();
|
let bind_address = config.bind_address();
|
||||||
HttpServer::new(move || {
|
let server = HttpServer::new(move || {
|
||||||
let app = App::new()
|
let app = App::new()
|
||||||
.app_data(web::Data::new(db.clone()))
|
.app_data(web::Data::new(db.clone()))
|
||||||
.app_data(web::Data::new(state.clone()))
|
.app_data(web::Data::new(state.clone()))
|
||||||
@ -164,7 +215,8 @@ async fn main() -> Result<(), anyhow::Error> {
|
|||||||
.app_data(web::Data::new(actors.clone()))
|
.app_data(web::Data::new(actors.clone()))
|
||||||
.app_data(web::Data::new(config.clone()))
|
.app_data(web::Data::new(config.clone()))
|
||||||
.app_data(web::Data::new(job_server.clone()))
|
.app_data(web::Data::new(job_server.clone()))
|
||||||
.app_data(web::Data::new(media.clone()));
|
.app_data(web::Data::new(media.clone()))
|
||||||
|
.app_data(web::Data::new(collector.clone()));
|
||||||
|
|
||||||
let app = if let Some(data) = config.admin_config() {
|
let app = if let Some(data) = config.admin_config() {
|
||||||
app.app_data(data)
|
app.app_data(data)
|
||||||
@ -172,7 +224,9 @@ async fn main() -> Result<(), anyhow::Error> {
|
|||||||
app
|
app
|
||||||
};
|
};
|
||||||
|
|
||||||
app.wrap(TracingLogger::default())
|
app.wrap(Compress::default())
|
||||||
|
.wrap(TracingLogger::default())
|
||||||
|
.wrap(Timings)
|
||||||
.service(web::resource("/").route(web::get().to(index)))
|
.service(web::resource("/").route(web::get().to(index)))
|
||||||
.service(web::resource("/media/{path}").route(web::get().to(routes::media)))
|
.service(web::resource("/media/{path}").route(web::get().to(routes::media)))
|
||||||
.service(
|
.service(
|
||||||
@ -203,16 +257,35 @@ async fn main() -> Result<(), anyhow::Error> {
|
|||||||
.route("/unblock", web::post().to(admin::routes::unblock))
|
.route("/unblock", web::post().to(admin::routes::unblock))
|
||||||
.route("/allowed", web::get().to(admin::routes::allowed))
|
.route("/allowed", web::get().to(admin::routes::allowed))
|
||||||
.route("/blocked", web::get().to(admin::routes::blocked))
|
.route("/blocked", web::get().to(admin::routes::blocked))
|
||||||
.route("/connected", web::get().to(admin::routes::connected)),
|
.route("/connected", web::get().to(admin::routes::connected))
|
||||||
|
.route("/stats", web::get().to(admin::routes::stats)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
})
|
});
|
||||||
.bind(bind_address)?
|
|
||||||
.run()
|
if let Some((certs, key)) = keys {
|
||||||
.await?;
|
tracing::warn!("Binding to {}:{} with TLS", bind_address.0, bind_address.1);
|
||||||
|
let server_config = ServerConfig::builder()
|
||||||
|
.with_safe_default_cipher_suites()
|
||||||
|
.with_safe_default_kx_groups()
|
||||||
|
.with_safe_default_protocol_versions()?
|
||||||
|
.with_no_client_auth()
|
||||||
|
.with_single_cert(certs, key)?;
|
||||||
|
server
|
||||||
|
.bind_rustls(bind_address, server_config)?
|
||||||
|
.run()
|
||||||
|
.await?;
|
||||||
|
} else {
|
||||||
|
tracing::warn!("Binding to {}:{}", bind_address.0, bind_address.1);
|
||||||
|
server.bind(bind_address)?.run().await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
tracing::warn!("Server closed");
|
||||||
|
|
||||||
drop(manager);
|
drop(manager);
|
||||||
|
|
||||||
|
tracing::warn!("Main complete");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
mod payload;
|
mod payload;
|
||||||
|
mod timings;
|
||||||
mod verifier;
|
mod verifier;
|
||||||
mod webfinger;
|
mod webfinger;
|
||||||
|
|
||||||
pub(crate) use payload::DebugPayload;
|
pub(crate) use payload::DebugPayload;
|
||||||
|
pub(crate) use timings::Timings;
|
||||||
pub(crate) use verifier::MyVerify;
|
pub(crate) use verifier::MyVerify;
|
||||||
pub(crate) use webfinger::RelayResolver;
|
pub(crate) use webfinger::RelayResolver;
|
@ -5,7 +5,7 @@ use actix_web::{
|
|||||||
HttpMessage,
|
HttpMessage,
|
||||||
};
|
};
|
||||||
use futures_util::{
|
use futures_util::{
|
||||||
future::{LocalBoxFuture, TryFutureExt},
|
future::TryFutureExt,
|
||||||
stream::{once, TryStreamExt},
|
stream::{once, TryStreamExt},
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
@ -45,7 +45,7 @@ where
|
|||||||
{
|
{
|
||||||
type Response = S::Response;
|
type Response = S::Response;
|
||||||
type Error = S::Error;
|
type Error = S::Error;
|
||||||
type Future = LocalBoxFuture<'static, Result<S::Response, S::Error>>;
|
type Future = S::Future;
|
||||||
|
|
||||||
fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
self.1.poll_ready(cx)
|
self.1.poll_ready(cx)
|
||||||
@ -68,13 +68,9 @@ where
|
|||||||
)),
|
)),
|
||||||
});
|
});
|
||||||
|
|
||||||
let fut = self.1.call(req);
|
self.1.call(req)
|
||||||
|
|
||||||
Box::pin(async move { fut.await })
|
|
||||||
} else {
|
} else {
|
||||||
let fut = self.1.call(req);
|
self.1.call(req)
|
||||||
|
|
||||||
Box::pin(async move { fut.await })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
143
src/middleware/timings.rs
Normal file
143
src/middleware/timings.rs
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
use actix_web::{
|
||||||
|
body::MessageBody,
|
||||||
|
dev::{Service, ServiceRequest, ServiceResponse, Transform},
|
||||||
|
http::StatusCode,
|
||||||
|
};
|
||||||
|
use std::{
|
||||||
|
future::{ready, Future, Ready},
|
||||||
|
time::Instant,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub(crate) struct Timings;
|
||||||
|
pub(crate) struct TimingsMiddleware<S>(S);
|
||||||
|
|
||||||
|
struct LogOnDrop {
|
||||||
|
begin: Instant,
|
||||||
|
path: String,
|
||||||
|
method: String,
|
||||||
|
arm: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pin_project_lite::pin_project! {
|
||||||
|
pub(crate) struct TimingsFuture<F> {
|
||||||
|
#[pin]
|
||||||
|
future: F,
|
||||||
|
|
||||||
|
log_on_drop: Option<LogOnDrop>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pin_project_lite::pin_project! {
|
||||||
|
pub(crate) struct TimingsBody<B> {
|
||||||
|
#[pin]
|
||||||
|
body: B,
|
||||||
|
|
||||||
|
log_on_drop: LogOnDrop,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for LogOnDrop {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if self.arm {
|
||||||
|
let duration = self.begin.elapsed();
|
||||||
|
metrics::histogram!("relay.request.complete", duration, "path" => self.path.clone(), "method" => self.method.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S, B> Transform<S, ServiceRequest> for Timings
|
||||||
|
where
|
||||||
|
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = actix_web::Error>,
|
||||||
|
S::Future: 'static,
|
||||||
|
{
|
||||||
|
type Response = ServiceResponse<TimingsBody<B>>;
|
||||||
|
type Error = S::Error;
|
||||||
|
type InitError = ();
|
||||||
|
type Transform = TimingsMiddleware<S>;
|
||||||
|
type Future = Ready<Result<Self::Transform, Self::InitError>>;
|
||||||
|
|
||||||
|
fn new_transform(&self, service: S) -> Self::Future {
|
||||||
|
ready(Ok(TimingsMiddleware(service)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S, B> Service<ServiceRequest> for TimingsMiddleware<S>
|
||||||
|
where
|
||||||
|
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = actix_web::Error>,
|
||||||
|
S::Future: 'static,
|
||||||
|
{
|
||||||
|
type Response = ServiceResponse<TimingsBody<B>>;
|
||||||
|
type Error = S::Error;
|
||||||
|
type Future = TimingsFuture<S::Future>;
|
||||||
|
|
||||||
|
fn poll_ready(
|
||||||
|
&self,
|
||||||
|
ctx: &mut core::task::Context<'_>,
|
||||||
|
) -> std::task::Poll<Result<(), Self::Error>> {
|
||||||
|
self.0.poll_ready(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call(&self, req: ServiceRequest) -> Self::Future {
|
||||||
|
let log_on_drop = LogOnDrop {
|
||||||
|
begin: Instant::now(),
|
||||||
|
path: req.path().to_string(),
|
||||||
|
method: req.method().to_string(),
|
||||||
|
arm: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
let future = self.0.call(req);
|
||||||
|
|
||||||
|
TimingsFuture {
|
||||||
|
future,
|
||||||
|
log_on_drop: Some(log_on_drop),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F, B> Future for TimingsFuture<F>
|
||||||
|
where
|
||||||
|
F: Future<Output = Result<ServiceResponse<B>, actix_web::Error>>,
|
||||||
|
{
|
||||||
|
type Output = Result<ServiceResponse<TimingsBody<B>>, actix_web::Error>;
|
||||||
|
|
||||||
|
fn poll(
|
||||||
|
self: std::pin::Pin<&mut Self>,
|
||||||
|
cx: &mut std::task::Context<'_>,
|
||||||
|
) -> std::task::Poll<Self::Output> {
|
||||||
|
let this = self.project();
|
||||||
|
|
||||||
|
let res = std::task::ready!(this.future.poll(cx));
|
||||||
|
|
||||||
|
let mut log_on_drop = this
|
||||||
|
.log_on_drop
|
||||||
|
.take()
|
||||||
|
.expect("TimingsFuture polled after completion");
|
||||||
|
|
||||||
|
let status = match &res {
|
||||||
|
Ok(res) => res.status(),
|
||||||
|
Err(e) => e.as_response_error().status_code(),
|
||||||
|
};
|
||||||
|
|
||||||
|
log_on_drop.arm =
|
||||||
|
status != StatusCode::NOT_FOUND && status != StatusCode::METHOD_NOT_ALLOWED;
|
||||||
|
|
||||||
|
let res = res.map(|r| r.map_body(|_, body| TimingsBody { body, log_on_drop }));
|
||||||
|
|
||||||
|
std::task::Poll::Ready(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B: MessageBody> MessageBody for TimingsBody<B> {
|
||||||
|
type Error = B::Error;
|
||||||
|
|
||||||
|
fn size(&self) -> actix_web::body::BodySize {
|
||||||
|
self.body.size()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_next(
|
||||||
|
self: std::pin::Pin<&mut Self>,
|
||||||
|
cx: &mut std::task::Context<'_>,
|
||||||
|
) -> std::task::Poll<Option<Result<actix_web::web::Bytes, Self::Error>>> {
|
||||||
|
self.project().body.poll_next(cx)
|
||||||
|
}
|
||||||
|
}
|
@ -16,7 +16,7 @@ use std::{future::Future, pin::Pin};
|
|||||||
pub(crate) struct MyVerify(pub Requests, pub ActorCache, pub State);
|
pub(crate) struct MyVerify(pub Requests, pub ActorCache, pub State);
|
||||||
|
|
||||||
impl MyVerify {
|
impl MyVerify {
|
||||||
#[tracing::instrument("Verify signature", skip(self, signature))]
|
#[tracing::instrument("Verify request", skip(self, signature, signing_string))]
|
||||||
async fn verify(
|
async fn verify(
|
||||||
&self,
|
&self,
|
||||||
algorithm: Option<Algorithm>,
|
algorithm: Option<Algorithm>,
|
||||||
@ -106,6 +106,7 @@ impl PublicKeyResponse {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument("Verify signature")]
|
||||||
async fn do_verify(
|
async fn do_verify(
|
||||||
public_key: &str,
|
public_key: &str,
|
||||||
signature: String,
|
signature: String,
|
||||||
@ -113,15 +114,20 @@ async fn do_verify(
|
|||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let public_key = RsaPublicKey::from_public_key_pem(public_key.trim())?;
|
let public_key = RsaPublicKey::from_public_key_pem(public_key.trim())?;
|
||||||
|
|
||||||
|
let span = tracing::Span::current();
|
||||||
web::block(move || {
|
web::block(move || {
|
||||||
let decoded = base64::decode(signature)?;
|
span.in_scope(|| {
|
||||||
let signature = Signature::from_bytes(&decoded)?;
|
let decoded = base64::decode(signature)?;
|
||||||
let hashed = Sha256::new_with_prefix(signing_string.as_bytes());
|
let signature = Signature::from_bytes(&decoded).map_err(ErrorKind::ReadSignature)?;
|
||||||
|
let hashed = Sha256::new_with_prefix(signing_string.as_bytes());
|
||||||
|
|
||||||
let verifying_key = VerifyingKey::new_with_prefix(public_key);
|
let verifying_key = VerifyingKey::new_with_prefix(public_key);
|
||||||
verifying_key.verify_digest(hashed, &signature)?;
|
verifying_key
|
||||||
|
.verify_digest(hashed, &signature)
|
||||||
|
.map_err(ErrorKind::VerifySignature)?;
|
||||||
|
|
||||||
Ok(()) as Result<(), Error>
|
Ok(()) as Result<(), Error>
|
||||||
|
})
|
||||||
})
|
})
|
||||||
.await??;
|
.await??;
|
||||||
|
|
||||||
|
@ -7,6 +7,19 @@ use actix_web::{web, HttpResponse};
|
|||||||
use rand::{seq::SliceRandom, thread_rng};
|
use rand::{seq::SliceRandom, thread_rng};
|
||||||
use std::io::BufWriter;
|
use std::io::BufWriter;
|
||||||
|
|
||||||
|
const MINIFY_CONFIG: minify_html::Cfg = minify_html::Cfg {
|
||||||
|
do_not_minify_doctype: true,
|
||||||
|
ensure_spec_compliant_unquoted_attribute_values: true,
|
||||||
|
keep_closing_tags: true,
|
||||||
|
keep_html_and_head_opening_tags: false,
|
||||||
|
keep_spaces_between_attributes: true,
|
||||||
|
keep_comments: false,
|
||||||
|
minify_js: true,
|
||||||
|
minify_css: true,
|
||||||
|
remove_bangs: true,
|
||||||
|
remove_processing_instructions: true,
|
||||||
|
};
|
||||||
|
|
||||||
fn open_reg(node: &Node) -> bool {
|
fn open_reg(node: &Node) -> bool {
|
||||||
node.instance
|
node.instance
|
||||||
.as_ref()
|
.as_ref()
|
||||||
@ -20,7 +33,28 @@ pub(crate) async fn route(
|
|||||||
state: web::Data<State>,
|
state: web::Data<State>,
|
||||||
config: web::Data<Config>,
|
config: web::Data<Config>,
|
||||||
) -> Result<HttpResponse, Error> {
|
) -> Result<HttpResponse, Error> {
|
||||||
let mut nodes = state.node_cache().nodes().await?;
|
let all_nodes = state.node_cache().nodes().await?;
|
||||||
|
|
||||||
|
let mut nodes = Vec::new();
|
||||||
|
let mut local = Vec::new();
|
||||||
|
|
||||||
|
for node in all_nodes {
|
||||||
|
if node
|
||||||
|
.base
|
||||||
|
.authority_str()
|
||||||
|
.map(|authority| {
|
||||||
|
config
|
||||||
|
.local_domains()
|
||||||
|
.iter()
|
||||||
|
.any(|domain| domain.as_str() == authority)
|
||||||
|
})
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
local.push(node);
|
||||||
|
} else {
|
||||||
|
nodes.push(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
nodes.sort_by(|lhs, rhs| match (open_reg(lhs), open_reg(rhs)) {
|
nodes.sort_by(|lhs, rhs| match (open_reg(lhs), open_reg(rhs)) {
|
||||||
(true, true) | (false, false) => std::cmp::Ordering::Equal,
|
(true, true) | (false, false) => std::cmp::Ordering::Equal,
|
||||||
@ -37,11 +71,13 @@ pub(crate) async fn route(
|
|||||||
|
|
||||||
let mut buf = BufWriter::new(Vec::new());
|
let mut buf = BufWriter::new(Vec::new());
|
||||||
|
|
||||||
crate::templates::index(&mut buf, &nodes, &config)?;
|
crate::templates::index(&mut buf, &local, &nodes, &config)?;
|
||||||
let buf = buf.into_inner().map_err(|e| {
|
let html = buf.into_inner().map_err(|e| {
|
||||||
tracing::error!("Error rendering template, {}", e.error());
|
tracing::error!("Error rendering template, {}", e.error());
|
||||||
ErrorKind::FlushBuffer
|
ErrorKind::FlushBuffer
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(HttpResponse::Ok().content_type("text/html").body(buf))
|
let html = minify_html::minify(&html, &MINIFY_CONFIG);
|
||||||
|
|
||||||
|
Ok(HttpResponse::Ok().content_type("text/html").body(html))
|
||||||
}
|
}
|
||||||
|
@ -4,15 +4,15 @@
|
|||||||
@(contact: &Contact, base: &IriString)
|
@(contact: &Contact, base: &IriString)
|
||||||
|
|
||||||
<div class="admin">
|
<div class="admin">
|
||||||
<div class="left">
|
<div class="left">
|
||||||
<figure class="avatar">
|
<figure class="avatar">
|
||||||
<img src="@contact.avatar" alt="@contact.display_name's avatar">
|
<img src="@contact.avatar" alt="@contact.display_name's avatar">
|
||||||
</figure>
|
</figure>
|
||||||
</div>
|
</div>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<p class="display-name"><a href="@contact.url">@contact.display_name</a></p>
|
<p class="display-name"><a href="@contact.url">@contact.display_name</a></p>
|
||||||
<p class="username">
|
<p class="username">
|
||||||
@@@contact.username@if let Some(authority) = base.authority_str() {@@@authority}
|
@@@contact.username@if let Some(authority) = base.authority_str() {@@@authority}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -4,7 +4,7 @@ data::Node,
|
|||||||
templates::{info, instance, statics::index_css},
|
templates::{info, instance, statics::index_css},
|
||||||
};
|
};
|
||||||
|
|
||||||
@(nodes: &[Node], config: &Config)
|
@(local: &[Node], nodes: &[Node], config: &Config)
|
||||||
|
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
@ -24,16 +24,23 @@ templates::{info, instance, statics::index_css},
|
|||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<main>
|
<main>
|
||||||
<section>
|
@if !local.is_empty() || config.local_blurb().is_some() {
|
||||||
<h3>@nodes.len() instances fédérées.</h3>
|
<article>
|
||||||
@if nodes.is_empty() {
|
<h3>About</h3>
|
||||||
<p>Aucune instance fédérée en ce moment.</p>
|
<section class="local-explainer">
|
||||||
} else {
|
@if let Some(blurb) = config.local_blurb() {
|
||||||
|
@blurb
|
||||||
|
} else {
|
||||||
|
<p>Ces domaines sont administrés par la même équipe que ce relais.</p>
|
||||||
|
}
|
||||||
|
</section>
|
||||||
|
@if !local.is_empty() {
|
||||||
<ul>
|
<ul>
|
||||||
@for node in nodes {
|
@for node in local {
|
||||||
@if let Some(inst) = node.instance.as_ref() {
|
@if let Some(inst) = node.instance.as_ref() {
|
||||||
<li>
|
<li>
|
||||||
@:instance(inst, node.info.as_ref().map(|info| { info.software.as_ref() }), node.contact.as_ref(), &node.base)
|
@:instance(inst, node.info.as_ref().map(|info| { info.software.as_ref() }), node.contact.as_ref(),
|
||||||
|
&node.base)
|
||||||
</li>
|
</li>
|
||||||
} else {
|
} else {
|
||||||
@if let Some(inf) = node.info.as_ref() {
|
@if let Some(inf) = node.info.as_ref() {
|
||||||
@ -45,10 +52,11 @@ templates::{info, instance, statics::index_css},
|
|||||||
}
|
}
|
||||||
</ul>
|
</ul>
|
||||||
}
|
}
|
||||||
</section>
|
</article>
|
||||||
<section>
|
}
|
||||||
<a name="#joining"><h3>Rejoindre</h3></a>
|
<article>
|
||||||
<article class="joining">
|
<a name="#joining"><h3>Joining</h3></a>
|
||||||
|
<section class="joining">
|
||||||
@if config.restricted_mode() {
|
@if config.restricted_mode() {
|
||||||
<h4>
|
<h4>
|
||||||
Ce relais est restreint.
|
Ce relais est restreint.
|
||||||
@ -77,10 +85,34 @@ templates::{info, instance, statics::index_css},
|
|||||||
<p>
|
<p>
|
||||||
Vérifiez la documentation de votre installation, qui suit probablement la convention de Mastodon ou de Pleroma.
|
Vérifiez la documentation de votre installation, qui suit probablement la convention de Mastodon ou de Pleroma.
|
||||||
</p>
|
</p>
|
||||||
</article>
|
</section>
|
||||||
</section>
|
</article>
|
||||||
|
@if !nodes.is_empty() {
|
||||||
|
<article>
|
||||||
|
<h3>@nodes.len() Connected Servers</h3>
|
||||||
|
<ul>
|
||||||
|
@for node in nodes {
|
||||||
|
@if let Some(inst) = node.instance.as_ref() {
|
||||||
|
<li>
|
||||||
|
@:instance(inst, node.info.as_ref().map(|info| { info.software.as_ref() }), node.contact.as_ref(),
|
||||||
|
&node.base)
|
||||||
|
</li>
|
||||||
|
} else {
|
||||||
|
@if let Some(inf) = node.info.as_ref() {
|
||||||
|
<li>
|
||||||
|
@:info(inf, &node.base)
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</article>
|
||||||
|
}
|
||||||
</main>
|
</main>
|
||||||
<footer>
|
<footer>
|
||||||
|
@if let Some(blurb) = config.footer_blurb() {
|
||||||
|
<div>@blurb</div>
|
||||||
|
}
|
||||||
<p>
|
<p>
|
||||||
Code source de l'application disponible ici:
|
Code source de l'application disponible ici:
|
||||||
<a href="@config.source_code()">@config.source_code()</a>
|
<a href="@config.source_code()">@config.source_code()</a>
|
||||||
|
@ -3,14 +3,14 @@
|
|||||||
|
|
||||||
@(info: &Info, base: &IriString)
|
@(info: &Info, base: &IriString)
|
||||||
|
|
||||||
<article class="info">
|
<section class="info">
|
||||||
@if let Some(authority) = base.authority_str() {
|
@if let Some(authority) = base.authority_str() {
|
||||||
<h4 class="padded"><a href="@base">@authority</a></h4>
|
<h4 class="padded"><a href="@base">@authority</a></h4>
|
||||||
|
}
|
||||||
|
<p class="padded">
|
||||||
|
Utilise @info.software, version @info.version.
|
||||||
|
@if info.reg {
|
||||||
|
Inscriptions ouvertes
|
||||||
}
|
}
|
||||||
<p class="padded">
|
</p>
|
||||||
Utilise @info.software, version @info.version.
|
</section>
|
||||||
@if info.reg {
|
|
||||||
Enregistrement ouvert.
|
|
||||||
}
|
|
||||||
</p>
|
|
||||||
</article>
|
|
||||||
|
@ -3,17 +3,17 @@
|
|||||||
|
|
||||||
@(instance: &Instance, software: Option<&str>, contact: Option<&Contact>, base: &IriString)
|
@(instance: &Instance, software: Option<&str>, contact: Option<&Contact>, base: &IriString)
|
||||||
|
|
||||||
<article class="instance">
|
<section class="instance">
|
||||||
<h4 class="padded"><a href="@base">@instance.title</a></h4>
|
<h4 class="padded"><a href="@base">@instance.title</a></h4>
|
||||||
<p class="padded">
|
<p class="padded">
|
||||||
@if let Some(software) = software {
|
@if let Some(software) = software {
|
||||||
Utilise @software, version @instance.version.
|
Utilise @software, version @instance.version.
|
||||||
}
|
}
|
||||||
<br>
|
<br>
|
||||||
@if instance.reg {
|
@if instance.reg {
|
||||||
@if instance.requires_approval {
|
@if instance.requires_approval {
|
||||||
<span class="moderated">Inscriptions soumises à approbation.</span>
|
<span class="moderated">Inscriptions soumises à approbation.</span>
|
||||||
} else{
|
} else {
|
||||||
<span class="open">Inscriptions ouvertes.</span>
|
<span class="open">Inscriptions ouvertes.</span>
|
||||||
}
|
}
|
||||||
} else{
|
} else{
|
||||||
@ -35,5 +35,9 @@
|
|||||||
@:admin(contact, base)
|
@:admin(contact, base)
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
}
|
@if let Some(contact) = contact {
|
||||||
</article>
|
<h5 class="instance-admin">@instance.title's admin:</h5>
|
||||||
|
@:admin(contact, base)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</section>
|
||||||
|
Loading…
Reference in New Issue
Block a user