From 5aceca72bd467d77e6e91ec730f0be7ddcc3c1c8 Mon Sep 17 00:00:00 2001 From: Natty Date: Sun, 31 Mar 2024 05:10:48 +0200 Subject: [PATCH] Implemented host-meta and some overdue fixes --- .dev/Caddyfile | 4 + Cargo.lock | 418 +++++++++++++++++- Cargo.toml | 25 +- ext_calckey_model/src/note_model/mod.rs | 1 - ext_federation/Cargo.toml | 38 ++ .../src}/client/federation_client.rs | 69 +-- {src => ext_federation/src}/client/mod.rs | 0 ext_federation/src/lib.rs | 102 +++++ ext_host_meta/src/lib.rs | 7 + src/host_meta.rs | 12 + src/main.rs | 3 + src/model/processing/note.rs | 109 ++--- src/model/processing/user.rs | 8 +- src/web/extractors.rs | 35 ++ src/web/mod.rs | 1 + 15 files changed, 687 insertions(+), 145 deletions(-) create mode 100644 ext_federation/Cargo.toml rename {src => ext_federation/src}/client/federation_client.rs (72%) rename {src => ext_federation/src}/client/mod.rs (100%) create mode 100644 ext_federation/src/lib.rs create mode 100644 src/host_meta.rs create mode 100644 src/web/extractors.rs diff --git a/.dev/Caddyfile b/.dev/Caddyfile index 564725e..5ef0369 100644 --- a/.dev/Caddyfile +++ b/.dev/Caddyfile @@ -2,6 +2,10 @@ magnetar-dev.local { log { } + + handle /.well-known/host-meta { + reverse_proxy 127.0.0.1:4939 + } handle /.well-known/webfinger { reverse_proxy 127.0.0.1:4939 diff --git a/Cargo.lock b/Cargo.lock index 3b7a535..77b11e3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -206,10 +206,10 @@ dependencies = [ "axum-macros", "bytes", "futures-util", - "http", - "http-body", + "http 1.0.0", + "http-body 1.0.0", "http-body-util", - "hyper", + "hyper 1.1.0", "hyper-util", "itoa", "matchit", @@ -239,8 +239,8 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http", - "http-body", + "http 1.0.0", + "http-body 1.0.0", "http-body-util", "mime", "pin-project-lite", @@ -262,8 +262,8 @@ dependencies = [ "bytes", "futures-util", "headers", - "http", - "http-body", + "http 1.0.0", + "http-body 1.0.0", "http-body-util", "mime", "pin-project-lite", @@ -636,6 +636,16 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.6" @@ -819,6 +829,15 @@ dependencies = [ "phf", ] +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -889,6 +908,21 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -1061,6 +1095,25 @@ dependencies = [ "walkdir", ] +[[package]] +name = "h2" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "h2" version = "0.4.2" @@ -1072,7 +1125,7 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http", + "http 1.0.0", "indexmap", "slab", "tokio", @@ -1117,7 +1170,7 @@ dependencies = [ "base64", "bytes", "headers-core", - "http", + "http 1.0.0", "httpdate", "mime", "sha1", @@ -1129,7 +1182,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" dependencies = [ - "http", + "http 1.0.0", ] [[package]] @@ -1180,6 +1233,17 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http" version = "1.0.0" @@ -1191,6 +1255,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + [[package]] name = "http-body" version = "1.0.0" @@ -1198,7 +1273,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" dependencies = [ "bytes", - "http", + "http 1.0.0", ] [[package]] @@ -1209,8 +1284,8 @@ checksum = "41cb79eb393015dadd30fc252023adb0b2400a0caee0fa2a077e6e21a551e840" dependencies = [ "bytes", "futures-util", - "http", - "http-body", + "http 1.0.0", + "http-body 1.0.0", "pin-project-lite", ] @@ -1241,6 +1316,30 @@ dependencies = [ "libm", ] +[[package]] +name = "hyper" +version = "0.14.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.3.24", + "http 0.2.12", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.5.5", + "tokio", + "tower-service", + "tracing", + "want", +] + [[package]] name = "hyper" version = "1.1.0" @@ -1250,9 +1349,9 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "h2", - "http", - "http-body", + "h2 0.4.2", + "http 1.0.0", + "http-body 1.0.0", "httparse", "httpdate", "itoa", @@ -1261,6 +1360,19 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper 0.14.28", + "native-tls", + "tokio", + "tokio-native-tls", +] + [[package]] name = "hyper-util" version = "0.1.3" @@ -1269,9 +1381,9 @@ checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" dependencies = [ "bytes", "futures-util", - "http", - "http-body", - "hyper", + "http 1.0.0", + "http-body 1.0.0", + "hyper 1.1.0", "pin-project-lite", "socket2 0.5.5", "tokio", @@ -1362,6 +1474,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + [[package]] name = "is-terminal" version = "0.4.12" @@ -1482,18 +1600,21 @@ dependencies = [ "futures", "futures-util", "headers", - "hyper", + "hyper 1.1.0", "idna", "itertools", "lru", "magnetar_calckey_model", "magnetar_common", "magnetar_core", + "magnetar_federation", + "magnetar_host_meta", "magnetar_nodeinfo", "magnetar_sdk", "magnetar_webfinger", "miette", "percent-encoding", + "quick-xml", "regex", "serde", "serde_json", @@ -1520,7 +1641,7 @@ dependencies = [ "chrono", "dotenvy", "headers", - "hyper", + "hyper 1.1.0", "magnetar_common", "miette", "percent-encoding", @@ -1582,6 +1703,42 @@ dependencies = [ "serde_json", ] +[[package]] +name = "magnetar_federation" +version = "0.3.0-alpha" +dependencies = [ + "async-stream", + "chrono", + "futures", + "futures-core", + "futures-util", + "headers", + "http 1.0.0", + "hyper 1.1.0", + "magnetar_common", + "magnetar_core", + "magnetar_host_meta", + "magnetar_webfinger", + "miette", + "percent-encoding", + "quick-xml", + "reqwest", + "serde", + "serde_json", + "thiserror", + "tokio", + "url", +] + +[[package]] +name = "magnetar_host_meta" +version = "0.3.0-alpha" +dependencies = [ + "magnetar_core", + "quick-xml", + "serde", +] + [[package]] name = "magnetar_mmm_parser" version = "0.3.0-alpha" @@ -1611,7 +1768,7 @@ name = "magnetar_sdk" version = "0.3.0-alpha" dependencies = [ "chrono", - "http", + "http 1.0.0", "magnetar_mmm_parser", "magnetar_sdk_macros", "serde", @@ -1744,6 +1901,24 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "nix" version = "0.27.1" @@ -1875,6 +2050,50 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "openssl" +version = "0.10.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +dependencies = [ + "bitflags 2.4.2", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.49", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dda2b0f344e78efc2facf7d195d098df0dd72151b26ab98da807afc26c198dff" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "ordered-float" version = "3.9.2" @@ -2329,6 +2548,48 @@ dependencies = [ "bytecheck", ] +[[package]] +name = "reqwest" +version = "0.11.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78bf93c4af7a8bb7d879d51cebe797356ff10ae8516ace542b5182d9dcac10b2" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2 0.3.24", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.28", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-native-tls", + "tokio-util", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "winreg", +] + [[package]] name = "ring" version = "0.17.7" @@ -2478,6 +2739,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -2659,6 +2929,29 @@ version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" +[[package]] +name = "security-framework" +version = "2.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "serde" version = "1.0.196" @@ -3220,6 +3513,27 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tap" version = "1.0.1" @@ -3396,6 +3710,16 @@ dependencies = [ "syn 2.0.49", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-stream" version = "0.1.14" @@ -3491,8 +3815,8 @@ dependencies = [ "bitflags 2.4.2", "bytes", "futures-util", - "http", - "http-body", + "http 1.0.0", + "http-body 1.0.0", "http-body-util", "http-range-header", "httpdate", @@ -3742,6 +4066,7 @@ dependencies = [ "form_urlencoded", "idna", "percent-encoding", + "serde", ] [[package]] @@ -3833,6 +4158,18 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877b9c3f61ceea0e56331985743b13f3d25c406a7098d45180fb5f09bc19ed97" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.91" @@ -3862,6 +4199,29 @@ version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" +[[package]] +name = "wasm-streams" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96565907687f7aceb35bc5fc03770a8a0471d82e479f25832f54a0e3f4b28446" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "webpki-roots" version = "0.25.4" @@ -4064,6 +4424,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "wyz" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index 9e53666..9ceee9a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,15 +7,16 @@ license = "AGPL-3.0-only" [workspace] members = [ - ".", - "ext_nodeinfo", - "ext_webfinger", - "ext_calckey_model", - "fe_calckey", - "magnetar_common", - "magnetar_sdk", - "magnetar_mmm_parser", - "core", + ".", + "ext_federation", + "ext_nodeinfo", + "ext_webfinger", + "ext_calckey_model", + "fe_calckey", + "magnetar_common", + "magnetar_sdk", + "magnetar_mmm_parser", + "core", ] [workspace.package] @@ -75,6 +76,8 @@ walkdir = "2.3" [dependencies] magnetar_core = { path = "./core" } magnetar_common = { path = "./magnetar_common" } +magnetar_federation = { path = "./ext_federation" } +magnetar_host_meta = { path = "./ext_host_meta" } magnetar_webfinger = { path = "./ext_webfinger" } magnetar_nodeinfo = { path = "./ext_nodeinfo" } magnetar_calckey_model = { path = "./ext_calckey_model" } @@ -87,7 +90,7 @@ chrono = { workspace = true } dotenvy = { workspace = true } axum = { workspace = true, features = ["macros"] } -axum-extra = { workspace = true, features = ["typed-header"]} +axum-extra = { workspace = true, features = ["typed-header"] } async-stream = { workspace = true } headers = { workspace = true } hyper = { workspace = true, features = ["full"] } @@ -96,7 +99,6 @@ tokio-stream = { workspace = true } tower = { workspace = true } tower-http = { workspace = true, features = ["cors", "trace", "fs"] } url = { workspace = true } - idna = { workspace = true } regex = { workspace = true } @@ -121,6 +123,7 @@ serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } serde_urlencoded = { workspace = true } toml = { workspace = true } +quick-xml = { workspace = true, features = ["serialize", "overlapped-lists"] } unicode-segmentation = { workspace = true } diff --git a/ext_calckey_model/src/note_model/mod.rs b/ext_calckey_model/src/note_model/mod.rs index 807c8ce..2dceb11 100644 --- a/ext_calckey_model/src/note_model/mod.rs +++ b/ext_calckey_model/src/note_model/mod.rs @@ -248,7 +248,6 @@ impl NoteResolver { .add(join_columns_default( note::Relation::SelfRef2.with_alias(note_tbl, &reply_tbl), )) - .add(note_tbl.col(note::Column::IsQuote).not()) .add(visibility_filter); q.join_columns_on( JoinType::LeftJoin, diff --git a/ext_federation/Cargo.toml b/ext_federation/Cargo.toml new file mode 100644 index 0000000..a805a6b --- /dev/null +++ b/ext_federation/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "magnetar_federation" +version.workspace = true +edition.workspace = true +license = "MIT OR Apache-2.0" + +[lib] +crate-type = ["rlib"] + +[dependencies] +magnetar_core = { path = "../core" } +magnetar_common = { path = "../magnetar_common" } +magnetar_host_meta = { path = "../ext_host_meta" } +magnetar_webfinger = { path = "../ext_webfinger" } + +async-stream = { workspace = true } +futures = { workspace = true } +futures-core = { workspace = true } +futures-util = { workspace = true } + +quick-xml = { workspace = true, features = ["serialize"] } +serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true } +url = { workspace = true, features = ["serde"] } + +chrono = { workspace = true, features = ["serde"] } +thiserror = { workspace = true } + +http = { workspace = true } +headers = { workspace = true } +hyper = { workspace = true, features = ["full"] } +percent-encoding = { workspace = true } +reqwest = { workspace = true, features = ["stream"] } + +tokio = { workspace = true, features = ["full"] } + +[dev-dependencies] +miette = { workspace = true, features = ["fancy"] } diff --git a/src/client/federation_client.rs b/ext_federation/src/client/federation_client.rs similarity index 72% rename from src/client/federation_client.rs rename to ext_federation/src/client/federation_client.rs index 17dc499..0bfaf7c 100644 --- a/src/client/federation_client.rs +++ b/ext_federation/src/client/federation_client.rs @@ -1,12 +1,9 @@ -use std::io::Cursor; - use async_stream::stream; use futures_util::{select, stream::StreamExt, FutureExt, Stream, TryStreamExt}; +use headers::Header; use hyper::body::Bytes; -use magnetar_common::config::MagnetarNetworkingProtocol; -use magnetar_core::web_model::content_type::ContentActivityStreams; -use magnetar_host_meta::Xrd; -use reqwest::{Client, RequestBuilder}; +use magnetar_core::web_model::{content_type::ContentActivityStreams, ContentType}; +use reqwest::{redirect::Policy, Client, RequestBuilder}; use serde_json::Value; use thiserror::Error; use tokio::pin; @@ -55,7 +52,10 @@ impl FederationClient { body_limit: usize, timeout_seconds: u64, ) -> Result { - let client = Client::builder().https_only(force_https).build()?; + let client = Client::builder() + .https_only(force_https) + .redirect(Policy::limited(5)) + .build()?; Ok(FederationClient { client, @@ -66,7 +66,7 @@ impl FederationClient { pub fn builder(&self, method: reqwest::Method, url: Url) -> FederationRequestBuilder<'_> { FederationRequestBuilder { - client: &self, + client: self, builder: self.client.request(method, url), } } @@ -74,28 +74,19 @@ impl FederationClient { pub fn get(&self, url: Url) -> FederationRequestBuilder<'_> { self.builder(reqwest::Method::GET, url) } - - pub async fn host_meta( - &self, - protocol: MagnetarNetworkingProtocol, - host: &str, - ) -> Result { - let host_meta_xml = self - .get(Url::parse(&format!( - "{}://{}/.well-known/host-meta", - protocol.as_ref(), - host - ))?) - .send() - .await?; - - let reader = quick_xml::de::from_reader(Cursor::new(host_meta_xml))?; - - Ok(reader) - } } impl FederationRequestBuilder<'_> { + pub fn content_type(self, content_type: impl ContentType) -> Self { + Self { + client: self.client, + builder: self.builder.header( + headers::ContentType::name().to_string(), + content_type.mime_type().to_string(), + ), + } + } + pub fn headers(self, headers: reqwest::header::HeaderMap) -> Self { Self { client: self.client, @@ -129,7 +120,7 @@ impl FederationRequestBuilder<'_> { }) } - async fn send(self) -> Result, FederationClientError> { + pub async fn send(self) -> Result, FederationClientError> { let sleep = tokio::time::sleep(tokio::time::Duration::from_secs( self.client.timeout_seconds, )) @@ -164,28 +155,8 @@ impl FederationRequestBuilder<'_> { ); let data = self.send().await?; - let json = - serde_json::from_slice::(&data).map_err(FederationClientError::JsonError)?; + let json = serde_json::from_slice::(&data)?; Ok(json) } } - -#[cfg(test)] -mod test { - use magnetar_common::config::MagnetarNetworkingProtocol; - use miette::IntoDiagnostic; - - use super::FederationClient; - - #[tokio::test] - async fn test() -> miette::Result<()> { - let client = FederationClient::new(true, 1024 * 1024, 30).into_diagnostic()?; - let host_meta = client - .host_meta(MagnetarNetworkingProtocol::Https, "astolfo.social") - .await - .into_diagnostic()?; - - Ok(()) - } -} diff --git a/src/client/mod.rs b/ext_federation/src/client/mod.rs similarity index 100% rename from src/client/mod.rs rename to ext_federation/src/client/mod.rs diff --git a/ext_federation/src/lib.rs b/ext_federation/src/lib.rs new file mode 100644 index 0000000..9458292 --- /dev/null +++ b/ext_federation/src/lib.rs @@ -0,0 +1,102 @@ +use std::{io::Cursor, sync::Arc}; + +use client::federation_client::{FederationClient, FederationClientError}; +use magnetar_common::{config::MagnetarNetworkingProtocol, util::FediverseTag}; +use magnetar_host_meta::{Xrd, XrdXml}; +use magnetar_webfinger::webfinger::WebFinger; +use serde::{Deserialize, Serialize}; +use url::Url; + +pub mod client; + +/// The *visible* domain of fediverse handles, that gets resolved by WebFinger +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(transparent)] +pub struct HostUnmapped(String); + +/// The real domain of fediverse handles used for federation +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(transparent)] +pub struct HostMapped(String); + +pub trait HostMetaResolverService { + type Error; + + async fn resolve(&self, host: &HostUnmapped) -> Result; +} + +pub struct HostMetaResolverProviderDefault { + client: Arc, + protocol: MagnetarNetworkingProtocol, +} + +impl HostMetaResolverService for HostMetaResolverProviderDefault { + type Error = FederationClientError; + + async fn resolve(&self, HostUnmapped(host): &HostUnmapped) -> Result { + let host_meta_xml = self + .client + .get(Url::parse(&format!( + "{}://{}/.well-known/host-meta", + self.protocol.as_ref(), + host + ))?) + .send() + .await?; + + let XrdXml::Xrd(xrd) = quick_xml::de::from_reader(Cursor::new(host_meta_xml))?; + + Ok(xrd) + } +} + +pub trait WebFingerResolverService { + type Error; + + async fn resolve_url(&self, url: &str) -> Result; + + async fn resolve( + &self, + template_url: &str, + resolved_uri: &str, + ) -> Result { + self.resolve_url( + &template_url.replace( + "{}", + &percent_encoding::utf8_percent_encode( + resolved_uri, + percent_encoding::NON_ALPHANUMERIC, + ) + .to_string(), + ), + ) + .await + } +} + +pub struct WebFingerResolverProviderDefault { + client: Arc, +} + +impl WebFingerResolverService for WebFingerResolverProviderDefault { + type Error = FederationClientError; + + async fn resolve_url(&self, url: &str) -> Result { + let host_meta_xml = self.client.get(Url::parse(url)?).send().await?; + let webfinger = serde_json::from_reader(Cursor::new(host_meta_xml))?; + Ok(webfinger) + } +} + +#[derive(Debug)] +pub struct MappedUser { + pub host_meta: Xrd, + pub webfinger: WebFinger, + pub tag_mapped: FediverseTag, +} + +pub trait FederationService { + type Error; + + async fn map_fedi_tag(&self, host: &HostUnmapped) -> Result; +} diff --git a/ext_host_meta/src/lib.rs b/ext_host_meta/src/lib.rs index fbf19d2..d433ea0 100644 --- a/ext_host_meta/src/lib.rs +++ b/ext_host_meta/src/lib.rs @@ -30,6 +30,13 @@ impl Xrd { }], } } + + pub fn get_webfinger_template(&self) -> Option<&str> { + self.links + .iter() + .find(|l| l.rel.as_deref() == Some(ContentXrdXml.as_ref())) + .and_then(|l| l.template.as_deref()) + } } #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] diff --git a/src/host_meta.rs b/src/host_meta.rs new file mode 100644 index 0000000..0e3189e --- /dev/null +++ b/src/host_meta.rs @@ -0,0 +1,12 @@ +use axum::{extract::State, response::IntoResponse}; +use magnetar_common::config::MagnetarConfig; +use magnetar_host_meta::Xrd; + +use crate::web::extractors::XrdXmlExt; + +pub async fn handle_host_meta(State(config): State<&'static MagnetarConfig>) -> impl IntoResponse { + XrdXmlExt(magnetar_host_meta::XrdXml::Xrd(Xrd::default_host_meta( + config.networking.protocol.as_ref(), + &config.networking.host, + ))) +} diff --git a/src/main.rs b/src/main.rs index f7c0a5f..a6da7bf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ mod api_v1; +pub mod host_meta; pub mod model; pub mod nodeinfo; pub mod service; @@ -7,6 +8,7 @@ pub mod web; pub mod webfinger; use crate::api_v1::create_api_router; +use crate::host_meta::handle_host_meta; use crate::nodeinfo::{handle_nodeinfo, handle_nodeinfo_20, handle_nodeinfo_21}; use crate::service::MagnetarService; use axum::routing::get; @@ -66,6 +68,7 @@ async fn main() -> miette::Result<()> { "/webfinger", get(webfinger::handle_webfinger).with_state((config, db)), ) + .route("/host-meta", get(handle_host_meta)) .route("/nodeinfo", get(handle_nodeinfo)); let nodeinfo_router = Router::new() diff --git a/src/model/processing/note.rs b/src/model/processing/note.rs index 05f8bb9..e3fa810 100644 --- a/src/model/processing/note.rs +++ b/src/model/processing/note.rs @@ -6,7 +6,7 @@ use crate::model::processing::{get_mm_token_emoji, PackError, PackResult}; use crate::model::{PackType, PackingContext}; use compact_str::CompactString; use either::Either; -use futures_util::future::{try_join_all, BoxFuture}; +use futures_util::future::try_join_all; use futures_util::{FutureExt, StreamExt, TryStreamExt}; use magnetar_calckey_model::ck::sea_orm_active_enums::NoteVisibilityEnum; use magnetar_calckey_model::model_ext::AliasColumnExt; @@ -446,67 +446,68 @@ impl NoteModel { ))) } - pub fn pack_full_single<'b, 'a: 'b>( - &'a self, - ctx: &'a PackingContext, - note: &'b (dyn NoteShapedData<'a> + 'b), - ) -> BoxFuture<'b, PackResult> { - async move { - let drive_model = DriveModel; + pub async fn pack_full_single( + &self, + ctx: &PackingContext, + note: &dyn NoteShapedData<'_>, + ) -> PackResult { + let drive_model = DriveModel; - let reply_target = async { - match note.reply() { - Some(r) if self.with_context => { - self.pack_full_single(ctx, r.as_ref()).await.map(Some) - } - _ => Ok(None), + let reply_target = async { + match note.reply() { + Some(r) if self.with_context => Box::pin(self.pack_full_single(ctx, r.as_ref())) + .await + .map(Some), + _ => Ok(None), + } + }; + + let renote_target = async { + match (note.renote(), ¬e.note().renote_id, note.note().is_quote) { + (Some(r), _, _) if self.with_context => { + Box::pin(self.pack_full_single(ctx, r.as_ref())) + .await + .map(Some) } - }; - - let renote_target = async { - match (note.renote(), ¬e.note().renote_id, note.note().is_quote) { - (Some(r), _, _) if self.with_context => { - self.pack_full_single(ctx, r.as_ref()).await.map(Some) - } - (_, Some(renote_id), Some(true)) => self.fetch_single(ctx, renote_id).await, - _ => Ok(None), + (_, Some(renote_id), Some(true)) => { + Box::pin(self.fetch_single(ctx, renote_id)).await } - }; + _ => Ok(None), + } + }; - let ( - PackNoteMaybeAttachments { - id, - note, - user_context, - attachment, - }, - reply_target_pack, - renote_target_pack, - ) = try_join!( - self.pack_single_attachments(ctx, &drive_model, note), - reply_target, - renote_target - )?; - - let detail = self.with_context.then(|| { - NoteDetailExt::extract( - ctx, - NoteDetailSource { - parent_note: reply_target_pack.as_ref(), - renoted_note: renote_target_pack.as_ref(), - }, - ) - }); - - Ok(PackNoteMaybeFull::pack_from(( + let ( + PackNoteMaybeAttachments { id, note, user_context, attachment, - Optional(detail), - ))) - } - .boxed() + }, + reply_target_pack, + renote_target_pack, + ) = try_join!( + self.pack_single_attachments(ctx, &drive_model, note), + reply_target, + renote_target + )?; + + let detail = self.with_context.then(|| { + NoteDetailExt::extract( + ctx, + NoteDetailSource { + parent_note: reply_target_pack.as_ref(), + renoted_note: renote_target_pack.as_ref(), + }, + ) + }); + + Ok(PackNoteMaybeFull::pack_from(( + id, + note, + user_context, + attachment, + Optional(detail), + ))) } pub async fn fetch_single( diff --git a/src/model/processing/user.rs b/src/model/processing/user.rs index a3b2fba..9a597af 100644 --- a/src/model/processing/user.rs +++ b/src/model/processing/user.rs @@ -26,7 +26,7 @@ use magnetar_sdk::types::{Id, MmXml}; use magnetar_sdk::{mmm, Optional, Packed, Required}; use serde::{Deserialize, Serialize}; use tokio::{join, try_join}; -use tracing::{instrument, warn}; +use tracing::warn; use url::Url; pub trait UserShapedData<'a>: Send + Sync { @@ -227,12 +227,8 @@ impl UserModel { let moved = ctx.service.db.get_user_by_uri(uri).await?; moved.and_then(|m| { - let Some(uri) = m.uri else { - return None; - }; - Some(MovedTo { - moved_to_uri: uri, + moved_to_uri: m.uri?, username: m.username, host: m .host diff --git a/src/web/extractors.rs b/src/web/extractors.rs new file mode 100644 index 0000000..1e6b44f --- /dev/null +++ b/src/web/extractors.rs @@ -0,0 +1,35 @@ +use axum::{http::HeaderValue, response::IntoResponse}; +use hyper::{header, StatusCode}; +use magnetar_core::web_model::{content_type::ContentXrdXml, ContentType}; +use serde::Serialize; + +use crate::web::{ApiError, ErrorCode}; + +pub struct XrdXmlExt(pub T); + +impl IntoResponse for XrdXmlExt { + fn into_response(self) -> axum::response::Response { + let mut buf = r#""#.to_string(); + + match quick_xml::se::to_writer(&mut buf, &self.0) { + Ok(()) => ( + [( + header::CONTENT_TYPE, + HeaderValue::from_static(ContentXrdXml.mime_type()), + )], + buf.into_bytes(), + ) + .into_response(), + Err(e) => ApiError { + status: StatusCode::INTERNAL_SERVER_ERROR, + code: ErrorCode("XmlSerializationError".into()), + message: if cfg!(debug_assertions) { + format!("Serialization error: {}", e) + } else { + "Serialization error".to_string() + }, + } + .into_response(), + } + } +} diff --git a/src/web/mod.rs b/src/web/mod.rs index 9e2be6e..db44b6e 100644 --- a/src/web/mod.rs +++ b/src/web/mod.rs @@ -10,6 +10,7 @@ use std::fmt::{Display, Formatter}; use thiserror::Error; pub mod auth; +pub mod extractors; pub mod pagination; #[derive(Debug, Clone, Serialize)]