diff --git a/Cargo.lock b/Cargo.lock index e7bdf9c9d..b9096e67c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -77,7 +77,7 @@ checksum = "d1eb7c4fcde1858a6796c18a729b661346d38e05a207e2d9028bce822fc20283" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -213,7 +213,7 @@ dependencies = [ "argh_shared", "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -286,7 +286,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -323,16 +323,16 @@ checksum = "f28243a43d821d11341ab73c80bed182dc015c514b951616cf79bd4af39af0c3" dependencies = [ "concurrent-queue", "event-listener 5.2.0", - "event-listener-strategy 0.5.0", + "event-listener-strategy 0.5.1", "futures-core", "pin-project-lite", ] [[package]] name = "async-compression" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a116f46a969224200a0a97f29cfd4c50e7534e4b4826bd23ea2c3c533039c82c" +checksum = "86a9249d1447a85f95810c620abea82e001fe58a31713fcce614caf52499f905" dependencies = [ "brotli", "flate2", @@ -409,7 +409,7 @@ dependencies = [ "proc-macro2", "quote", "strum", - "syn 2.0.55", + "syn 2.0.57", "thiserror", ] @@ -476,7 +476,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -493,7 +493,7 @@ checksum = "a507401cad91ec6a857ed5513a2073c82a9b9048762b885bb98655b306964681" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -569,7 +569,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -729,7 +729,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -1295,7 +1295,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -1307,13 +1307,40 @@ checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "client-sdk-common" version = "0.1.0" -source = "git+https://github.com/Lantern-chat/client-sdk-rs#e9ba517b8e20b8289c6f314ee8401bb4fcb30293" +source = "git+https://github.com/Lantern-chat/client-sdk-rs#cbf459cd5d2fb47aea128e50d3375a7cd6c84d39" dependencies = [ "bitflags_serde_shim", "rkyv", "serde", ] +[[package]] +name = "color-eyre" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55146f5e46f237f7423d74111267d4597b59b0dad0ffaf7303bce9945d843ad5" +dependencies = [ + "backtrace", + "color-spantrace", + "eyre", + "indenter", + "once_cell", + "owo-colors 3.5.0", + "tracing-error", +] + +[[package]] +name = "color-spantrace" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd6be1b2a7e382e2b98b43b2adcca6bb0e465af0bdd38123873ae61eb17a72c2" +dependencies = [ + "once_cell", + "owo-colors 3.5.0", + "tracing-core", + "tracing-error", +] + [[package]] name = "colorchoice" version = "1.0.0" @@ -1717,7 +1744,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" dependencies = [ "quote", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -1794,7 +1821,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -1842,7 +1869,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.10.0", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -1864,7 +1891,7 @@ checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" dependencies = [ "darling_core 0.20.8", "quote", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -1912,7 +1939,7 @@ dependencies = [ "darling 0.20.8", "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -1922,7 +1949,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b" dependencies = [ "derive_builder_core", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -1955,7 +1982,7 @@ checksum = "2bba3e9872d7c58ce7ef0fcf1844fcc3e23ef2a58377b50df35dd98e42a5726e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -1995,7 +2022,7 @@ dependencies = [ "diesel_table_macro_syntax", "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -2024,7 +2051,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5" dependencies = [ - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -2148,9 +2175,9 @@ dependencies = [ [[package]] name = "email-encoding" -version = "0.2.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a87260449b06739ee78d6281c68d2a0ff3e3af64a78df63d3a1aeb3c06997c8a" +checksum = "60d1d33cdaede7e24091f039632eb5d3c7469fe5b066a985281a34fc70fa317f" dependencies = [ "base64 0.22.0", "memchr", @@ -2204,19 +2231,19 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] name = "enum_dispatch" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f33313078bb8d4d05a2733a94ac4c2d8a0df9a2b84424ebf4f33bfc224a890e" +checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd" dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -2293,9 +2320,9 @@ dependencies = [ [[package]] name = "event-listener-strategy" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feedafcaa9b749175d5ac357452a9d41ea2911da598fde46ce1fe02c37751291" +checksum = "332f51cb23d20b0de8458b86580878211da09bcd4503cb579c225b3d124cabb3" dependencies = [ "event-listener 5.2.0", "pin-project-lite", @@ -2309,6 +2336,16 @@ dependencies = [ "wit-bindgen", ] +[[package]] +name = "eyre" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +dependencies = [ + "indenter", + "once_cell", +] + [[package]] name = "fallible-iterator" version = "0.2.0" @@ -2537,7 +2574,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -2620,7 +2657,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -3254,6 +3291,12 @@ dependencies = [ "miniz_oxide 0.5.4", ] +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + [[package]] name = "indexmap" version = "1.9.3" @@ -3278,9 +3321,9 @@ dependencies = [ [[package]] name = "insta" -version = "1.37.0" +version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1718b3f2b85bb5054baf8ce406e36401f27c3169205f4175504c4b1d98252d3f" +checksum = "3eab73f58e59ca6526037208f0e98851159ec1633cf17b6cd2e1f2c3fd5d53cc" dependencies = [ "console", "globset", @@ -3302,9 +3345,9 @@ dependencies = [ [[package]] name = "io-extras" -version = "0.18.1" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c301e73fb90e8a29e600a9f402d095765f74310d582916a952f618836a1bd1ed" +checksum = "c9f046b9af244f13b3bd939f55d16830ac3a201e8a9ba9661bfcb03e2be72b9b" dependencies = [ "io-lifetimes", "windows-sys 0.52.0", @@ -3509,6 +3552,7 @@ dependencies = [ "camino", "chrono", "clap", + "color-eyre", "cursiv", "der", "diesel", @@ -3550,7 +3594,6 @@ dependencies = [ "kitsune-wasm-mrf", "kitsune-webfinger", "metrics", - "miette", "mimalloc", "mime", "mime_guess", @@ -3594,6 +3637,7 @@ dependencies = [ "base64-simd", "diesel", "diesel-async", + "eyre", "futures-util", "headers", "http 1.1.0", @@ -3620,7 +3664,6 @@ dependencies = [ "mime_guess", "pretty_assertions", "rsa", - "scoped-futures", "serde", "sha2", "simd-json", @@ -3669,13 +3712,13 @@ name = "kitsune-cli" version = "0.0.1-pre.6" dependencies = [ "clap", + "color-eyre", "diesel", "diesel-async", "dotenvy", "envy", "kitsune-config", "kitsune-db", - "miette", "serde", "speedy-uuid", "tokio", @@ -3687,8 +3730,8 @@ dependencies = [ name = "kitsune-config" version = "0.0.1-pre.6" dependencies = [ + "eyre", "isolang", - "miette", "serde", "smol_str", "tokio", @@ -3701,6 +3744,7 @@ version = "0.0.1-pre.6" dependencies = [ "async-trait", "const_format", + "eyre", "http 1.1.0", "kitsune-db", "kitsune-messaging", @@ -3726,10 +3770,9 @@ dependencies = [ "kitsune-language", "kitsune-test", "kitsune-type", - "miette", "num-derive", "num-traits", - "rustls 0.22.2", + "rustls 0.23.4", "rustls-native-certs", "serde", "simd-json", @@ -3754,9 +3797,7 @@ dependencies = [ "kitsune-db", "kitsune-url", "lettre", - "miette", "mrml", - "scoped-futures", "speedy-uuid", "thiserror", "typed-builder", @@ -3806,7 +3847,6 @@ dependencies = [ "globset", "kitsune-config", "kitsune-type", - "miette", "thiserror", "url", ] @@ -3840,6 +3880,7 @@ version = "0.0.1-pre.6" dependencies = [ "athena", "clap", + "color-eyre", "just-retry", "kitsune-config", "kitsune-core", @@ -3852,7 +3893,6 @@ dependencies = [ "kitsune-service", "kitsune-url", "kitsune-wasm-mrf", - "miette", "mimalloc", "multiplex-pool", "redis", @@ -3869,12 +3909,11 @@ dependencies = [ "derive_more 1.0.0-beta.6", "diesel", "diesel-async", + "eyre", "futures-util", "kitsune-core", "kitsune-db", "kitsune-email", - "miette", - "scoped-futures", "serde", "speedy-uuid", "tracing", @@ -3912,7 +3951,6 @@ dependencies = [ "kitsune-url", "kitsune-util", "mime", - "scoped-futures", "serde", "simd-json", "smol_str", @@ -3945,6 +3983,7 @@ name = "kitsune-observability" version = "0.0.1-pre.6" dependencies = [ "async-trait", + "eyre", "http-body-util", "http-compat", "hyper 1.2.0", @@ -3954,7 +3993,6 @@ dependencies = [ "metrics-opentelemetry", "metrics-tracing-context", "metrics-util", - "miette", "opentelemetry", "opentelemetry-http", "opentelemetry-otlp", @@ -3974,7 +4012,6 @@ dependencies = [ "http-compat", "kitsune-config", "kitsune-http-client", - "miette", "moka", "multiplex-pool", "once_cell", @@ -4027,7 +4064,6 @@ dependencies = [ "kitsune-db", "kitsune-language", "meilisearch-sdk", - "miette", "serde", "speedy-uuid", "strum", @@ -4049,6 +4085,7 @@ dependencies = [ "derive_builder", "diesel", "diesel-async", + "eyre", "futures-util", "garde", "hex-simd", @@ -4076,7 +4113,6 @@ dependencies = [ "kitsune-url", "kitsune-util", "kitsune-webfinger", - "miette", "mime", "multiplex-pool", "password-hash", @@ -4087,7 +4123,6 @@ dependencies = [ "redis", "rsa", "rusty-s3", - "scoped-futures", "serde", "simd-json", "smol_str", @@ -4184,12 +4219,12 @@ name = "kitsune-wasm-mrf" version = "0.0.1-pre.6" dependencies = [ "async-trait", + "color-eyre", "derive_more 1.0.0-beta.6", "enum_dispatch", "futures-util", "kitsune-config", "kitsune-type", - "miette", "mrf-manifest", "multiplex-pool", "redis", @@ -4214,6 +4249,7 @@ version = "0.0.1-pre.6" dependencies = [ "async-trait", "autometrics", + "eyre", "futures-util", "http 1.1.0", "http-body-util", @@ -4256,9 +4292,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "lettre" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8305b122b8ccc64e437b0de101851f9f00aade5886246e85f341c1d9a15a91b7" +checksum = "47460276655930189e0919e4fbf46e46476b14f934f18a63dd726a5fb7b60e2e" dependencies = [ "async-trait", "base64 0.22.0", @@ -4416,7 +4452,7 @@ checksum = "adf157a4dc5a29b7b464aa8fe7edeff30076e07e13646a1c3874f58477dc99f8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -4477,7 +4513,7 @@ dependencies = [ "proc-macro2", "quote", "regex-syntax 0.8.3", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -4615,7 +4651,7 @@ dependencies = [ "proc-macro2", "quote", "structmeta", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -4647,9 +4683,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "memfd" @@ -4735,7 +4771,7 @@ dependencies = [ "backtrace-ext", "cfg-if", "miette-derive", - "owo-colors", + "owo-colors 4.0.0", "supports-color", "supports-hyperlinks", "supports-unicode", @@ -4753,7 +4789,7 @@ checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -4867,7 +4903,6 @@ version = "0.0.1-pre.6" dependencies = [ "insta", "leb128", - "miette", "olpc-cjson", "schemars", "semver", @@ -4884,7 +4919,7 @@ name = "mrf-tool" version = "0.0.1-pre.6" dependencies = [ "clap", - "miette", + "color-eyre", "mrf-manifest", "serde_json", "wasmparser 0.202.0", @@ -4910,7 +4945,7 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b99915c25e2b56916308ccd9ca090cb0759226271e5f3b8101e7bbdf7fb8d35" dependencies = [ - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -4923,7 +4958,7 @@ dependencies = [ "mrml-common-macros", "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -5037,7 +5072,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -5204,9 +5239,9 @@ dependencies = [ [[package]] name = "openssl-sys" -version = "0.9.101" +version = "0.9.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dda2b0f344e78efc2facf7d195d098df0dd72151b26ab98da807afc26c198dff" +checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" dependencies = [ "cc", "libc", @@ -5232,9 +5267,9 @@ dependencies = [ [[package]] name = "opentelemetry-http" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cbfa5308166ca861434f0b0913569579b8e587430a3d6bcd7fd671921ec145a" +checksum = "7690dc77bf776713848c4faa6501157469017eaf332baccd4eb1cea928743d94" dependencies = [ "async-trait", "bytes", @@ -5332,6 +5367,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "owo-colors" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" + [[package]] name = "owo-colors" version = "4.0.0" @@ -5633,7 +5674,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -5680,14 +5721,14 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" @@ -5951,7 +5992,7 @@ dependencies = [ "itertools 0.11.0", "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -6014,9 +6055,9 @@ checksum = "d5d8f9aa0e3cbcfaf8bf00300004ee3b72f74770f9cbac93f6928771f613276b" [[package]] name = "quanta" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ca0b7bac0b97248c40bb77288fc52029cf1459c0461ea1b05ee32ccf011de2c" +checksum = "8e5167a477619228a0b284fac2674e3c388cba90631d7b7de620e6f1fcd08da5" dependencies = [ "crossbeam-utils", "libc", @@ -6225,7 +6266,7 @@ dependencies = [ "itoa 1.0.11", "percent-encoding", "pin-project-lite", - "rustls 0.22.2", + "rustls 0.22.3", "rustls-native-certs", "rustls-pemfile 2.1.1", "rustls-pki-types", @@ -6274,7 +6315,7 @@ checksum = "5fddb4f8d99b0a2ebafc65a87a69a7b9875e4b1ae1f00db265d300ef7f28bccc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -6513,7 +6554,7 @@ dependencies = [ "proc-macro2", "quote", "rust-embed-utils", - "syn 2.0.55", + "syn 2.0.57", "walkdir", ] @@ -6578,9 +6619,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.22.2" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e87c9956bd9807afa1f77e0f7594af32566e830e088a5576d27c5b6f30f49d41" +checksum = "99008d7ad0bbbea527ec27bddbc0e432c5b87d8175178cee68d2eec9c4a1813c" dependencies = [ "log", "ring", @@ -6801,9 +6842,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.9.2" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" dependencies = [ "bitflags 1.3.2", "core-foundation", @@ -6814,9 +6855,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.9.1" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef" dependencies = [ "core-foundation-sys", "libc", @@ -6897,7 +6938,7 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -6913,9 +6954,9 @@ dependencies = [ [[package]] name = "serde_html_form" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50437e6a58912eecc08865e35ea2e8d365fbb2db0debb1c8bb43bf1faf055f25" +checksum = "8de514ef58196f1fc96dcaef80fe6170a1ce6215df9687a93fe8300e773fefc5" dependencies = [ "form_urlencoded", "indexmap 2.2.6", @@ -7033,7 +7074,7 @@ dependencies = [ "darling 0.20.8", "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -7145,9 +7186,9 @@ checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" [[package]] name = "similar" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32fea41aca09ee824cc9724996433064c89f7777e60762749a4170a14abbfa21" +checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640" [[package]] name = "siphasher" @@ -7283,7 +7324,7 @@ checksum = "c87e960f4dca2788eeb86bbdde8dd246be8948790b7618d656e68f9b720a86e8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -7403,7 +7444,7 @@ dependencies = [ "proc-macro2", "quote", "structmeta-derive", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -7414,7 +7455,7 @@ checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -7437,7 +7478,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -7480,9 +7521,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.55" +version = "2.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "002a1b3dbf967edfafc32655d0f377ab0bb7b994aa1d32c8cc7e9b8bf3ebb8f0" +checksum = "11a6ae1e52eb25aab8f3fb9fca13be982a373b8f1157ca14b897a825ba4a2d35" dependencies = [ "proc-macro2", "quote", @@ -7503,9 +7544,9 @@ checksum = "384595c11a4e2969895cad5a8c4029115f5ab956a9e5ef4de79d11a426e5f20c" [[package]] name = "system-interface" -version = "0.27.1" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aef1f9d4c1dbdd1cb3a63be9efd2f04d8ddbc919d46112982c76818ffc2f1a7" +checksum = "b858526d22750088a9b3cf2e3c2aacebd5377f13adeec02860c30d09113010a6" dependencies = [ "bitflags 2.5.0", "cap-fs-ext", @@ -7654,7 +7695,7 @@ checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -7734,9 +7775,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.36.0" +version = "1.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" +checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" dependencies = [ "backtrace", "bytes", @@ -7769,7 +7810,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -7801,15 +7842,13 @@ dependencies = [ [[package]] name = "tokio-postgres-rustls" version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ea13f22eda7127c827983bdaf0d7fff9df21c8817bab02815ac277a21143677" +source = "git+https://github.com/jbg/tokio-postgres-rustls.git?rev=b3b59ac2fa1b5823f2426fef78a0fb74c004ec38#b3b59ac2fa1b5823f2426fef78a0fb74c004ec38" dependencies = [ - "futures", "ring", - "rustls 0.22.2", + "rustls 0.23.4", "tokio", "tokio-postgres", - "tokio-rustls 0.25.0", + "tokio-rustls 0.26.0", "x509-certificate", ] @@ -7840,7 +7879,7 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" dependencies = [ - "rustls 0.22.2", + "rustls 0.22.3", "rustls-pki-types", "tokio", ] @@ -8116,7 +8155,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -8240,7 +8279,7 @@ checksum = "563b3b88238ec95680aef36bdece66896eaa7ce3c0f1b4f39d38fb2435261352" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -8373,7 +8412,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.55", + "syn 2.0.57", "uuid", ] @@ -8528,7 +8567,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.57", "wasm-bindgen-shared", ] @@ -8562,7 +8601,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.57", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -8593,9 +8632,9 @@ dependencies = [ [[package]] name = "wasm-metadata" -version = "0.201.0" +version = "0.202.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fd83062c17b9f4985d438603cde0a5e8c5c8198201a6937f778b607924c7da2" +checksum = "094aea3cb90e09f16ee25a4c0e324b3e8c934e7fd838bfa039aef5352f44a917" dependencies = [ "anyhow", "indexmap 2.2.6", @@ -8603,8 +8642,8 @@ dependencies = [ "serde_derive", "serde_json", "spdx", - "wasm-encoder 0.201.0", - "wasmparser 0.201.0", + "wasm-encoder 0.202.0", + "wasmparser 0.202.0", ] [[package]] @@ -8697,10 +8736,10 @@ dependencies = [ "anyhow", "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.57", "wasmtime-component-util", "wasmtime-wit-bindgen", - "wit-parser", + "wit-parser 0.201.0", ] [[package]] @@ -8856,7 +8895,7 @@ checksum = "6d6d967f01032da7d4c6303da32f6a00d5efe1bac124b156e7342d8ace6ffdfc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -8915,7 +8954,7 @@ dependencies = [ "anyhow", "heck 0.4.1", "indexmap 2.2.6", - "wit-parser", + "wit-parser 0.201.0", ] [[package]] @@ -9236,36 +9275,38 @@ dependencies = [ [[package]] name = "wit-bindgen" -version = "0.22.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "288f992ea30e6b5c531b52cdd5f3be81c148554b09ea416f058d16556ba92c27" +checksum = "041a3276f25dce240b10f5b34d9d490b007f18e8ead05984ae7948b283b4059e" dependencies = [ - "bitflags 2.5.0", "wit-bindgen-rt", "wit-bindgen-rust-macro", ] [[package]] name = "wit-bindgen-core" -version = "0.22.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e85e72719ffbccf279359ad071497e47eb0675fe22106dea4ed2d8a7fcb60ba4" +checksum = "b0834150cd852e64e1eddcff4fea9524b788161b4111d83a94c9eda715f8f442" dependencies = [ "anyhow", - "wit-parser", + "wit-parser 0.202.0", ] [[package]] name = "wit-bindgen-rt" -version = "0.22.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb8738270f32a2d6739973cbbb7c1b6dd8959ce515578a6e19165853272ee64" +checksum = "d6ccd4c6a69667c75474ddb30c36773c5e70cdca099ec2e293005458886ac81b" +dependencies = [ + "bitflags 2.5.0", +] [[package]] name = "wit-bindgen-rust" -version = "0.22.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8a39a15d1ae2077688213611209849cad40e9e5cccf6e61951a425850677ff3" +checksum = "60620df421d4c787e2660f0159fd58f2ae6998dc42ccf2e09b8d9d96d16885a9" dependencies = [ "anyhow", "heck 0.4.1", @@ -9277,23 +9318,23 @@ dependencies = [ [[package]] name = "wit-bindgen-rust-macro" -version = "0.22.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d376d3ae5850526dfd00d937faea0d81a06fa18f7ac1e26f386d760f241a8f4b" +checksum = "96e875e7dd09a0d2acc1a27df6a4b0586288741d940b40a717e2daed3bc3d979" dependencies = [ "anyhow", "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.57", "wit-bindgen-core", "wit-bindgen-rust", ] [[package]] name = "wit-component" -version = "0.201.0" +version = "0.202.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "421c0c848a0660a8c22e2fd217929a0191f14476b68962afd2af89fd22e39825" +checksum = "0c836b1fd9932de0431c1758d8be08212071b6bba0151f7bac826dbc4312a2a9" dependencies = [ "anyhow", "bitflags 2.5.0", @@ -9302,10 +9343,10 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.201.0", + "wasm-encoder 0.202.0", "wasm-metadata", - "wasmparser 0.201.0", - "wit-parser", + "wasmparser 0.202.0", + "wit-parser 0.202.0", ] [[package]] @@ -9326,6 +9367,24 @@ dependencies = [ "wasmparser 0.201.0", ] +[[package]] +name = "wit-parser" +version = "0.202.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "744237b488352f4f27bca05a10acb79474415951c450e52ebd0da784c1df2bcc" +dependencies = [ + "anyhow", + "id-arena", + "indexmap 2.2.6", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser 0.202.0", +] + [[package]] name = "wyz" version = "0.5.1" @@ -9405,7 +9464,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] @@ -9425,7 +9484,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn 2.0.57", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 70e74e193..277b8ccf7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -117,3 +117,4 @@ install-updater = true [patch.crates-io] diesel-async = { git = "https://github.com/weiznich/diesel_async.git", rev = "017ebe2fb7a2709ab5db92148dea5ce812a35e09" } +tokio-postgres-rustls = { git = "https://github.com/jbg/tokio-postgres-rustls.git", rev = "b3b59ac2fa1b5823f2426fef78a0fb74c004ec38" } diff --git a/_typos.toml b/_typos.toml index da646b9d2..ed8ad5313 100644 --- a/_typos.toml +++ b/_typos.toml @@ -1,3 +1,6 @@ +[default] +extend-ignore-words-re = ["guid"] + [files] extend-exclude = [ "crates/kitsune-language/examples/basic.rs", @@ -10,4 +13,7 @@ extend-exclude = [ "lib/http-signatures/tests/data.rs", "lib/post-process/tests/input/*", + + # Exclude all snapshot files + "*.snap", ] diff --git a/crates/kitsune-activitypub/Cargo.toml b/crates/kitsune-activitypub/Cargo.toml index c38c537e9..504734144 100644 --- a/crates/kitsune-activitypub/Cargo.toml +++ b/crates/kitsune-activitypub/Cargo.toml @@ -11,6 +11,7 @@ autometrics = { version = "1.0.1", default-features = false } base64-simd = "0.8.0" diesel = "2.1.5" diesel-async = "0.4.1" +eyre = "0.6.12" futures-util = "0.3.30" headers = "0.4.0" http = "1.1.0" @@ -32,7 +33,6 @@ kitsune-wasm-mrf = { path = "../kitsune-wasm-mrf" } mime = "0.3.17" mime_guess = { version = "2.0.4", default-features = false } rsa = "0.9.6" -scoped-futures = "0.1.3" serde = "1.0.197" sha2 = "0.10.8" simd-json = "0.13.9" @@ -52,7 +52,7 @@ kitsune-config = { path = "../kitsune-config" } kitsune-test = { path = "../kitsune-test" } kitsune-webfinger = { path = "../kitsune-webfinger" } pretty_assertions = "1.4.0" -tokio = { version = "1.36.0", features = ["macros"] } +tokio = { version = "1.37.0", features = ["macros"] } tower = { version = "0.4.13", default-features = false, features = ["util"] } [lints] diff --git a/crates/kitsune-activitypub/src/deliverer/mod.rs b/crates/kitsune-activitypub/src/deliverer/mod.rs index 770df0a2b..5e7266ef8 100644 --- a/crates/kitsune-activitypub/src/deliverer/mod.rs +++ b/crates/kitsune-activitypub/src/deliverer/mod.rs @@ -11,20 +11,16 @@ use diesel::{ use diesel_async::RunQueryDsl; use futures_util::TryStreamExt; use iso8601_timestamp::Timestamp; -use kitsune_core::{ - error::BoxError, - traits::{deliverer::Action, Deliverer as DelivererTrait}, -}; +use kitsune_core::traits::{deliverer::Action, Deliverer as DelivererTrait}; use kitsune_db::{ model::{account::Account, favourite::Favourite, follower::Follow, post::Post, user::User}, schema::{accounts, posts, users}, - PgPool, + with_connection, PgPool, }; use kitsune_service::attachment::AttachmentService; use kitsune_type::ap::{ap_context, Activity, ActivityType, ObjectField}; use kitsune_url::UrlService; use kitsune_util::try_join; -use scoped_futures::ScopedFutureExt; use std::sync::Arc; use typed_builder::TypedBuilder; @@ -61,26 +57,21 @@ impl Deliverer { } async fn accept_follow(&self, follow: Follow) -> Result<()> { - let (follower_inbox_url, (followed_account, followed_user)): (String, _) = self - .db_pool - .with_connection(|db_conn| { - async move { - let follower_inbox_url_fut = accounts::table - .find(follow.follower_id) - .select(accounts::inbox_url.assume_not_null()) - .get_result::(db_conn); - - let followed_info_fut = accounts::table - .find(follow.account_id) - .inner_join(users::table.on(accounts::id.eq(users::account_id))) - .select(<(Account, User)>::as_select()) - .get_result::<(Account, User)>(db_conn); - - try_join!(follower_inbox_url_fut, followed_info_fut) - } - .scoped() - }) - .await?; + let (follower_inbox_url, (followed_account, followed_user)): (String, _) = + with_connection!(self.db_pool, |db_conn| { + let follower_inbox_url_fut = accounts::table + .find(follow.follower_id) + .select(accounts::inbox_url.assume_not_null()) + .get_result::(db_conn); + + let followed_info_fut = accounts::table + .find(follow.account_id) + .inner_join(users::table.on(accounts::id.eq(users::account_id))) + .select(<(Account, User)>::as_select()) + .get_result::<(Account, User)>(db_conn); + + try_join!(follower_inbox_url_fut, followed_info_fut) + })?; let followed_account_url = self.service.url.user_url(followed_account.id); @@ -111,17 +102,14 @@ impl Deliverer { } async fn create_or_repost(&self, post: Post) -> Result<()> { - let (account, user) = self - .db_pool - .with_connection(|db_conn| { - accounts::table - .find(post.account_id) - .inner_join(users::table) - .select(<(Account, User)>::as_select()) - .get_result::<(Account, User)>(db_conn) - .scoped() - }) - .await?; + let (account, user) = with_connection!(self.db_pool, |db_conn| { + accounts::table + .find(post.account_id) + .inner_join(users::table) + .select(<(Account, User)>::as_select()) + .get_result::<(Account, User)>(db_conn) + .await + })?; let inbox_stream = self .inbox_resolver @@ -141,21 +129,15 @@ impl Deliverer { } async fn delete_or_unrepost(&self, post: Post) -> Result<()> { - let account_user_data = self - .db_pool - .with_connection(|db_conn| { - async move { - accounts::table - .find(post.account_id) - .inner_join(users::table) - .select(<(Account, User)>::as_select()) - .get_result::<(Account, User)>(db_conn) - .await - .optional() - } - .scoped() - }) - .await?; + let account_user_data = with_connection!(self.db_pool, |db_conn| { + accounts::table + .find(post.account_id) + .inner_join(users::table) + .select(<(Account, User)>::as_select()) + .get_result::<(Account, User)>(db_conn) + .await + .optional() + })?; let Some((account, user)) = account_user_data else { return Ok(()); @@ -179,27 +161,21 @@ impl Deliverer { } async fn favourite(&self, favourite: Favourite) -> Result<()> { - let ((account, user), inbox_url) = self - .db_pool - .with_connection(|db_conn| { - async move { - let account_user_fut = accounts::table - .find(favourite.account_id) - .inner_join(users::table) - .select(<(Account, User)>::as_select()) - .get_result(db_conn); - - let inbox_url_fut = posts::table - .find(favourite.post_id) - .inner_join(accounts::table) - .select(accounts::inbox_url) - .get_result::>(db_conn); - - try_join!(account_user_fut, inbox_url_fut) - } - .scoped() - }) - .await?; + let ((account, user), inbox_url) = with_connection!(self.db_pool, |db_conn| { + let account_user_fut = accounts::table + .find(favourite.account_id) + .inner_join(users::table) + .select(<(Account, User)>::as_select()) + .get_result(db_conn); + + let inbox_url_fut = posts::table + .find(favourite.post_id) + .inner_join(accounts::table) + .select(accounts::inbox_url) + .get_result::>(db_conn); + + try_join!(account_user_fut, inbox_url_fut) + })?; if let Some(ref inbox_url) = inbox_url { let activity = favourite.into_activity(self.mapping_state()).await?; @@ -213,26 +189,21 @@ impl Deliverer { } async fn follow(&self, follow: Follow) -> Result<()> { - let ((follower, follower_user), followed_inbox) = self - .db_pool - .with_connection(|db_conn| { - async move { - let follower_info_fut = accounts::table - .find(follow.follower_id) - .inner_join(users::table) - .select(<(Account, User)>::as_select()) - .get_result::<(Account, User)>(db_conn); - - let followed_inbox_fut = accounts::table - .find(follow.account_id) - .select(accounts::inbox_url) - .get_result::>(db_conn); - - try_join!(follower_info_fut, followed_inbox_fut) - } - .scoped() - }) - .await?; + let ((follower, follower_user), followed_inbox) = + with_connection!(self.db_pool, |db_conn| { + let follower_info_fut = accounts::table + .find(follow.follower_id) + .inner_join(users::table) + .select(<(Account, User)>::as_select()) + .get_result::<(Account, User)>(db_conn); + + let followed_inbox_fut = accounts::table + .find(follow.account_id) + .select(accounts::inbox_url) + .get_result::>(db_conn); + + try_join!(follower_info_fut, followed_inbox_fut) + })?; if let Some(followed_inbox) = followed_inbox { let follow_activity = follow.into_activity(self.mapping_state()).await?; @@ -246,28 +217,23 @@ impl Deliverer { } async fn reject_follow(&self, follow: Follow) -> Result<()> { - let (follower_inbox_url, (followed_account, followed_user), _delete_result) = self - .db_pool - .with_connection(|db_conn| { - async { - let follower_inbox_url_fut = accounts::table - .find(follow.follower_id) - .select(accounts::inbox_url.assume_not_null()) - .get_result::(db_conn); - - let followed_info_fut = accounts::table - .find(follow.account_id) - .inner_join(users::table.on(accounts::id.eq(users::account_id))) - .select(<(Account, User)>::as_select()) - .get_result::<(Account, User)>(db_conn); - - let delete_fut = diesel::delete(&follow).execute(db_conn); - - try_join!(follower_inbox_url_fut, followed_info_fut, delete_fut) - } - .scoped() - }) - .await?; + let (follower_inbox_url, (followed_account, followed_user), _delete_result) = + with_connection!(self.db_pool, |db_conn| { + let follower_inbox_url_fut = accounts::table + .find(follow.follower_id) + .select(accounts::inbox_url.assume_not_null()) + .get_result::(db_conn); + + let followed_info_fut = accounts::table + .find(follow.account_id) + .inner_join(users::table.on(accounts::id.eq(users::account_id))) + .select(<(Account, User)>::as_select()) + .get_result::<(Account, User)>(db_conn); + + let delete_fut = diesel::delete(&follow).execute(db_conn); + + try_join!(follower_inbox_url_fut, followed_info_fut, delete_fut) + })?; let followed_account_url = self.service.url.user_url(followed_account.id); @@ -298,27 +264,21 @@ impl Deliverer { } async fn unfavourite(&self, favourite: Favourite) -> Result<()> { - let ((account, user), inbox_url) = self - .db_pool - .with_connection(|db_conn| { - async move { - let account_user_fut = accounts::table - .find(favourite.account_id) - .inner_join(users::table) - .select(<(Account, User)>::as_select()) - .get_result(db_conn); - - let inbox_url_fut = posts::table - .find(favourite.post_id) - .inner_join(accounts::table) - .select(accounts::inbox_url) - .get_result::>(db_conn); - - try_join!(account_user_fut, inbox_url_fut) - } - .scoped() - }) - .await?; + let ((account, user), inbox_url) = with_connection!(self.db_pool, |db_conn| { + let account_user_fut = accounts::table + .find(favourite.account_id) + .inner_join(users::table) + .select(<(Account, User)>::as_select()) + .get_result(db_conn); + + let inbox_url_fut = posts::table + .find(favourite.post_id) + .inner_join(accounts::table) + .select(accounts::inbox_url) + .get_result::>(db_conn); + + try_join!(account_user_fut, inbox_url_fut) + })?; if let Some(ref inbox_url) = inbox_url { let activity = favourite.into_negate_activity(self.mapping_state()).await?; @@ -331,26 +291,21 @@ impl Deliverer { } async fn unfollow(&self, follow: Follow) -> Result<()> { - let ((follower, follower_user), followed_account_inbox_url) = self - .db_pool - .with_connection(|db_conn| { - async { - let follower_info_fut = accounts::table - .find(follow.follower_id) - .inner_join(users::table) - .select(<(Account, User)>::as_select()) - .get_result::<(Account, User)>(db_conn); - - let followed_account_inbox_url_fut = accounts::table - .find(follow.account_id) - .select(accounts::inbox_url) - .get_result::>(db_conn); - - try_join!(follower_info_fut, followed_account_inbox_url_fut) - } - .scoped() - }) - .await?; + let ((follower, follower_user), followed_account_inbox_url) = + with_connection!(self.db_pool, |db_conn| { + let follower_info_fut = accounts::table + .find(follow.follower_id) + .inner_join(users::table) + .select(<(Account, User)>::as_select()) + .get_result::<(Account, User)>(db_conn); + + let followed_account_inbox_url_fut = accounts::table + .find(follow.account_id) + .select(accounts::inbox_url) + .get_result::>(db_conn); + + try_join!(follower_info_fut, followed_account_inbox_url_fut) + })?; if let Some(ref followed_account_inbox_url) = followed_account_inbox_url { let follow_activity = follow.into_negate_activity(self.mapping_state()).await?; @@ -369,20 +324,14 @@ impl Deliverer { } async fn update_account(&self, account: Account) -> Result<()> { - let user = self - .db_pool - .with_connection(|db_conn| { - async move { - users::table - .filter(users::account_id.eq(account.id)) - .select(User::as_select()) - .get_result(db_conn) - .await - .optional() - } - .scoped() - }) - .await?; + let user = with_connection!(self.db_pool, |db_conn| { + users::table + .filter(users::account_id.eq(account.id)) + .select(User::as_select()) + .get_result(db_conn) + .await + .optional() + })?; let Some(user) = user else { return Ok(()); @@ -404,22 +353,16 @@ impl Deliverer { } async fn update_post(&self, post: Post) -> Result<()> { - let post_account_user_data = self - .db_pool - .with_connection(|db_conn| { - async move { - posts::table - .find(post.id) - .inner_join(accounts::table) - .inner_join(users::table.on(accounts::id.eq(users::account_id))) - .select(<(Account, User)>::as_select()) - .get_result(db_conn) - .await - .optional() - } - .scoped() - }) - .await?; + let post_account_user_data = with_connection!(self.db_pool, |db_conn| { + posts::table + .find(post.id) + .inner_join(accounts::table) + .inner_join(users::table.on(accounts::id.eq(users::account_id))) + .select(<(Account, User)>::as_select()) + .get_result(db_conn) + .await + .optional() + })?; let Some((account, user)) = post_account_user_data else { return Ok(()); @@ -447,7 +390,7 @@ impl Deliverer { #[async_trait] impl DelivererTrait for Deliverer { - async fn deliver(&self, action: Action) -> Result<(), BoxError> { + async fn deliver(&self, action: Action) -> eyre::Result<()> { match action { Action::AcceptFollow(follow) => self.accept_follow(follow).await, Action::Create(post) | Action::Repost(post) => self.create_or_repost(post).await, diff --git a/crates/kitsune-activitypub/src/error.rs b/crates/kitsune-activitypub/src/error.rs index c03f70c2c..51b5cfb87 100644 --- a/crates/kitsune-activitypub/src/error.rs +++ b/crates/kitsune-activitypub/src/error.rs @@ -1,10 +1,6 @@ use diesel_async::pooled_connection::bb8; -use kitsune_core::error::BoxError; use rsa::pkcs8::der; -use std::{ - convert::Infallible, - fmt::{Debug, Display}, -}; +use std::{convert::Infallible, fmt::Debug}; use thiserror::Error; pub type Result = std::result::Result; @@ -33,13 +29,13 @@ pub enum Error { FederationFilter(#[from] kitsune_federation_filter::error::Error), #[error(transparent)] - FetchAccount(BoxError), + FetchAccount(eyre::Report), #[error(transparent)] - FetchEmoji(BoxError), + FetchEmoji(eyre::Report), #[error(transparent)] - FetchPost(BoxError), + FetchPost(eyre::Report), #[error(transparent)] Http(#[from] http::Error), @@ -66,7 +62,7 @@ pub enum Error { NotFound, #[error(transparent)] - Resolver(BoxError), + Resolver(eyre::Report), #[error(transparent)] Search(#[from] kitsune_search::Error), @@ -89,15 +85,3 @@ impl From for Error { match err {} } } - -impl From> for Error -where - E: Into + Debug + Display, -{ - fn from(value: kitsune_db::PoolError) -> Self { - match value { - kitsune_db::PoolError::Pool(err) => err.into(), - kitsune_db::PoolError::User(err) => err.into(), - } - } -} diff --git a/crates/kitsune-activitypub/src/fetcher/actor.rs b/crates/kitsune-activitypub/src/fetcher/actor.rs index cd926ce01..472e6b318 100644 --- a/crates/kitsune-activitypub/src/fetcher/actor.rs +++ b/crates/kitsune-activitypub/src/fetcher/actor.rs @@ -11,11 +11,11 @@ use kitsune_core::traits::fetcher::AccountFetchOptions; use kitsune_db::{ model::account::{Account, AccountConflictChangeset, NewAccount, UpdateAccountMedia}, schema::accounts, + with_connection, with_transaction, }; use kitsune_search::SearchBackend; use kitsune_type::ap::actor::Actor; use kitsune_util::{convert::timestamp_to_uuid, sanitize::CleanHtmlExt}; -use scoped_futures::ScopedFutureExt; use url::Url; impl Fetcher { @@ -36,20 +36,14 @@ impl Fetcher { return Ok(Some(user)); } - let user_data = self - .db_pool - .with_connection(|db_conn| { - async move { - accounts::table - .filter(accounts::url.eq(opts.url)) - .select(Account::as_select()) - .first(db_conn) - .await - .optional() - } - .scoped() - }) - .await?; + let user_data = with_connection!(self.db_pool, |db_conn| { + accounts::table + .filter(accounts::url.eq(opts.url)) + .select(Account::as_select()) + .first(db_conn) + .await + .optional() + })?; if let Some(user) = user_data { return Ok(Some(user)); @@ -96,93 +90,87 @@ impl Fetcher { actor.clean_html(); - let account: Account = self - .db_pool - .with_transaction(|tx| { - async move { - let account = diesel::insert_into(accounts::table) - .values(NewAccount { - id: timestamp_to_uuid(actor.published), - display_name: actor.name.as_deref(), - note: actor.subject.as_deref(), - username: actor.preferred_username.as_str(), - locked: actor.manually_approves_followers, - local: false, - domain, - actor_type: actor.r#type.into(), - url: actor.id.as_str(), - featured_collection_url: actor.featured.as_deref(), - followers_url: actor.followers.as_deref(), - following_url: actor.following.as_deref(), - inbox_url: Some(actor.inbox.as_str()), - outbox_url: actor.outbox.as_deref(), - shared_inbox_url: actor - .endpoints - .and_then(|endpoints| endpoints.shared_inbox) - .as_deref(), - public_key_id: actor.public_key.id.as_str(), - public_key: actor.public_key.public_key_pem.as_str(), - created_at: Some(actor.published), - }) - .on_conflict(accounts::url) - .do_update() - .set(AccountConflictChangeset { - display_name: actor.name.as_deref(), - note: actor.subject.as_deref(), - locked: actor.manually_approves_followers, - public_key_id: actor.public_key.id.as_str(), - public_key: actor.public_key.public_key_pem.as_str(), - }) + let account: Account = with_transaction!(self.db_pool, |tx| { + let account = diesel::insert_into(accounts::table) + .values(NewAccount { + id: timestamp_to_uuid(actor.published), + display_name: actor.name.as_deref(), + note: actor.subject.as_deref(), + username: actor.preferred_username.as_str(), + locked: actor.manually_approves_followers, + local: false, + domain, + actor_type: actor.r#type.into(), + url: actor.id.as_str(), + featured_collection_url: actor.featured.as_deref(), + followers_url: actor.followers.as_deref(), + following_url: actor.following.as_deref(), + inbox_url: Some(actor.inbox.as_str()), + outbox_url: actor.outbox.as_deref(), + shared_inbox_url: actor + .endpoints + .and_then(|endpoints| endpoints.shared_inbox) + .as_deref(), + public_key_id: actor.public_key.id.as_str(), + public_key: actor.public_key.public_key_pem.as_str(), + created_at: Some(actor.published), + }) + .on_conflict(accounts::url) + .do_update() + .set(AccountConflictChangeset { + display_name: actor.name.as_deref(), + note: actor.subject.as_deref(), + locked: actor.manually_approves_followers, + public_key_id: actor.public_key.id.as_str(), + public_key: actor.public_key.public_key_pem.as_str(), + }) + .returning(Account::as_returning()) + .get_result::(tx) + .await?; + + let avatar_id = if let Some(icon) = actor.icon { + process_attachments(tx, &account, &[icon]).await?.pop() + } else { + None + }; + + let header_id = if let Some(image) = actor.image { + process_attachments(tx, &account, &[image]).await?.pop() + } else { + None + }; + + let mut update_changeset = UpdateAccountMedia::default(); + if let Some(avatar_id) = avatar_id { + update_changeset = UpdateAccountMedia { + avatar_id: Some(avatar_id), + ..update_changeset + }; + } + + if let Some(header_id) = header_id { + update_changeset = UpdateAccountMedia { + header_id: Some(header_id), + ..update_changeset + }; + } + + let account = match update_changeset { + UpdateAccountMedia { + avatar_id: None, + header_id: None, + } => account, + _ => { + diesel::update(&account) + .set(update_changeset) .returning(Account::as_returning()) - .get_result::(tx) - .await?; - - let avatar_id = if let Some(icon) = actor.icon { - process_attachments(tx, &account, &[icon]).await?.pop() - } else { - None - }; - - let header_id = if let Some(image) = actor.image { - process_attachments(tx, &account, &[image]).await?.pop() - } else { - None - }; - - let mut update_changeset = UpdateAccountMedia::default(); - if let Some(avatar_id) = avatar_id { - update_changeset = UpdateAccountMedia { - avatar_id: Some(avatar_id), - ..update_changeset - }; - } - - if let Some(header_id) = header_id { - update_changeset = UpdateAccountMedia { - header_id: Some(header_id), - ..update_changeset - }; - } - - let account = match update_changeset { - UpdateAccountMedia { - avatar_id: None, - header_id: None, - } => account, - _ => { - diesel::update(&account) - .set(update_changeset) - .returning(Account::as_returning()) - .get_result(tx) - .await? - } - }; - - Ok::<_, Error>(account) + .get_result(tx) + .await? } - .scoped() - }) - .await?; + }; + + Ok::<_, Error>(account) + })?; self.search_backend .add_to_index(account.clone().into()) diff --git a/crates/kitsune-activitypub/src/fetcher/emoji.rs b/crates/kitsune-activitypub/src/fetcher/emoji.rs index 37ef484f7..ba340b299 100644 --- a/crates/kitsune-activitypub/src/fetcher/emoji.rs +++ b/crates/kitsune-activitypub/src/fetcher/emoji.rs @@ -9,28 +9,22 @@ use kitsune_db::{ media_attachment::{MediaAttachment, NewMediaAttachment}, }, schema::{custom_emojis, media_attachments}, + with_connection, with_transaction, }; use kitsune_type::ap::emoji::Emoji; -use scoped_futures::ScopedFutureExt; use speedy_uuid::Uuid; use url::Url; impl Fetcher { pub(crate) async fn fetch_emoji(&self, url: &str) -> Result> { - let existing_emoji = self - .db_pool - .with_connection(|db_conn| { - async move { - custom_emojis::table - .filter(custom_emojis::remote_id.eq(url)) - .select(CustomEmoji::as_select()) - .first(db_conn) - .await - .optional() - } - .scoped() - }) - .await?; + let existing_emoji = with_connection!(self.db_pool, |db_conn| { + custom_emojis::table + .filter(custom_emojis::remote_id.eq(url)) + .select(CustomEmoji::as_select()) + .first(db_conn) + .await + .optional() + })?; if let Some(emoji) = existing_emoji { return Ok(Some(emoji)); @@ -57,42 +51,36 @@ impl Fetcher { let name_pure = emoji.name.replace(':', ""); - let emoji: CustomEmoji = self - .db_pool - .with_transaction(|tx| { - async move { - let media_attachment = diesel::insert_into(media_attachments::table) - .values(NewMediaAttachment { - id: Uuid::now_v7(), - account_id: None, - content_type, - description: None, - blurhash: None, - file_path: None, - remote_url: Some(&emoji.icon.url), - }) - .returning(MediaAttachment::as_returning()) - .get_result::(tx) - .await?; - let emoji = diesel::insert_into(custom_emojis::table) - .values(CustomEmoji { - id: Uuid::now_v7(), - remote_id: emoji.id, - shortcode: name_pure.to_string(), - domain: Some(domain.to_string()), - media_attachment_id: media_attachment.id, - endorsed: false, - created_at: Timestamp::now_utc(), - updated_at: Timestamp::now_utc(), - }) - .returning(CustomEmoji::as_returning()) - .get_result::(tx) - .await?; - Ok::<_, Error>(emoji) - } - .scoped() - }) - .await?; + let emoji: CustomEmoji = with_transaction!(self.db_pool, |tx| { + let media_attachment = diesel::insert_into(media_attachments::table) + .values(NewMediaAttachment { + id: Uuid::now_v7(), + account_id: None, + content_type, + description: None, + blurhash: None, + file_path: None, + remote_url: Some(&emoji.icon.url), + }) + .returning(MediaAttachment::as_returning()) + .get_result::(tx) + .await?; + let emoji = diesel::insert_into(custom_emojis::table) + .values(CustomEmoji { + id: Uuid::now_v7(), + remote_id: emoji.id, + shortcode: name_pure.to_string(), + domain: Some(domain.to_string()), + media_attachment_id: media_attachment.id, + endorsed: false, + created_at: Timestamp::now_utc(), + updated_at: Timestamp::now_utc(), + }) + .returning(CustomEmoji::as_returning()) + .get_result::(tx) + .await?; + Ok::<_, Error>(emoji) + })?; Ok(Some(emoji)) } diff --git a/crates/kitsune-activitypub/src/fetcher/mod.rs b/crates/kitsune-activitypub/src/fetcher/mod.rs index e62ecf649..d481c078c 100644 --- a/crates/kitsune-activitypub/src/fetcher/mod.rs +++ b/crates/kitsune-activitypub/src/fetcher/mod.rs @@ -6,7 +6,6 @@ use kitsune_cache::ArcCache; use kitsune_config::language_detection::Configuration as LanguageDetectionConfig; use kitsune_core::{ consts::USER_AGENT, - error::BoxError, traits::{ fetcher::{AccountFetchOptions, PostFetchOptions}, Fetcher as FetcherTrait, Resolver, @@ -124,18 +123,15 @@ impl FetcherTrait for Fetcher { Arc::new(self.resolver.clone()) } - async fn fetch_account( - &self, - opts: AccountFetchOptions<'_>, - ) -> Result, BoxError> { + async fn fetch_account(&self, opts: AccountFetchOptions<'_>) -> eyre::Result> { Ok(self.fetch_actor(opts).await?) } - async fn fetch_emoji(&self, url: &str) -> Result, BoxError> { + async fn fetch_emoji(&self, url: &str) -> eyre::Result> { Ok(self.fetch_emoji(url).await?) } - async fn fetch_post(&self, opts: PostFetchOptions<'_>) -> Result, BoxError> { + async fn fetch_post(&self, opts: PostFetchOptions<'_>) -> eyre::Result> { Ok(self.fetch_object(opts.url, opts.call_depth).await?) } } diff --git a/crates/kitsune-activitypub/src/fetcher/object.rs b/crates/kitsune-activitypub/src/fetcher/object.rs index 3a8405db0..3b7817e4e 100644 --- a/crates/kitsune-activitypub/src/fetcher/object.rs +++ b/crates/kitsune-activitypub/src/fetcher/object.rs @@ -4,8 +4,7 @@ use autometrics::autometrics; use diesel::{ExpressionMethods, OptionalExtension, QueryDsl, SelectableHelper}; use diesel_async::RunQueryDsl; use kitsune_cache::CacheBackend; -use kitsune_db::{model::post::Post, schema::posts}; -use scoped_futures::ScopedFutureExt; +use kitsune_db::{model::post::Post, schema::posts, with_connection}; // Maximum call depth of fetching new posts. Prevents unbounded recursion. // Setting this to >=40 would cause the `fetch_infinitely_long_reply_chain` test to run into stack overflow @@ -23,20 +22,14 @@ impl Fetcher { return Ok(Some(post)); } - let post = self - .db_pool - .with_connection(|db_conn| { - async move { - posts::table - .filter(posts::url.eq(url)) - .select(Post::as_select()) - .first(db_conn) - .await - .optional() - } - .scoped() - }) - .await?; + let post = with_connection!(self.db_pool, |db_conn| { + posts::table + .filter(posts::url.eq(url)) + .select(Post::as_select()) + .first(db_conn) + .await + .optional() + })?; if let Some(post) = post { self.post_cache.set(url, &post).await?; diff --git a/crates/kitsune-activitypub/src/inbox_resolver.rs b/crates/kitsune-activitypub/src/inbox_resolver.rs index acd8a362b..6fc2a7259 100644 --- a/crates/kitsune-activitypub/src/inbox_resolver.rs +++ b/crates/kitsune-activitypub/src/inbox_resolver.rs @@ -13,9 +13,8 @@ use kitsune_db::{ post::{Post, Visibility}, }, schema::{accounts, accounts_follows}, - PgPool, + with_connection, PgPool, }; -use scoped_futures::ScopedFutureExt; pub struct InboxResolver { db_pool: PgPool, @@ -32,27 +31,25 @@ impl InboxResolver { &self, account: &Account, ) -> Result> + Send + '_> { - self.db_pool - .with_connection(|db_conn| { - accounts_follows::table - .filter(accounts_follows::account_id.eq(account.id)) - .inner_join( - accounts::table.on(accounts::id.eq(accounts_follows::follower_id).and( - accounts::inbox_url - .is_not_null() - .or(accounts::shared_inbox_url.is_not_null()), - )), - ) - .distinct() - .select(coalesce_nullable( - accounts::shared_inbox_url, - accounts::inbox_url, - )) - .load_stream(db_conn) - .scoped() - }) - .await - .map_err(Error::from) + with_connection!(self.db_pool, |db_conn| { + accounts_follows::table + .filter(accounts_follows::account_id.eq(account.id)) + .inner_join( + accounts::table.on(accounts::id.eq(accounts_follows::follower_id).and( + accounts::inbox_url + .is_not_null() + .or(accounts::shared_inbox_url.is_not_null()), + )), + ) + .distinct() + .select(coalesce_nullable( + accounts::shared_inbox_url, + accounts::inbox_url, + )) + .load_stream(db_conn) + .await + }) + .map_err(Error::from) } #[instrument(skip_all, fields(post_id = %post.id))] @@ -60,35 +57,29 @@ impl InboxResolver { &self, post: &Post, ) -> Result> + Send + '_> { - let (account, mentioned_inbox_stream) = self - .db_pool - .with_connection(|db_conn| { - async move { - let account = accounts::table - .find(post.account_id) - .select(Account::as_select()) - .first(db_conn) - .await?; + let (account, mentioned_inbox_stream) = with_connection!(self.db_pool, |db_conn| { + let account = accounts::table + .find(post.account_id) + .select(Account::as_select()) + .first(db_conn) + .await?; - let mentioned_inbox_stream = Mention::belonging_to(post) - .inner_join(accounts::table) - .filter( - accounts::shared_inbox_url - .is_not_null() - .or(accounts::inbox_url.is_not_null()), - ) - .select(coalesce_nullable( - accounts::shared_inbox_url, - accounts::inbox_url, - )) - .load_stream(db_conn) - .await?; + let mentioned_inbox_stream = Mention::belonging_to(post) + .inner_join(accounts::table) + .filter( + accounts::shared_inbox_url + .is_not_null() + .or(accounts::inbox_url.is_not_null()), + ) + .select(coalesce_nullable( + accounts::shared_inbox_url, + accounts::inbox_url, + )) + .load_stream(db_conn) + .await?; - Ok::<_, Error>((account, mentioned_inbox_stream)) - } - .scoped() - }) - .await?; + Ok::<_, Error>((account, mentioned_inbox_stream)) + })?; let stream = if post.visibility == Visibility::MentionOnly { Either::Left(mentioned_inbox_stream) diff --git a/crates/kitsune-activitypub/src/lib.rs b/crates/kitsune-activitypub/src/lib.rs index 8222ddcbc..76923d0bd 100644 --- a/crates/kitsune-activitypub/src/lib.rs +++ b/crates/kitsune-activitypub/src/lib.rs @@ -20,14 +20,13 @@ use kitsune_db::{ schema::{ media_attachments, posts, posts_custom_emojis, posts_media_attachments, posts_mentions, }, - PgPool, + with_transaction, PgPool, }; use kitsune_embed::Client as EmbedClient; use kitsune_language::Language; use kitsune_search::{AnySearchBackend, SearchBackend}; use kitsune_type::ap::{object::MediaAttachment, Object, Tag, TagType}; use kitsune_util::{convert::timestamp_to_uuid, process, sanitize::CleanHtmlExt, CowBox}; -use scoped_futures::ScopedFutureExt; use speedy_uuid::Uuid; use typed_builder::TypedBuilder; @@ -288,58 +287,53 @@ pub async fn process_new_object(process_data: ProcessNewObject<'_>) -> Result(tx) + .await?; + + let attachment_ids = process_attachments(tx, &user, &object.attachment).await?; + diesel::insert_into(posts_media_attachments::table) + .values( + attachment_ids + .into_iter() + .map(|attachment_id| NewPostMediaAttachment { + post_id: new_post.id, + media_attachment_id: attachment_id, }) - .on_conflict(posts::url) - .do_update() - .set(PostConflictChangeset { - subject: object.summary.as_deref(), - content: object.content.as_str(), - }) - .returning(Post::as_returning()) - .get_result::(tx) - .await?; - - let attachment_ids = process_attachments(tx, &user, &object.attachment).await?; - diesel::insert_into(posts_media_attachments::table) - .values( - attachment_ids - .into_iter() - .map(|attachment_id| NewPostMediaAttachment { - post_id: new_post.id, - media_attachment_id: attachment_id, - }) - .collect::>(), - ) - .execute(tx) - .await?; - - handle_mentions(tx, &user, new_post.id, &object.tag).await?; - handle_custom_emojis(tx, new_post.id, fetcher, &object.tag).await?; - - Ok::<_, Error>(new_post) - } - .scoped() - }) - .await?; + .collect::>(), + ) + .execute(tx) + .await?; + + handle_mentions(tx, &user, new_post.id, &object.tag).await?; + handle_custom_emojis(tx, new_post.id, fetcher, &object.tag).await?; + + Ok::<_, Error>(new_post) + })?; if post.visibility == Visibility::Public || post.visibility == Visibility::Unlisted { search_backend.add_to_index(post.clone().into()).await?; @@ -362,51 +356,46 @@ pub async fn update_object(process_data: ProcessNewObject<'_>) -> Result { search_backend, } = preprocess_object(process_data).await?; - let post = db_pool - .with_transaction(|tx| { - async move { - let updated_post = diesel::update(posts::table) - .filter(posts::url.eq(object.id.as_str())) - .set(FullPostChangeset { - account_id: user.id, - in_reply_to_id, - reposted_post_id: None, - subject: object.summary.as_deref(), - content: object.content.as_str(), - content_source: "", - content_lang: content_lang.into(), - link_preview_url: link_preview_url.as_deref(), - is_sensitive: object.sensitive, - visibility, - is_local: false, - updated_at: Timestamp::now_utc(), + let post = with_transaction!(db_pool, |tx| { + let updated_post = diesel::update(posts::table) + .filter(posts::url.eq(object.id.as_str())) + .set(FullPostChangeset { + account_id: user.id, + in_reply_to_id, + reposted_post_id: None, + subject: object.summary.as_deref(), + content: object.content.as_str(), + content_source: "", + content_lang: content_lang.into(), + link_preview_url: link_preview_url.as_deref(), + is_sensitive: object.sensitive, + visibility, + is_local: false, + updated_at: Timestamp::now_utc(), + }) + .returning(Post::as_returning()) + .get_result::(tx) + .await?; + + let attachment_ids = process_attachments(tx, &user, &object.attachment).await?; + diesel::insert_into(posts_media_attachments::table) + .values( + attachment_ids + .into_iter() + .map(|attachment_id| NewPostMediaAttachment { + post_id: updated_post.id, + media_attachment_id: attachment_id, }) - .returning(Post::as_returning()) - .get_result::(tx) - .await?; - - let attachment_ids = process_attachments(tx, &user, &object.attachment).await?; - diesel::insert_into(posts_media_attachments::table) - .values( - attachment_ids - .into_iter() - .map(|attachment_id| NewPostMediaAttachment { - post_id: updated_post.id, - media_attachment_id: attachment_id, - }) - .collect::>(), - ) - .on_conflict_do_nothing() - .execute(tx) - .await?; - - handle_mentions(tx, &user, updated_post.id, &object.tag).await?; - - Ok::<_, Error>(updated_post) - } - .scoped() - }) - .await?; + .collect::>(), + ) + .on_conflict_do_nothing() + .execute(tx) + .await?; + + handle_mentions(tx, &user, updated_post.id, &object.tag).await?; + + Ok::<_, Error>(updated_post) + })?; if post.visibility == Visibility::Public || post.visibility == Visibility::Unlisted { search_backend.update_in_index(post.clone().into()).await?; diff --git a/crates/kitsune-activitypub/src/mapping/activity.rs b/crates/kitsune-activitypub/src/mapping/activity.rs index aae38deca..87d152964 100644 --- a/crates/kitsune-activitypub/src/mapping/activity.rs +++ b/crates/kitsune-activitypub/src/mapping/activity.rs @@ -6,10 +6,10 @@ use iso8601_timestamp::Timestamp; use kitsune_db::{ model::{account::Account, favourite::Favourite, follower::Follow, post::Post}, schema::{accounts, posts}, + with_connection, }; use kitsune_type::ap::{ap_context, Activity, ActivityType, ObjectField}; use kitsune_util::try_join; -use scoped_futures::ScopedFutureExt; use std::future::Future; pub trait IntoActivity { @@ -50,25 +50,19 @@ impl IntoActivity for Favourite { type NegateOutput = Activity; async fn into_activity(self, state: State<'_>) -> Result { - let (account_url, post_url) = state - .db_pool - .with_connection(|db_conn| { - async move { - let account_url_fut = accounts::table - .find(self.account_id) - .select(accounts::url) - .get_result::(db_conn); - - let post_url_fut = posts::table - .find(self.post_id) - .select(posts::url) - .get_result(db_conn); - - try_join!(account_url_fut, post_url_fut) - } - .scoped() - }) - .await?; + let (account_url, post_url) = with_connection!(state.db_pool, |db_conn| { + let account_url_fut = accounts::table + .find(self.account_id) + .select(accounts::url) + .get_result::(db_conn); + + let post_url_fut = posts::table + .find(self.post_id) + .select(posts::url) + .get_result(db_conn); + + try_join!(account_url_fut, post_url_fut) + })?; Ok(Activity { context: ap_context(), @@ -81,16 +75,13 @@ impl IntoActivity for Favourite { } async fn into_negate_activity(self, state: State<'_>) -> Result { - let account_url = state - .db_pool - .with_connection(|db_conn| { - accounts::table - .find(self.account_id) - .select(accounts::url) - .get_result::(db_conn) - .scoped() - }) - .await?; + let account_url = with_connection!(state.db_pool, |db_conn| { + accounts::table + .find(self.account_id) + .select(accounts::url) + .get_result::(db_conn) + .await + })?; Ok(Activity { context: ap_context(), @@ -108,25 +99,19 @@ impl IntoActivity for Follow { type NegateOutput = Activity; async fn into_activity(self, state: State<'_>) -> Result { - let (attributed_to, object) = state - .db_pool - .with_connection(|db_conn| { - async move { - let attributed_to_fut = accounts::table - .find(self.follower_id) - .select(accounts::url) - .get_result::(db_conn); - - let object_fut = accounts::table - .find(self.account_id) - .select(accounts::url) - .get_result::(db_conn); - - try_join!(attributed_to_fut, object_fut) - } - .scoped() - }) - .await?; + let (attributed_to, object) = with_connection!(state.db_pool, |db_conn| { + let attributed_to_fut = accounts::table + .find(self.follower_id) + .select(accounts::url) + .get_result::(db_conn); + + let object_fut = accounts::table + .find(self.account_id) + .select(accounts::url) + .get_result::(db_conn); + + try_join!(attributed_to_fut, object_fut) + })?; Ok(Activity { context: ap_context(), @@ -139,16 +124,13 @@ impl IntoActivity for Follow { } async fn into_negate_activity(self, state: State<'_>) -> Result { - let attributed_to = state - .db_pool - .with_connection(|db_conn| { - accounts::table - .find(self.follower_id) - .select(accounts::url) - .get_result::(db_conn) - .scoped() - }) - .await?; + let attributed_to = with_connection!(state.db_pool, |db_conn| { + accounts::table + .find(self.follower_id) + .select(accounts::url) + .get_result::(db_conn) + .await + })?; Ok(Activity { context: ap_context(), @@ -169,16 +151,13 @@ impl IntoActivity for Post { let account_url = state.service.url.user_url(self.account_id); if let Some(reposted_post_id) = self.reposted_post_id { - let reposted_post_url = state - .db_pool - .with_connection(|db_conn| { - posts::table - .find(reposted_post_id) - .select(posts::url) - .get_result(db_conn) - .scoped() - }) - .await?; + let reposted_post_url = with_connection!(state.db_pool, |db_conn| { + posts::table + .find(reposted_post_id) + .select(posts::url) + .get_result(db_conn) + .await + })?; Ok(Activity { context: ap_context(), diff --git a/crates/kitsune-activitypub/src/mapping/object.rs b/crates/kitsune-activitypub/src/mapping/object.rs index 37b9e3ff1..e099fd46a 100644 --- a/crates/kitsune-activitypub/src/mapping/object.rs +++ b/crates/kitsune-activitypub/src/mapping/object.rs @@ -12,6 +12,7 @@ use kitsune_db::{ post::Post, }, schema::{accounts, custom_emojis, media_attachments, posts, posts_custom_emojis}, + with_connection, }; use kitsune_type::ap::{ actor::{Actor, PublicKey}, @@ -22,7 +23,6 @@ use kitsune_type::ap::{ }; use kitsune_util::try_join; use mime::Mime; -use scoped_futures::ScopedFutureExt; use std::{future::Future, str::FromStr}; pub trait IntoObject { @@ -101,56 +101,51 @@ impl IntoObject for Post { return Err(Error::NotFound); } - let (account, in_reply_to, mentions, emojis, attachment_stream) = state - .db_pool - .with_connection(|db_conn| { - async { - let account_fut = accounts::table - .find(self.account_id) - .select(Account::as_select()) - .get_result(db_conn); + let (account, in_reply_to, mentions, emojis, attachment_stream) = + with_connection!(state.db_pool, |db_conn| { + let account_fut = accounts::table + .find(self.account_id) + .select(Account::as_select()) + .get_result(db_conn); - let in_reply_to_fut = - OptionFuture::from(self.in_reply_to_id.map(|in_reply_to_id| { - posts::table - .find(in_reply_to_id) - .select(posts::url) - .get_result(db_conn) - })) - .map(Option::transpose); + let in_reply_to_fut = + OptionFuture::from(self.in_reply_to_id.map(|in_reply_to_id| { + posts::table + .find(in_reply_to_id) + .select(posts::url) + .get_result(db_conn) + })) + .map(Option::transpose); - let mentions_fut = Mention::belonging_to(&self) - .inner_join(accounts::table) - .select((Mention::as_select(), Account::as_select())) - .load::<(Mention, Account)>(db_conn); + let mentions_fut = Mention::belonging_to(&self) + .inner_join(accounts::table) + .select((Mention::as_select(), Account::as_select())) + .load::<(Mention, Account)>(db_conn); - let custom_emojis_fut = custom_emojis::table - .inner_join(posts_custom_emojis::table) - .inner_join(media_attachments::table) - .filter(posts_custom_emojis::post_id.eq(self.id)) - .select(( - CustomEmoji::as_select(), - PostCustomEmoji::as_select(), - DbMediaAttachment::as_select(), - )) - .load::<(CustomEmoji, PostCustomEmoji, DbMediaAttachment)>(db_conn); + let custom_emojis_fut = custom_emojis::table + .inner_join(posts_custom_emojis::table) + .inner_join(media_attachments::table) + .filter(posts_custom_emojis::post_id.eq(self.id)) + .select(( + CustomEmoji::as_select(), + PostCustomEmoji::as_select(), + DbMediaAttachment::as_select(), + )) + .load::<(CustomEmoji, PostCustomEmoji, DbMediaAttachment)>(db_conn); - let attachment_stream_fut = PostMediaAttachment::belonging_to(&self) - .inner_join(media_attachments::table) - .select(DbMediaAttachment::as_select()) - .load_stream::(db_conn); + let attachment_stream_fut = PostMediaAttachment::belonging_to(&self) + .inner_join(media_attachments::table) + .select(DbMediaAttachment::as_select()) + .load_stream::(db_conn); - try_join!( - account_fut, - in_reply_to_fut, - mentions_fut, - custom_emojis_fut, - attachment_stream_fut - ) - } - .scoped() - }) - .await?; + try_join!( + account_fut, + in_reply_to_fut, + mentions_fut, + custom_emojis_fut, + attachment_stream_fut + ) + })?; let attachment = attachment_stream .map_err(Error::from) @@ -197,34 +192,28 @@ impl IntoObject for Account { type Output = Actor; async fn into_object(self, state: State<'_>) -> Result { - let (icon, image) = state - .db_pool - .with_connection(|db_conn| { - async move { - // These calls also probably allocate two cocnnections. ugh. - let icon_fut = OptionFuture::from(self.avatar_id.map(|avatar_id| { - media_attachments::table - .find(avatar_id) - .get_result::(db_conn) - .map_err(Error::from) - .and_then(|media_attachment| media_attachment.into_object(state)) - })) - .map(Option::transpose); + let (icon, image) = with_connection!(state.db_pool, |db_conn| { + // These calls also probably allocate two cocnnections. ugh. + let icon_fut = OptionFuture::from(self.avatar_id.map(|avatar_id| { + media_attachments::table + .find(avatar_id) + .get_result::(db_conn) + .map_err(Error::from) + .and_then(|media_attachment| media_attachment.into_object(state)) + })) + .map(Option::transpose); - let image_fut = OptionFuture::from(self.header_id.map(|header_id| { - media_attachments::table - .find(header_id) - .get_result::(db_conn) - .map_err(Error::from) - .and_then(|media_attachment| media_attachment.into_object(state)) - })) - .map(Option::transpose); + let image_fut = OptionFuture::from(self.header_id.map(|header_id| { + media_attachments::table + .find(header_id) + .get_result::(db_conn) + .map_err(Error::from) + .and_then(|media_attachment| media_attachment.into_object(state)) + })) + .map(Option::transpose); - try_join!(icon_fut, image_fut) - } - .scoped() - }) - .await?; + try_join!(icon_fut, image_fut) + })?; let user_url = state.service.url.user_url(self.id); let inbox = state.service.url.inbox_url(self.id); @@ -269,17 +258,14 @@ impl IntoObject for CustomEmoji { Some(_) => Err(Error::NotFound), }?; - let icon = state - .db_pool - .with_connection(|db_conn| { - media_attachments::table - .find(self.media_attachment_id) - .get_result::(db_conn) - .map_err(Error::from) - .and_then(|media_attachment| media_attachment.into_object(state)) - .scoped() - }) - .await?; + let icon = with_connection!(state.db_pool, |db_conn| { + media_attachments::table + .find(self.media_attachment_id) + .get_result::(db_conn) + .map_err(Error::from) + .and_then(|media_attachment| media_attachment.into_object(state)) + .await + })?; Ok(Emoji { context: ap_context(), diff --git a/crates/kitsune-activitypub/tests/fetcher/basic.rs b/crates/kitsune-activitypub/tests/fetcher/basic.rs index 25489cd11..7c3fac8f0 100644 --- a/crates/kitsune-activitypub/tests/fetcher/basic.rs +++ b/crates/kitsune-activitypub/tests/fetcher/basic.rs @@ -8,6 +8,7 @@ use kitsune_core::traits::Fetcher as _; use kitsune_db::{ model::{account::Account, media_attachment::MediaAttachment}, schema::{accounts, media_attachments}, + with_connection_panicky, }; use kitsune_federation_filter::FederationFilter; use kitsune_http_client::Client; @@ -15,7 +16,6 @@ use kitsune_search::NoopSearchService; use kitsune_test::{database_test, language_detection_config}; use kitsune_webfinger::Webfinger; use pretty_assertions::assert_eq; -use scoped_futures::ScopedFutureExt; use std::sync::Arc; use tower::service_fn; @@ -92,15 +92,14 @@ async fn fetch_emoji() { assert_eq!(emoji.shortcode, "Blobhaj"); assert_eq!(emoji.domain, Some(String::from("corteximplant.com"))); - let media_attachment = db_pool - .with_connection(|db_conn| { + let media_attachment = + with_connection_panicky!(db_pool, |db_conn| { media_attachments::table .find(emoji.media_attachment_id) .select(MediaAttachment::as_select()) .get_result::(db_conn) - .scoped() + .await }) - .await .expect("Get media attachment"); assert_eq!( @@ -149,16 +148,14 @@ async fn fetch_note() { "https://corteximplant.com/users/0x0/statuses/109501674056556919" ); - let author = db_pool - .with_connection(|db_conn| { - accounts::table - .find(note.account_id) - .select(Account::as_select()) - .get_result::(db_conn) - .scoped() - }) - .await - .expect("Get author"); + let author = with_connection_panicky!(db_pool, |db_conn| { + accounts::table + .find(note.account_id) + .select(Account::as_select()) + .get_result::(db_conn) + .await + }) + .expect("Get author"); assert_eq!(author.username, "0x0"); assert_eq!(author.url, "https://corteximplant.com/users/0x0"); diff --git a/crates/kitsune-cache/Cargo.toml b/crates/kitsune-cache/Cargo.toml index 6273236dc..7d04b258e 100644 --- a/crates/kitsune-cache/Cargo.toml +++ b/crates/kitsune-cache/Cargo.toml @@ -6,7 +6,7 @@ version.workspace = true license.workspace = true [dependencies] -enum_dispatch = "0.3.12" +enum_dispatch = "0.3.13" moka = { version = "0.12.5", features = ["future"] } multiplex-pool = { path = "../../lib/multiplex-pool" } redis = { version = "0.25.2", default-features = false, features = [ @@ -20,7 +20,7 @@ tracing = "0.1.40" typed-builder = "0.18.1" [dev-dependencies] -tokio = { version = "1.36.0", features = ["macros", "rt"] } +tokio = { version = "1.37.0", features = ["macros", "rt"] } [lints] workspace = true diff --git a/crates/kitsune-captcha/Cargo.toml b/crates/kitsune-captcha/Cargo.toml index 78dcacb33..35d915212 100644 --- a/crates/kitsune-captcha/Cargo.toml +++ b/crates/kitsune-captcha/Cargo.toml @@ -6,7 +6,7 @@ edition.workspace = true license.workspace = true [dependencies] -enum_dispatch = "0.3.12" +enum_dispatch = "0.3.13" http = "1.1.0" kitsune-http-client = { path = "../kitsune-http-client" } serde = { version = "1.0.197", features = ["derive"] } diff --git a/crates/kitsune-config/Cargo.toml b/crates/kitsune-config/Cargo.toml index 6db435922..158fa4f43 100644 --- a/crates/kitsune-config/Cargo.toml +++ b/crates/kitsune-config/Cargo.toml @@ -6,11 +6,11 @@ version.workspace = true license.workspace = true [dependencies] +eyre = "0.6.12" isolang = { version = "2.4.0", features = ["serde"] } -miette = "7.2.0" serde = { version = "1.0.197", features = ["derive"] } smol_str = { version = "0.2.1", features = ["serde"] } -tokio = { version = "1.36.0", features = ["fs"] } +tokio = { version = "1.37.0", features = ["fs"] } toml = { version = "0.8.12", default-features = false, features = ["parse"] } [lints] diff --git a/crates/kitsune-config/src/lib.rs b/crates/kitsune-config/src/lib.rs index 1b6100026..71dbcb50c 100644 --- a/crates/kitsune-config/src/lib.rs +++ b/crates/kitsune-config/src/lib.rs @@ -15,7 +15,7 @@ pub mod server; pub mod storage; pub mod url; -use miette::{Context, IntoDiagnostic}; +use eyre::{Result, WrapErr}; use serde::{Deserialize, Serialize}; use std::path::Path; use tokio::fs; @@ -41,17 +41,14 @@ pub struct Configuration { } impl Configuration { - pub async fn load

(path: P) -> miette::Result + pub async fn load

(path: P) -> Result where P: AsRef, { let content = fs::read_to_string(path) .await - .into_diagnostic() .wrap_err("Couldn't read configuration file")?; - toml::from_str(&content) - .into_diagnostic() - .wrap_err("Failed to parse configuration file") + toml::from_str(&content).wrap_err("Failed to parse configuration file") } } diff --git a/crates/kitsune-core/Cargo.toml b/crates/kitsune-core/Cargo.toml index b0581b58a..ad8fa39bc 100644 --- a/crates/kitsune-core/Cargo.toml +++ b/crates/kitsune-core/Cargo.toml @@ -9,6 +9,7 @@ build = "build.rs" [dependencies] async-trait = "0.1.79" const_format = "0.2.32" +eyre = "0.6.12" http = "1.1.0" kitsune-db = { path = "../kitsune-db" } kitsune-messaging = { path = "../kitsune-messaging" } diff --git a/crates/kitsune-core/src/error.rs b/crates/kitsune-core/src/error.rs index a6e423c48..093083db4 100644 --- a/crates/kitsune-core/src/error.rs +++ b/crates/kitsune-core/src/error.rs @@ -1,10 +1,7 @@ use http::StatusCode; use std::borrow::Cow; -use std::error::Error as StdError; use thiserror::Error; -pub type BoxError = Box; - macro_rules! http_error { ($($variant_name:ident => $status_code:path),*$(,)?) => { #[derive(Debug, Error)] diff --git a/crates/kitsune-core/src/traits/deliverer.rs b/crates/kitsune-core/src/traits/deliverer.rs index c3e298ff2..72120d58d 100644 --- a/crates/kitsune-core/src/traits/deliverer.rs +++ b/crates/kitsune-core/src/traits/deliverer.rs @@ -1,5 +1,5 @@ -use crate::error::BoxError; use async_trait::async_trait; +use eyre::Result; use kitsune_db::model::{account::Account, favourite::Favourite, follower::Follow, post::Post}; use serde::{Deserialize, Serialize}; use std::sync::Arc; @@ -22,12 +22,12 @@ pub enum Action { #[async_trait] pub trait Deliverer: Send + Sync + 'static { - async fn deliver(&self, action: Action) -> Result<(), BoxError>; + async fn deliver(&self, action: Action) -> Result<()>; } #[async_trait] impl Deliverer for Arc { - async fn deliver(&self, action: Action) -> Result<(), BoxError> { + async fn deliver(&self, action: Action) -> Result<()> { (**self).deliver(action).await } } @@ -37,7 +37,7 @@ impl Deliverer for Vec where T: Deliverer, { - async fn deliver(&self, action: Action) -> Result<(), BoxError> { + async fn deliver(&self, action: Action) -> Result<()> { for deliverer in self { deliverer.deliver(action.clone()).await?; } diff --git a/crates/kitsune-core/src/traits/fetcher.rs b/crates/kitsune-core/src/traits/fetcher.rs index e5c02ec99..1849dd91f 100644 --- a/crates/kitsune-core/src/traits/fetcher.rs +++ b/crates/kitsune-core/src/traits/fetcher.rs @@ -1,6 +1,6 @@ use super::Resolver; -use crate::error::BoxError; use async_trait::async_trait; +use eyre::Result; use kitsune_db::model::{account::Account, custom_emoji::CustomEmoji, post::Post}; use std::sync::Arc; use typed_builder::TypedBuilder; @@ -8,7 +8,7 @@ use typed_builder::TypedBuilder; #[derive(Clone, Copy, Debug, TypedBuilder)] /// Options passed to the fetcher pub struct AccountFetchOptions<'a> { - /// Prefetched WebFinger `acct` URI + /// Prefetched Webfinger `acct` URI #[builder(default, setter(strip_option))] pub acct: Option<(&'a str, &'a str)>, @@ -50,14 +50,11 @@ impl<'a> From<&'a str> for PostFetchOptions<'a> { pub trait Fetcher: Send + Sync + 'static { fn resolver(&self) -> Arc; - async fn fetch_account( - &self, - opts: AccountFetchOptions<'_>, - ) -> Result, BoxError>; + async fn fetch_account(&self, opts: AccountFetchOptions<'_>) -> Result>; - async fn fetch_emoji(&self, url: &str) -> Result, BoxError>; + async fn fetch_emoji(&self, url: &str) -> Result>; - async fn fetch_post(&self, opts: PostFetchOptions<'_>) -> Result, BoxError>; + async fn fetch_post(&self, opts: PostFetchOptions<'_>) -> Result>; } #[async_trait] @@ -66,18 +63,15 @@ impl Fetcher for Arc { (**self).resolver() } - async fn fetch_account( - &self, - opts: AccountFetchOptions<'_>, - ) -> Result, BoxError> { + async fn fetch_account(&self, opts: AccountFetchOptions<'_>) -> Result> { (**self).fetch_account(opts).await } - async fn fetch_emoji(&self, url: &str) -> Result, BoxError> { + async fn fetch_emoji(&self, url: &str) -> Result> { (**self).fetch_emoji(url).await } - async fn fetch_post(&self, opts: PostFetchOptions<'_>) -> Result, BoxError> { + async fn fetch_post(&self, opts: PostFetchOptions<'_>) -> Result> { (**self).fetch_post(opts).await } } @@ -91,10 +85,7 @@ where Arc::new(self.iter().map(Fetcher::resolver).collect::>()) } - async fn fetch_account( - &self, - opts: AccountFetchOptions<'_>, - ) -> Result, BoxError> { + async fn fetch_account(&self, opts: AccountFetchOptions<'_>) -> Result> { for fetcher in self { if let Some(account) = fetcher.fetch_account(opts).await? { return Ok(Some(account)); @@ -104,7 +95,7 @@ where Ok(None) } - async fn fetch_emoji(&self, url: &str) -> Result, BoxError> { + async fn fetch_emoji(&self, url: &str) -> Result> { for fetcher in self { if let Some(emoji) = fetcher.fetch_emoji(url).await? { return Ok(Some(emoji)); @@ -114,7 +105,7 @@ where Ok(None) } - async fn fetch_post(&self, opts: PostFetchOptions<'_>) -> Result, BoxError> { + async fn fetch_post(&self, opts: PostFetchOptions<'_>) -> Result> { for fetcher in self { if let Some(post) = fetcher.fetch_post(opts).await? { return Ok(Some(post)); diff --git a/crates/kitsune-core/src/traits/resolver.rs b/crates/kitsune-core/src/traits/resolver.rs index a2aa23566..6458d7efd 100644 --- a/crates/kitsune-core/src/traits/resolver.rs +++ b/crates/kitsune-core/src/traits/resolver.rs @@ -1,5 +1,5 @@ -use crate::error::BoxError; use async_trait::async_trait; +use eyre::Result; use serde::{Deserialize, Serialize}; use std::sync::Arc; @@ -20,7 +20,7 @@ pub trait Resolver: Send + Sync + 'static { &self, username: &str, domain: &str, - ) -> Result, BoxError>; + ) -> Result>; } #[async_trait] @@ -29,7 +29,7 @@ impl Resolver for Arc { &self, username: &str, domain: &str, - ) -> Result, BoxError> { + ) -> Result> { (**self).resolve_account(username, domain).await } } @@ -43,7 +43,7 @@ where &self, username: &str, domain: &str, - ) -> Result, BoxError> { + ) -> Result> { for resolver in self { if let Some(resource) = resolver.resolve_account(username, domain).await? { return Ok(Some(resource)); diff --git a/crates/kitsune-db/Cargo.toml b/crates/kitsune-db/Cargo.toml index bd30815a5..687fc80f5 100644 --- a/crates/kitsune-db/Cargo.toml +++ b/crates/kitsune-db/Cargo.toml @@ -24,16 +24,20 @@ iso8601-timestamp = { version = "0.2.17", features = ["diesel-pg"] } kitsune-config = { path = "../kitsune-config" } kitsune-language = { path = "../kitsune-language" } kitsune-type = { path = "../kitsune-type" } -miette = "7.2.0" num-derive = "0.4.2" num-traits = "0.2.18" -rustls = "=0.22.2" +rustls = { version = "0.23.4", default-features = false, features = [ + "logging", + "ring", + "std", + "tls12", +] } rustls-native-certs = "0.7.0" serde = { version = "1.0.197", features = ["derive"] } simd-json = "0.13.9" speedy-uuid = { path = "../../lib/speedy-uuid", features = ["diesel"] } thiserror = "1.0.58" -tokio = { version = "1.36.0", features = ["rt"] } +tokio = { version = "1.37.0", features = ["rt"] } tokio-postgres = "0.7.10" tokio-postgres-rustls = "0.11.1" tracing = "0.1.40" @@ -42,7 +46,7 @@ typed-builder = "0.18.1" [dev-dependencies] kitsune-test = { path = "../kitsune-test" } -tokio = { version = "1.36.0", features = ["macros"] } +tokio = { version = "1.37.0", features = ["macros"] } [lints] workspace = true diff --git a/crates/kitsune-db/src/error.rs b/crates/kitsune-db/src/error.rs index e57e9002b..12daf1669 100644 --- a/crates/kitsune-db/src/error.rs +++ b/crates/kitsune-db/src/error.rs @@ -1,6 +1,5 @@ use core::fmt; use diesel_async::pooled_connection::bb8; -use miette::Diagnostic; use std::error::Error as StdError; use thiserror::Error; @@ -37,7 +36,7 @@ impl fmt::Display for IsoCodeConversionError { impl StdError for IsoCodeConversionError {} -#[derive(Debug, Diagnostic, Error)] +#[derive(Debug, Error)] pub enum Error { #[error(transparent)] Blocking(#[from] blowocking::Error), diff --git a/crates/kitsune-db/src/lib.rs b/crates/kitsune-db/src/lib.rs index d89c75a5d..a9e851a86 100644 --- a/crates/kitsune-db/src/lib.rs +++ b/crates/kitsune-db/src/lib.rs @@ -11,10 +11,11 @@ use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness}; use kitsune_config::database::Configuration as DatabaseConfig; use tracing_log::LogTracer; -pub use crate::{ - error::{Error, Result}, - pool::{PgPool, PoolError}, -}; +pub type PgPool = Pool; + +pub use crate::error::{Error, Result}; +#[doc(hidden)] +pub use diesel_async; mod error; mod pool; @@ -75,5 +76,5 @@ pub async fn connect(config: &DatabaseConfig) -> Result { .await?; } - Ok(pool.into()) + Ok(pool) } diff --git a/crates/kitsune-db/src/pool.rs b/crates/kitsune-db/src/pool.rs index 8c9fa44c5..2201c9758 100644 --- a/crates/kitsune-db/src/pool.rs +++ b/crates/kitsune-db/src/pool.rs @@ -1,70 +1,41 @@ -use diesel_async::{ - pooled_connection::bb8::{self, Pool}, - scoped_futures::{ScopedFutureExt, ScopedFutureWrapper}, - AsyncConnection, AsyncPgConnection, -}; -use miette::Diagnostic; -use std::{ - fmt::{Debug, Display}, - future::Future, -}; -use thiserror::Error; - -#[derive(Debug, Diagnostic, Error)] -pub enum PoolError -where - E: Display + Debug, -{ - #[error(transparent)] - Pool(#[from] bb8::RunError), - - #[error("{0}")] - User(E), +#[macro_export] +macro_rules! with_connection { + ($pool:expr, |$conn_name:ident| $code:block) => {{ + let mut conn = $pool.get().await?; + let $conn_name = &mut *conn; + async { $code }.await + }}; } -/// Small wrapper around [`Pool`] -/// -/// The intent of this API is to encourage and make short-lived ownership of connections easier. -/// With the traditional RAII guard based approach, it is rather hard (and/or ugly) to define clear scopes for connections -/// (especially when they are used *a lot* throughout the code). -/// -/// The API of this wrapper is based on closures, meaning you have no choice but to be aware of the scope. -/// And the extra level of indentation this forces is supposed to coerce users to keep the scope as small as possible. -#[derive(Clone)] -pub struct PgPool { - inner: Pool, +#[macro_export] +macro_rules! catch_error { + ($($tt:tt)*) => {{ + let result: ::std::result::Result<_, ::diesel_async::pooled_connection::bb8::RunError> = async { + Ok({ $($tt)* }) + }.await; + result + }}; } -impl PgPool { - /// Run the code inside a context with a database connection - pub async fn with_connection<'a, F, Fut, T, E>(&self, func: F) -> Result> - where - for<'conn> F: FnOnce(&'conn mut AsyncPgConnection) -> ScopedFutureWrapper<'conn, 'a, Fut>, - Fut: Future>, - E: Display + Debug, - { - let mut conn = self.inner.get().await?; - func(&mut conn).await.map_err(PoolError::User) - } - - /// Run the code inside a context with a database transaction - pub async fn with_transaction<'a, F, Fut, T, E>(&self, func: F) -> Result> - where - for<'conn> F: - FnOnce(&'conn mut AsyncPgConnection) -> ScopedFutureWrapper<'conn, 'a, Fut> + Send, - Fut: Future> + Send, - T: Send, - E: From + Debug + Display + Send, - { - let mut conn = self.inner.get().await?; - conn.transaction(|conn| func(conn).scope_boxed()) - .await - .map_err(PoolError::User) - } +#[macro_export] +macro_rules! with_connection_panicky { + ($pool:expr, $($other:tt)*) => {{ + $crate::catch_error!($crate::with_connection!($pool, $($other)*)).unwrap() + }}; } -impl From> for PgPool { - fn from(value: Pool) -> Self { - Self { inner: value } - } +#[macro_export] +macro_rules! with_transaction { + ($pool:expr, |$conn_name:ident| $code:block) => {{ + use $crate::diesel_async::AsyncConnection; + + let mut conn = $pool.get().await?; + conn.transaction(|conn| { + Box::pin(async move { + let $conn_name = conn; + $code + }) + }) + .await + }}; } diff --git a/crates/kitsune-db/tests/unicode_collation.rs b/crates/kitsune-db/tests/unicode_collation.rs index cf7ef860b..026992cc1 100644 --- a/crates/kitsune-db/tests/unicode_collation.rs +++ b/crates/kitsune-db/tests/unicode_collation.rs @@ -1,11 +1,12 @@ use diesel::SelectableHelper; -use diesel_async::{scoped_futures::ScopedFutureExt, AsyncPgConnection, RunQueryDsl}; +use diesel_async::{AsyncPgConnection, RunQueryDsl}; use kitsune_db::{ model::{ account::{Account, ActorType, NewAccount}, user::{NewUser, User}, }, schema::{accounts, users}, + with_connection_panicky, }; use kitsune_test::database_test; use speedy_uuid::Uuid; @@ -65,30 +66,22 @@ async fn create_user(conn: &mut AsyncPgConnection, username: &str) -> Result; pub type Result = std::result::Result; -#[derive(Debug, Diagnostic, Error)] +#[derive(Debug, Error)] pub enum Error { #[error(transparent)] Address(#[from] lettre::address::AddressError), @@ -35,15 +31,3 @@ pub enum Error { #[error(transparent)] Rendering(#[from] mrml::prelude::render::Error), } - -impl From> for Error -where - E: Into + Debug + Display, -{ - fn from(value: kitsune_db::PoolError) -> Self { - match value { - kitsune_db::PoolError::Pool(err) => err.into(), - kitsune_db::PoolError::User(err) => err.into(), - } - } -} diff --git a/crates/kitsune-email/src/service.rs b/crates/kitsune-email/src/service.rs index 9e4c84986..fc844c735 100644 --- a/crates/kitsune-email/src/service.rs +++ b/crates/kitsune-email/src/service.rs @@ -1,10 +1,9 @@ use crate::{error::Result, mails::confirm_account::ConfirmAccount, MailSender}; use diesel::{ExpressionMethods, NullableExpressionMethods, QueryDsl}; use diesel_async::RunQueryDsl; -use kitsune_db::{function::now, model::user::User, schema::users, PgPool}; +use kitsune_db::{function::now, model::user::User, schema::users, with_connection, PgPool}; use kitsune_url::UrlService; use lettre::{AsyncSmtpTransport, Tokio1Executor}; -use scoped_futures::ScopedFutureExt; use speedy_uuid::Uuid; use typed_builder::TypedBuilder; @@ -27,31 +26,27 @@ impl Mailing { } pub async fn mark_as_confirmed(&self, user_id: Uuid) -> Result<()> { - self.db_pool - .with_connection(|db_conn| { - diesel::update(users::table.find(user_id)) - .set(users::confirmed_at.eq(now().nullable())) - .execute(db_conn) - .scoped() - }) - .await?; + with_connection!(self.db_pool, |db_conn| { + diesel::update(users::table.find(user_id)) + .set(users::confirmed_at.eq(now().nullable())) + .execute(db_conn) + .await + })?; Ok(()) } pub async fn mark_as_confirmed_by_token(&self, confirmation_token: &str) -> Result<()> { - self.db_pool - .with_connection(|db_conn| { - diesel::update( - users::table - .filter(users::confirmation_token.eq(confirmation_token)) - .filter(users::confirmed_at.is_null()), - ) - .set(users::confirmed_at.eq(now().nullable())) - .execute(db_conn) - .scoped() - }) - .await?; + with_connection!(self.db_pool, |db_conn| { + diesel::update( + users::table + .filter(users::confirmation_token.eq(confirmation_token)) + .filter(users::confirmed_at.is_null()), + ) + .set(users::confirmed_at.eq(now().nullable())) + .execute(db_conn) + .await + })?; Ok(()) } diff --git a/crates/kitsune-embed/src/lib.rs b/crates/kitsune-embed/src/lib.rs index cd8b0f614..806a13d1d 100644 --- a/crates/kitsune-embed/src/lib.rs +++ b/crates/kitsune-embed/src/lib.rs @@ -1,5 +1,5 @@ use diesel::{OptionalExtension, QueryDsl}; -use diesel_async::{pooled_connection::bb8, scoped_futures::ScopedFutureExt, RunQueryDsl}; +use diesel_async::{pooled_connection::bb8, RunQueryDsl}; use embed_sdk::EmbedWithExpire; use http::{Method, Request}; use iso8601_timestamp::Timestamp; @@ -7,13 +7,13 @@ use kitsune_db::{ json::Json, model::link_preview::{ConflictLinkPreviewChangeset, LinkPreview, NewLinkPreview}, schema::link_previews, - PgPool, + with_connection, PgPool, }; use kitsune_http_client::Client as HttpClient; use once_cell::sync::Lazy; use scraper::{Html, Selector}; use smol_str::SmolStr; -use std::fmt::{Debug, Display}; +use std::fmt::Debug; use typed_builder::TypedBuilder; pub use embed_sdk; @@ -46,18 +46,6 @@ pub enum Error { Pool(#[from] bb8::RunError), } -impl From> for Error -where - E: Into + Debug + Display, -{ - fn from(value: kitsune_db::PoolError) -> Self { - match value { - kitsune_db::PoolError::Pool(err) => err.into(), - kitsune_db::PoolError::User(err) => err.into(), - } - } -} - #[derive(Clone, TypedBuilder)] pub struct Client { db_pool: PgPool, @@ -83,19 +71,13 @@ impl Client { } pub async fn fetch_embed(&self, url: &str) -> Result> { - let embed_data = self - .db_pool - .with_connection(|db_conn| { - async move { - link_previews::table - .find(url) - .get_result::>(db_conn) - .await - .optional() - } - .scoped() - }) - .await?; + let embed_data = with_connection!(self.db_pool, |db_conn| { + link_previews::table + .find(url) + .get_result::>(db_conn) + .await + .optional() + })?; if let Some(data) = embed_data { if data.expires_at > Timestamp::now_utc() { @@ -112,25 +94,22 @@ impl Client { let response = HttpClient::execute(&self.http_client, request).await?; let (expires_at, embed_data): EmbedWithExpire = response.json().await?; - let embed_data = self - .db_pool - .with_connection(|db_conn| { - diesel::insert_into(link_previews::table) - .values(NewLinkPreview { - url, - embed_data: Json(&embed_data), - expires_at, - }) - .on_conflict(link_previews::url) - .do_update() - .set(ConflictLinkPreviewChangeset { - embed_data: Json(&embed_data), - expires_at, - }) - .get_result(db_conn) - .scoped() - }) - .await?; + let embed_data = with_connection!(self.db_pool, |db_conn| { + diesel::insert_into(link_previews::table) + .values(NewLinkPreview { + url, + embed_data: Json(&embed_data), + expires_at, + }) + .on_conflict(link_previews::url) + .do_update() + .set(ConflictLinkPreviewChangeset { + embed_data: Json(&embed_data), + expires_at, + }) + .get_result(db_conn) + .await + })?; Ok(embed_data) } diff --git a/crates/kitsune-federation-filter/Cargo.toml b/crates/kitsune-federation-filter/Cargo.toml index 397207e8d..069ac0102 100644 --- a/crates/kitsune-federation-filter/Cargo.toml +++ b/crates/kitsune-federation-filter/Cargo.toml @@ -9,7 +9,6 @@ license.workspace = true globset = "0.4.14" kitsune-config = { path = "../kitsune-config" } kitsune-type = { path = "../kitsune-type" } -miette = "7.2.0" thiserror = "1.0.58" url = "2.5.0" diff --git a/crates/kitsune-federation-filter/src/error.rs b/crates/kitsune-federation-filter/src/error.rs index 09dc106d6..99d0991e5 100644 --- a/crates/kitsune-federation-filter/src/error.rs +++ b/crates/kitsune-federation-filter/src/error.rs @@ -1,9 +1,8 @@ -use miette::Diagnostic; use thiserror::Error; pub type Result = std::result::Result; -#[derive(Debug, Diagnostic, Error)] +#[derive(Debug, Error)] pub enum Error { #[error(transparent)] Glob(#[from] globset::Error), diff --git a/crates/kitsune-http-client/Cargo.toml b/crates/kitsune-http-client/Cargo.toml index bb78349d5..d7b889a53 100644 --- a/crates/kitsune-http-client/Cargo.toml +++ b/crates/kitsune-http-client/Cargo.toml @@ -48,7 +48,7 @@ tower-http = { version = "0.5.2", features = [ ] } [dev-dependencies] -tokio = { version = "1.36.0", features = ["macros", "rt"] } +tokio = { version = "1.37.0", features = ["macros", "rt"] } [lints] workspace = true diff --git a/crates/kitsune-jobs/Cargo.toml b/crates/kitsune-jobs/Cargo.toml index 2765720bc..0ceccd7ef 100644 --- a/crates/kitsune-jobs/Cargo.toml +++ b/crates/kitsune-jobs/Cargo.toml @@ -10,12 +10,11 @@ athena = { path = "../../lib/athena" } derive_more = { version = "1.0.0-beta.6", features = ["from"] } diesel = "2.1.5" diesel-async = "0.4.1" +eyre = "0.6.12" futures-util = "0.3.30" kitsune-core = { path = "../kitsune-core" } kitsune-db = { path = "../kitsune-db" } kitsune-email = { path = "../kitsune-email" } -miette = "7.2.0" -scoped-futures = "0.1.3" serde = { version = "1.0.197", features = ["derive"] } speedy-uuid = { path = "../../lib/speedy-uuid" } tracing = "0.1.40" diff --git a/crates/kitsune-jobs/src/deliver/accept.rs b/crates/kitsune-jobs/src/deliver/accept.rs index ae53696b9..b4f0863ca 100644 --- a/crates/kitsune-jobs/src/deliver/accept.rs +++ b/crates/kitsune-jobs/src/deliver/accept.rs @@ -2,8 +2,7 @@ use crate::{JobRunnerContext, Runnable}; use diesel::{OptionalExtension, QueryDsl}; use diesel_async::RunQueryDsl; use kitsune_core::traits::deliverer::Action; -use kitsune_db::{model::follower::Follow, schema::accounts_follows}; -use scoped_futures::ScopedFutureExt; +use kitsune_db::{model::follower::Follow, schema::accounts_follows, with_connection}; use serde::{Deserialize, Serialize}; use speedy_uuid::Uuid; @@ -14,32 +13,23 @@ pub struct DeliverAccept { impl Runnable for DeliverAccept { type Context = JobRunnerContext; - type Error = miette::Report; + type Error = eyre::Report; #[instrument(skip_all, fields(follow_id = %self.follow_id))] async fn run(&self, ctx: &Self::Context) -> Result<(), Self::Error> { - let follow = ctx - .db_pool - .with_connection(|db_conn| { - async move { - accounts_follows::table - .find(self.follow_id) - .get_result::(db_conn) - .await - .optional() - } - .scoped() - }) - .await?; + let follow = with_connection!(ctx.db_pool, |db_conn| { + accounts_follows::table + .find(self.follow_id) + .get_result::(db_conn) + .await + .optional() + })?; let Some(follow) = follow else { return Ok(()); }; - ctx.deliverer - .deliver(Action::AcceptFollow(follow)) - .await - .map_err(|err| miette::Report::new_boxed(err.into()))?; + ctx.deliverer.deliver(Action::AcceptFollow(follow)).await?; Ok(()) } diff --git a/crates/kitsune-jobs/src/deliver/create.rs b/crates/kitsune-jobs/src/deliver/create.rs index 3d157c1a2..24eb7bd0c 100644 --- a/crates/kitsune-jobs/src/deliver/create.rs +++ b/crates/kitsune-jobs/src/deliver/create.rs @@ -3,8 +3,7 @@ use athena::Runnable; use diesel::{OptionalExtension, QueryDsl, SelectableHelper}; use diesel_async::RunQueryDsl; use kitsune_core::traits::deliverer::Action; -use kitsune_db::{model::post::Post, schema::posts}; -use scoped_futures::ScopedFutureExt; +use kitsune_db::{model::post::Post, schema::posts, with_connection}; use serde::{Deserialize, Serialize}; use speedy_uuid::Uuid; @@ -15,33 +14,24 @@ pub struct DeliverCreate { impl Runnable for DeliverCreate { type Context = JobRunnerContext; - type Error = miette::Report; + type Error = eyre::Report; #[instrument(skip_all, fields(post_id = %self.post_id))] async fn run(&self, ctx: &Self::Context) -> Result<(), Self::Error> { - let post = ctx - .db_pool - .with_connection(|db_conn| { - async move { - posts::table - .find(self.post_id) - .select(Post::as_select()) - .get_result::(db_conn) - .await - .optional() - } - .scoped() - }) - .await?; + let post = with_connection!(ctx.db_pool, |db_conn| { + posts::table + .find(self.post_id) + .select(Post::as_select()) + .get_result::(db_conn) + .await + .optional() + })?; let Some(post) = post else { return Ok(()); }; - ctx.deliverer - .deliver(Action::Create(post)) - .await - .map_err(|err| miette::Report::new_boxed(err.into()))?; + ctx.deliverer.deliver(Action::Create(post)).await?; Ok(()) } diff --git a/crates/kitsune-jobs/src/deliver/delete.rs b/crates/kitsune-jobs/src/deliver/delete.rs index 3295c8c5d..7c7a2131c 100644 --- a/crates/kitsune-jobs/src/deliver/delete.rs +++ b/crates/kitsune-jobs/src/deliver/delete.rs @@ -3,8 +3,7 @@ use athena::Runnable; use diesel::{OptionalExtension, QueryDsl, SelectableHelper}; use diesel_async::RunQueryDsl; use kitsune_core::traits::deliverer::Action; -use kitsune_db::{model::post::Post, schema::posts}; -use scoped_futures::ScopedFutureExt; +use kitsune_db::{model::post::Post, schema::posts, with_connection}; use serde::{Deserialize, Serialize}; use speedy_uuid::Uuid; @@ -15,41 +14,30 @@ pub struct DeliverDelete { impl Runnable for DeliverDelete { type Context = JobRunnerContext; - type Error = miette::Report; + type Error = eyre::Report; #[instrument(skip_all, fields(post_id = %self.post_id))] async fn run(&self, ctx: &Self::Context) -> Result<(), Self::Error> { - let post = ctx - .db_pool - .with_connection(|db_conn| { - async move { - posts::table - .find(self.post_id) - .select(Post::as_select()) - .get_result::(db_conn) - .await - .optional() - } - .scoped() - }) - .await?; + let post = with_connection!(ctx.db_pool, |db_conn| { + posts::table + .find(self.post_id) + .select(Post::as_select()) + .get_result::(db_conn) + .await + .optional() + })?; let Some(post) = post else { return Ok(()); }; - ctx.deliverer - .deliver(Action::Delete(post)) - .await - .map_err(|err| miette::Report::new_boxed(err.into()))?; + ctx.deliverer.deliver(Action::Delete(post)).await?; - ctx.db_pool - .with_connection(|db_conn| { - diesel::delete(posts::table.find(self.post_id)) - .execute(db_conn) - .scoped() - }) - .await?; + with_connection!(ctx.db_pool, |db_conn| { + diesel::delete(posts::table.find(self.post_id)) + .execute(db_conn) + .await + })?; Ok(()) } diff --git a/crates/kitsune-jobs/src/deliver/favourite.rs b/crates/kitsune-jobs/src/deliver/favourite.rs index 1dbc8d152..46a7b2d96 100644 --- a/crates/kitsune-jobs/src/deliver/favourite.rs +++ b/crates/kitsune-jobs/src/deliver/favourite.rs @@ -3,8 +3,7 @@ use athena::Runnable; use diesel::QueryDsl; use diesel_async::RunQueryDsl; use kitsune_core::traits::deliverer::Action; -use kitsune_db::{model::favourite::Favourite, schema::posts_favourites}; -use scoped_futures::ScopedFutureExt; +use kitsune_db::{model::favourite::Favourite, schema::posts_favourites, with_connection}; use serde::{Deserialize, Serialize}; use speedy_uuid::Uuid; @@ -15,24 +14,18 @@ pub struct DeliverFavourite { impl Runnable for DeliverFavourite { type Context = JobRunnerContext; - type Error = miette::Report; + type Error = eyre::Report; #[instrument(skip_all, fields(favourite_id = %self.favourite_id))] async fn run(&self, ctx: &Self::Context) -> Result<(), Self::Error> { - let favourite = ctx - .db_pool - .with_connection(|db_conn| { - posts_favourites::table - .find(self.favourite_id) - .get_result::(db_conn) - .scoped() - }) - .await?; + let favourite = with_connection!(ctx.db_pool, |db_conn| { + posts_favourites::table + .find(self.favourite_id) + .get_result::(db_conn) + .await + })?; - ctx.deliverer - .deliver(Action::Favourite(favourite)) - .await - .map_err(|err| miette::Report::new_boxed(err.into()))?; + ctx.deliverer.deliver(Action::Favourite(favourite)).await?; Ok(()) } diff --git a/crates/kitsune-jobs/src/deliver/follow.rs b/crates/kitsune-jobs/src/deliver/follow.rs index b7e5312ce..1465999a6 100644 --- a/crates/kitsune-jobs/src/deliver/follow.rs +++ b/crates/kitsune-jobs/src/deliver/follow.rs @@ -3,8 +3,7 @@ use athena::Runnable; use diesel::{OptionalExtension, QueryDsl}; use diesel_async::RunQueryDsl; use kitsune_core::traits::deliverer::Action; -use kitsune_db::{model::follower::Follow, schema::accounts_follows}; -use scoped_futures::ScopedFutureExt; +use kitsune_db::{model::follower::Follow, schema::accounts_follows, with_connection}; use serde::{Deserialize, Serialize}; use speedy_uuid::Uuid; @@ -15,32 +14,23 @@ pub struct DeliverFollow { impl Runnable for DeliverFollow { type Context = JobRunnerContext; - type Error = miette::Report; + type Error = eyre::Report; #[instrument(skip_all, fields(follow_id = %self.follow_id))] async fn run(&self, ctx: &Self::Context) -> Result<(), Self::Error> { - let follow = ctx - .db_pool - .with_connection(|db_conn| { - async move { - accounts_follows::table - .find(self.follow_id) - .get_result::(db_conn) - .await - .optional() - } - .scoped() - }) - .await?; + let follow = with_connection!(ctx.db_pool, |db_conn| { + accounts_follows::table + .find(self.follow_id) + .get_result::(db_conn) + .await + .optional() + })?; let Some(follow) = follow else { return Ok(()); }; - ctx.deliverer - .deliver(Action::Follow(follow)) - .await - .map_err(|err| miette::Report::new_boxed(err.into()))?; + ctx.deliverer.deliver(Action::Follow(follow)).await?; Ok(()) } diff --git a/crates/kitsune-jobs/src/deliver/reject.rs b/crates/kitsune-jobs/src/deliver/reject.rs index 0d0fb50b9..050431acf 100644 --- a/crates/kitsune-jobs/src/deliver/reject.rs +++ b/crates/kitsune-jobs/src/deliver/reject.rs @@ -2,8 +2,7 @@ use crate::{JobRunnerContext, Runnable}; use diesel::{OptionalExtension, QueryDsl}; use diesel_async::RunQueryDsl; use kitsune_core::traits::deliverer::Action; -use kitsune_db::{model::follower::Follow, schema::accounts_follows}; -use scoped_futures::ScopedFutureExt; +use kitsune_db::{model::follower::Follow, schema::accounts_follows, with_connection}; use serde::{Deserialize, Serialize}; use speedy_uuid::Uuid; @@ -14,40 +13,29 @@ pub struct DeliverReject { impl Runnable for DeliverReject { type Context = JobRunnerContext; - type Error = miette::Report; + type Error = eyre::Report; #[instrument(skip_all, fields(follow_id = %self.follow_id))] async fn run(&self, ctx: &Self::Context) -> Result<(), Self::Error> { - let follow = ctx - .db_pool - .with_connection(|db_conn| { - async move { - accounts_follows::table - .find(self.follow_id) - .get_result::(db_conn) - .await - .optional() - } - .scoped() - }) - .await?; + let follow = with_connection!(ctx.db_pool, |db_conn| { + accounts_follows::table + .find(self.follow_id) + .get_result::(db_conn) + .await + .optional() + })?; let Some(follow) = follow else { return Ok(()); }; - ctx.deliverer - .deliver(Action::RejectFollow(follow)) - .await - .map_err(|err| miette::Report::new_boxed(err.into()))?; + ctx.deliverer.deliver(Action::RejectFollow(follow)).await?; - ctx.db_pool - .with_connection(|db_conn| { - diesel::delete(accounts_follows::table.find(self.follow_id)) - .execute(db_conn) - .scoped() - }) - .await?; + with_connection!(ctx.db_pool, |db_conn| { + diesel::delete(accounts_follows::table.find(self.follow_id)) + .execute(db_conn) + .await + })?; Ok(()) } diff --git a/crates/kitsune-jobs/src/deliver/unfavourite.rs b/crates/kitsune-jobs/src/deliver/unfavourite.rs index 1cd9133ce..046c000de 100644 --- a/crates/kitsune-jobs/src/deliver/unfavourite.rs +++ b/crates/kitsune-jobs/src/deliver/unfavourite.rs @@ -3,8 +3,7 @@ use athena::Runnable; use diesel::{OptionalExtension, QueryDsl}; use diesel_async::RunQueryDsl; use kitsune_core::traits::deliverer::Action; -use kitsune_db::{model::favourite::Favourite, schema::posts_favourites}; -use scoped_futures::ScopedFutureExt; +use kitsune_db::{model::favourite::Favourite, schema::posts_favourites, with_connection}; use serde::{Deserialize, Serialize}; use speedy_uuid::Uuid; @@ -15,23 +14,17 @@ pub struct DeliverUnfavourite { impl Runnable for DeliverUnfavourite { type Context = JobRunnerContext; - type Error = miette::Report; + type Error = eyre::Report; #[instrument(skip_all, fields(favourite_id = %self.favourite_id))] async fn run(&self, ctx: &Self::Context) -> Result<(), Self::Error> { - let favourite = ctx - .db_pool - .with_connection(|db_conn| { - async move { - posts_favourites::table - .find(self.favourite_id) - .get_result::(db_conn) - .await - .optional() - } - .scoped() - }) - .await?; + let favourite = with_connection!(ctx.db_pool, |db_conn| { + posts_favourites::table + .find(self.favourite_id) + .get_result::(db_conn) + .await + .optional() + })?; let Some(favourite) = favourite else { return Ok(()); @@ -39,17 +32,14 @@ impl Runnable for DeliverUnfavourite { ctx.deliverer .deliver(Action::Unfavourite(favourite)) - .await - .map_err(|err| miette::Report::new_boxed(err.into()))?; - - ctx.db_pool - .with_connection(|db_conn| { - diesel::delete(posts_favourites::table.find(self.favourite_id)) - .execute(db_conn) - .scoped() - }) .await?; + with_connection!(ctx.db_pool, |db_conn| { + diesel::delete(posts_favourites::table.find(self.favourite_id)) + .execute(db_conn) + .await + })?; + Ok(()) } } diff --git a/crates/kitsune-jobs/src/deliver/unfollow.rs b/crates/kitsune-jobs/src/deliver/unfollow.rs index 26abbea60..cfa1db94a 100644 --- a/crates/kitsune-jobs/src/deliver/unfollow.rs +++ b/crates/kitsune-jobs/src/deliver/unfollow.rs @@ -3,8 +3,7 @@ use athena::Runnable; use diesel::{OptionalExtension, QueryDsl}; use diesel_async::RunQueryDsl; use kitsune_core::traits::deliverer::Action; -use kitsune_db::{model::follower::Follow, schema::accounts_follows}; -use scoped_futures::ScopedFutureExt; +use kitsune_db::{model::follower::Follow, schema::accounts_follows, with_connection}; use serde::{Deserialize, Serialize}; use speedy_uuid::Uuid; @@ -15,39 +14,28 @@ pub struct DeliverUnfollow { impl Runnable for DeliverUnfollow { type Context = JobRunnerContext; - type Error = miette::Report; + type Error = eyre::Report; async fn run(&self, ctx: &Self::Context) -> Result<(), Self::Error> { - let follow = ctx - .db_pool - .with_connection(|db_conn| { - async move { - accounts_follows::table - .find(self.follow_id) - .get_result::(db_conn) - .await - .optional() - } - .scoped() - }) - .await?; + let follow = with_connection!(ctx.db_pool, |db_conn| { + accounts_follows::table + .find(self.follow_id) + .get_result::(db_conn) + .await + .optional() + })?; let Some(follow) = follow else { return Ok(()); }; - ctx.deliverer - .deliver(Action::Unfollow(follow)) - .await - .map_err(|err| miette::Report::new_boxed(err.into()))?; + ctx.deliverer.deliver(Action::Unfollow(follow)).await?; - ctx.db_pool - .with_connection(|db_conn| { - diesel::delete(accounts_follows::table.find(self.follow_id)) - .execute(db_conn) - .scoped() - }) - .await?; + with_connection!(ctx.db_pool, |db_conn| { + diesel::delete(accounts_follows::table.find(self.follow_id)) + .execute(db_conn) + .await + })?; Ok(()) } diff --git a/crates/kitsune-jobs/src/deliver/update.rs b/crates/kitsune-jobs/src/deliver/update.rs index b6dddca7f..ae605b86b 100644 --- a/crates/kitsune-jobs/src/deliver/update.rs +++ b/crates/kitsune-jobs/src/deliver/update.rs @@ -6,8 +6,8 @@ use kitsune_core::traits::deliverer::Action; use kitsune_db::{ model::{account::Account, post::Post}, schema::{accounts, posts}, + with_connection, }; -use scoped_futures::ScopedFutureExt; use serde::{Deserialize, Serialize}; use speedy_uuid::Uuid; @@ -25,25 +25,19 @@ pub struct DeliverUpdate { impl Runnable for DeliverUpdate { type Context = JobRunnerContext; - type Error = miette::Report; + type Error = eyre::Report; async fn run(&self, ctx: &Self::Context) -> Result<(), Self::Error> { let action = match self.entity { UpdateEntity::Account => { - let account = ctx - .db_pool - .with_connection(|db_conn| { - async move { - accounts::table - .find(self.id) - .select(Account::as_select()) - .get_result(db_conn) - .await - .optional() - } - .scoped() - }) - .await?; + let account = with_connection!(ctx.db_pool, |db_conn| { + accounts::table + .find(self.id) + .select(Account::as_select()) + .get_result(db_conn) + .await + .optional() + })?; let Some(account) = account else { return Ok(()); @@ -52,20 +46,14 @@ impl Runnable for DeliverUpdate { Action::UpdateAccount(account) } UpdateEntity::Status => { - let post = ctx - .db_pool - .with_connection(|db_conn| { - async move { - posts::table - .find(self.id) - .select(Post::as_select()) - .get_result(db_conn) - .await - .optional() - } - .scoped() - }) - .await?; + let post = with_connection!(ctx.db_pool, |db_conn| { + posts::table + .find(self.id) + .select(Post::as_select()) + .get_result(db_conn) + .await + .optional() + })?; let Some(post) = post else { return Ok(()); @@ -75,10 +63,7 @@ impl Runnable for DeliverUpdate { } }; - ctx.deliverer - .deliver(action) - .await - .map_err(|err| miette::Report::new_boxed(err.into()))?; + ctx.deliverer.deliver(action).await?; Ok(()) } diff --git a/crates/kitsune-jobs/src/lib.rs b/crates/kitsune-jobs/src/lib.rs index f9686e304..315fe863e 100644 --- a/crates/kitsune-jobs/src/lib.rs +++ b/crates/kitsune-jobs/src/lib.rs @@ -19,11 +19,9 @@ use kitsune_db::{ json::Json, model::job_context::{JobContext, NewJobContext}, schema::job_context, - PgPool, + with_connection, PgPool, }; use kitsune_email::MailingService; -use miette::IntoDiagnostic; -use scoped_futures::ScopedFutureExt; use serde::{Deserialize, Serialize}; use speedy_uuid::Uuid; use typed_builder::TypedBuilder; @@ -57,7 +55,7 @@ pub enum Job { impl Runnable for Job { type Context = JobRunnerContext; - type Error = miette::Report; + type Error = eyre::Report; async fn run(&self, ctx: &Self::Context) -> Result<(), Self::Error> { match self { @@ -89,37 +87,32 @@ impl KitsuneContextRepo { impl JobContextRepository for KitsuneContextRepo { type JobContext = Job; - type Error = miette::Report; + type Error = eyre::Report; type Stream = BoxStream<'static, Result<(Uuid, Self::JobContext), Self::Error>>; async fn fetch_context(&self, job_ids: I) -> Result where I: Iterator + Send + 'static, { - let stream = self - .db_pool - .with_connection(|conn| { - job_context::table - .filter(job_context::id.eq_any(job_ids)) - .load_stream::>(conn) - .scoped() - }) - .await?; + let stream = with_connection!(self.db_pool, |conn| { + job_context::table + .filter(job_context::id.eq_any(job_ids)) + .load_stream::>(conn) + .await + })?; Ok(stream .map_ok(|ctx| (ctx.id, ctx.context.0)) - .map(IntoDiagnostic::into_diagnostic) + .map_err(eyre::Report::from) .boxed()) } async fn remove_context(&self, job_id: Uuid) -> Result<(), Self::Error> { - self.db_pool - .with_connection(|conn| { - diesel::delete(job_context::table.find(job_id)) - .execute(conn) - .scoped() - }) - .await?; + with_connection!(self.db_pool, |conn| { + diesel::delete(job_context::table.find(job_id)) + .execute(conn) + .await + })?; Ok(()) } @@ -129,17 +122,15 @@ impl JobContextRepository for KitsuneContextRepo { job_id: Uuid, context: Self::JobContext, ) -> Result<(), Self::Error> { - self.db_pool - .with_connection(|conn| { - diesel::insert_into(job_context::table) - .values(NewJobContext { - id: job_id, - context: Json(context), - }) - .execute(conn) - .scoped() - }) - .await?; + with_connection!(self.db_pool, |conn| { + diesel::insert_into(job_context::table) + .values(NewJobContext { + id: job_id, + context: Json(context), + }) + .execute(conn) + .await + })?; Ok(()) } diff --git a/crates/kitsune-jobs/src/mailing/confirmation.rs b/crates/kitsune-jobs/src/mailing/confirmation.rs index ad0b268a5..62de30776 100644 --- a/crates/kitsune-jobs/src/mailing/confirmation.rs +++ b/crates/kitsune-jobs/src/mailing/confirmation.rs @@ -2,8 +2,7 @@ use crate::JobRunnerContext; use athena::Runnable; use diesel::{QueryDsl, SelectableHelper}; use diesel_async::RunQueryDsl; -use kitsune_db::{model::user::User, schema::users}; -use scoped_futures::ScopedFutureExt; +use kitsune_db::{model::user::User, schema::users, with_connection}; use serde::{Deserialize, Serialize}; use speedy_uuid::Uuid; @@ -14,7 +13,7 @@ pub struct SendConfirmationMail { impl Runnable for SendConfirmationMail { type Context = JobRunnerContext; - type Error = miette::Report; + type Error = eyre::Report; async fn run(&self, ctx: &Self::Context) -> Result<(), Self::Error> { let mailing_service = &ctx.service.mailing; @@ -24,16 +23,13 @@ impl Runnable for SendConfirmationMail { mailing_service.mark_as_confirmed(self.user_id).await?; } - let user = ctx - .db_pool - .with_connection(|db_conn| { - users::table - .find(self.user_id) - .select(User::as_select()) - .get_result(db_conn) - .scoped() - }) - .await?; + let user = with_connection!(ctx.db_pool, |db_conn| { + users::table + .find(self.user_id) + .select(User::as_select()) + .get_result(db_conn) + .await + })?; mailing_service.send_confirmation_email(&user).await?; diff --git a/crates/kitsune-mastodon/Cargo.toml b/crates/kitsune-mastodon/Cargo.toml index 46db665ec..557ca1e05 100644 --- a/crates/kitsune-mastodon/Cargo.toml +++ b/crates/kitsune-mastodon/Cargo.toml @@ -20,13 +20,12 @@ kitsune-type = { path = "../kitsune-type" } kitsune-url = { path = "../kitsune-url" } kitsune-util = { path = "../kitsune-util" } mime = "0.3.17" -scoped-futures = "0.1.3" serde = "1.0.197" simd-json = "0.13.9" smol_str = "0.2.1" speedy-uuid = { path = "../../lib/speedy-uuid" } thiserror = "1.0.58" -tokio = { version = "1.36.0", features = ["rt"] } +tokio = { version = "1.37.0", features = ["rt"] } tracing = "0.1.40" typed-builder = "0.18.1" diff --git a/crates/kitsune-mastodon/src/error.rs b/crates/kitsune-mastodon/src/error.rs index 8aa8daee0..a82188284 100644 --- a/crates/kitsune-mastodon/src/error.rs +++ b/crates/kitsune-mastodon/src/error.rs @@ -1,6 +1,5 @@ -use std::fmt::{Debug, Display}; - use diesel_async::pooled_connection::bb8; +use std::fmt::Debug; pub type Result = std::result::Result; @@ -21,15 +20,3 @@ pub enum Error { #[error(transparent)] Service(#[from] kitsune_service::error::Error), } - -impl From> for Error -where - E: Into + Debug + Display, -{ - fn from(value: kitsune_db::PoolError) -> Self { - match value { - kitsune_db::PoolError::Pool(err) => err.into(), - kitsune_db::PoolError::User(err) => err.into(), - } - } -} diff --git a/crates/kitsune-mastodon/src/sealed.rs b/crates/kitsune-mastodon/src/sealed.rs index eb1f5e80b..f70c7b759 100644 --- a/crates/kitsune-mastodon/src/sealed.rs +++ b/crates/kitsune-mastodon/src/sealed.rs @@ -24,7 +24,7 @@ use kitsune_db::{ accounts, accounts_follows, custom_emojis, media_attachments, notifications, posts, posts_favourites, }, - PgPool, + with_connection, PgPool, }; use kitsune_embed::Client as EmbedClient; use kitsune_embed::{embed_sdk::EmbedType, Embed}; @@ -40,7 +40,6 @@ use kitsune_type::mastodon::{ use kitsune_url::UrlService; use kitsune_util::try_join; use mime::Mime; -use scoped_futures::ScopedFutureExt; use serde::{de::DeserializeOwned, Serialize}; use smol_str::SmolStr; use speedy_uuid::Uuid; @@ -78,30 +77,25 @@ impl IntoMastodon for DbAccount { } async fn into_mastodon(self, state: MapperState<'_>) -> Result { - let (statuses_count, followers_count, following_count) = state - .db_pool - .with_connection(|db_conn| { - async { - let statuses_count_fut = posts::table - .filter(posts::account_id.eq(self.id)) - .count() - .get_result::(db_conn); - - let followers_count_fut = accounts_follows::table - .filter(accounts_follows::account_id.eq(self.id)) - .count() - .get_result::(db_conn); - - let following_count_fut = accounts_follows::table - .filter(accounts_follows::follower_id.eq(self.id)) - .count() - .get_result::(db_conn); - - try_join!(statuses_count_fut, followers_count_fut, following_count_fut) - } - .scoped() - }) - .await?; + let (statuses_count, followers_count, following_count) = + with_connection!(state.db_pool, |db_conn| { + let statuses_count_fut = posts::table + .filter(posts::account_id.eq(self.id)) + .count() + .get_result::(db_conn); + + let followers_count_fut = accounts_follows::table + .filter(accounts_follows::account_id.eq(self.id)) + .count() + .get_result::(db_conn); + + let following_count_fut = accounts_follows::table + .filter(accounts_follows::follower_id.eq(self.id)) + .count() + .get_result::(db_conn); + + try_join!(statuses_count_fut, followers_count_fut, following_count_fut) + })?; let mut acct = self.username.clone(); if !self.local { @@ -163,39 +157,34 @@ impl IntoMastodon for (&DbAccount, &DbAccount) { async fn into_mastodon(self, state: MapperState<'_>) -> Result { let (requester, target) = self; - let ((following, follow_requested), followed_by) = state - .db_pool - .with_connection(|db_conn| { - async move { - let following_requested_fut = accounts_follows::table - .filter( - accounts_follows::account_id - .eq(target.id) - .and(accounts_follows::follower_id.eq(requester.id)), - ) - .get_result::(db_conn) - .map(OptionalExtension::optional) - .map_ok(|optional_follow| { - optional_follow.map_or((false, false), |follow| { - (follow.approved_at.is_some(), follow.approved_at.is_none()) - }) - }); - - let followed_by_fut = accounts_follows::table - .filter( - accounts_follows::account_id - .eq(requester.id) - .and(accounts_follows::follower_id.eq(target.id)), - ) - .count() - .get_result::(db_conn) - .map_ok(|count| count != 0); - - try_join!(following_requested_fut, followed_by_fut) - } - .scoped() - }) - .await?; + let ((following, follow_requested), followed_by) = + with_connection!(state.db_pool, |db_conn| { + let following_requested_fut = accounts_follows::table + .filter( + accounts_follows::account_id + .eq(target.id) + .and(accounts_follows::follower_id.eq(requester.id)), + ) + .get_result::(db_conn) + .map(OptionalExtension::optional) + .map_ok(|optional_follow| { + optional_follow.map_or((false, false), |follow| { + (follow.approved_at.is_some(), follow.approved_at.is_none()) + }) + }); + + let followed_by_fut = accounts_follows::table + .filter( + accounts_follows::account_id + .eq(requester.id) + .and(accounts_follows::follower_id.eq(target.id)), + ) + .count() + .get_result::(db_conn) + .map_ok(|count| count != 0); + + try_join!(following_requested_fut, followed_by_fut) + })?; Ok(Relationship { id: target.id, @@ -223,16 +212,13 @@ impl IntoMastodon for DbMention { } async fn into_mastodon(self, state: MapperState<'_>) -> Result { - let account: DbAccount = state - .db_pool - .with_connection(|db_conn| { - accounts::table - .find(self.account_id) - .select(DbAccount::as_select()) - .get_result(db_conn) - .scoped() - }) - .await?; + let account: DbAccount = with_connection!(state.db_pool, |db_conn| { + accounts::table + .find(self.account_id) + .select(DbAccount::as_select()) + .get_result(db_conn) + .await + })?; let mut acct = account.username.clone(); if !account.local { @@ -289,29 +275,23 @@ impl IntoMastodon for (&DbAccount, DbPost) { async fn into_mastodon(self, state: MapperState<'_>) -> Result { let (account, post) = self; - let (favourited, reblogged) = state - .db_pool - .with_connection(|db_conn| { - async move { - let favourited_fut = posts_favourites::table - .filter(posts_favourites::account_id.eq(account.id)) - .filter(posts_favourites::post_id.eq(post.id)) - .count() - .get_result::(db_conn) - .map_ok(|count| count != 0); - - let reblogged_fut = posts::table - .filter(posts::account_id.eq(account.id)) - .filter(posts::reposted_post_id.eq(post.id)) - .count() - .get_result::(db_conn) - .map_ok(|count| count != 0); - - try_join!(favourited_fut, reblogged_fut) - } - .scoped() - }) - .await?; + let (favourited, reblogged) = with_connection!(state.db_pool, |db_conn| { + let favourited_fut = posts_favourites::table + .filter(posts_favourites::account_id.eq(account.id)) + .filter(posts_favourites::post_id.eq(post.id)) + .count() + .get_result::(db_conn) + .map_ok(|count| count != 0); + + let reblogged_fut = posts::table + .filter(posts::account_id.eq(account.id)) + .filter(posts::reposted_post_id.eq(post.id)) + .count() + .get_result::(db_conn) + .map_ok(|count| count != 0); + + try_join!(favourited_fut, reblogged_fut) + })?; let mut status = post.into_mastodon(state).await?; status.favourited = favourited; @@ -341,62 +321,56 @@ impl IntoMastodon for DbPost { media_attachments, mentions_stream, custom_emojis_stream, - ) = state - .db_pool - .with_connection(|db_conn| { - async { - let account_fut = accounts::table - .find(self.account_id) - .select(DbAccount::as_select()) - .get_result::(db_conn) - .map_err(Error::from) - .and_then(|db_account| db_account.into_mastodon(state)); - - let reblog_count_fut = posts::table - .filter(posts::reposted_post_id.eq(self.id)) - .count() - .get_result::(db_conn) - .map_err(Error::from); - - let favourites_count_fut = DbFavourite::belonging_to(&self) - .count() - .get_result::(db_conn) - .map_err(Error::from); - - let media_attachments_fut = DbPostMediaAttachment::belonging_to(&self) - .inner_join(media_attachments::table) - .select(DbMediaAttachment::as_select()) - .load_stream::(db_conn) + ) = with_connection!(state.db_pool, |db_conn| { + let account_fut = accounts::table + .find(self.account_id) + .select(DbAccount::as_select()) + .get_result::(db_conn) + .map_err(Error::from) + .and_then(|db_account| db_account.into_mastodon(state)); + + let reblog_count_fut = posts::table + .filter(posts::reposted_post_id.eq(self.id)) + .count() + .get_result::(db_conn) + .map_err(Error::from); + + let favourites_count_fut = DbFavourite::belonging_to(&self) + .count() + .get_result::(db_conn) + .map_err(Error::from); + + let media_attachments_fut = DbPostMediaAttachment::belonging_to(&self) + .inner_join(media_attachments::table) + .select(DbMediaAttachment::as_select()) + .load_stream::(db_conn) + .map_err(Error::from) + .and_then(|attachment_stream| { + attachment_stream .map_err(Error::from) - .and_then(|attachment_stream| { - attachment_stream - .map_err(Error::from) - .and_then(|attachment| attachment.into_mastodon(state)) - .try_collect() - }); - - let mentions_stream_fut = DbMention::belonging_to(&self) - .load_stream::(db_conn) - .map_err(Error::from); - - let custom_emojis_stream_fut = DbPostCustomEmoji::belonging_to(&self) - .inner_join(custom_emojis::table.inner_join(media_attachments::table)) - .select((DbCustomEmoji::as_select(), DbMediaAttachment::as_select())) - .load_stream::<(DbCustomEmoji, DbMediaAttachment)>(db_conn) - .map_err(Error::from); - - try_join!( - account_fut, - reblog_count_fut, - favourites_count_fut, - media_attachments_fut, - mentions_stream_fut, - custom_emojis_stream_fut - ) - } - .scoped() - }) - .await?; + .and_then(|attachment| attachment.into_mastodon(state)) + .try_collect() + }); + + let mentions_stream_fut = DbMention::belonging_to(&self) + .load_stream::(db_conn) + .map_err(Error::from); + + let custom_emojis_stream_fut = DbPostCustomEmoji::belonging_to(&self) + .inner_join(custom_emojis::table.inner_join(media_attachments::table)) + .select((DbCustomEmoji::as_select(), DbMediaAttachment::as_select())) + .load_stream::<(DbCustomEmoji, DbMediaAttachment)>(db_conn) + .map_err(Error::from); + + try_join!( + account_fut, + reblog_count_fut, + favourites_count_fut, + media_attachments_fut, + mentions_stream_fut, + custom_emojis_stream_fut + ) + })?; let link_preview = OptionFuture::from( self.link_preview_url @@ -423,30 +397,24 @@ impl IntoMastodon for DbPost { .try_collect() .await?; - let reblog = state - .db_pool - .with_connection(|db_conn| { - async { - OptionFuture::from( - OptionFuture::from(self.reposted_post_id.map(|id| { - posts::table - .find(id) - .select(DbPost::as_select()) - .get_result::(db_conn) - .map(OptionalExtension::optional) - })) - .await - .transpose()? - .flatten() - .map(|post| post.into_mastodon(state)), // This will allocate two database connections. Fuck. - ) - .await - .transpose() - } - .scoped() - }) - .await? - .map(Box::new); + let reblog = with_connection!(state.db_pool, |db_conn| { + OptionFuture::from( + OptionFuture::from(self.reposted_post_id.map(|id| { + posts::table + .find(id) + .select(DbPost::as_select()) + .get_result::(db_conn) + .map(OptionalExtension::optional) + })) + .await + .transpose()? + .flatten() + .map(|post| post.into_mastodon(state)), // This will allocate two database connections. Fuck. + ) + .await + .transpose() + })? + .map(Box::new); let language = self.content_lang.to_639_1().map(str::to_string); @@ -581,9 +549,8 @@ impl IntoMastodon for DbNotification { } async fn into_mastodon(self, state: MapperState<'_>) -> Result { - let (notification, account, status): (DbNotification, DbAccount, Option) = state - .db_pool - .with_connection(|mut db_conn| { + let (notification, account, status): (DbNotification, DbAccount, Option) = + with_connection!(state.db_pool, |db_conn| { notifications::table .filter(notifications::receiving_account_id.eq(self.receiving_account_id)) .inner_join( @@ -592,10 +559,9 @@ impl IntoMastodon for DbNotification { ) .left_outer_join(posts::table) .select(<(DbNotification, DbAccount, Option)>::as_select()) - .get_result(&mut db_conn) - .scoped() - }) - .await?; + .get_result(db_conn) + .await + })?; let status = OptionFuture::from(status.map(|status| status.into_mastodon(state))) .await diff --git a/crates/kitsune-messaging/Cargo.toml b/crates/kitsune-messaging/Cargo.toml index 03e3f08df..86cd059d1 100644 --- a/crates/kitsune-messaging/Cargo.toml +++ b/crates/kitsune-messaging/Cargo.toml @@ -10,11 +10,11 @@ ahash = "0.8.11" derive_more = { version = "1.0.0-beta.6", features = ["from"] } futures-util = "0.3.30" just-retry = { path = "../../lib/just-retry" } -pin-project-lite = "0.2.13" +pin-project-lite = "0.2.14" redis = { version = "0.25.2", features = ["connection-manager", "tokio-comp"] } serde = "1.0.197" simd-json = "0.13.9" -tokio = { version = "1.36.0", features = ["macros", "rt", "sync"] } +tokio = { version = "1.37.0", features = ["macros", "rt", "sync"] } tokio-stream = { version = "0.1.15", features = ["sync"] } tracing = "0.1.40" diff --git a/crates/kitsune-observability/Cargo.toml b/crates/kitsune-observability/Cargo.toml index 245cc4d3e..d8905d3c2 100644 --- a/crates/kitsune-observability/Cargo.toml +++ b/crates/kitsune-observability/Cargo.toml @@ -7,6 +7,7 @@ license.workspace = true [dependencies] async-trait = "0.1.79" +eyre = "0.6.12" http-body-util = "0.1.1" http-compat = { path = "../../lib/http-compat" } hyper = { version = "1.2.0", default-features = false } @@ -16,11 +17,10 @@ metrics = "=0.22.0" metrics-opentelemetry = { git = "https://github.com/aumetra/metrics-opentelemetry.git", rev = "95537b16370e595981e195be52f98ea5983a7a8e" } metrics-tracing-context = "0.15.0" metrics-util = "0.16.3" -miette = "7.2.0" opentelemetry = { version = "0.22.0", default-features = false, features = [ "trace", ] } -opentelemetry-http = "0.11.0" +opentelemetry-http = "0.11.1" opentelemetry-otlp = { version = "0.15.0", default-features = false, features = [ "grpc-tonic", "http-proto", diff --git a/crates/kitsune-observability/src/lib.rs b/crates/kitsune-observability/src/lib.rs index e4b28d55d..7f9d827a7 100644 --- a/crates/kitsune-observability/src/lib.rs +++ b/crates/kitsune-observability/src/lib.rs @@ -1,11 +1,11 @@ use async_trait::async_trait; +use eyre::WrapErr; use http_body_util::BodyExt; use http_compat::Compat; use kitsune_config::{open_telemetry::Transport, Configuration}; use metrics_opentelemetry::OpenTelemetryRecorder; use metrics_tracing_context::{MetricsLayer, TracingContextLayer}; use metrics_util::layers::Layer as _; -use miette::{Context, IntoDiagnostic}; use opentelemetry::{ metrics::{noop::NoopMeterProvider, Meter, MeterProvider}, trace::{noop::NoopTracer, Tracer}, @@ -67,18 +67,13 @@ macro_rules! build_exporter { }}; } -fn initialise_logging(tracer: T) -> miette::Result<()> +fn initialise_logging(tracer: T) -> eyre::Result<()> where T: Tracer + PreSampledTracer + Send + Sync + 'static, { let env_filter = env::var("RUST_LOG") - .into_diagnostic() - .and_then(|targets| { - targets - .parse() - .into_diagnostic() - .wrap_err("Failed to parse RUST_LOG value") - }) + .map_err(eyre::Report::from) + .and_then(|targets| targets.parse().wrap_err("Failed to parse RUST_LOG value")) .unwrap_or_else(|_| Targets::default().with_default(LevelFilter::INFO)); let subscriber = Registry::default() @@ -89,22 +84,20 @@ where let subscriber = subscriber.with(MetricsLayer::new()); tracing::subscriber::set_global_default(subscriber) - .into_diagnostic() .wrap_err("Couldn't install the global tracing subscriber")?; Ok(()) } -fn initialise_metrics(meter: Meter) -> miette::Result<()> { +fn initialise_metrics(meter: Meter) -> eyre::Result<()> { let recorder = TracingContextLayer::all().layer(OpenTelemetryRecorder::new(meter)); metrics::set_global_recorder(recorder) - .into_diagnostic() .wrap_err("Couldn't install the global metrics recorder")?; Ok(()) } -pub fn initialise(app_name: &'static str, config: &Configuration) -> miette::Result<()> { +pub fn initialise(app_name: &'static str, config: &Configuration) -> eyre::Result<()> { if let Some(ref opentelemetry_config) = config.opentelemetry { let http_client = HttpClientAdapter { inner: kitsune_http_client::Client::default(), @@ -120,8 +113,7 @@ pub fn initialise(app_name: &'static str, config: &Configuration) -> miette::Res let tracer = opentelemetry_otlp::new_pipeline() .tracing() .with_exporter(trace_exporter) - .install_batch(Tokio) - .into_diagnostic()?; + .install_batch(Tokio)?; initialise_logging(tracer)?; @@ -135,8 +127,7 @@ pub fn initialise(app_name: &'static str, config: &Configuration) -> miette::Res let meter_provider = opentelemetry_otlp::new_pipeline() .metrics(Tokio) .with_exporter(metrics_exporter) - .build() - .into_diagnostic()?; + .build()?; initialise_metrics(meter_provider.meter(app_name))?; } else { diff --git a/crates/kitsune-oidc/Cargo.toml b/crates/kitsune-oidc/Cargo.toml index 18de256c1..f3c6c1416 100644 --- a/crates/kitsune-oidc/Cargo.toml +++ b/crates/kitsune-oidc/Cargo.toml @@ -6,12 +6,11 @@ version.workspace = true license.workspace = true [dependencies] -enum_dispatch = "0.3.12" +enum_dispatch = "0.3.13" http = "1.1.0" http-compat = { path = "../../lib/http-compat" } kitsune-config = { path = "../kitsune-config" } kitsune-http-client = { path = "../kitsune-http-client" } -miette = "7.2.0" moka = { version = "0.12.5", features = ["future"] } multiplex-pool = { path = "../../lib/multiplex-pool" } once_cell = "1.19.0" diff --git a/crates/kitsune-oidc/src/error.rs b/crates/kitsune-oidc/src/error.rs index aac546055..de10ab221 100644 --- a/crates/kitsune-oidc/src/error.rs +++ b/crates/kitsune-oidc/src/error.rs @@ -1,4 +1,3 @@ -use miette::Diagnostic; use openidconnect::{ core::CoreErrorResponseType, ClaimsVerificationError, DiscoveryError, RequestTokenError, SigningError, StandardErrorResponse, @@ -7,7 +6,7 @@ use thiserror::Error; pub type Result = std::result::Result; -#[derive(Debug, Diagnostic, Error)] +#[derive(Debug, Error)] pub enum Error { #[error(transparent)] ClaimsVerification(#[from] ClaimsVerificationError), diff --git a/crates/kitsune-s3/Cargo.toml b/crates/kitsune-s3/Cargo.toml index bc5269065..b861c5940 100644 --- a/crates/kitsune-s3/Cargo.toml +++ b/crates/kitsune-s3/Cargo.toml @@ -17,7 +17,7 @@ typed-builder = "0.18.1" [dev-dependencies] kitsune-test = { path = "../kitsune-test" } -tokio = { version = "1.36.0", features = ["macros", "rt"] } +tokio = { version = "1.37.0", features = ["macros", "rt"] } [lints] workspace = true diff --git a/crates/kitsune-s3/src/lib.rs b/crates/kitsune-s3/src/lib.rs index 71c5f0866..f98325f48 100644 --- a/crates/kitsune-s3/src/lib.rs +++ b/crates/kitsune-s3/src/lib.rs @@ -33,9 +33,9 @@ const fn s3_method_to_http(method: rusty_s3::Method) -> http::Method { } #[inline] -const fn http_method_by_value<'a, T: ?Sized>(_: &T) -> http::Method +const fn http_method_by_value<'a, T>(_: &T) -> http::Method where - T: S3Action<'a>, + T: S3Action<'a> + ?Sized, { s3_method_to_http(T::METHOD) } diff --git a/crates/kitsune-search/Cargo.toml b/crates/kitsune-search/Cargo.toml index 4a77fc44e..427b45a4f 100644 --- a/crates/kitsune-search/Cargo.toml +++ b/crates/kitsune-search/Cargo.toml @@ -12,12 +12,11 @@ ignored = ["isahc"] # To make `meilisearch` builds static diesel = "2.1.5" diesel-async = "0.4.1" diesel_full_text_search = { version = "2.1.1", default-features = false } -enum_dispatch = "0.3.12" +enum_dispatch = "0.3.13" futures-util = "0.3.30" kitsune-config = { path = "../kitsune-config" } kitsune-db = { path = "../kitsune-db" } kitsune-language = { path = "../kitsune-language" } -miette = "7.2.0" serde = { version = "1.0.197", features = ["derive"] } speedy-uuid = { path = "../../lib/speedy-uuid" } strum = { version = "0.26.2", features = ["derive"] } diff --git a/crates/kitsune-search/src/error.rs b/crates/kitsune-search/src/error.rs index 3e7f24866..dc94a6652 100644 --- a/crates/kitsune-search/src/error.rs +++ b/crates/kitsune-search/src/error.rs @@ -1,9 +1,8 @@ use diesel_async::pooled_connection::bb8; -use miette::Diagnostic; -use std::fmt::{Debug, Display}; +use std::fmt::Debug; use thiserror::Error; -#[derive(Debug, Diagnostic, Error)] +#[derive(Debug, Error)] pub enum Error { #[error(transparent)] Database(#[from] diesel::result::Error), @@ -15,15 +14,3 @@ pub enum Error { #[error(transparent)] Meilisearch(#[from] meilisearch_sdk::errors::Error), } - -impl From> for Error -where - E: Into + Debug + Display, -{ - fn from(value: kitsune_db::PoolError) -> Self { - match value { - kitsune_db::PoolError::Pool(err) => err.into(), - kitsune_db::PoolError::User(err) => err.into(), - } - } -} diff --git a/crates/kitsune-search/src/sql.rs b/crates/kitsune-search/src/sql.rs index a6048f50e..a2f76485a 100644 --- a/crates/kitsune-search/src/sql.rs +++ b/crates/kitsune-search/src/sql.rs @@ -1,6 +1,6 @@ use super::{Result, SearchBackend, SearchIndex, SearchItem, SearchResultReference}; use diesel::{ExpressionMethods, QueryDsl}; -use diesel_async::{scoped_futures::ScopedFutureExt, RunQueryDsl}; +use diesel_async::RunQueryDsl; use diesel_full_text_search::{websearch_to_tsquery_with_search_config, TsVectorExtensions}; use futures_util::TryStreamExt; use kitsune_config::language_detection::Configuration as LanguageDetectionConfig; @@ -9,7 +9,7 @@ use kitsune_db::{ lang::LanguageIsoCode, model::post::Visibility, schema::{accounts, posts}, - PgPool, + with_connection, PgPool, }; use speedy_uuid::Uuid; use typed_builder::TypedBuilder; @@ -62,23 +62,17 @@ impl SearchBackend for SearchService { query = query.filter(accounts::id.lt(max_id)); } - let results = self - .db_pool - .with_connection(move |db_conn| { - async move { - query - .limit(max_results as i64) - .offset(offset as i64) - .select(accounts::id) - .load_stream(db_conn) - .await? - .map_ok(|id| SearchResultReference { index, id }) - .try_collect() - .await - } - .scoped() - }) - .await?; + let results = with_connection!(self.db_pool, |db_conn| { + query + .limit(max_results as i64) + .offset(offset as i64) + .select(accounts::id) + .load_stream(db_conn) + .await? + .map_ok(|id| SearchResultReference { index, id }) + .try_collect() + .await + })?; Ok(results) } @@ -94,27 +88,20 @@ impl SearchBackend for SearchService { query = query.filter(posts::id.lt(max_id)); } - let results = self - .db_pool - .with_connection(|db_conn| { - async move { - query - .filter( - posts::visibility - .eq_any([Visibility::Public, Visibility::Unlisted]), - ) - .limit(max_results as i64) - .offset(offset as i64) - .select(posts::id) - .load_stream(db_conn) - .await? - .map_ok(|id| SearchResultReference { index, id }) - .try_collect() - .await - } - .scoped() - }) - .await?; + let results = with_connection!(self.db_pool, |db_conn| { + query + .filter( + posts::visibility.eq_any([Visibility::Public, Visibility::Unlisted]), + ) + .limit(max_results as i64) + .offset(offset as i64) + .select(posts::id) + .load_stream(db_conn) + .await? + .map_ok(|id| SearchResultReference { index, id }) + .try_collect() + .await + })?; Ok(results) } diff --git a/crates/kitsune-service/Cargo.toml b/crates/kitsune-service/Cargo.toml index 31e1b48a6..c119fddbb 100644 --- a/crates/kitsune-service/Cargo.toml +++ b/crates/kitsune-service/Cargo.toml @@ -15,6 +15,7 @@ bytes = "1.6.0" derive_builder = "0.20.0" diesel = "2.1.5" diesel-async = "0.4.1" +eyre = "0.6.12" futures-util = "0.3.30" garde = { version = "0.18.0", default-features = false, features = [ "derive", @@ -41,7 +42,6 @@ kitsune-search = { path = "../kitsune-search" } kitsune-storage = { path = "../kitsune-storage" } kitsune-url = { path = "../kitsune-url" } kitsune-util = { path = "../kitsune-util" } -miette = "7.2.0" mime = "0.3.17" multiplex-pool = { path = "../../lib/multiplex-pool" } password-hash = { version = "0.5.0", features = ["std"] } @@ -54,13 +54,12 @@ redis = { version = "0.25.2", default-features = false, features = [ ] } rsa = "0.9.6" rusty-s3 = { version = "0.5.0", default-features = false } -scoped-futures = "0.1.3" serde = "1.0.197" simd-json = "0.13.9" smol_str = "0.2.1" speedy-uuid = { path = "../../lib/speedy-uuid" } thiserror = "1.0.58" -tokio = { version = "1.36.0", features = ["macros", "sync"] } +tokio = { version = "1.37.0", features = ["macros", "sync"] } tracing = "0.1.40" typed-builder = "0.18.1" url = "2.5.0" diff --git a/crates/kitsune-service/src/account.rs b/crates/kitsune-service/src/account.rs index fe9b779c6..3bbac8f1d 100644 --- a/crates/kitsune-service/src/account.rs +++ b/crates/kitsune-service/src/account.rs @@ -7,8 +7,8 @@ use crate::error::{Error, Result}; use bytes::Bytes; use derive_builder::Builder; use diesel::{ - BoolExpressionMethods, ExpressionMethods, JoinOnDsl, OptionalExtension, QueryDsl, - SelectableHelper, + BoolExpressionMethods, ExpressionMethods, JoinOnDsl, NullableExpressionMethods, + OptionalExtension, QueryDsl, SelectableHelper, }; use diesel_async::RunQueryDsl; use futures_util::{Stream, TryStreamExt}; @@ -16,17 +16,17 @@ use garde::Validate; use iso8601_timestamp::Timestamp; use kitsune_core::traits::{fetcher::AccountFetchOptions, Fetcher, Resolver}; use kitsune_db::{ + function::now, model::{ account::{Account, UpdateAccount}, - follower::Follow as DbFollow, - follower::NewFollow, + follower::{Follow as DbFollow, NewFollow}, notification::NewNotification, post::Post, preference::Preferences, }, post_permission_check::{PermissionCheck, PostPermissionCheckExt}, schema::{accounts, accounts_follows, accounts_preferences, notifications, posts}, - PgPool, + with_connection, PgPool, }; use kitsune_jobs::deliver::{ accept::DeliverAccept, @@ -37,7 +37,6 @@ use kitsune_jobs::deliver::{ }; use kitsune_url::UrlService; use kitsune_util::{sanitize::CleanHtmlExt, try_join}; -use scoped_futures::ScopedFutureExt; use speedy_uuid::Uuid; use std::sync::Arc; use typed_builder::TypedBuilder; @@ -196,25 +195,19 @@ impl AccountService { /// /// Tuple of two account models. First model is the account the followee account, the second model is the followed account pub async fn follow(&self, follow: Follow, notify: bool) -> Result<(Account, Account)> { - let (account, follower) = self - .db_pool - .with_connection(|db_conn| { - async move { - let account_fut = accounts::table - .find(follow.account_id) - .select(Account::as_select()) - .get_result(db_conn); - - let follower_fut = accounts::table - .find(follow.follower_id) - .select(Account::as_select()) - .get_result(db_conn); - - try_join!(account_fut, follower_fut) - } - .scoped() - }) - .await?; + let (account, follower) = with_connection!(self.db_pool, |db_conn| { + let account_fut = accounts::table + .find(follow.account_id) + .select(Account::as_select()) + .get_result(db_conn); + + let follower_fut = accounts::table + .find(follow.follower_id) + .select(Account::as_select()) + .get_result(db_conn); + + try_join!(account_fut, follower_fut) + })?; let id = Uuid::now_v7(); let url = self.url_service.follow_url(id); @@ -232,28 +225,22 @@ impl AccountService { follow_model.approved_at = Some(Timestamp::now_utc()); } - let follow_id = self - .db_pool - .with_connection(|db_conn| { - diesel::insert_into(accounts_follows::table) - .values(follow_model) - .returning(accounts_follows::id) - .get_result(db_conn) - .scoped() - }) - .await?; + let follow_id = with_connection!(self.db_pool, |db_conn| { + diesel::insert_into(accounts_follows::table) + .values(follow_model) + .returning(accounts_follows::id) + .get_result(db_conn) + .await + })?; if account.local { - let preferences = self - .db_pool - .with_connection(|db_conn| { - accounts_preferences::table - .find(follow.account_id) - .select(Preferences::as_select()) - .get_result(db_conn) - .scoped() - }) - .await?; + let preferences = with_connection!(self.db_pool, |db_conn| { + accounts_preferences::table + .find(follow.account_id) + .select(Preferences::as_select()) + .get_result(db_conn) + .await + })?; if (preferences.notify_on_follow && !account.locked) || (preferences.notify_on_follow_request && account.locked) @@ -268,15 +255,13 @@ impl AccountService { .follow(follower.id) }; - self.db_pool - .with_connection(|mut db_conn| { - diesel::insert_into(notifications::table) - .values(notification) - .on_conflict_do_nothing() - .execute(&mut db_conn) - .scoped() - }) - .await?; + with_connection!(self.db_pool, |db_conn| { + diesel::insert_into(notifications::table) + .values(notification) + .on_conflict_do_nothing() + .execute(db_conn) + .await + })?; } } @@ -292,24 +277,18 @@ impl AccountService { /// Get an account by its username and domain pub async fn get(&self, get_user: GetUser<'_>) -> Result> { if let Some(domain) = get_user.domain { - let account = self - .db_pool - .with_connection(|db_conn| { - async move { - accounts::table - .filter( - accounts::username - .eq(get_user.username) - .and(accounts::domain.eq(domain)), - ) - .select(Account::as_select()) - .get_result(db_conn) - .await - .optional() - } - .scoped() - }) - .await?; + let account = with_connection!(self.db_pool, |db_conn| { + accounts::table + .filter( + accounts::username + .eq(get_user.username) + .and(accounts::domain.eq(domain)), + ) + .select(Account::as_select()) + .get_result(db_conn) + .await + .optional() + })?; if let Some(account) = account { return Ok(Some(account)); @@ -336,43 +315,33 @@ impl AccountService { .await .map_err(Error::Fetcher) } else { - self.db_pool - .with_connection(|db_conn| { - async move { - accounts::table - .filter( - accounts::username - .eq(get_user.username) - .and(accounts::local.eq(true)), - ) - .select(Account::as_select()) - .first(db_conn) - .await - .optional() - } - .scoped() - }) - .await - .map_err(Error::from) + with_connection!(self.db_pool, |db_conn| { + accounts::table + .filter( + accounts::username + .eq(get_user.username) + .and(accounts::local.eq(true)), + ) + .select(Account::as_select()) + .first(db_conn) + .await + .optional() + }) + .map_err(Error::from) } } /// Get an account by its ID pub async fn get_by_id(&self, account_id: Uuid) -> Result> { - self.db_pool - .with_connection(|db_conn| { - async move { - accounts::table - .find(account_id) - .select(Account::as_select()) - .get_result(db_conn) - .await - .optional() - } - .scoped() - }) - .await - .map_err(Error::from) + with_connection!(self.db_pool, |db_conn| { + accounts::table + .find(account_id) + .select(Account::as_select()) + .get_result(db_conn) + .await + .optional() + }) + .map_err(Error::from) } /// Get a stream of posts owned by the user @@ -408,14 +377,9 @@ impl AccountService { query = query.filter(posts::id.gt(min_id)).order(posts::id.asc()); } - self.db_pool - .with_connection(|db_conn| { - async move { - Ok::<_, Error>(query.load_stream(db_conn).await?.map_err(Error::from)) - }.scoped() - }) - .await - .map_err(Error::from) + with_connection!(self.db_pool, |db_conn| { + Ok::<_, Error>(query.load_stream(db_conn).await?.map_err(Error::from)) + }) } /// Undo the follow of an account @@ -424,43 +388,31 @@ impl AccountService { /// /// Tuple of two account models. First account is the account that was being followed, second account is the account that was following pub async fn unfollow(&self, unfollow: Unfollow) -> Result<(Account, Account)> { - let (account, follower) = self - .db_pool - .with_connection(|db_conn| { - async move { - let account_fut = accounts::table - .find(unfollow.account_id) - .select(Account::as_select()) - .get_result(db_conn); - - let follower_fut = accounts::table - .find(unfollow.follower_id) - .select(Account::as_select()) - .get_result(db_conn); - - try_join!(account_fut, follower_fut) - } - .scoped() - }) - .await?; - - let follow = self - .db_pool - .with_connection(|db_conn| { - async move { - accounts_follows::table - .filter( - accounts_follows::account_id - .eq(account.id) - .and(accounts_follows::follower_id.eq(follower.id)), - ) - .get_result::(db_conn) - .await - .optional() - } - .scoped() - }) - .await?; + let (account, follower) = with_connection!(self.db_pool, |db_conn| { + let account_fut = accounts::table + .find(unfollow.account_id) + .select(Account::as_select()) + .get_result(db_conn); + + let follower_fut = accounts::table + .find(unfollow.follower_id) + .select(Account::as_select()) + .get_result(db_conn); + + try_join!(account_fut, follower_fut) + })?; + + let follow = with_connection!(self.db_pool, |db_conn| { + accounts_follows::table + .filter( + accounts_follows::account_id + .eq(account.id) + .and(accounts_follows::follower_id.eq(follower.id)), + ) + .get_result::(db_conn) + .await + .optional() + })?; if let Some(follow) = follow { if !account.local { @@ -505,73 +457,52 @@ impl AccountService { query = query.filter(accounts::id.lt(max_id)); } - self.db_pool - .with_connection(|db_conn| { - async move { - Ok::<_, Error>(query.load_stream(db_conn).await?.map_err(Error::from)) - } - .scoped() - }) - .await - .map_err(Error::from) + with_connection!(self.db_pool, |db_conn| { + Ok::<_, Error>(query.load_stream(db_conn).await?.map_err(Error::from)) + }) + .map_err(Error::from) } pub async fn accept_follow_request( &self, follow_request: FollowRequest, ) -> Result> { - let (account, follower) = self - .db_pool - .with_connection(|db_conn| { - async move { - let account_fut = accounts::table - .find(follow_request.account_id) - .select(Account::as_select()) - .get_result(db_conn); - - let follower_fut = accounts::table - .find(follow_request.follower_id) - .select(Account::as_select()) - .get_result(db_conn); - - try_join!(account_fut, follower_fut) - } - .scoped() - }) - .await?; - - let follow = self - .db_pool - .with_connection(|db_conn| { - async move { - accounts_follows::table - .filter( - accounts_follows::account_id - .eq(account.id) - .and(accounts_follows::follower_id.eq(follower.id)), - ) - .get_result::(db_conn) - .await - .optional() - } - .scoped() - }) - .await?; - - if let Some(follow) = follow { - let now = Timestamp::now_utc(); - - self.db_pool - .with_connection(|db_conn| { - diesel::update(&follow) - .set(( - accounts_follows::approved_at.eq(now), - accounts_follows::updated_at.eq(now), - )) - .execute(db_conn) - .scoped() - }) - .await?; + let (account, follower) = with_connection!(self.db_pool, |db_conn| { + let account_fut = accounts::table + .find(follow_request.account_id) + .select(Account::as_select()) + .get_result(db_conn); + + let follower_fut = accounts::table + .find(follow_request.follower_id) + .select(Account::as_select()) + .get_result(db_conn); + + try_join!(account_fut, follower_fut) + })?; + + let follow = with_connection!(self.db_pool, |db_conn| { + accounts_follows::table + .filter( + accounts_follows::account_id + .eq(account.id) + .and(accounts_follows::follower_id.eq(follower.id)), + ) + .get_result::(db_conn) + .await + .optional() + })?; + + if let Some(ref follow) = follow { + with_connection!(self.db_pool, |db_conn| { + diesel::update(follow) + .set(( + accounts_follows::approved_at.eq(now().nullable()), + accounts_follows::updated_at.eq(now()), + )) + .execute(db_conn) + .await + })?; if !account.local { self.job_service @@ -595,49 +526,37 @@ impl AccountService { &self, follow_request: FollowRequest, ) -> Result> { - let (account, follower) = self - .db_pool - .with_connection(|db_conn| { - async move { - let account_fut = accounts::table - .find(follow_request.account_id) - .select(Account::as_select()) - .get_result(db_conn); - - let follower_fut = accounts::table - .find(follow_request.follower_id) - .select(Account::as_select()) - .get_result(db_conn); - - try_join!(account_fut, follower_fut) - } - .scoped() - }) - .await?; - - let follow = self - .db_pool - .with_connection(|db_conn| { - async move { - accounts_follows::table - .filter( - accounts_follows::account_id - .eq(account.id) - .and(accounts_follows::follower_id.eq(follower.id)), - ) - .get_result::(db_conn) - .await - .optional() - } - .scoped() - }) - .await?; + let (account, follower) = with_connection!(self.db_pool, |db_conn| { + let account_fut = accounts::table + .find(follow_request.account_id) + .select(Account::as_select()) + .get_result(db_conn); + + let follower_fut = accounts::table + .find(follow_request.follower_id) + .select(Account::as_select()) + .get_result(db_conn); + + try_join!(account_fut, follower_fut) + })?; + + let follow = with_connection!(self.db_pool, |db_conn| { + accounts_follows::table + .filter( + accounts_follows::account_id + .eq(account.id) + .and(accounts_follows::follower_id.eq(follower.id)), + ) + .get_result::(db_conn) + .await + .optional() + })?; if let Some(follow) = follow { if account.local { - self.db_pool - .with_connection(|db_conn| diesel::delete(&follow).execute(db_conn).scoped()) - .await?; + with_connection!(self.db_pool, |db_conn| { + diesel::delete(&follow).execute(db_conn).await + })?; } else { self.job_service .enqueue( @@ -704,16 +623,13 @@ impl AccountService { }; } - let updated_account: Account = self - .db_pool - .with_connection(|db_conn| { - diesel::update(accounts::table.find(update.account_id)) - .set(changeset) - .returning(Account::as_returning()) - .get_result(db_conn) - .scoped() - }) - .await?; + let updated_account: Account = with_connection!(self.db_pool, |db_conn| { + diesel::update(accounts::table.find(update.account_id)) + .set(changeset) + .returning(Account::as_returning()) + .get_result(db_conn) + .await + })?; self.job_service .enqueue( diff --git a/crates/kitsune-service/src/attachment.rs b/crates/kitsune-service/src/attachment.rs index ae165740f..400e1d3d9 100644 --- a/crates/kitsune-service/src/attachment.rs +++ b/crates/kitsune-service/src/attachment.rs @@ -10,12 +10,11 @@ use kitsune_core::consts::{MAX_MEDIA_DESCRIPTION_LENGTH, USER_AGENT}; use kitsune_db::{ model::media_attachment::{MediaAttachment, NewMediaAttachment, UpdateMediaAttachment}, schema::media_attachments, - PgPool, + with_connection, PgPool, }; use kitsune_http_client::Client; use kitsune_storage::{AnyStorageBackend, BoxError, StorageBackend}; use kitsune_url::UrlService; -use scoped_futures::ScopedFutureExt; use speedy_uuid::Uuid; use typed_builder::TypedBuilder; @@ -93,15 +92,10 @@ pub struct AttachmentService { impl AttachmentService { pub async fn get_by_id(&self, id: Uuid) -> Result { - self.db_pool - .with_connection(|db_conn| { - media_attachments::table - .find(id) - .get_result(db_conn) - .scoped() - }) - .await - .map_err(Error::from) + with_connection!(self.db_pool, |db_conn| { + media_attachments::table.find(id).get_result(db_conn).await + }) + .map_err(Error::from) } /// Get the URL to an attachment @@ -161,21 +155,19 @@ impl AttachmentService { }; } - self.db_pool - .with_connection(|db_conn| { - diesel::update( - media_attachments::table.filter( - media_attachments::id - .eq(update.attachment_id) - .and(media_attachments::account_id.eq(update.account_id)), - ), - ) - .set(changeset) - .get_result(db_conn) - .scoped() - }) + with_connection!(self.db_pool, |db_conn| { + diesel::update( + media_attachments::table.filter( + media_attachments::id + .eq(update.attachment_id) + .and(media_attachments::account_id.eq(update.account_id)), + ), + ) + .set(changeset) + .get_result(db_conn) .await - .map_err(Error::from) + }) + .map_err(Error::from) } pub async fn upload(&self, upload: Upload) -> Result @@ -217,23 +209,20 @@ impl AttachmentService { } .map_err(Error::Storage)?; - let media_attachment = self - .db_pool - .with_connection(|db_conn| { - diesel::insert_into(media_attachments::table) - .values(NewMediaAttachment { - id: Uuid::now_v7(), - content_type: upload.content_type.as_str(), - account_id: upload.account_id, - description: upload.description.as_deref(), - blurhash: None, - file_path: Some(upload.path.as_str()), - remote_url: None, - }) - .get_result(db_conn) - .scoped() - }) - .await?; + let media_attachment = with_connection!(self.db_pool, |db_conn| { + diesel::insert_into(media_attachments::table) + .values(NewMediaAttachment { + id: Uuid::now_v7(), + content_type: upload.content_type.as_str(), + account_id: upload.account_id, + description: upload.description.as_deref(), + blurhash: None, + file_path: Some(upload.path.as_str()), + remote_url: None, + }) + .get_result(db_conn) + .await + })?; Ok(media_attachment) } @@ -241,12 +230,9 @@ impl AttachmentService { #[cfg(test)] mod test { - use crate::{ - attachment::{AttachmentService, Upload}, - error::Error, - }; + use crate::attachment::{AttachmentService, Upload}; use bytes::{Bytes, BytesMut}; - use diesel_async::{AsyncConnection, AsyncPgConnection, RunQueryDsl}; + use diesel_async::{AsyncPgConnection, RunQueryDsl}; use futures_util::{future, pin_mut, stream, StreamExt}; use http::{Request, Response}; use http_body_util::Empty; @@ -261,12 +247,12 @@ mod test { media_attachment::MediaAttachment, }, schema::accounts, + with_connection_panicky, }; use kitsune_http_client::Client; use kitsune_storage::fs::Storage; use kitsune_test::database_test; use kitsune_url::UrlService; - use scoped_futures::ScopedFutureExt; use speedy_uuid::Uuid; use std::convert::Infallible; use tempfile::TempDir; @@ -277,12 +263,10 @@ mod test { database_test(|db_pool| async move { let client = Client::builder().service(service_fn(handle)); - let account_id = db_pool - .with_connection(|db_conn| { - async move { Ok::<_, miette::Report>(prepare_db(db_conn).await) }.scoped() - }) - .await - .unwrap(); + let account_id = with_connection_panicky!(db_pool, |db_conn| { + Ok::<_, eyre::Report>(prepare_db(db_conn).await) + }) + .unwrap(); let temp_dir = TempDir::new().unwrap(); let storage = Storage::new(temp_dir.path().to_owned()); @@ -350,38 +334,32 @@ mod test { async fn prepare_db(db_conn: &mut AsyncPgConnection) -> Uuid { // Create a local user `@alice` - db_conn - .transaction(|tx| { - async move { - let account_id = Uuid::now_v7(); - diesel::insert_into(accounts::table) - .values(NewAccount { - id: account_id, - display_name: None, - username: "alice", - locked: false, - note: None, - local: true, - domain: "example.com", - actor_type: ActorType::Person, - url: "https://example.com/users/alice", - featured_collection_url: None, - followers_url: None, - following_url: None, - inbox_url: None, - outbox_url: None, - shared_inbox_url: None, - public_key_id: "https://example.com/users/alice#main-key", - public_key: "", - created_at: None, - }) - .execute(tx) - .await?; - Ok::<_, Error>(account_id) - } - .scope_boxed() + let account_id = Uuid::now_v7(); + diesel::insert_into(accounts::table) + .values(NewAccount { + id: account_id, + display_name: None, + username: "alice", + locked: false, + note: None, + local: true, + domain: "example.com", + actor_type: ActorType::Person, + url: "https://example.com/users/alice", + featured_collection_url: None, + followers_url: None, + following_url: None, + inbox_url: None, + outbox_url: None, + shared_inbox_url: None, + public_key_id: "https://example.com/users/alice#main-key", + public_key: "", + created_at: None, }) + .execute(db_conn) .await - .unwrap() + .unwrap(); + + account_id } } diff --git a/crates/kitsune-service/src/custom_emoji.rs b/crates/kitsune-service/src/custom_emoji.rs index fdbc7eb03..2a0aa74d0 100644 --- a/crates/kitsune-service/src/custom_emoji.rs +++ b/crates/kitsune-service/src/custom_emoji.rs @@ -13,10 +13,9 @@ use kitsune_core::consts::MAX_EMOJI_SHORTCODE_LENGTH; use kitsune_db::{ model::{custom_emoji::CustomEmoji, media_attachment::MediaAttachment}, schema::{custom_emojis, media_attachments, posts, posts_custom_emojis}, - PgPool, + with_connection, PgPool, }; use kitsune_url::UrlService; -use scoped_futures::ScopedFutureExt; use speedy_uuid::Uuid; use typed_builder::TypedBuilder; @@ -80,12 +79,10 @@ impl CustomEmojiService { query = query.filter(custom_emojis::domain.eq(domain)); } - self.db_pool - .with_connection(|db_conn| { - async move { query.first(db_conn).await.optional() }.scoped() - }) - .await - .map_err(Error::from) + with_connection!(self.db_pool, |db_conn| { + query.first(db_conn).await.optional() + }) + .map_err(Error::from) } pub async fn get_by_id(&self, id: Uuid) -> Result { @@ -93,9 +90,7 @@ impl CustomEmojiService { .find(id) .select(CustomEmoji::as_select()); - self.db_pool - .with_connection(|db_conn| async move { query.get_result(db_conn).await }.scoped()) - .await + with_connection!(self.db_pool, |db_conn| { query.get_result(db_conn).await }) .map_err(Error::from) } @@ -132,13 +127,9 @@ impl CustomEmojiService { )) .limit(get_emoji_list.limit); - self.db_pool - .with_connection(|db_conn| { - async move { Ok::<_, Error>(query.load_stream(db_conn).await?.map_err(Error::from)) } - .scoped() - }) - .await - .map_err(Error::from) + with_connection!(self.db_pool, |db_conn| { + Ok::<_, Error>(query.load_stream(db_conn).await?.map_err(Error::from)) + }) } pub async fn add_emoji(&self, emoji_upload: EmojiUpload) -> Result @@ -158,24 +149,21 @@ impl CustomEmojiService { let id = Uuid::now_v7(); let remote_id = self.url_service.custom_emoji_url(id); - let custom_emoji = self - .db_pool - .with_connection(|db_conn| { - diesel::insert_into(custom_emojis::table) - .values(CustomEmoji { - id, - remote_id, - shortcode: emoji_upload.shortcode, - domain: None, - media_attachment_id: attachment.id, - endorsed: false, - created_at: Timestamp::now_utc(), - updated_at: Timestamp::now_utc(), - }) - .get_result(db_conn) - .scoped() - }) - .await?; + let custom_emoji = with_connection!(self.db_pool, |db_conn| { + diesel::insert_into(custom_emojis::table) + .values(CustomEmoji { + id, + remote_id, + shortcode: emoji_upload.shortcode, + domain: None, + media_attachment_id: attachment.id, + endorsed: false, + created_at: Timestamp::now_utc(), + updated_at: Timestamp::now_utc(), + }) + .get_result(db_conn) + .await + })?; Ok(custom_emoji) } diff --git a/crates/kitsune-service/src/error.rs b/crates/kitsune-service/src/error.rs index 585830276..c22ca6977 100644 --- a/crates/kitsune-service/src/error.rs +++ b/crates/kitsune-service/src/error.rs @@ -1,8 +1,5 @@ use diesel_async::pooled_connection::bb8; -use std::{ - error::Error as StdError, - fmt::{Debug, Display}, -}; +use std::{error::Error as StdError, fmt::Debug}; use thiserror::Error; pub type BoxError = Box; @@ -71,7 +68,7 @@ pub enum Error { Event(kitsune_messaging::BoxError), #[error(transparent)] - Fetcher(BoxError), + Fetcher(eyre::Report), #[error(transparent)] Http(#[from] http::Error), @@ -101,7 +98,7 @@ pub enum Error { PostProcessing(post_process::BoxError), #[error(transparent)] - Resolver(BoxError), + Resolver(eyre::Report), #[error(transparent)] Rsa(#[from] rsa::Error), @@ -130,15 +127,3 @@ pub enum Error { #[error(transparent)] Validate(#[from] garde::Report), } - -impl From> for Error -where - E: Into + Debug + Display, -{ - fn from(value: kitsune_db::PoolError) -> Self { - match value { - kitsune_db::PoolError::Pool(err) => err.into(), - kitsune_db::PoolError::User(err) => err.into(), - } - } -} diff --git a/crates/kitsune-service/src/instance.rs b/crates/kitsune-service/src/instance.rs index 6a979f8fe..2e3fb34e8 100644 --- a/crates/kitsune-service/src/instance.rs +++ b/crates/kitsune-service/src/instance.rs @@ -3,9 +3,8 @@ use diesel::{ExpressionMethods, QueryDsl}; use diesel_async::RunQueryDsl; use kitsune_db::{ schema::{accounts, posts, users}, - PgPool, + with_connection, PgPool, }; -use scoped_futures::ScopedFutureExt; use smol_str::SmolStr; use typed_builder::TypedBuilder; @@ -37,39 +36,29 @@ impl InstanceService { } pub async fn known_instances(&self) -> Result { - self.db_pool - .with_connection(|db_conn| { - async move { - accounts::table - .filter(accounts::local.eq(false)) - .select(accounts::domain) - .distinct() - .count() - .get_result::(db_conn) - .await - .map(|count| count as u64) - } - .scoped() - }) - .await - .map_err(Error::from) + with_connection!(self.db_pool, |db_conn| { + accounts::table + .filter(accounts::local.eq(false)) + .select(accounts::domain) + .distinct() + .count() + .get_result::(db_conn) + .await + .map(|count| count as u64) + }) + .map_err(Error::from) } pub async fn local_post_count(&self) -> Result { - self.db_pool - .with_connection(|db_conn| { - async move { - posts::table - .filter(posts::is_local.eq(true)) - .count() - .get_result::(db_conn) - .await - .map(|count| count as u64) - } - .scoped() - }) - .await - .map_err(Error::from) + with_connection!(self.db_pool, |db_conn| { + posts::table + .filter(posts::is_local.eq(true)) + .count() + .get_result::(db_conn) + .await + .map(|count| count as u64) + }) + .map_err(Error::from) } #[must_use] @@ -78,18 +67,13 @@ impl InstanceService { } pub async fn user_count(&self) -> Result { - self.db_pool - .with_connection(|db_conn| { - async move { - users::table - .count() - .get_result::(db_conn) - .await - .map(|count| count as u64) - } - .scoped() - }) - .await - .map_err(Error::from) + with_connection!(self.db_pool, |db_conn| { + users::table + .count() + .get_result::(db_conn) + .await + .map(|count| count as u64) + }) + .map_err(Error::from) } } diff --git a/crates/kitsune-service/src/notification.rs b/crates/kitsune-service/src/notification.rs index 1d100a3c0..dc885fd18 100644 --- a/crates/kitsune-service/src/notification.rs +++ b/crates/kitsune-service/src/notification.rs @@ -10,9 +10,8 @@ use garde::Validate; use kitsune_db::{ model::notification::{NewNotification, Notification, NotificationType}, schema::{accounts, accounts_follows, accounts_preferences, notifications, posts}, - PgPool, + with_connection, PgPool, }; -use scoped_futures::ScopedFutureExt; use speedy_uuid::Uuid; use typed_builder::TypedBuilder; @@ -104,15 +103,9 @@ impl NotificationService { .order(notifications::id.asc()); } - self.db_pool - .with_connection(|mut db_conn| { - async move { - Ok::<_, Error>(query.load_stream(&mut db_conn).await?.map_err(Error::from)) - } - .scoped() - }) - .await - .map_err(Error::from) + with_connection!(self.db_pool, |db_conn| { + Ok::<_, Error>(query.load_stream(db_conn).await?.map_err(Error::from)) + }) } pub async fn get_notification_by_id( @@ -120,61 +113,45 @@ impl NotificationService { id: Uuid, account_id: Uuid, ) -> Result> { - self.db_pool - .with_connection(|mut db_conn| { - async move { - notifications::table - .filter( - notifications::id - .eq(id) - .and(notifications::receiving_account_id.eq(account_id)), - ) - .select(Notification::as_select()) - .first(&mut db_conn) - .await - .optional() - } - .scoped() - }) - .await - .map_err(Error::from) + with_connection!(self.db_pool, |db_conn| { + notifications::table + .filter( + notifications::id + .eq(id) + .and(notifications::receiving_account_id.eq(account_id)), + ) + .select(Notification::as_select()) + .first(db_conn) + .await + .optional() + }) + .map_err(Error::from) } pub async fn dismiss(&self, id: Uuid, account_id: Uuid) -> Result<()> { - self.db_pool - .with_connection(|mut db_conn| { - async move { - diesel::delete( - notifications::table.filter( - notifications::id - .eq(id) - .and(notifications::receiving_account_id.eq(account_id)), - ), - ) - .execute(&mut db_conn) - .await - } - .scoped() - }) - .await?; + with_connection!(self.db_pool, |db_conn| { + diesel::delete( + notifications::table.filter( + notifications::id + .eq(id) + .and(notifications::receiving_account_id.eq(account_id)), + ), + ) + .execute(db_conn) + .await + })?; Ok(()) } pub async fn clear_all(&self, account_id: Uuid) -> Result<()> { - self.db_pool - .with_connection(|mut db_conn| { - async move { - diesel::delete( - notifications::table - .filter(notifications::receiving_account_id.eq(account_id)), - ) - .execute(&mut db_conn) - .await - } - .scoped() - }) - .await?; + with_connection!(self.db_pool, |db_conn| { + diesel::delete( + notifications::table.filter(notifications::receiving_account_id.eq(account_id)), + ) + .execute(db_conn) + .await + })?; Ok(()) } diff --git a/crates/kitsune-service/src/post/mod.rs b/crates/kitsune-service/src/post/mod.rs index 0d28c4ac9..fa217b233 100644 --- a/crates/kitsune-service/src/post/mod.rs +++ b/crates/kitsune-service/src/post/mod.rs @@ -33,7 +33,7 @@ use kitsune_db::{ posts_custom_emojis, posts_favourites, posts_media_attachments, posts_mentions, users_roles, }, - PgPool, + with_connection, with_transaction, PgPool, }; use kitsune_embed::Client as EmbedClient; use kitsune_jobs::deliver::{ @@ -47,7 +47,6 @@ use kitsune_language::Language; use kitsune_search::SearchBackend; use kitsune_url::UrlService; use kitsune_util::{process, sanitize::CleanHtmlExt}; -use scoped_futures::ScopedFutureExt; use speedy_uuid::Uuid; use typed_builder::TypedBuilder; @@ -478,59 +477,48 @@ impl PostService { let id = Uuid::now_v7(); let url = self.url_service.post_url(id); - let post = self - .db_pool - .with_transaction(move |tx| { - async move { - let in_reply_to_id = if let Some(in_reply_to_id) = create_post.in_reply_to_id { - (posts::table - .find(in_reply_to_id) - .count() - .get_result::(tx) - .await? - != 0) - .then_some(in_reply_to_id) - } else { - None - }; - - let post: Post = diesel::insert_into(posts::table) - .values(NewPost { - id, - account_id: create_post.author_id, - in_reply_to_id, - reposted_post_id: None, - subject: subject.as_deref(), - content: resolved.content.as_str(), - content_source: content_source.as_str(), - content_lang: content_lang.into(), - link_preview_url: link_preview_url.as_deref(), - is_sensitive: create_post.sensitive, - visibility: create_post.visibility, - is_local: true, - url: url.as_str(), - created_at: None, - }) - .returning(Post::as_returning()) - .get_result(tx) - .await?; - - Self::process_mentions( - tx, - post.account_id, - post.id, - resolved.mentioned_accounts, - ) - .await?; - Self::process_custom_emojis(tx, post.id, resolved.custom_emojis).await?; - Self::process_media_attachments(tx, post.id, &create_post.media_ids).await?; - NotificationService::notify_on_new_post(tx, post.account_id, post.id).await?; + let post = with_transaction!(self.db_pool, |tx| { + let in_reply_to_id = if let Some(in_reply_to_id) = create_post.in_reply_to_id { + (posts::table + .find(in_reply_to_id) + .count() + .get_result::(tx) + .await? + != 0) + .then_some(in_reply_to_id) + } else { + None + }; + + let post: Post = diesel::insert_into(posts::table) + .values(NewPost { + id, + account_id: create_post.author_id, + in_reply_to_id, + reposted_post_id: None, + subject: subject.as_deref(), + content: resolved.content.as_str(), + content_source: content_source.as_str(), + content_lang: content_lang.into(), + link_preview_url: link_preview_url.as_deref(), + is_sensitive: create_post.sensitive, + visibility: create_post.visibility, + is_local: true, + url: url.as_str(), + created_at: None, + }) + .returning(Post::as_returning()) + .get_result(tx) + .await?; - Ok::<_, Error>(post) - } - .scoped() - }) - .await?; + Self::process_mentions(tx, post.account_id, post.id, resolved.mentioned_accounts) + .await?; + Self::process_custom_emojis(tx, post.id, resolved.custom_emojis).await?; + Self::process_media_attachments(tx, post.id, &create_post.media_ids).await?; + NotificationService::notify_on_new_post(tx, post.account_id, post.id).await?; + + Ok::<_, Error>(post) + })?; self.job_service .enqueue( @@ -653,37 +641,29 @@ impl PostService { None }; - let post = self - .db_pool - .with_transaction(move |tx| { - async move { - let post: Post = diesel::update(posts::table) - .set(PartialPostChangeset { - id: update_post.post_id, - subject: subject.as_deref(), - content: content.as_deref(), - content_source: update_post.content.as_deref(), - content_lang: content_lang.map(Into::into), - link_preview_url: link_preview_url.as_deref(), - is_sensitive: update_post.sensitive, - updated_at: Timestamp::now_utc(), - }) - .returning(Post::as_returning()) - .get_result(tx) - .await?; - - Self::process_mentions(tx, post.account_id, post.id, mentioned_account_ids) - .await?; - Self::process_custom_emojis(tx, post.id, custom_emojis).await?; - Self::process_media_attachments(tx, post.id, &update_post.media_ids).await?; - NotificationService::notify_on_update_post(tx, post.account_id, post.id) - .await?; - - Ok::<_, Error>(post) - } - .scoped() - }) - .await?; + let post = with_transaction!(self.db_pool, |tx| { + let post: Post = diesel::update(posts::table) + .set(PartialPostChangeset { + id: update_post.post_id, + subject: subject.as_deref(), + content: content.as_deref(), + content_source: update_post.content.as_deref(), + content_lang: content_lang.map(Into::into), + link_preview_url: link_preview_url.as_deref(), + is_sensitive: update_post.sensitive, + updated_at: Timestamp::now_utc(), + }) + .returning(Post::as_returning()) + .get_result(tx) + .await?; + + Self::process_mentions(tx, post.account_id, post.id, mentioned_account_ids).await?; + Self::process_custom_emojis(tx, post.id, custom_emojis).await?; + Self::process_media_attachments(tx, post.id, &update_post.media_ids).await?; + NotificationService::notify_on_update_post(tx, post.account_id, post.id).await?; + + Ok::<_, Error>(post) + })?; self.job_service .enqueue( @@ -723,84 +703,69 @@ impl PostService { .fetching_account_id(Some(repost_post.account_id)) .build(); - let existing_repost: Option = self - .db_pool - .with_connection(|db_conn| { - async move { - posts::table - .filter( - posts::reposted_post_id - .eq(repost_post.post_id) - .and(posts::account_id.eq(repost_post.account_id)), - ) - .add_post_permission_check(permission_check) - .select(Post::as_select()) - .first(db_conn) - .await - .optional() - } - .scoped() - }) - .await?; + let existing_repost: Option = with_connection!(self.db_pool, |db_conn| { + posts::table + .filter( + posts::reposted_post_id + .eq(repost_post.post_id) + .and(posts::account_id.eq(repost_post.account_id)), + ) + .add_post_permission_check(permission_check) + .select(Post::as_select()) + .first(db_conn) + .await + .optional() + })?; if let Some(repost) = existing_repost { return Ok(repost); } - let post: Post = self - .db_pool - .with_connection(|db_conn| { - posts::table - .find(repost_post.post_id) - .add_post_permission_check(permission_check) - .select(Post::as_select()) - .get_result(db_conn) - .scoped() - }) - .await?; + let post: Post = with_connection!(self.db_pool, |db_conn| { + posts::table + .find(repost_post.post_id) + .add_post_permission_check(permission_check) + .select(Post::as_select()) + .get_result(db_conn) + .await + })?; let id = Uuid::now_v7(); let url = self.url_service.post_url(id); - let repost = self - .db_pool - .with_transaction(|tx| { - async move { - let new_repost = diesel::insert_into(posts::table) - .values(NewPost { - id, - account_id: repost_post.account_id, - in_reply_to_id: None, - reposted_post_id: Some(post.id), - subject: None, - content: "", - content_source: "", - content_lang: post.content_lang, - link_preview_url: None, - is_sensitive: post.is_sensitive, - visibility: repost_post.visibility, - is_local: true, - url: url.as_str(), - created_at: Some(Timestamp::now_utc()), - }) - .returning(Post::as_returning()) - .get_result(tx) - .await?; - - NotificationService::notify_on_repost( - tx, - post.account_id, - new_repost.account_id, - post.id, - ) - .await?; + let repost = with_transaction!(self.db_pool, |tx| { + let new_repost = diesel::insert_into(posts::table) + .values(NewPost { + id, + account_id: repost_post.account_id, + in_reply_to_id: None, + reposted_post_id: Some(post.id), + subject: None, + content: "", + content_source: "", + content_lang: post.content_lang, + link_preview_url: None, + is_sensitive: post.is_sensitive, + visibility: repost_post.visibility, + is_local: true, + url: url.as_str(), + created_at: Some(Timestamp::now_utc()), + }) + .returning(Post::as_returning()) + .get_result(tx) + .await?; - Ok::<_, Error>(new_repost) - } - .scoped() - }) + NotificationService::notify_on_repost( + tx, + post.account_id, + new_repost.account_id, + post.id, + ) .await?; + Ok::<_, Error>(new_repost) + })?; + self.job_service .enqueue( Enqueue::builder() @@ -830,21 +795,18 @@ impl PostService { .fetching_account_id(Some(unrepost_post.account_id)) .build(); - let post: Post = self - .db_pool - .with_connection(|db_conn| { - posts::table - .filter( - posts::account_id - .eq(unrepost_post.account_id) - .and(posts::reposted_post_id.eq(unrepost_post.post_id)), - ) - .add_post_permission_check(permission_check) - .select(Post::as_select()) - .first(db_conn) - .scoped() - }) - .await?; + let post: Post = with_connection!(self.db_pool, |db_conn| { + posts::table + .filter( + posts::account_id + .eq(unrepost_post.account_id) + .and(posts::reposted_post_id.eq(unrepost_post.post_id)), + ) + .add_post_permission_check(permission_check) + .select(Post::as_select()) + .first(db_conn) + .await + })?; self.job_service .enqueue( @@ -875,65 +837,57 @@ impl PostService { .fetching_account_id(Some(favouriting_account_id)) .build(); - let post: Post = self - .db_pool - .with_connection(|db_conn| { - posts::table - .find(post_id) - .add_post_permission_check(permission_check) - .select(Post::as_select()) - .get_result(db_conn) - .scoped() - }) - .await?; + let post: Post = with_connection!(self.db_pool, |db_conn| { + posts::table + .find(post_id) + .add_post_permission_check(permission_check) + .select(Post::as_select()) + .get_result(db_conn) + .await + })?; let id = Uuid::now_v7(); let url = self.url_service.favourite_url(id); - let favourite_id = self - .db_pool - .with_transaction(|tx| { - async move { - let favourite = diesel::insert_into(posts_favourites::table) - .values(NewFavourite { - id, - account_id: favouriting_account_id, - post_id: post.id, - url, - created_at: None, - }) - .returning(posts_favourites::id) - .get_result(tx) - .await?; - let account_id = accounts::table - .inner_join(accounts_preferences::table) - .filter( - accounts::id - .eq(post.account_id) - .and(accounts_preferences::notify_on_favourite.eq(true)), - ) - .select(accounts::id) - .get_result::(tx) - .await - .optional()?; - - if let Some(account_id) = account_id { - diesel::insert_into(notifications::table) - .values( - NewNotification::builder() - .receiving_account_id(account_id) - .favourite(favouriting_account_id, post.id), - ) - .on_conflict_do_nothing() - .execute(tx) - .await?; - } - - Ok::<_, Error>(favourite) - } - .scoped() - }) - .await?; + let favourite_id = with_transaction!(self.db_pool, |tx| { + let favourite = diesel::insert_into(posts_favourites::table) + .values(NewFavourite { + id, + account_id: favouriting_account_id, + post_id: post.id, + url, + created_at: None, + }) + .returning(posts_favourites::id) + .get_result(tx) + .await?; + + let account_id = accounts::table + .inner_join(accounts_preferences::table) + .filter( + accounts::id + .eq(post.account_id) + .and(accounts_preferences::notify_on_favourite.eq(true)), + ) + .select(accounts::id) + .get_result::(tx) + .await + .optional()?; + + if let Some(account_id) = account_id { + diesel::insert_into(notifications::table) + .values( + NewNotification::builder() + .receiving_account_id(account_id) + .favourite(favouriting_account_id, post.id), + ) + .on_conflict_do_nothing() + .execute(tx) + .await?; + } + + Ok::<_, Error>(favourite) + })?; self.job_service .enqueue( @@ -956,19 +910,13 @@ impl PostService { .get_by_id(post_id, Some(favouriting_account_id)) .await?; - let favourite = self - .db_pool - .with_connection(|db_conn| { - async { - Favourite::belonging_to(&post) - .filter(posts_favourites::account_id.eq(favouriting_account_id)) - .get_result::(db_conn) - .await - .optional() - } - .scoped() - }) - .await?; + let favourite = with_connection!(self.db_pool, |db_conn| { + Favourite::belonging_to(&post) + .filter(posts_favourites::account_id.eq(favouriting_account_id)) + .get_result::(db_conn) + .await + .optional() + })?; if let Some(favourite) = favourite { self.job_service @@ -1018,15 +966,9 @@ impl PostService { .order(posts_favourites::id.asc()); } - self.db_pool - .with_connection(|db_conn| { - async move { - Ok::<_, Error>(query.load_stream(db_conn).await?.map_err(Error::from)) - } - .scoped() - }) - .await - .map_err(Error::from) + with_connection!(self.db_pool, |db_conn| { + Ok::<_, Error>(query.load_stream(db_conn).await?.map_err(Error::from)) + }) } /// Get accounts that reblogged a post @@ -1067,15 +1009,10 @@ impl PostService { .order(accounts::id.desc()) .limit(get_reblogs.limit as i64); - self.db_pool - .with_connection(|db_conn| { - async move { - Ok::<_, Error>(query.load_stream(db_conn).await?.map_err(Error::from)) - } - .scoped() - }) - .await - .map_err(Error::from) + with_connection!(self.db_pool, |db_conn| { + Ok::<_, Error>(query.load_stream(db_conn).await?.map_err(Error::from)) + }) + .map_err(Error::from) } /// Get a post by its ID @@ -1090,17 +1027,15 @@ impl PostService { .fetching_account_id(fetching_account_id) .build(); - self.db_pool - .with_connection(|db_conn| { - posts::table - .find(id) - .add_post_permission_check(permission_check) - .select(Post::as_select()) - .get_result(db_conn) - .scoped() - }) - .await - .map_err(Error::from) + with_connection!(self.db_pool, |db_conn| { + posts::table + .find(id) + .add_post_permission_check(permission_check) + .select(Post::as_select()) + .get_result(db_conn) + .await + }) + .map_err(Error::from) } /// Get a post's source by its ID @@ -1116,17 +1051,15 @@ impl PostService { .fetching_account_id(fetching_account_id) .build(); - self.db_pool - .with_connection(|db_conn| { - posts::table - .find(id) - .add_post_permission_check(permission_check) - .select(PostSource::as_select()) - .get_result(db_conn) - .scoped() - }) - .await - .map_err(Error::from) + with_connection!(self.db_pool, |db_conn| { + posts::table + .find(id) + .add_post_permission_check(permission_check) + .select(PostSource::as_select()) + .get_result(db_conn) + .await + }) + .map_err(Error::from) } /// Get the ancestors of the post @@ -1139,6 +1072,19 @@ impl PostService { id: Uuid, fetching_account_id: Option, ) -> impl Stream> + '_ { + let load_post = move |in_reply_to_id, permission_check| async move { + let post = with_connection!(self.db_pool, |db_conn| { + posts::table + .find(in_reply_to_id) + .add_post_permission_check(permission_check) + .select(Post::as_select()) + .get_result::(db_conn) + .await + })?; + + Ok::<_, Error>(post) + }; + try_stream! { let mut last_post = self.get_by_id(id, fetching_account_id).await?; let permission_check = PermissionCheck::builder() @@ -1146,16 +1092,7 @@ impl PostService { .build(); while let Some(in_reply_to_id) = last_post.in_reply_to_id { - let post = self.db_pool - .with_connection(|db_conn| { - posts::table - .find(in_reply_to_id) - .add_post_permission_check(permission_check) - .select(Post::as_select()) - .get_result::(db_conn) - .scoped() - }) - .await?; + let post = load_post(in_reply_to_id, permission_check).await?; yield post.clone(); @@ -1175,22 +1112,25 @@ impl PostService { id: Uuid, fetching_account_id: Option, ) -> BoxStream<'_, Result> { + let load_post = move |id, permission_check| async move { + let post = with_connection!(self.db_pool, |db_conn| { + posts::table + .filter(posts::in_reply_to_id.eq(id)) + .add_post_permission_check(permission_check) + .select(Post::as_select()) + .load_stream::(db_conn) + .await + })?; + + Ok::<_, Error>(post) + }; + try_stream! { let permission_check = PermissionCheck::builder() .fetching_account_id(fetching_account_id) .build(); - let descendant_stream = self.db_pool - .with_connection(|db_conn| { - posts::table - .filter(posts::in_reply_to_id.eq(id)) - .add_post_permission_check(permission_check) - .select(Post::as_select()) - .load_stream::(db_conn) - .scoped() - }) - .await?; - + let descendant_stream = load_post(id, permission_check).await?; for await descendant in descendant_stream { let descendant = descendant?; let descendant_id = descendant.id; @@ -1212,33 +1152,27 @@ impl PostService { account_id: Uuid, user_id: Option, ) -> Result { - let post: Post = self - .db_pool - .with_connection(|db_conn| { - posts::table - .find(post_id) - .select(Post::as_select()) - .first(db_conn) - .scoped() - }) - .await?; + let post: Post = with_connection!(self.db_pool, |db_conn| { + posts::table + .find(post_id) + .select(Post::as_select()) + .first(db_conn) + .await + })?; if post.account_id != account_id { if let Some(user_id) = user_id { - let admin_role_count = self - .db_pool - .with_connection(|db_conn| { - users_roles::table - .filter( - users_roles::user_id - .eq(user_id) - .and(users_roles::role.eq(Role::Administrator)), - ) - .count() - .get_result::(db_conn) - .scoped() - }) - .await?; + let admin_role_count = with_connection!(self.db_pool, |db_conn| { + users_roles::table + .filter( + users_roles::user_id + .eq(user_id) + .and(users_roles::role.eq(Role::Administrator)), + ) + .count() + .get_result::(db_conn) + .await + })?; if admin_role_count == 0 { return Err(PostError::Unauthorised.into()); diff --git a/crates/kitsune-service/src/post/resolver.rs b/crates/kitsune-service/src/post/resolver.rs index aa6a35bcc..ecd404a1a 100644 --- a/crates/kitsune-service/src/post/resolver.rs +++ b/crates/kitsune-service/src/post/resolver.rs @@ -126,6 +126,7 @@ mod test { account::Account, custom_emoji::CustomEmoji, media_attachment::NewMediaAttachment, }, schema::{accounts, custom_emojis, media_attachments}, + with_connection_panicky, }; use kitsune_federation_filter::FederationFilter; use kitsune_http_client::Client; @@ -137,7 +138,6 @@ mod test { use kitsune_util::try_join; use kitsune_webfinger::Webfinger; use pretty_assertions::assert_eq; - use scoped_futures::ScopedFutureExt; use speedy_uuid::Uuid; use std::sync::Arc; use tower::service_fn; @@ -221,69 +221,63 @@ mod test { let emoji_ids = (Uuid::now_v7(), Uuid::now_v7()); let media_attachment_ids = (Uuid::now_v7(), Uuid::now_v7()); - db_pool - .with_connection(|db_conn| { - async { - let media_fut = diesel::insert_into(media_attachments::table) - .values(NewMediaAttachment { - id: media_attachment_ids.0, - content_type: "image/jpeg", - account_id: None, - description: None, - blurhash: None, - file_path: None, - remote_url: None, - }) - .execute(db_conn); - let emoji_fut = diesel::insert_into(custom_emojis::table) - .values(CustomEmoji { - id: emoji_ids.0, - shortcode: String::from("blobhaj_happy"), - domain: None, - remote_id: String::from("https://local.domain/emoji/blobhaj_happy"), - media_attachment_id: media_attachment_ids.0, - endorsed: false, - created_at: Timestamp::now_utc(), - updated_at: Timestamp::now_utc() - }) - .execute(db_conn); - try_join!(media_fut, emoji_fut) - }.scoped() - }) - .await - .expect("Failed to insert the local emoji"); - db_pool - .with_connection(|db_conn| { - async { - let media_fut = diesel::insert_into(media_attachments::table) - .values(NewMediaAttachment { - id: media_attachment_ids.1, - content_type: "image/jpeg", - account_id: None, - description: None, - blurhash: None, - file_path: None, - remote_url: Some("https://media.example.com/emojis/blobhaj.jpeg"), - }) - .execute(db_conn); - let emoji_fut = diesel::insert_into(custom_emojis::table) - .values(CustomEmoji { - id: emoji_ids.1, - shortcode: String::from("blobhaj_sad"), - domain: Some(String::from("example.com")), - remote_id: String::from("https://example.com/emojis/1"), - media_attachment_id: media_attachment_ids.1, - endorsed: false, - created_at: Timestamp::now_utc(), - updated_at: Timestamp::now_utc(), - }) - .execute(db_conn); - try_join!(media_fut, emoji_fut) - }.scoped() - }) - .await - .expect("Failed to insert the remote emoji"); + with_connection_panicky!(db_pool, |db_conn| { + let media_fut = diesel::insert_into(media_attachments::table) + .values(NewMediaAttachment { + id: media_attachment_ids.0, + content_type: "image/jpeg", + account_id: None, + description: None, + blurhash: None, + file_path: None, + remote_url: None, + }) + .execute(db_conn); + let emoji_fut = diesel::insert_into(custom_emojis::table) + .values(CustomEmoji { + id: emoji_ids.0, + shortcode: String::from("blobhaj_happy"), + domain: None, + remote_id: String::from("https://local.domain/emoji/blobhaj_happy"), + media_attachment_id: media_attachment_ids.0, + endorsed: false, + created_at: Timestamp::now_utc(), + updated_at: Timestamp::now_utc() + }) + .execute(db_conn); + + try_join!(media_fut, emoji_fut) + }) + .expect("Failed to insert the local emoji"); + + with_connection_panicky!(db_pool, |db_conn| { + let media_fut = diesel::insert_into(media_attachments::table) + .values(NewMediaAttachment { + id: media_attachment_ids.1, + content_type: "image/jpeg", + account_id: None, + description: None, + blurhash: None, + file_path: None, + remote_url: Some("https://media.example.com/emojis/blobhaj.jpeg"), + }) + .execute(db_conn); + let emoji_fut = diesel::insert_into(custom_emojis::table) + .values(CustomEmoji { + id: emoji_ids.1, + shortcode: String::from("blobhaj_sad"), + domain: Some(String::from("example.com")), + remote_id: String::from("https://example.com/emojis/1"), + media_attachment_id: media_attachment_ids.1, + endorsed: false, + created_at: Timestamp::now_utc(), + updated_at: Timestamp::now_utc(), + }) + .execute(db_conn); + try_join!(media_fut, emoji_fut) + }) + .expect("Failed to insert the remote emoji"); let post_resolver = PostResolver::builder() .account(account_service) @@ -300,16 +294,14 @@ mod test { assert_eq!(resolved.custom_emojis.len(), 2); let (account_id, _mention_text) = &resolved.mentioned_accounts[0]; - let mentioned_account = db_pool - .with_connection(|db_conn| { - accounts::table - .find(account_id) - .select(Account::as_select()) - .get_result::(db_conn) - .scoped() - }) - .await - .expect("Failed to fetch account"); + let mentioned_account = with_connection_panicky!(db_pool, |db_conn| { + accounts::table + .find(account_id) + .select(Account::as_select()) + .get_result::(db_conn) + .await + }) + .expect("Failed to fetch account"); assert_eq!(mentioned_account.username, "0x0"); assert_eq!(mentioned_account.domain, "corteximplant.com"); diff --git a/crates/kitsune-service/src/prepare.rs b/crates/kitsune-service/src/prepare.rs index 2904e5bdd..155ab644e 100644 --- a/crates/kitsune-service/src/prepare.rs +++ b/crates/kitsune-service/src/prepare.rs @@ -1,3 +1,4 @@ +use eyre::WrapErr; use kitsune_cache::{ArcCache, InMemoryCache, NoopCache, RedisCache}; use kitsune_captcha::AnyCaptcha; use kitsune_captcha::{hcaptcha::Captcha as HCaptcha, mcaptcha::Captcha as MCaptcha}; @@ -12,7 +13,6 @@ use kitsune_messaging::{ }; use kitsune_search::{AnySearchBackend, NoopSearchService, SqlSearchService}; use kitsune_storage::{fs::Storage as FsStorage, s3::Storage as S3Storage, AnyStorageBackend}; -use miette::{Context, IntoDiagnostic}; use multiplex_pool::RoundRobinStrategy; use redis::aio::ConnectionManager; use serde::{de::DeserializeOwned, Serialize}; @@ -22,7 +22,7 @@ use tokio::sync::OnceCell; pub async fn cache( config: &cache::Configuration, cache_name: &str, -) -> miette::Result> +) -> eyre::Result> where K: Display + Send + Sync + ?Sized + 'static, V: Clone + DeserializeOwned + Serialize + Send + Sync + 'static, @@ -45,8 +45,7 @@ where ) .await }) - .await - .into_diagnostic()?; + .await?; RedisCache::builder() .prefix(cache_name) @@ -79,7 +78,7 @@ pub fn captcha(config: &captcha::Configuration) -> AnyCaptcha { } } -pub fn storage(config: &storage::Configuration) -> miette::Result { +pub fn storage(config: &storage::Configuration) -> eyre::Result { let storage = match config { storage::Configuration::Fs(ref fs_config) => { FsStorage::new(fs_config.upload_dir.as_str().into()).into() @@ -96,12 +95,11 @@ pub fn storage(config: &storage::Configuration) -> miette::Result miette::Result miette::Result>> { +) -> eyre::Result>> { let transport_builder = if config.starttls { AsyncSmtpTransport::::starttls_relay(config.host.as_str()) } else { AsyncSmtpTransport::::relay(config.host.as_str()) - } - .into_diagnostic()?; + }?; let transport = transport_builder .credentials((config.username.as_str(), config.password.as_str()).into()) @@ -126,11 +123,11 @@ pub fn mail_sender( Ok(MailSender::builder() .backend(transport) - .from_mailbox(Mailbox::from_str(config.from_address.as_str()).into_diagnostic()?) + .from_mailbox(Mailbox::from_str(config.from_address.as_str())?) .build()) } -pub async fn messaging(config: &messaging::Configuration) -> miette::Result { +pub async fn messaging(config: &messaging::Configuration) -> eyre::Result { let backend = match config { messaging::Configuration::InProcess => { MessagingHub::new(TokioBroadcastMessagingBackend::default()) @@ -138,7 +135,6 @@ pub async fn messaging(config: &messaging::Configuration) -> miette::Result { let redis_messaging_backend = RedisMessagingBackend::new(&redis_config.url) .await - .into_diagnostic() .wrap_err("Failed to initialise Redis messaging backend")?; MessagingHub::new(redis_messaging_backend) @@ -153,7 +149,7 @@ pub async fn search( search_config: &search::Configuration, language_detection_config: language_detection::Configuration, db_pool: &PgPool, -) -> miette::Result { +) -> eyre::Result { let service = match search_config { search::Configuration::Meilisearch(_config) => { #[cfg(not(feature = "meilisearch"))] diff --git a/crates/kitsune-service/src/search.rs b/crates/kitsune-service/src/search.rs index 3e870f60c..a1d6d1a9f 100644 --- a/crates/kitsune-service/src/search.rs +++ b/crates/kitsune-service/src/search.rs @@ -8,10 +8,9 @@ use kitsune_core::{consts::API_MAX_LIMIT, traits::Fetcher}; use kitsune_db::{ model::{account::Account, post::Post}, schema::{accounts, posts}, - PgPool, + with_connection, PgPool, }; use kitsune_search::{SearchBackend, SearchIndex}; -use scoped_futures::ScopedFutureExt; use speedy_uuid::Uuid; use std::sync::Arc; use typed_builder::TypedBuilder; @@ -97,30 +96,27 @@ impl SearchService { .try_concat() .await?; - let search_backend_results = self - .db_pool - .with_connection(|db_conn| { - result_references - .iter() - .map(|result| match result.index { - SearchIndex::Account => accounts::table - .find(result.id) - .select(Account::as_select()) - .get_result::(db_conn) - .map_ok(SearchResult::Account) - .left_future(), - SearchIndex::Post => posts::table - .find(result.id) - .select(Post::as_select()) - .get_result::(db_conn) - .map_ok(SearchResult::Post) - .right_future(), - }) - .collect::>() - .try_collect::>() - .scoped() - }) - .await?; + let search_backend_results = with_connection!(self.db_pool, |db_conn| { + result_references + .iter() + .map(|result| match result.index { + SearchIndex::Account => accounts::table + .find(result.id) + .select(Account::as_select()) + .get_result::(db_conn) + .map_ok(SearchResult::Account) + .left_future(), + SearchIndex::Post => posts::table + .find(result.id) + .select(Post::as_select()) + .get_result::(db_conn) + .map_ok(SearchResult::Post) + .right_future(), + }) + .collect::>() + .try_collect::>() + .await + })?; results.extend(search_backend_results); diff --git a/crates/kitsune-service/src/timeline.rs b/crates/kitsune-service/src/timeline.rs index 0a0be2feb..49f783214 100644 --- a/crates/kitsune-service/src/timeline.rs +++ b/crates/kitsune-service/src/timeline.rs @@ -8,9 +8,8 @@ use kitsune_db::{ model::post::{Post, Visibility}, post_permission_check::{PermissionCheck, PostPermissionCheckExt}, schema::{accounts_follows, posts, posts_mentions}, - PgPool, + with_connection, PgPool, }; -use scoped_futures::ScopedFutureExt; use speedy_uuid::Uuid; use typed_builder::TypedBuilder; @@ -121,15 +120,9 @@ impl TimelineService { query = query.filter(posts::id.gt(min_id)).order(posts::id.asc()); } - self.db_pool - .with_connection(|db_conn| { - async move { - Ok::<_, Error>(query.load_stream(db_conn).await?.map_err(Error::from)) - } - .scoped() - }) - .await - .map_err(Error::from) + with_connection!(self.db_pool, |db_conn| { + Ok::<_, Error>(query.load_stream(db_conn).await?.map_err(Error::from)) + }) } /// Get a stream of public posts @@ -169,14 +162,8 @@ impl TimelineService { query = query.filter(posts::is_local.eq(false)); } - self.db_pool - .with_connection(|db_conn| { - async move { - Ok::<_, Error>(query.load_stream(db_conn).await?.map_err(Error::from)) - } - .scoped() - }) - .await - .map_err(Error::from) + with_connection!(self.db_pool, |db_conn| { + Ok::<_, Error>(query.load_stream(db_conn).await?.map_err(Error::from)) + }) } } diff --git a/crates/kitsune-service/src/user.rs b/crates/kitsune-service/src/user.rs index ea093800e..ea615b4f5 100644 --- a/crates/kitsune-service/src/user.rs +++ b/crates/kitsune-service/src/user.rs @@ -15,7 +15,7 @@ use kitsune_db::{ user::{NewUser, User}, }, schema::{accounts, accounts_preferences, users}, - PgPool, + with_transaction, PgPool, }; use kitsune_jobs::mailing::confirmation::SendConfirmationMail; use kitsune_url::UrlService; @@ -24,7 +24,6 @@ use rsa::{ pkcs8::{EncodePrivateKey, EncodePublicKey, LineEnding}, RsaPrivateKey, }; -use scoped_futures::ScopedFutureExt; use speedy_uuid::Uuid; use std::fmt::Write; use typed_builder::TypedBuilder; @@ -167,67 +166,61 @@ impl UserService { let url = self.url_service.user_url(account_id); let public_key_id = self.url_service.public_key_id(account_id); - let new_user = self - .db_pool - .with_transaction(|tx| { - async move { - let account_fut = diesel::insert_into(accounts::table) - .values(NewAccount { - id: account_id, - display_name: None, - username: register.username.as_str(), - locked: false, - note: None, - local: true, - domain: domain.as_str(), - actor_type: ActorType::Person, - url: url.as_str(), - featured_collection_url: None, - followers_url: None, - following_url: None, - inbox_url: None, - outbox_url: None, - shared_inbox_url: None, - public_key_id: public_key_id.as_str(), - public_key: public_key_str.as_str(), - created_at: None, - }) - .execute(tx); - - let confirmation_token = generate_secret(); - let user_fut = diesel::insert_into(users::table) - .values(NewUser { - id: Uuid::now_v7(), - account_id, - username: register.username.as_str(), - oidc_id: register.oidc_id.as_deref(), - email: register.email.as_str(), - password: hashed_password.as_deref(), - domain: domain.as_str(), - private_key: private_key_str.as_str(), - confirmation_token: confirmation_token.as_str(), - }) - .get_result::(tx); - - let preferences_fut = diesel::insert_into(accounts_preferences::table) - .values(Preferences { - account_id, - notify_on_follow: true, - notify_on_follow_request: true, - notify_on_repost: false, - notify_on_favourite: false, - notify_on_mention: true, - notify_on_post_update: true, - }) - .execute(tx); - - let (_, user, _) = try_join!(account_fut, user_fut, preferences_fut)?; - - Ok::<_, Error>(user) - } - .scoped() - }) - .await?; + let new_user = with_transaction!(self.db_pool, |tx| { + let account_fut = diesel::insert_into(accounts::table) + .values(NewAccount { + id: account_id, + display_name: None, + username: register.username.as_str(), + locked: false, + note: None, + local: true, + domain: domain.as_str(), + actor_type: ActorType::Person, + url: url.as_str(), + featured_collection_url: None, + followers_url: None, + following_url: None, + inbox_url: None, + outbox_url: None, + shared_inbox_url: None, + public_key_id: public_key_id.as_str(), + public_key: public_key_str.as_str(), + created_at: None, + }) + .execute(tx); + + let confirmation_token = generate_secret(); + let user_fut = diesel::insert_into(users::table) + .values(NewUser { + id: Uuid::now_v7(), + account_id, + username: register.username.as_str(), + oidc_id: register.oidc_id.as_deref(), + email: register.email.as_str(), + password: hashed_password.as_deref(), + domain: domain.as_str(), + private_key: private_key_str.as_str(), + confirmation_token: confirmation_token.as_str(), + }) + .get_result::(tx); + + let preferences_fut = diesel::insert_into(accounts_preferences::table) + .values(Preferences { + account_id, + notify_on_follow: true, + notify_on_follow_request: true, + notify_on_repost: false, + notify_on_favourite: false, + notify_on_mention: true, + notify_on_post_update: true, + }) + .execute(tx); + + let (_, user, _) = try_join!(account_fut, user_fut, preferences_fut)?; + + Ok::<_, Error>(user) + })?; self.job_service .enqueue( diff --git a/crates/kitsune-storage/Cargo.toml b/crates/kitsune-storage/Cargo.toml index 86cc6f994..c79320c78 100644 --- a/crates/kitsune-storage/Cargo.toml +++ b/crates/kitsune-storage/Cargo.toml @@ -11,12 +11,12 @@ derive_more = { version = "1.0.0-beta.6", features = ["from"] } futures-util = "0.3.30" kitsune-s3 = { path = "../kitsune-s3" } rusty-s3 = { version = "0.5.0", default-features = false } -tokio = { version = "1.36.0", features = ["fs", "io-util"] } +tokio = { version = "1.37.0", features = ["fs", "io-util"] } tokio-util = { version = "0.7.10", features = ["io"] } [dev-dependencies] tempfile = "3.10.1" -tokio = { version = "1.36.0", features = ["macros", "rt"] } +tokio = { version = "1.37.0", features = ["macros", "rt"] } [lints] workspace = true diff --git a/crates/kitsune-test/Cargo.toml b/crates/kitsune-test/Cargo.toml index 9213f89cc..1282d83f4 100644 --- a/crates/kitsune-test/Cargo.toml +++ b/crates/kitsune-test/Cargo.toml @@ -16,7 +16,7 @@ kitsune-config = { path = "../kitsune-config" } kitsune-db = { path = "../kitsune-db" } kitsune-s3 = { path = "../kitsune-s3" } multiplex-pool = { path = "../../lib/multiplex-pool" } -pin-project-lite = "0.2.13" +pin-project-lite = "0.2.14" rand = "0.8.5" redis = { version = "0.25.2", default-features = false, features = [ "connection-manager", @@ -29,7 +29,7 @@ testcontainers-modules = { version = "0.3.6", features = [ "postgres", "redis", ] } -tokio = { version = "1.36.0", features = ["time"] } +tokio = { version = "1.37.0", features = ["time"] } url = "2.5.0" uuid = { version = "1.8.0", features = ["fast-rng", "v4"] } diff --git a/crates/kitsune-util/Cargo.toml b/crates/kitsune-util/Cargo.toml index ca8de3e4e..ad79444ab 100644 --- a/crates/kitsune-util/Cargo.toml +++ b/crates/kitsune-util/Cargo.toml @@ -16,7 +16,7 @@ pulldown-cmark = { version = "0.10.0", default-features = false, features = [ ] } rand = "0.8.5" speedy-uuid = { path = "../../lib/speedy-uuid" } -tokio = { version = "1.36.0", features = ["macros"] } +tokio = { version = "1.37.0", features = ["macros"] } [lints] workspace = true diff --git a/crates/kitsune-wasm-mrf/Cargo.toml b/crates/kitsune-wasm-mrf/Cargo.toml index 529b7e00e..42bb168d0 100644 --- a/crates/kitsune-wasm-mrf/Cargo.toml +++ b/crates/kitsune-wasm-mrf/Cargo.toml @@ -8,14 +8,14 @@ build = "build.rs" [dependencies] async-trait = "0.1.79" +color-eyre = "0.6.3" derive_more = { version = "1.0.0-beta.6", features = ["from"] } -enum_dispatch = "0.3.12" +enum_dispatch = "0.3.13" futures-util = { version = "0.3.30", default-features = false, features = [ "alloc", ] } kitsune-config = { path = "../kitsune-config" } kitsune-type = { path = "../kitsune-type" } -miette = "7.2.0" mrf-manifest = { path = "../../lib/mrf-manifest", features = ["decode"] } multiplex-pool = { path = "../../lib/multiplex-pool" } redis = { version = "0.25.2", default-features = false, features = [ @@ -27,7 +27,7 @@ slab = "0.4.9" sled = "0.34.7" smol_str = "0.2.1" thiserror = "1.0.58" -tokio = { version = "1.36.0", features = ["fs"] } +tokio = { version = "1.37.0", features = ["fs"] } tracing = "0.1.40" typed-builder = "0.18.1" walkdir = "2.5.0" @@ -44,7 +44,7 @@ wasmtime-wasi = { version = "19.0.0", default-features = false } [dev-dependencies] tempfile = "3.10.1" -tokio = { version = "1.36.0", features = ["macros", "rt"] } +tokio = { version = "1.37.0", features = ["macros", "rt"] } tracing-subscriber = "0.3.18" [lints] diff --git a/crates/kitsune-wasm-mrf/example-mrf/Cargo.toml b/crates/kitsune-wasm-mrf/example-mrf/Cargo.toml index 731c3c9e8..5a31e10f5 100644 --- a/crates/kitsune-wasm-mrf/example-mrf/Cargo.toml +++ b/crates/kitsune-wasm-mrf/example-mrf/Cargo.toml @@ -11,7 +11,7 @@ crate-type = ["cdylib"] [dependencies] rand = "0.8.5" -wit-bindgen = "0.22.0" +wit-bindgen = "0.23.0" [lints] workspace = true diff --git a/crates/kitsune-wasm-mrf/src/error.rs b/crates/kitsune-wasm-mrf/src/error.rs index ff670a613..db7f9690c 100644 --- a/crates/kitsune-wasm-mrf/src/error.rs +++ b/crates/kitsune-wasm-mrf/src/error.rs @@ -1,7 +1,6 @@ -use miette::Diagnostic; use thiserror::Error; -#[derive(Debug, Diagnostic, Error)] +#[derive(Debug, Error)] pub enum Error { #[error(transparent)] Json(#[from] simd_json::Error), diff --git a/crates/kitsune-wasm-mrf/src/kv_storage/fs.rs b/crates/kitsune-wasm-mrf/src/kv_storage/fs.rs index bd098e0f6..9ee6befc9 100644 --- a/crates/kitsune-wasm-mrf/src/kv_storage/fs.rs +++ b/crates/kitsune-wasm-mrf/src/kv_storage/fs.rs @@ -1,5 +1,5 @@ use super::{Backend, BoxError, BucketBackend}; -use miette::IntoDiagnostic; +use color_eyre::eyre; use std::path::Path; pub struct FsBackend { @@ -7,12 +7,12 @@ pub struct FsBackend { } impl FsBackend { - pub fn from_path

(path: P) -> miette::Result + pub fn from_path

(path: P) -> eyre::Result where P: AsRef, { Ok(Self { - inner: sled::open(path).into_diagnostic()?, + inner: sled::open(path)?, }) } } diff --git a/crates/kitsune-wasm-mrf/src/kv_storage/redis.rs b/crates/kitsune-wasm-mrf/src/kv_storage/redis.rs index b4b66fc3f..17ee67da3 100644 --- a/crates/kitsune-wasm-mrf/src/kv_storage/redis.rs +++ b/crates/kitsune-wasm-mrf/src/kv_storage/redis.rs @@ -1,5 +1,5 @@ use super::BoxError; -use miette::IntoDiagnostic; +use color_eyre::eyre; use redis::{aio::ConnectionManager, AsyncCommands}; const REDIS_NAMESPACE: &str = "MRF-KV-STORE"; @@ -9,14 +9,13 @@ pub struct RedisBackend { } impl RedisBackend { - pub async fn from_client(client: redis::Client, pool_size: usize) -> miette::Result { + pub async fn from_client(client: redis::Client, pool_size: usize) -> eyre::Result { let pool = multiplex_pool::Pool::from_producer( || client.get_connection_manager(), pool_size, multiplex_pool::RoundRobinStrategy::default(), ) - .await - .into_diagnostic()?; + .await?; Ok(Self { pool }) } diff --git a/crates/kitsune-wasm-mrf/src/lib.rs b/crates/kitsune-wasm-mrf/src/lib.rs index 5477faffb..cda5bdc6b 100644 --- a/crates/kitsune-wasm-mrf/src/lib.rs +++ b/crates/kitsune-wasm-mrf/src/lib.rs @@ -5,12 +5,12 @@ use self::{ ctx::{construct_store, Context}, mrf_wit::v1::fep::mrf::types::{Direction, Error as MrfError}, }; -use futures_util::{stream::FuturesUnordered, Stream, StreamExt, TryFutureExt, TryStreamExt}; +use color_eyre::{eyre, Section}; +use futures_util::{stream::FuturesUnordered, Stream, TryFutureExt, TryStreamExt}; use kitsune_config::mrf::{ Configuration as MrfConfiguration, FsKvStorage, KvStorage, RedisKvStorage, }; use kitsune_type::ap::Activity; -use miette::{Diagnostic, IntoDiagnostic}; use mrf_manifest::{Manifest, ManifestV1}; use smol_str::SmolStr; use std::{ @@ -20,7 +20,6 @@ use std::{ path::{Path, PathBuf}, sync::Arc, }; -use thiserror::Error; use tokio::fs; use typed_builder::TypedBuilder; use walkdir::WalkDir; @@ -64,14 +63,11 @@ fn load_mrf_module( engine: &Engine, module_path: &Path, bytes: &[u8], -) -> miette::Result, Component)>> { - let component = Component::new(engine, bytes).map_err(|err| { - miette::Report::new(ComponentParseError { - path_help: format!("path to the module: {}", module_path.display()), - advice: "Did you make the WASM file a component via `wasm-tools`?", - }) - .wrap_err(err) - })?; +) -> eyre::Result, Component)>> { + let component = Component::new(engine, bytes) + .map_err(eyre::Report::msg) + .with_note(|| format!("path to the module: {}", module_path.display())) + .suggestion("Did you make the WASM file a component via `wasm-tools`?")?; let Some((manifest, _section_range)) = mrf_manifest::decode(bytes)? else { error!("missing manifest. skipping load."); @@ -93,14 +89,6 @@ pub enum Outcome<'a> { Reject, } -#[derive(Debug, Diagnostic, Error)] -#[error("{path_help}")] -struct ComponentParseError { - path_help: String, - #[help] - advice: &'static str, -} - pub struct MrfModule { pub component: Component, pub config: SmolStr, @@ -121,11 +109,11 @@ impl MrfService { engine: Engine, modules: Vec, storage: kv_storage::BackendDispatch, - ) -> miette::Result { + ) -> eyre::Result { let mut linker = Linker::::new(&engine); - mrf_wit::v1::Mrf::add_to_linker(&mut linker, |ctx| ctx).map_err(miette::Report::msg)?; - wasmtime_wasi::command::add_to_linker(&mut linker).map_err(miette::Report::msg)?; + mrf_wit::v1::Mrf::add_to_linker(&mut linker, |ctx| ctx).map_err(eyre::Report::msg)?; + wasmtime_wasi::command::add_to_linker(&mut linker).map_err(eyre::Report::msg)?; Ok(Self { engine, @@ -136,13 +124,13 @@ impl MrfService { } #[instrument(skip_all, fields(module_dir = %config.module_dir))] - pub async fn from_config(config: &MrfConfiguration) -> miette::Result { + pub async fn from_config(config: &MrfConfiguration) -> eyre::Result { let storage = match config.storage { KvStorage::Fs(FsKvStorage { ref path }) => { kv_storage::FsBackend::from_path(path.as_str())?.into() } KvStorage::Redis(RedisKvStorage { ref url, pool_size }) => { - let client = redis::Client::open(url.as_str()).into_diagnostic()?; + let client = redis::Client::open(url.as_str())?; kv_storage::RedisBackend::from_client(client, pool_size.get()) .await? .into() @@ -155,9 +143,9 @@ impl MrfService { .async_support(true) .wasm_component_model(true); - let engine = Engine::new(&engine_config).map_err(miette::Report::msg)?; + let engine = Engine::new(&engine_config).map_err(eyre::Report::msg)?; let wasm_data_stream = find_mrf_modules(config.module_dir.as_str()) - .map(IntoDiagnostic::into_diagnostic) + .map_err(eyre::Report::from) .and_then(|(module_path, wasm_data)| { let engine = &engine; diff --git a/crates/kitsune-webfinger/Cargo.toml b/crates/kitsune-webfinger/Cargo.toml index 8440cfdcb..26dfee60a 100644 --- a/crates/kitsune-webfinger/Cargo.toml +++ b/crates/kitsune-webfinger/Cargo.toml @@ -8,6 +8,7 @@ license.workspace = true [dependencies] async-trait = "0.1.79" autometrics = { version = "1.0.1", default-features = false } +eyre = "0.6.12" futures-util = "0.3.30" http = "1.1.0" kitsune-cache = { path = "../kitsune-cache" } @@ -28,7 +29,7 @@ http-body-util = "0.1.1" hyper = "1.2.0" pretty_assertions = "1.4.0" simd-json = "0.13.9" -tokio = { version = "1.36.0", features = ["macros"] } +tokio = { version = "1.37.0", features = ["macros"] } tower = { version = "0.4.13", default-features = false, features = ["util"] } [lints] diff --git a/crates/kitsune-webfinger/src/lib.rs b/crates/kitsune-webfinger/src/lib.rs index 581fad62f..cd2c02aa8 100644 --- a/crates/kitsune-webfinger/src/lib.rs +++ b/crates/kitsune-webfinger/src/lib.rs @@ -6,9 +6,8 @@ use autometrics::autometrics; use futures_util::future::{FutureExt, OptionFuture}; use http::{HeaderValue, StatusCode}; use kitsune_cache::{ArcCache, CacheBackend, RedisCache}; -use kitsune_core::consts::USER_AGENT; use kitsune_core::{ - error::BoxError, + consts::USER_AGENT, traits::{resolver::AccountResource, Resolver}, }; use kitsune_http_client::Client; @@ -73,7 +72,7 @@ impl Resolver for Webfinger { &self, username: &str, domain: &str, - ) -> Result, BoxError> { + ) -> eyre::Result> { // XXX: Assigning the arguments to local bindings because the `#[instrument]` attribute // desugars to an `async move {}` block, inside which mutating the function arguments would // upset the borrowck diff --git a/flake.lock b/flake.lock index b4d584edb..635450cac 100644 --- a/flake.lock +++ b/flake.lock @@ -71,11 +71,11 @@ "pre-commit-hooks": "pre-commit-hooks_2" }, "locked": { - "lastModified": 1711122363, - "narHash": "sha256-Yw/t9HCY9U/EpkXlzW+5o/WEpZKUNgrSbcuHOZFpAXU=", + "lastModified": 1711676035, + "narHash": "sha256-m8AHpQR0WVVEoWWOpXVNvqfB5cXiMRnHfcYYm2zg7tk=", "owner": "cachix", "repo": "devenv", - "rev": "63c7109f20b5ded0bc07f95ece9518bbb7fdea5b", + "rev": "19098329b4e79f60705d7fb241a3617266658f98", "type": "github" }, "original": { @@ -468,11 +468,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1711001935, - "narHash": "sha256-URtGpHue7HHZK0mrHnSf8wJ6OmMKYSsoLmJybrOLFSQ=", + "lastModified": 1711523803, + "narHash": "sha256-UKcYiHWHQynzj6CN/vTcix4yd1eCu1uFdsuarupdCQQ=", "owner": "nixos", "repo": "nixpkgs", - "rev": "20f77aa09916374aa3141cbc605c955626762c9a", + "rev": "2726f127c15a4cc9810843b96cad73c7eb39e443", "type": "github" }, "original": { @@ -581,11 +581,11 @@ ] }, "locked": { - "lastModified": 1711073443, - "narHash": "sha256-PpNb4xq7U5Q/DdX40qe7CijUsqhVVM3VZrhN0+c6Lcw=", + "lastModified": 1711678273, + "narHash": "sha256-7lIB0hMRnfzx/9oSIwTnwXmVnbvVGRoadOCW+1HI5zY=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "eec55ba9fcde6be4c63942827247e42afef7fafe", + "rev": "42a168449605950935f15ea546f6f770e5f7f629", "type": "github" }, "original": { diff --git a/kitsune-cli/Cargo.toml b/kitsune-cli/Cargo.toml index 92611a826..d0408b62c 100644 --- a/kitsune-cli/Cargo.toml +++ b/kitsune-cli/Cargo.toml @@ -14,16 +14,16 @@ eula = false [dependencies] clap = { version = "4.5.4", features = ["derive", "wrap_help"] } +color-eyre = "0.6.3" diesel = "2.1.5" diesel-async = "0.4.1" dotenvy = "0.15.7" envy = "0.4.2" kitsune-config = { path = "../crates/kitsune-config" } kitsune-db = { path = "../crates/kitsune-db" } -miette = { version = "7.2.0", features = ["fancy"] } serde = { version = "1.0.197", features = ["derive"] } speedy-uuid = { path = "../lib/speedy-uuid" } -tokio = { version = "1.36.0", features = ["full"] } +tokio = { version = "1.37.0", features = ["full"] } tracing-subscriber = "0.3.18" [build-dependencies] diff --git a/kitsune-cli/src/main.rs b/kitsune-cli/src/main.rs index 01690ec8d..add367524 100644 --- a/kitsune-cli/src/main.rs +++ b/kitsune-cli/src/main.rs @@ -1,8 +1,7 @@ use self::{config::Configuration, role::RoleSubcommand}; use clap::{Parser, Subcommand}; -use diesel_async::scoped_futures::ScopedFutureExt; +use color_eyre::eyre::Result; use kitsune_config::database::Configuration as DatabaseConfig; -use miette::{IntoDiagnostic, Result}; mod config; mod role; @@ -24,11 +23,11 @@ struct App { #[tokio::main] async fn main() -> Result<()> { - miette::set_panic_hook(); + color_eyre::install()?; dotenvy::dotenv().ok(); tracing_subscriber::fmt::init(); - let config: Configuration = envy::from_env().into_diagnostic()?; + let config: Configuration = envy::from_env()?; let db_conn = kitsune_db::connect(&DatabaseConfig { url: config.database_url.into(), max_connections: 1, @@ -38,18 +37,10 @@ async fn main() -> Result<()> { let cmd = App::parse(); - db_conn - .with_connection(|db_conn| { - async move { - match cmd.subcommand { - AppSubcommand::Role(cmd) => self::role::handle(cmd, db_conn).await?, - } - - Ok::<_, miette::Report>(()) - } - .scoped() - }) - .await?; + let mut db_conn = db_conn.get().await?; + match cmd.subcommand { + AppSubcommand::Role(cmd) => self::role::handle(cmd, &mut db_conn).await?, + } Ok(()) } diff --git a/kitsune-cli/src/role.rs b/kitsune-cli/src/role.rs index 3595b39ed..290b74da5 100644 --- a/kitsune-cli/src/role.rs +++ b/kitsune-cli/src/role.rs @@ -1,11 +1,11 @@ use clap::{Args, Subcommand, ValueEnum}; +use color_eyre::eyre::Result; use diesel::{BelongingToDsl, BoolExpressionMethods, ExpressionMethods, QueryDsl}; use diesel_async::{AsyncPgConnection, RunQueryDsl}; use kitsune_db::model::{ user::User, user_role::{NewUserRole, Role as DbRole, UserRole}, }; -use miette::{IntoDiagnostic, Result}; use speedy_uuid::Uuid; #[derive(Subcommand)] @@ -57,8 +57,7 @@ async fn add_role(db_conn: &mut AsyncPgConnection, username_str: &str, role: Rol let user = users .filter(username.eq(username_str)) .first::(db_conn) - .await - .into_diagnostic()?; + .await?; let new_role = NewUserRole { id: Uuid::now_v7(), @@ -69,8 +68,7 @@ async fn add_role(db_conn: &mut AsyncPgConnection, username_str: &str, role: Rol diesel::insert_into(users_roles::table) .values(&new_role) .execute(db_conn) - .await - .into_diagnostic()?; + .await?; Ok(()) } @@ -81,13 +79,11 @@ async fn list_roles(db_conn: &mut AsyncPgConnection, username_str: &str) -> Resu let user: User = users::table .filter(users::username.eq(username_str)) .first(db_conn) - .await - .into_diagnostic()?; + .await?; let roles = UserRole::belonging_to(&user) .load::(db_conn) - .await - .into_diagnostic()?; + .await?; println!("User \"{username_str}\" has the following roles:"); for role in roles { @@ -107,8 +103,7 @@ async fn remove_role( let user = users::table .filter(users::username.eq(username_str)) .first::(db_conn) - .await - .into_diagnostic()?; + .await?; diesel::delete( users_roles::table.filter( @@ -118,8 +113,7 @@ async fn remove_role( ), ) .execute(db_conn) - .await - .into_diagnostic()?; + .await?; Ok(()) } diff --git a/kitsune-job-runner/Cargo.toml b/kitsune-job-runner/Cargo.toml index 620328659..fde6425a2 100644 --- a/kitsune-job-runner/Cargo.toml +++ b/kitsune-job-runner/Cargo.toml @@ -14,6 +14,7 @@ eula = false [dependencies] athena = { path = "../lib/athena" } clap = { version = "4.5.4", features = ["derive", "wrap_help"] } +color-eyre = "0.6.3" just-retry = { path = "../lib/just-retry" } kitsune-config = { path = "../crates/kitsune-config" } kitsune-core = { path = "../crates/kitsune-core" } @@ -26,7 +27,6 @@ kitsune-observability = { path = "../crates/kitsune-observability" } kitsune-service = { path = "../crates/kitsune-service" } kitsune-url = { path = "../crates/kitsune-url" } kitsune-wasm-mrf = { path = "../crates/kitsune-wasm-mrf" } -miette = { version = "7.2.0", features = ["fancy"] } mimalloc = "0.1.39" multiplex-pool = { path = "../lib/multiplex-pool" } redis = { version = "0.25.2", default-features = false, features = [ @@ -34,7 +34,7 @@ redis = { version = "0.25.2", default-features = false, features = [ "connection-manager", "tokio-rustls-comp", ] } -tokio = { version = "1.36.0", features = ["full"] } +tokio = { version = "1.37.0", features = ["full"] } tracing = "0.1.40" typed-builder = "0.18.1" diff --git a/kitsune-job-runner/src/main.rs b/kitsune-job-runner/src/main.rs index 8194d0da7..8912bc4af 100644 --- a/kitsune-job-runner/src/main.rs +++ b/kitsune-job-runner/src/main.rs @@ -1,4 +1,5 @@ use clap::Parser; +use color_eyre::eyre; use kitsune_config::Configuration; use kitsune_core::consts::VERSION; use kitsune_federation_filter::FederationFilter; @@ -6,7 +7,6 @@ use kitsune_job_runner::JobDispatcherState; use kitsune_service::{attachment::AttachmentService, prepare}; use kitsune_url::UrlService; use kitsune_wasm_mrf::MrfService; -use miette::IntoDiagnostic; use std::path::PathBuf; #[global_allocator] @@ -22,8 +22,8 @@ struct Args { } #[tokio::main] -async fn main() -> miette::Result<()> { - miette::set_panic_hook(); +async fn main() -> eyre::Result<()> { + color_eyre::install()?; let args = Args::parse(); let config = Configuration::load(args.config).await?; @@ -31,9 +31,8 @@ async fn main() -> miette::Result<()> { kitsune_observability::initialise(env!("CARGO_PKG_NAME"), &config)?; let db_pool = kitsune_db::connect(&config.database).await?; - let job_queue = kitsune_job_runner::prepare_job_queue(db_pool.clone(), &config.job_queue) - .await - .into_diagnostic()?; + let job_queue = + kitsune_job_runner::prepare_job_queue(db_pool.clone(), &config.job_queue).await?; let mrf_service = MrfService::from_config(&config.mrf).await?; let url_service = UrlService::builder() diff --git a/kitsune/Cargo.toml b/kitsune/Cargo.toml index 936e2bc4e..4def0ed3f 100644 --- a/kitsune/Cargo.toml +++ b/kitsune/Cargo.toml @@ -69,7 +69,6 @@ kitsune-util = { path = "../crates/kitsune-util" } kitsune-wasm-mrf = { path = "../crates/kitsune-wasm-mrf" } kitsune-webfinger = { path = "../crates/kitsune-webfinger" } metrics = "=0.22.0" -miette = { version = "7.2.0", features = ["fancy"] } mimalloc = "0.1.39" mime = "0.3.17" mime_guess = { version = "2.0.4", default-features = false } @@ -90,7 +89,7 @@ strum = { version = "0.26.2", features = ["derive", "phf"] } tempfile = "3.10.1" thiserror = "1.0.58" time = "0.3.34" -tokio = { version = "1.36.0", features = ["full"] } +tokio = { version = "1.37.0", features = ["full"] } tokio-util = { version = "0.7.10", features = ["compat"] } tower-stop-using-brave = { path = "../lib/tower-stop-using-brave" } tower-x-clacks-overhead = { path = "../lib/tower-x-clacks-overhead" } @@ -126,6 +125,7 @@ kitsune-mastodon = { path = "../crates/kitsune-mastodon", optional = true } # "oidc" feature kitsune-oidc = { path = "../crates/kitsune-oidc", optional = true } +color-eyre = "0.6.3" [build-dependencies] camino = "1.1.6" diff --git a/kitsune/src/error.rs b/kitsune/src/error.rs index 1de24ae55..53452374f 100644 --- a/kitsune/src/error.rs +++ b/kitsune/src/error.rs @@ -4,14 +4,12 @@ use axum::{ extract::multipart::MultipartError, response::{IntoResponse, Response}, }; +use color_eyre::eyre; use diesel_async::pooled_connection::bb8; use http::StatusCode; -use kitsune_core::error::{BoxError, HttpError}; +use kitsune_core::error::HttpError; use kitsune_service::error::{Error as ServiceError, PostError}; -use std::{ - fmt::{Debug, Display}, - str::ParseBoolError, -}; +use std::{fmt::Debug, str::ParseBoolError}; use thiserror::Error; pub type Result = std::result::Result; @@ -41,7 +39,7 @@ pub enum Error { Der(#[from] der::Error), #[error(transparent)] - Fetcher(BoxError), + Fetcher(eyre::Report), #[error(transparent)] Http(#[from] http::Error), @@ -115,18 +113,6 @@ pub enum OAuth2Error { Web(#[from] oxide_auth_axum::WebError), } -impl From> for Error -where - E: Into + Debug + Display, -{ - fn from(value: kitsune_db::PoolError) -> Self { - match value { - kitsune_db::PoolError::Pool(err) => err.into(), - kitsune_db::PoolError::User(err) => err.into(), - } - } -} - impl From for Response { fn from(err: Error) -> Response { err.into_response() diff --git a/kitsune/src/http/extractor/auth.rs b/kitsune/src/http/extractor/auth.rs index 298260ba7..7101c993c 100644 --- a/kitsune/src/http/extractor/auth.rs +++ b/kitsune/src/http/extractor/auth.rs @@ -11,10 +11,11 @@ use diesel_async::RunQueryDsl; use headers::{authorization::Bearer, Authorization}; use http::request::Parts; use kitsune_db::{ + catch_error, model::{account::Account, user::User}, schema::{accounts, oauth2_access_tokens, users}, + with_connection, }; -use scoped_futures::ScopedFutureExt; use time::OffsetDateTime; /// Mastodon-specific auth extractor alias @@ -62,16 +63,14 @@ impl FromRequestParts .filter(oauth2_access_tokens::expires_at.gt(OffsetDateTime::now_utc())); } - let (user, account) = state - .db_pool - .with_connection(|db_conn| { - user_account_query - .select(<(User, Account)>::as_select()) - .get_result(db_conn) - .scoped() - }) - .await - .map_err(Error::from)?; + let (user, account) = catch_error!(with_connection!(state.db_pool, |db_conn| { + user_account_query + .select(<(User, Account)>::as_select()) + .get_result(db_conn) + .await + .map_err(Error::from) + })) + .map_err(Error::from)??; Ok(Self(UserData { account, user })) } diff --git a/kitsune/src/http/extractor/signed_activity.rs b/kitsune/src/http/extractor/signed_activity.rs index 7ff5fa87e..62b2bae42 100644 --- a/kitsune/src/http/extractor/signed_activity.rs +++ b/kitsune/src/http/extractor/signed_activity.rs @@ -15,7 +15,7 @@ use diesel_async::RunQueryDsl; use http::StatusCode; use http_body_util::BodyExt; use kitsune_core::{error::HttpError, traits::fetcher::AccountFetchOptions}; -use kitsune_db::{model::account::Account, schema::accounts, PgPool}; +use kitsune_db::{model::account::Account, schema::accounts, with_connection, PgPool}; use kitsune_type::ap::Activity; use kitsune_wasm_mrf::Outcome; use scoped_futures::ScopedFutureExt; @@ -116,20 +116,18 @@ impl FromRequest for SignedActivity { async fn verify_signature( req: &http::Request<()>, - db_conn: &PgPool, + db_pool: &PgPool, expected_account: Option<&Account>, ) -> Result { let is_valid = http_signatures::cavage::easy::verify(req, |key_id| { async move { - let remote_user: Account = db_conn - .with_connection(|db_conn| { - accounts::table - .filter(accounts::public_key_id.eq(key_id)) - .select(Account::as_select()) - .first(db_conn) - .scoped() - }) - .await?; + let remote_user: Account = with_connection!(db_pool, |db_conn| { + accounts::table + .filter(accounts::public_key_id.eq(key_id)) + .select(Account::as_select()) + .first(db_conn) + .await + })?; // If we have an expected account, which we have in the case of an incoming new activity, // then we do this comparison. diff --git a/kitsune/src/http/graphql/query/instance.rs b/kitsune/src/http/graphql/query/instance.rs index 07a86d6f3..6b31d3fd0 100644 --- a/kitsune/src/http/graphql/query/instance.rs +++ b/kitsune/src/http/graphql/query/instance.rs @@ -1,7 +1,6 @@ use crate::http::graphql::{types::Instance, ContextExt}; use async_graphql::{Context, Object, Result}; use kitsune_core::consts::VERSION; -use std::convert::Into; #[derive(Default)] pub struct InstanceQuery; diff --git a/kitsune/src/http/graphql/types/account.rs b/kitsune/src/http/graphql/types/account.rs index 8f015c1d5..eb9626a46 100644 --- a/kitsune/src/http/graphql/types/account.rs +++ b/kitsune/src/http/graphql/types/account.rs @@ -12,9 +12,9 @@ use kitsune_db::{ account::Account as DbAccount, media_attachment::MediaAttachment as DbMediaAttachment, }, schema::media_attachments, + with_connection, }; use kitsune_service::account::GetPosts; -use scoped_futures::ScopedFutureExt; use speedy_uuid::Uuid; use time::OffsetDateTime; @@ -41,20 +41,15 @@ impl Account { let db_pool = &ctx.state().db_pool; if let Some(avatar_id) = self.avatar_id { - db_pool - .with_connection(|db_conn| { - async move { - media_attachments::table - .find(avatar_id) - .get_result::(db_conn) - .await - .optional() - .map(|attachment| attachment.map(Into::into)) - } - .scoped() - }) - .await - .map_err(Into::into) + with_connection!(db_pool, |db_conn| { + media_attachments::table + .find(avatar_id) + .get_result::(db_conn) + .await + .optional() + .map(|attachment| attachment.map(Into::into)) + }) + .map_err(Into::into) } else { Ok(None) } @@ -64,20 +59,15 @@ impl Account { let db_pool = &ctx.state().db_pool; if let Some(header_id) = self.header_id { - db_pool - .with_connection(|db_conn| { - async move { - media_attachments::table - .find(header_id) - .get_result::(db_conn) - .await - .optional() - .map(|attachment| attachment.map(Into::into)) - } - .scoped() - }) - .await - .map_err(Into::into) + with_connection!(db_pool, |db_conn| { + media_attachments::table + .find(header_id) + .get_result::(db_conn) + .await + .optional() + .map(|attachment| attachment.map(Into::into)) + }) + .map_err(Into::into) } else { Ok(None) } diff --git a/kitsune/src/http/graphql/types/post.rs b/kitsune/src/http/graphql/types/post.rs index 94a9dd343..d8a18d6f1 100644 --- a/kitsune/src/http/graphql/types/post.rs +++ b/kitsune/src/http/graphql/types/post.rs @@ -7,8 +7,8 @@ use futures_util::TryStreamExt; use kitsune_db::{ model::{media_attachment::MediaAttachment as DbMediaAttachment, post::Post as DbPost}, schema::{media_attachments, posts_media_attachments}, + with_connection, }; -use scoped_futures::ScopedFutureExt; use speedy_uuid::Uuid; use time::OffsetDateTime; @@ -44,22 +44,17 @@ impl Post { pub async fn attachments(&self, ctx: &Context<'_>) -> Result> { let db_pool = &ctx.state().db_pool; - let attachments = db_pool - .with_connection(|db_conn| { - async move { - media_attachments::table - .inner_join(posts_media_attachments::table) - .filter(posts_media_attachments::post_id.eq(self.id)) - .select(DbMediaAttachment::as_select()) - .load_stream(db_conn) - .await? - .map_ok(Into::into) - .try_collect() - .await - } - .scoped() - }) - .await?; + let attachments = with_connection!(db_pool, |db_conn| { + media_attachments::table + .inner_join(posts_media_attachments::table) + .filter(posts_media_attachments::post_id.eq(self.id)) + .select(DbMediaAttachment::as_select()) + .load_stream(db_conn) + .await? + .map_ok(Into::into) + .try_collect() + .await + })?; Ok(attachments) } diff --git a/kitsune/src/http/graphql/types/user.rs b/kitsune/src/http/graphql/types/user.rs index a7952fe25..9dc763ac0 100644 --- a/kitsune/src/http/graphql/types/user.rs +++ b/kitsune/src/http/graphql/types/user.rs @@ -6,8 +6,8 @@ use diesel_async::RunQueryDsl; use kitsune_db::{ model::{account::Account as DbAccount, user::User as DbUser}, schema::{accounts, users}, + with_connection, }; -use scoped_futures::ScopedFutureExt; use speedy_uuid::Uuid; use time::OffsetDateTime; @@ -27,21 +27,16 @@ pub struct User { impl User { pub async fn account(&self, ctx: &Context<'_>) -> Result { let db_pool = &ctx.state().db_pool; - db_pool - .with_connection(|db_conn| { - async move { - users::table - .find(self.id) - .inner_join(accounts::table) - .select(DbAccount::as_select()) - .get_result::(db_conn) - .await - .map(Into::into) - } - .scoped() - }) - .await - .map_err(Into::into) + with_connection!(db_pool, |db_conn| { + users::table + .find(self.id) + .inner_join(accounts::table) + .select(DbAccount::as_select()) + .get_result::(db_conn) + .await + .map(Into::into) + }) + .map_err(Into::into) } } diff --git a/kitsune/src/http/handler/mastodon/api/v1/accounts/relationships.rs b/kitsune/src/http/handler/mastodon/api/v1/accounts/relationships.rs index 4c0625e2d..b0f4ac7bb 100644 --- a/kitsune/src/http/handler/mastodon/api/v1/accounts/relationships.rs +++ b/kitsune/src/http/handler/mastodon/api/v1/accounts/relationships.rs @@ -7,10 +7,9 @@ use axum_extra::extract::Query; use diesel::{ExpressionMethods, QueryDsl, SelectableHelper}; use diesel_async::RunQueryDsl; use futures_util::StreamExt; -use kitsune_db::{model::account::Account, schema::accounts, PgPool}; +use kitsune_db::{model::account::Account, schema::accounts, with_connection, PgPool}; use kitsune_mastodon::MastodonMapper; use kitsune_type::mastodon::relationship::Relationship; -use scoped_futures::ScopedFutureExt; use serde::Deserialize; use speedy_uuid::Uuid; use utoipa::IntoParams; @@ -39,15 +38,13 @@ pub async fn get( State(mastodon_mapper): State, Query(query): Query, ) -> Result>> { - let mut account_stream = db_pool - .with_connection(|db_conn| { - accounts::table - .filter(accounts::id.eq_any(&query.id)) - .select(Account::as_select()) - .load_stream::(db_conn) - .scoped() - }) - .await?; + let mut account_stream = with_connection!(db_pool, |db_conn| { + accounts::table + .filter(accounts::id.eq_any(&query.id)) + .select(Account::as_select()) + .load_stream::(db_conn) + .await + })?; let mut relationships = Vec::with_capacity(query.id.len()); while let Some(account) = account_stream.next().await.transpose()? { diff --git a/kitsune/src/http/handler/nodeinfo/two_one.rs b/kitsune/src/http/handler/nodeinfo/two_one.rs index 5be023b7c..9983c2450 100644 --- a/kitsune/src/http/handler/nodeinfo/two_one.rs +++ b/kitsune/src/http/handler/nodeinfo/two_one.rs @@ -5,14 +5,13 @@ use diesel_async::RunQueryDsl; use kitsune_core::consts::VERSION; use kitsune_db::{ schema::{posts, users}, - PgPool, + with_connection, PgPool, }; use kitsune_service::user::UserService; use kitsune_type::nodeinfo::two_one::{ Protocol, Services, Software, TwoOne, Usage, UsageUsers, Version, }; use kitsune_util::try_join; -use scoped_futures::ScopedFutureExt; use simd_json::{OwnedValue, ValueBuilder}; #[debug_handler(state = crate::state::Zustand)] @@ -27,20 +26,15 @@ async fn get( State(db_pool): State, State(user_service): State, ) -> Result> { - let (total, local_posts) = db_pool - .with_connection(|db_conn| { - async move { - let total_fut = users::table.count().get_result::(db_conn); - let local_posts_fut = posts::table - .filter(posts::is_local.eq(true)) - .count() - .get_result::(db_conn); + let (total, local_posts) = with_connection!(db_pool, |db_conn| { + let total_fut = users::table.count().get_result::(db_conn); + let local_posts_fut = posts::table + .filter(posts::is_local.eq(true)) + .count() + .get_result::(db_conn); - try_join!(total_fut, local_posts_fut) - } - .scoped() - }) - .await?; + try_join!(total_fut, local_posts_fut) + })?; Ok(Json(TwoOne { version: Version::TwoOne, diff --git a/kitsune/src/http/handler/oauth/authorize.rs b/kitsune/src/http/handler/oauth/authorize.rs index 5df998605..78ae3b376 100644 --- a/kitsune/src/http/handler/oauth/authorize.rs +++ b/kitsune/src/http/handler/oauth/authorize.rs @@ -19,10 +19,10 @@ use axum_flash::{Flash, IncomingFlashes}; use cursiv::CsrfHandle; use diesel::{ExpressionMethods, OptionalExtension, QueryDsl}; use diesel_async::RunQueryDsl; +use kitsune_db::with_connection; use kitsune_db::{model::user::User, schema::users, PgPool}; use oxide_auth_async::endpoint::authorization::AuthorizationFlow; use oxide_auth_axum::{OAuthRequest, OAuthResponse}; -use scoped_futures::ScopedFutureExt; use serde::Deserialize; use speedy_uuid::Uuid; @@ -69,15 +69,13 @@ pub async fn get( ) -> Result> { #[cfg(feature = "oidc")] if let Some(oidc_service) = oidc_service { - let application = db_pool - .with_connection(|db_conn| { - oauth2_applications::table - .find(query.client_id) - .filter(oauth2_applications::redirect_uri.eq(query.redirect_uri)) - .get_result::(db_conn) - .scoped() - }) - .await?; + let application = with_connection!(db_pool, |db_conn| { + oauth2_applications::table + .find(query.client_id) + .filter(oauth2_applications::redirect_uri.eq(query.redirect_uri)) + .get_result::(db_conn) + .await + })?; let auth_url = oidc_service .authorisation_url(application.id, query.scope, query.state) @@ -88,10 +86,9 @@ pub async fn get( let authenticated_user = if let Some(user_id) = cookies.get("user_id") { let id = user_id.value().parse::()?; - - db_pool - .with_connection(|db_conn| users::table.find(id).get_result(db_conn).scoped()) - .await? + with_connection!(db_pool, |db_conn| { + users::table.find(id).get_result(db_conn).await + })? } else { return Ok(Either3::E2(LoginPage { flash_messages })); }; @@ -123,18 +120,13 @@ pub async fn post( original_url.path() }; - let user = db_pool - .with_connection(|db_conn| { - async move { - users::table - .filter(users::username.eq(form.username)) - .first::(db_conn) - .await - .optional() - } - .scoped() - }) - .await?; + let user = with_connection!(db_pool, |db_conn| { + users::table + .filter(users::username.eq(form.username)) + .first::(db_conn) + .await + .optional() + })?; let Some(user) = user else { return Ok(Either::E2(( diff --git a/kitsune/src/http/handler/oidc/callback.rs b/kitsune/src/http/handler/oidc/callback.rs index e4ab4665a..0f92faaa7 100644 --- a/kitsune/src/http/handler/oidc/callback.rs +++ b/kitsune/src/http/handler/oidc/callback.rs @@ -11,11 +11,10 @@ use diesel_async::RunQueryDsl; use kitsune_core::error::HttpError; use kitsune_db::{ schema::{oauth2_applications, users}, - PgPool, + with_connection, PgPool, }; use kitsune_oidc::OidcService; use kitsune_service::user::{Register, UserService}; -use scoped_futures::ScopedFutureExt; use serde::Deserialize; #[derive(Debug, Deserialize)] @@ -36,18 +35,13 @@ pub async fn get( }; let user_info = oidc_service.get_user_info(query.state, query.code).await?; - let user = db_pool - .with_connection(|db_conn| { - async { - users::table - .filter(users::oidc_id.eq(&user_info.subject)) - .get_result(db_conn) - .await - .optional() - } - .scoped() - }) - .await?; + let user = with_connection!(db_pool, |db_conn| { + users::table + .filter(users::oidc_id.eq(&user_info.subject)) + .get_result(db_conn) + .await + .optional() + })?; let user = if let Some(user) = user { user @@ -62,14 +56,12 @@ pub async fn get( user_service.register(register).await? }; - let application = db_pool - .with_connection(|db_conn| { - oauth2_applications::table - .find(user_info.oauth2.application_id) - .get_result(db_conn) - .scoped() - }) - .await?; + let application = with_connection!(db_pool, |db_conn| { + oauth2_applications::table + .find(user_info.oauth2.application_id) + .get_result(db_conn) + .await + })?; let authorisation_code = AuthorisationCode::builder() .application(application) diff --git a/kitsune/src/http/handler/users/followers.rs b/kitsune/src/http/handler/users/followers.rs index 14c113cd8..a1732373f 100644 --- a/kitsune/src/http/handler/users/followers.rs +++ b/kitsune/src/http/handler/users/followers.rs @@ -2,13 +2,15 @@ use crate::{error::Result, http::responder::ActivityPubJson, state::Zustand}; use axum::extract::{OriginalUri, Path, State}; use diesel::{BoolExpressionMethods, ExpressionMethods, JoinOnDsl, QueryDsl}; use diesel_async::RunQueryDsl; -use kitsune_db::schema::{accounts, accounts_follows}; +use kitsune_db::{ + schema::{accounts, accounts_follows}, + with_connection, +}; use kitsune_type::ap::{ ap_context, collection::{Collection, CollectionType}, }; use kitsune_url::UrlService; -use scoped_futures::ScopedFutureExt; use speedy_uuid::Uuid; pub async fn get( @@ -17,22 +19,19 @@ pub async fn get( OriginalUri(original_uri): OriginalUri, Path(account_id): Path, ) -> Result> { - let follower_count = state - .db_pool - .with_connection(|db_conn| { - accounts_follows::table - .inner_join( - accounts::table.on(accounts_follows::account_id - .eq(accounts::id) - .and(accounts_follows::approved_at.is_not_null()) - .and(accounts::id.eq(account_id)) - .and(accounts::local.eq(true))), - ) - .count() - .get_result::(db_conn) - .scoped() - }) - .await?; + let follower_count = with_connection!(state.db_pool, |db_conn| { + accounts_follows::table + .inner_join( + accounts::table.on(accounts_follows::account_id + .eq(accounts::id) + .and(accounts_follows::approved_at.is_not_null()) + .and(accounts::id.eq(account_id)) + .and(accounts::local.eq(true))), + ) + .count() + .get_result::(db_conn) + .await + })?; let mut id = url_service.base_url(); id.push_str(original_uri.path()); diff --git a/kitsune/src/http/handler/users/following.rs b/kitsune/src/http/handler/users/following.rs index 30747a23d..478c01390 100644 --- a/kitsune/src/http/handler/users/following.rs +++ b/kitsune/src/http/handler/users/following.rs @@ -2,13 +2,15 @@ use crate::{error::Result, http::responder::ActivityPubJson, state::Zustand}; use axum::extract::{OriginalUri, Path, State}; use diesel::{BoolExpressionMethods, ExpressionMethods, JoinOnDsl, QueryDsl}; use diesel_async::RunQueryDsl; -use kitsune_db::schema::{accounts, accounts_follows}; +use kitsune_db::{ + schema::{accounts, accounts_follows}, + with_connection, +}; use kitsune_type::ap::{ ap_context, collection::{Collection, CollectionType}, }; use kitsune_url::UrlService; -use scoped_futures::ScopedFutureExt; use speedy_uuid::Uuid; pub async fn get( @@ -17,22 +19,19 @@ pub async fn get( OriginalUri(original_uri): OriginalUri, Path(account_id): Path, ) -> Result> { - let following_count = state - .db_pool - .with_connection(|db_conn| { - accounts_follows::table - .inner_join( - accounts::table.on(accounts_follows::follower_id - .eq(accounts::id) - .and(accounts_follows::approved_at.is_not_null()) - .and(accounts::id.eq(account_id)) - .and(accounts::local.eq(true))), - ) - .count() - .get_result::(db_conn) - .scoped() - }) - .await?; + let following_count = with_connection!(state.db_pool, |db_conn| { + accounts_follows::table + .inner_join( + accounts::table.on(accounts_follows::follower_id + .eq(accounts::id) + .and(accounts_follows::approved_at.is_not_null()) + .and(accounts::id.eq(account_id)) + .and(accounts::local.eq(true))), + ) + .count() + .get_result::(db_conn) + .await + })?; let id = format!("{}{}", url_service.base_url(), original_uri.path()); Ok(ActivityPubJson(Collection { diff --git a/kitsune/src/http/handler/users/inbox.rs b/kitsune/src/http/handler/users/inbox.rs index a05f52293..3e383080a 100644 --- a/kitsune/src/http/handler/users/inbox.rs +++ b/kitsune/src/http/handler/users/inbox.rs @@ -25,28 +25,23 @@ use kitsune_db::{ }, post_permission_check::{PermissionCheck, PostPermissionCheckExt}, schema::{accounts_follows, accounts_preferences, notifications, posts, posts_favourites}, + with_connection, }; use kitsune_federation_filter::FederationFilter; use kitsune_jobs::deliver::accept::DeliverAccept; use kitsune_service::job::Enqueue; use kitsune_type::ap::{Activity, ActivityType}; use kitsune_util::try_join; -use scoped_futures::ScopedFutureExt; use speedy_uuid::Uuid; use std::ops::Not; async fn accept_activity(state: &Zustand, activity: Activity) -> Result<()> { - state - .db_pool - .with_connection(|db_conn| { - diesel::update( - accounts_follows::table.filter(accounts_follows::url.eq(activity.object())), - ) + with_connection!(state.db_pool, |db_conn| { + diesel::update(accounts_follows::table.filter(accounts_follows::url.eq(activity.object()))) .set(accounts_follows::approved_at.eq(Timestamp::now_utc())) .execute(db_conn) - .scoped() - }) - .await?; + .await + })?; Ok(()) } @@ -61,30 +56,27 @@ async fn announce_activity(state: &Zustand, author: Account, activity: Activity) return Err(HttpError::BadRequest.into()); }; - state - .db_pool - .with_connection(|db_conn| { - diesel::insert_into(posts::table) - .values(NewPost { - id: Uuid::now_v7(), - account_id: author.id, - in_reply_to_id: None, - reposted_post_id: Some(reposted_post.id), - is_sensitive: false, - subject: None, - content: "", - content_source: "", - content_lang: kitsune_language::Language::Eng.into(), - link_preview_url: None, - visibility: reposted_post.visibility, - is_local: false, - url: activity.id.as_str(), - created_at: None, - }) - .execute(db_conn) - .scoped() - }) - .await?; + with_connection!(state.db_pool, |db_conn| { + diesel::insert_into(posts::table) + .values(NewPost { + id: Uuid::now_v7(), + account_id: author.id, + in_reply_to_id: None, + reposted_post_id: Some(reposted_post.id), + is_sensitive: false, + subject: None, + content: "", + content_source: "", + content_lang: kitsune_language::Language::Eng.into(), + link_preview_url: None, + visibility: reposted_post.visibility, + is_local: false, + url: activity.id.as_str(), + created_at: None, + }) + .execute(db_conn) + .await + })?; Ok(()) } @@ -117,26 +109,20 @@ async fn create_activity(state: &Zustand, author: Account, activity: Activity) - } async fn delete_activity(state: &Zustand, author: Account, activity: Activity) -> Result<()> { - let post_id = state - .db_pool - .with_connection(|db_conn| { - async move { - let post_id = posts::table - .filter(posts::account_id.eq(author.id)) - .filter(posts::url.eq(activity.object())) - .select(posts::id) - .get_result(db_conn) - .await?; - - diesel::delete(posts::table.find(post_id)) - .execute(db_conn) - .await?; + let post_id = with_connection!(state.db_pool, |db_conn| { + let post_id = posts::table + .filter(posts::account_id.eq(author.id)) + .filter(posts::url.eq(activity.object())) + .select(posts::id) + .get_result(db_conn) + .await?; - Ok::<_, Error>(post_id) - } - .scoped() - }) - .await?; + diesel::delete(posts::table.find(post_id)) + .execute(db_conn) + .await?; + + Ok::<_, Error>(post_id) + })?; state .event_emitter @@ -163,36 +149,31 @@ async fn follow_activity(state: &Zustand, author: Account, activity: Activity) - let approved_at = followed_user.locked.not().then(Timestamp::now_utc); - let follow_id = state - .db_pool - .with_connection(|db_conn| { - diesel::insert_into(accounts_follows::table) - .values(NewFollow { - id: Uuid::now_v7(), - account_id: followed_user.id, - follower_id: author.id, - approved_at, - url: activity.id.as_str(), - notify: false, - created_at: Some(activity.published), - }) - .returning(accounts_follows::id) - .get_result(db_conn) - .scoped() - }) - .await?; + let follow_id = with_connection!(state.db_pool, |db_conn| { + diesel::insert_into(accounts_follows::table) + .values(NewFollow { + id: Uuid::now_v7(), + account_id: followed_user.id, + follower_id: author.id, + approved_at, + url: activity.id.as_str(), + notify: false, + created_at: Some(activity.published), + }) + .returning(accounts_follows::id) + .get_result(db_conn) + .await + })?; if followed_user.local { - let preferences = state - .db_pool - .with_connection(|mut db_conn| { - accounts_preferences::table - .find(followed_user.id) - .select(Preferences::as_select()) - .get_result(&mut db_conn) - .scoped() - }) - .await?; + let preferences = with_connection!(state.db_pool, |db_conn| { + accounts_preferences::table + .find(followed_user.id) + .select(Preferences::as_select()) + .get_result(db_conn) + .await + })?; + if (preferences.notify_on_follow && !followed_user.locked) || (preferences.notify_on_follow_request && followed_user.locked) { @@ -205,16 +186,14 @@ async fn follow_activity(state: &Zustand, author: Account, activity: Activity) - .receiving_account_id(followed_user.id) .follow(author.id) }; - state - .db_pool - .with_connection(|mut db_conn| { - diesel::insert_into(notifications::table) - .values(notification) - .on_conflict_do_nothing() - .execute(&mut db_conn) - .scoped() - }) - .await?; + + with_connection!(state.db_pool, |db_conn| { + diesel::insert_into(notifications::table) + .values(notification) + .on_conflict_do_nothing() + .execute(db_conn) + .await + })?; } state .service @@ -231,94 +210,79 @@ async fn like_activity(state: &Zustand, author: Account, activity: Activity) -> .fetching_account_id(Some(author.id)) .build(); - state - .db_pool - .with_connection(|db_conn| { - async move { - let post = posts::table - .filter(posts::url.eq(activity.object())) - .add_post_permission_check(permission_check) - .select(Post::as_select()) - .get_result::(db_conn) - .await?; - - diesel::insert_into(posts_favourites::table) - .values(NewFavourite { - id: Uuid::now_v7(), - account_id: author.id, - post_id: post.id, - url: activity.id, - created_at: Some(Timestamp::now_utc()), - }) - .execute(db_conn) - .await?; + with_connection!(state.db_pool, |db_conn| { + let post = posts::table + .filter(posts::url.eq(activity.object())) + .add_post_permission_check(permission_check) + .select(Post::as_select()) + .get_result::(db_conn) + .await?; - Ok::<_, Error>(()) - } - .scoped() - }) - .await?; + diesel::insert_into(posts_favourites::table) + .values(NewFavourite { + id: Uuid::now_v7(), + account_id: author.id, + post_id: post.id, + url: activity.id, + created_at: Some(Timestamp::now_utc()), + }) + .execute(db_conn) + .await?; + + Ok::<_, Error>(()) + })?; Ok(()) } async fn reject_activity(state: &Zustand, author: Account, activity: Activity) -> Result<()> { - state - .db_pool - .with_connection(|db_conn| { - diesel::delete( - accounts_follows::table.filter( - accounts_follows::account_id - .eq(author.id) - .and(accounts_follows::url.eq(activity.object())), - ), - ) - .execute(db_conn) - .scoped() - }) - .await?; + with_connection!(state.db_pool, |db_conn| { + diesel::delete( + accounts_follows::table.filter( + accounts_follows::account_id + .eq(author.id) + .and(accounts_follows::url.eq(activity.object())), + ), + ) + .execute(db_conn) + .await + })?; Ok(()) } async fn undo_activity(state: &Zustand, author: Account, activity: Activity) -> Result<()> { - state - .db_pool - .with_connection(|db_conn| { - async move { - // An undo activity can apply for likes and follows and announces - let favourite_delete_fut = diesel::delete( - posts_favourites::table.filter( - posts_favourites::account_id - .eq(author.id) - .and(posts_favourites::url.eq(activity.object())), - ), - ) - .execute(db_conn); - - let follow_delete_fut = diesel::delete( - accounts_follows::table.filter( - accounts_follows::follower_id - .eq(author.id) - .and(accounts_follows::url.eq(activity.object())), - ), - ) - .execute(db_conn); - - let repost_delete_fut = diesel::delete( - posts::table.filter( - posts::url - .eq(activity.object()) - .and(posts::account_id.eq(author.id)), - ), - ) - .execute(db_conn); - - try_join!(favourite_delete_fut, follow_delete_fut, repost_delete_fut) - } - .scoped() - }) - .await?; + with_connection!(state.db_pool, |db_conn| { + // An undo activity can apply for likes and follows and announces + let favourite_delete_fut = diesel::delete( + posts_favourites::table.filter( + posts_favourites::account_id + .eq(author.id) + .and(posts_favourites::url.eq(activity.object())), + ), + ) + .execute(db_conn); + + let follow_delete_fut = diesel::delete( + accounts_follows::table.filter( + accounts_follows::follower_id + .eq(author.id) + .and(accounts_follows::url.eq(activity.object())), + ), + ) + .execute(db_conn); + + let repost_delete_fut = diesel::delete( + posts::table.filter( + posts::url + .eq(activity.object()) + .and(posts::account_id.eq(author.id)), + ), + ) + .execute(db_conn); + + try_join!(favourite_delete_fut, follow_delete_fut, repost_delete_fut) + })?; Ok(()) } diff --git a/kitsune/src/http/handler/users/outbox.rs b/kitsune/src/http/handler/users/outbox.rs index 304a75ea8..ab8a713af 100644 --- a/kitsune/src/http/handler/users/outbox.rs +++ b/kitsune/src/http/handler/users/outbox.rs @@ -8,6 +8,7 @@ use kitsune_db::{ model::{account::Account, post::Post}, post_permission_check::{PermissionCheck, PostPermissionCheckExt}, schema::accounts, + with_connection, }; use kitsune_service::account::GetPosts; use kitsune_type::ap::{ @@ -16,7 +17,6 @@ use kitsune_type::ap::{ Activity, }; use kitsune_url::UrlService; -use scoped_futures::ScopedFutureExt; use serde::{Deserialize, Serialize}; use speedy_uuid::Uuid; @@ -37,19 +37,16 @@ pub async fn get( Path(account_id): Path, Query(query): Query, ) -> Result>, ActivityPubJson>> { - let account = state - .db_pool - .with_connection(|db_conn| { - use diesel_async::RunQueryDsl; + let account = with_connection!(state.db_pool, |db_conn| { + use diesel_async::RunQueryDsl; - accounts::table - .find(account_id) - .filter(accounts::local.eq(true)) - .select(Account::as_select()) - .get_result::(db_conn) - .scoped() - }) - .await?; + accounts::table + .find(account_id) + .filter(accounts::local.eq(true)) + .select(Account::as_select()) + .get_result::(db_conn) + .await + })?; let base_url = format!("{}{}", url_service.base_url(), original_uri.path()); @@ -93,18 +90,15 @@ pub async fn get( ordered_items, }))) } else { - let public_post_count = state - .db_pool - .with_connection(|db_conn| { - use diesel_async::RunQueryDsl; + let public_post_count = with_connection!(state.db_pool, |db_conn| { + use diesel_async::RunQueryDsl; - Post::belonging_to(&account) - .add_post_permission_check(PermissionCheck::default()) - .count() - .get_result::(db_conn) - .scoped() - }) - .await?; + Post::belonging_to(&account) + .add_post_permission_check(PermissionCheck::default()) + .count() + .get_result::(db_conn) + .await + })?; let first = format!("{base_url}?page=true"); let last = format!("{base_url}?page=true&min_id={}", Uuid::nil()); diff --git a/kitsune/src/http/handler/well_known/webfinger.rs b/kitsune/src/http/handler/well_known/webfinger.rs index d3e9b410b..01a5f0049 100644 --- a/kitsune/src/http/handler/well_known/webfinger.rs +++ b/kitsune/src/http/handler/well_known/webfinger.rs @@ -83,7 +83,7 @@ mod tests { use kitsune_db::{ model::account::{ActorType, NewAccount}, schema::accounts, - PgPool, + with_connection_panicky, PgPool, }; use kitsune_federation_filter::FederationFilter; use kitsune_http_client::Client; @@ -169,12 +169,8 @@ mod tests { async fn basic() { database_test(|db_pool| { redis_test(|redis_pool| async move { - let account_id = db_pool - .with_connection(|db_conn| { - async move { Ok::<_, miette::Report>(prepare_db(db_conn).await) }.scoped() - }) - .await - .unwrap(); + let account_id = + with_connection_panicky!(db_pool, |db_conn| { prepare_db(db_conn).await }); let account_url = format!("https://example.com/users/{account_id}"); let url_service = UrlService::builder() @@ -236,16 +232,9 @@ mod tests { async fn custom_domain() { database_test(|db_pool| { redis_test(|redis_pool| async move { - db_pool - .with_connection(|db_conn| { - async move { - prepare_db(db_conn).await; - Ok::<_, miette::Report>(()) - } - .scoped() - }) - .await - .unwrap(); + with_connection_panicky!(db_pool, |db_conn| { + prepare_db(db_conn).await; + }); let url_service = UrlService::builder() .scheme("https") diff --git a/kitsune/src/http/mod.rs b/kitsune/src/http/mod.rs index d4b269f6d..7682d256a 100644 --- a/kitsune/src/http/mod.rs +++ b/kitsune/src/http/mod.rs @@ -6,9 +6,9 @@ use self::{ }; use crate::state::Zustand; use axum::{extract::DefaultBodyLimit, Router}; +use color_eyre::eyre::{self, Context}; use cursiv::CsrfLayer; use kitsune_config::server; -use miette::{Context, IntoDiagnostic}; use std::time::Duration; use tokio::net::TcpListener; use tower_http::{ @@ -37,7 +37,7 @@ pub mod extractor; pub fn create_router( state: Zustand, server_config: &server::Configuration, -) -> miette::Result { +) -> eyre::Result { let frontend_dir = &server_config.frontend_dir; let frontend_index_path = { let mut tmp = frontend_dir.to_string(); @@ -85,7 +85,6 @@ pub fn create_router( if !server_config.clacks_overhead.is_empty() { let clacks_overhead_layer = XClacksOverheadLayer::new(server_config.clacks_overhead.iter().map(AsRef::as_ref)) - .into_diagnostic() .wrap_err("Invalid clacks overhead values")?; router = router.layer(clacks_overhead_layer); @@ -112,16 +111,13 @@ pub async fn run( state: Zustand, server_config: server::Configuration, shutdown_signal: crate::signal::Receiver, -) -> miette::Result<()> { +) -> eyre::Result<()> { let router = create_router(state, &server_config)?; - let listener = TcpListener::bind(("0.0.0.0", server_config.port)) - .await - .into_diagnostic()?; + let listener = TcpListener::bind(("0.0.0.0", server_config.port)).await?; axum::serve(listener, router) .with_graceful_shutdown(shutdown_signal.wait()) - .await - .into_diagnostic()?; + .await?; Ok(()) } diff --git a/kitsune/src/lib.rs b/kitsune/src/lib.rs index 55b180744..1da99707d 100644 --- a/kitsune/src/lib.rs +++ b/kitsune/src/lib.rs @@ -16,6 +16,7 @@ use self::{ state::{EventEmitter, Service, SessionConfig, Zustand, ZustandInner}, }; use athena::JobQueue; +use color_eyre::eyre; use kitsune_config::Configuration; use kitsune_db::PgPool; use kitsune_email::MailingService; @@ -50,7 +51,7 @@ pub async fn initialise_state( config: &Configuration, db_pool: PgPool, job_queue: JobQueue, -) -> miette::Result { +) -> eyre::Result { let messaging_hub = prepare::messaging(&config.messaging).await?; let status_event_emitter = messaging_hub.emitter("event.status".into()); diff --git a/kitsune/src/main.rs b/kitsune/src/main.rs index 123e410eb..79f17be8a 100644 --- a/kitsune/src/main.rs +++ b/kitsune/src/main.rs @@ -1,9 +1,9 @@ use clap::Parser; +use color_eyre::eyre::{self, Context}; use kitsune::consts::STARTUP_FIGLET; use kitsune_config::Configuration; use kitsune_core::consts::VERSION; use kitsune_job_runner::JobDispatcherState; -use miette::{Context, IntoDiagnostic}; use std::{env, path::PathBuf}; #[global_allocator] @@ -18,7 +18,7 @@ struct Args { config: PathBuf, } -async fn boot() -> miette::Result<()> { +async fn boot() -> eyre::Result<()> { println!("{STARTUP_FIGLET}"); let args = Args::parse(); @@ -31,7 +31,6 @@ async fn boot() -> miette::Result<()> { let job_queue = kitsune_job_runner::prepare_job_queue(conn.clone(), &config.job_queue) .await - .into_diagnostic() .wrap_err("Failed to connect to the Redis instance for the job scheduler")?; let state = kitsune::initialise_state(&config, conn, job_queue.clone()).await?; @@ -58,21 +57,20 @@ async fn boot() -> miette::Result<()> { )); tokio::select! { - res = server_fut => res.into_diagnostic()??, - res = job_runner_fut => res.into_diagnostic()?, + res = server_fut => res??, + res = job_runner_fut => res?, } Ok(()) } -fn main() -> miette::Result<()> { - miette::set_panic_hook(); +fn main() -> eyre::Result<()> { + color_eyre::install()?; let runtime = tokio::runtime::Builder::new_multi_thread() .enable_all() .thread_stack_size(4 * 1024 * 1024) // Set the stack size to 4MiB - .build() - .into_diagnostic()?; + .build()?; runtime.block_on(boot()) } diff --git a/kitsune/src/oauth2/authorizer.rs b/kitsune/src/oauth2/authorizer.rs index 26c50858b..9c4184413 100644 --- a/kitsune/src/oauth2/authorizer.rs +++ b/kitsune/src/oauth2/authorizer.rs @@ -3,14 +3,14 @@ use async_trait::async_trait; use diesel::{OptionalExtension, QueryDsl}; use diesel_async::RunQueryDsl; use kitsune_db::{ + catch_error, model::oauth2, schema::{oauth2_applications, oauth2_authorization_codes}, - PgPool, + with_connection, PgPool, }; use kitsune_util::generate_secret; use oxide_auth::primitives::grant::{Extensions, Grant}; use oxide_auth_async::primitives::Authorizer; -use scoped_futures::ScopedFutureExt; #[derive(Clone)] pub struct OAuthAuthorizer { @@ -26,40 +26,34 @@ impl Authorizer for OAuthAuthorizer { let secret = generate_secret(); let expires_at = chrono_to_timestamp(grant.until); - self.db_pool - .with_connection(|db_conn| { - diesel::insert_into(oauth2_authorization_codes::table) - .values(oauth2::NewAuthorizationCode { - code: secret.as_str(), - application_id, - user_id, - scopes: scopes.as_str(), - expires_at, - }) - .returning(oauth2_authorization_codes::code) - .get_result(db_conn) - .scoped() - }) - .await - .map_err(|_| ()) + catch_error!(with_connection!(self.db_pool, |db_conn| { + diesel::insert_into(oauth2_authorization_codes::table) + .values(oauth2::NewAuthorizationCode { + code: secret.as_str(), + application_id, + user_id, + scopes: scopes.as_str(), + expires_at, + }) + .returning(oauth2_authorization_codes::code) + .get_result(db_conn) + .await + })) + .map_err(|_| ())? + .map_err(|_| ()) } async fn extract(&mut self, authorization_code: &str) -> Result, ()> { - let oauth_data = self - .db_pool - .with_connection(|db_conn| { - async move { - oauth2_authorization_codes::table - .find(authorization_code) - .inner_join(oauth2_applications::table) - .first::<(oauth2::AuthorizationCode, oauth2::Application)>(db_conn) - .await - .optional() - } - .scoped() - }) - .await - .map_err(|_| ())?; + let oauth_data = catch_error!(with_connection!(self.db_pool, |db_conn| { + oauth2_authorization_codes::table + .find(authorization_code) + .inner_join(oauth2_applications::table) + .first::<(oauth2::AuthorizationCode, oauth2::Application)>(db_conn) + .await + .optional() + })) + .map_err(|_| ())? + .map_err(|_| ())?; let oauth_data = oauth_data.map(|(code, app)| { let scope = app.scopes.parse().unwrap(); diff --git a/kitsune/src/oauth2/issuer.rs b/kitsune/src/oauth2/issuer.rs index 44ee138f6..4074a675b 100644 --- a/kitsune/src/oauth2/issuer.rs +++ b/kitsune/src/oauth2/issuer.rs @@ -4,9 +4,10 @@ use async_trait::async_trait; use diesel::{ExpressionMethods, OptionalExtension, QueryDsl, SelectableHelper}; use diesel_async::RunQueryDsl; use kitsune_db::{ + catch_error, model::oauth2, schema::{oauth2_access_tokens, oauth2_applications, oauth2_refresh_tokens}, - PgPool, + with_connection, with_transaction, PgPool, }; use kitsune_util::generate_secret; use oxide_auth::primitives::{ @@ -15,7 +16,6 @@ use oxide_auth::primitives::{ prelude::IssuedToken, }; use oxide_auth_async::primitives::Issuer; -use scoped_futures::ScopedFutureExt; #[derive(Clone)] pub struct OAuthIssuer { @@ -30,38 +30,33 @@ impl Issuer for OAuthIssuer { let scopes = grant.scope.to_string(); let expires_at = chrono_to_timestamp(grant.until); - let (access_token, refresh_token) = self - .db_pool - .with_transaction(|tx| { - async move { - let access_token = diesel::insert_into(oauth2_access_tokens::table) - .values(oauth2::NewAccessToken { - token: generate_secret().as_str(), - user_id: Some(user_id), - application_id: Some(application_id), - scopes: scopes.as_str(), - expires_at, - }) - .returning(oauth2::AccessToken::as_returning()) - .get_result::(tx) - .await?; - - let refresh_token = diesel::insert_into(oauth2_refresh_tokens::table) - .values(oauth2::NewRefreshToken { - token: generate_secret().as_str(), - access_token: access_token.token.as_str(), - application_id, - }) - .returning(oauth2::RefreshToken::as_returning()) - .get_result::(tx) - .await?; - - Ok::<_, Error>((access_token, refresh_token)) - } - .scoped() - }) - .await - .map_err(|_| ())?; + let (access_token, refresh_token) = catch_error!(with_transaction!(self.db_pool, |tx| { + let access_token = diesel::insert_into(oauth2_access_tokens::table) + .values(oauth2::NewAccessToken { + token: generate_secret().as_str(), + user_id: Some(user_id), + application_id: Some(application_id), + scopes: scopes.as_str(), + expires_at, + }) + .returning(oauth2::AccessToken::as_returning()) + .get_result::(tx) + .await?; + + let refresh_token = diesel::insert_into(oauth2_refresh_tokens::table) + .values(oauth2::NewRefreshToken { + token: generate_secret().as_str(), + access_token: access_token.token.as_str(), + application_id, + }) + .returning(oauth2::RefreshToken::as_returning()) + .get_result::(tx) + .await?; + + Ok::<_, Error>((access_token, refresh_token)) + })) + .map_err(|_| ())? + .map_err(|_| ())?; Ok(IssuedToken { token: access_token.token, @@ -72,49 +67,41 @@ impl Issuer for OAuthIssuer { } async fn refresh(&mut self, refresh_token: &str, grant: Grant) -> Result { - let (refresh_token, access_token) = self - .db_pool - .with_connection(|db_conn| { + let (refresh_token, access_token) = + catch_error!(with_connection!(self.db_pool, |db_conn| { oauth2_refresh_tokens::table .find(refresh_token) .inner_join(oauth2_access_tokens::table) .select(<(oauth2::RefreshToken, oauth2::AccessToken)>::as_select()) .get_result::<(oauth2::RefreshToken, oauth2::AccessToken)>(db_conn) - .scoped() - }) - .await + .await + })) + .map_err(|_| ())? .map_err(|_| ())?; - let (access_token, refresh_token) = self - .db_pool - .with_transaction(|tx| { - async move { - let new_access_token = diesel::insert_into(oauth2_access_tokens::table) - .values(oauth2::NewAccessToken { - user_id: access_token.user_id, - token: generate_secret().as_str(), - application_id: access_token.application_id, - scopes: access_token.scopes.as_str(), - expires_at: chrono_to_timestamp(grant.until), - }) - .get_result::(tx) - .await?; - - let refresh_token = diesel::update(&refresh_token) - .set( - oauth2_refresh_tokens::access_token.eq(new_access_token.token.as_str()), - ) - .get_result::(tx) - .await?; - - diesel::delete(&access_token).execute(tx).await?; - - Ok::<_, Error>((new_access_token, refresh_token)) - } - .scoped() - }) - .await - .map_err(|_| ())?; + let (access_token, refresh_token) = catch_error!(with_transaction!(self.db_pool, |tx| { + let new_access_token = diesel::insert_into(oauth2_access_tokens::table) + .values(oauth2::NewAccessToken { + user_id: access_token.user_id, + token: generate_secret().as_str(), + application_id: access_token.application_id, + scopes: access_token.scopes.as_str(), + expires_at: chrono_to_timestamp(grant.until), + }) + .get_result::(tx) + .await?; + + let refresh_token = diesel::update(&refresh_token) + .set(oauth2_refresh_tokens::access_token.eq(new_access_token.token.as_str())) + .get_result::(tx) + .await?; + + diesel::delete(&access_token).execute(tx).await?; + + Ok::<_, Error>((new_access_token, refresh_token)) + })) + .map_err(|_| ())? + .map_err(|_| ())?; Ok(RefreshedToken { token: access_token.token, @@ -125,22 +112,17 @@ impl Issuer for OAuthIssuer { } async fn recover_token(&mut self, access_token: &str) -> Result, ()> { - let oauth_data = self - .db_pool - .with_connection(|db_conn| { - async move { - oauth2_access_tokens::table - .find(access_token) - .inner_join(oauth2_applications::table) - .select(<(oauth2::AccessToken, oauth2::Application)>::as_select()) - .get_result::<(oauth2::AccessToken, oauth2::Application)>(db_conn) - .await - .optional() - } - .scoped() - }) - .await - .map_err(|_| ())?; + let oauth_data = catch_error!(with_connection!(self.db_pool, |db_conn| { + oauth2_access_tokens::table + .find(access_token) + .inner_join(oauth2_applications::table) + .select(<(oauth2::AccessToken, oauth2::Application)>::as_select()) + .get_result::<(oauth2::AccessToken, oauth2::Application)>(db_conn) + .await + .optional() + })) + .map_err(|_| ())? + .map_err(|_| ())?; let oauth_data = oauth_data.map(|(access_token, app)| { let scope = app.scopes.parse().unwrap(); @@ -165,23 +147,18 @@ impl Issuer for OAuthIssuer { } async fn recover_refresh(&mut self, refresh_token: &str) -> Result, ()> { - let oauth_data = self - .db_pool - .with_connection(|db_conn| { - async move { - oauth2_refresh_tokens::table - .find(refresh_token) - .inner_join(oauth2_access_tokens::table) - .inner_join(oauth2_applications::table) - .select(<(oauth2::AccessToken, oauth2::Application)>::as_select()) - .get_result::<(oauth2::AccessToken, oauth2::Application)>(db_conn) - .await - .optional() - } - .scoped() - }) - .await - .map_err(|_| ())?; + let oauth_data = catch_error!(with_connection!(self.db_pool, |db_conn| { + oauth2_refresh_tokens::table + .find(refresh_token) + .inner_join(oauth2_access_tokens::table) + .inner_join(oauth2_applications::table) + .select(<(oauth2::AccessToken, oauth2::Application)>::as_select()) + .get_result::<(oauth2::AccessToken, oauth2::Application)>(db_conn) + .await + .optional() + })) + .map_err(|_| ())? + .map_err(|_| ())?; let oauth_data = oauth_data.map(|(access_token, app)| { let scope = access_token.scopes.parse().unwrap(); diff --git a/kitsune/src/oauth2/mod.rs b/kitsune/src/oauth2/mod.rs index 5df192737..f171ecb3c 100644 --- a/kitsune/src/oauth2/mod.rs +++ b/kitsune/src/oauth2/mod.rs @@ -8,12 +8,11 @@ use iso8601_timestamp::Timestamp; use kitsune_db::{ model::oauth2, schema::{oauth2_applications, oauth2_authorization_codes}, - PgPool, + with_connection, PgPool, }; use kitsune_url::UrlService; use kitsune_util::generate_secret; use oxide_auth::endpoint::Scope; -use scoped_futures::ScopedFutureExt; use speedy_uuid::Uuid; use std::str::{self, FromStr}; use strum::{AsRefStr, EnumIter, EnumMessage, EnumString}; @@ -94,22 +93,20 @@ pub struct OAuth2Service { impl OAuth2Service { pub async fn create_app(&self, create_app: CreateApp) -> Result { let secret = generate_secret(); - self.db_pool - .with_connection(|db_conn| { - diesel::insert_into(oauth2_applications::table) - .values(oauth2::NewApplication { - id: Uuid::now_v7(), - secret: secret.as_str(), - name: create_app.name.as_str(), - redirect_uri: create_app.redirect_uris.as_str(), - scopes: "", - website: None, - }) - .get_result(db_conn) - .scoped() - }) - .await - .map_err(Error::from) + with_connection!(self.db_pool, |db_conn| { + diesel::insert_into(oauth2_applications::table) + .values(oauth2::NewApplication { + id: Uuid::now_v7(), + secret: secret.as_str(), + name: create_app.name.as_str(), + redirect_uri: create_app.redirect_uris.as_str(), + scopes: "", + website: None, + }) + .get_result(db_conn) + .await + }) + .map_err(Error::from) } pub async fn create_authorisation_code_response( @@ -124,9 +121,8 @@ impl OAuth2Service { let secret = generate_secret(); let scopes = scopes.to_string(); - let authorization_code: oauth2::AuthorizationCode = self - .db_pool - .with_connection(|db_conn| { + let authorization_code: oauth2::AuthorizationCode = + with_connection!(self.db_pool, |db_conn| { diesel::insert_into(oauth2_authorization_codes::table) .values(oauth2::NewAuthorizationCode { code: secret.as_str(), @@ -136,9 +132,8 @@ impl OAuth2Service { expires_at: Timestamp::now_utc() + AUTH_TOKEN_VALID_DURATION, }) .get_result(db_conn) - .scoped() - }) - .await?; + .await + })?; if application.redirect_uri == SHOW_TOKEN_URI { Ok(ShowTokenPage { diff --git a/kitsune/src/oauth2/registrar.rs b/kitsune/src/oauth2/registrar.rs index de35904f6..d6f567974 100644 --- a/kitsune/src/oauth2/registrar.rs +++ b/kitsune/src/oauth2/registrar.rs @@ -1,13 +1,14 @@ use async_trait::async_trait; use diesel::{ExpressionMethods, OptionalExtension, QueryDsl}; use diesel_async::RunQueryDsl; -use kitsune_db::{model::oauth2, schema::oauth2_applications, PgPool}; +use kitsune_db::{ + catch_error, model::oauth2, schema::oauth2_applications, with_connection, PgPool, +}; use oxide_auth::{ endpoint::{PreGrant, Scope}, primitives::registrar::{BoundClient, ClientUrl, ExactUrl, RegisteredUrl, RegistrarError}, }; use oxide_auth_async::primitives::Registrar; -use scoped_futures::ScopedFutureExt; use speedy_uuid::Uuid; use std::{ borrow::Cow, @@ -47,22 +48,17 @@ impl Registrar for OAuthRegistrar { .parse() .map_err(|_| RegistrarError::PrimitiveError)?; - let client = self - .db_pool - .with_connection(|db_conn| { - async move { - oauth2_applications::table - .find(client_id) - .filter(oauth2_applications::redirect_uri.eq(client.redirect_uri.as_str())) - .get_result::(db_conn) - .await - .optional() - } - .scoped() - }) - .await - .map_err(|_| RegistrarError::PrimitiveError)? - .ok_or(RegistrarError::Unspecified)?; + let client = catch_error!(with_connection!(self.db_pool, |db_conn| { + oauth2_applications::table + .find(client_id) + .filter(oauth2_applications::redirect_uri.eq(client.redirect_uri.as_str())) + .get_result::(db_conn) + .await + .optional() + })) + .map_err(|_| RegistrarError::PrimitiveError)? + .map_err(|_| RegistrarError::PrimitiveError)? + .ok_or(RegistrarError::Unspecified)?; let client_id = client.id.to_string(); let redirect_uri = ExactUrl::new(client.redirect_uri) @@ -111,20 +107,16 @@ impl Registrar for OAuthRegistrar { client_query = client_query.filter(oauth2_applications::secret.eq(passphrase)); } - self.db_pool - .with_connection(|db_conn| { - async move { - client_query - .select(oauth2_applications::id) - .execute(db_conn) - .await - .optional() - } - .scoped() - }) - .await - .map_err(|_| RegistrarError::PrimitiveError)? - .map(|_| ()) - .ok_or(RegistrarError::Unspecified) + catch_error!(with_connection!(self.db_pool, |db_conn| { + client_query + .select(oauth2_applications::id) + .execute(db_conn) + .await + .optional() + })) + .map_err(|_| RegistrarError::PrimitiveError)? + .map_err(|_| RegistrarError::PrimitiveError)? + .map(|_| ()) + .ok_or(RegistrarError::Unspecified) } } diff --git a/kitsune/src/oauth2/solicitor.rs b/kitsune/src/oauth2/solicitor.rs index aa664e172..a2b0371ed 100644 --- a/kitsune/src/oauth2/solicitor.rs +++ b/kitsune/src/oauth2/solicitor.rs @@ -4,11 +4,12 @@ use async_trait::async_trait; use cursiv::CsrfHandle; use diesel::{OptionalExtension, QueryDsl}; use diesel_async::RunQueryDsl; -use kitsune_db::{model::user::User, schema::oauth2_applications, PgPool}; +use kitsune_db::{ + catch_error, model::user::User, schema::oauth2_applications, with_connection, PgPool, +}; use oxide_auth::endpoint::{OAuthError, OwnerConsent, QueryParameter, Solicitation, WebRequest}; use oxide_auth_async::endpoint::OwnerSolicitor; use oxide_auth_axum::{OAuthRequest, OAuthResponse, WebError}; -use scoped_futures::ScopedFutureExt; use speedy_uuid::Uuid; use std::{borrow::Cow, str::FromStr}; use strum::EnumMessage; @@ -80,22 +81,17 @@ impl OAuthOwnerSolicitor { .parse() .map_err(|_| WebError::Endpoint(OAuthError::BadRequest))?; - let app_name = self - .db_pool - .with_connection(|db_conn| { - async move { - oauth2_applications::table - .find(client_id) - .select(oauth2_applications::name) - .get_result::(db_conn) - .await - .optional() - } - .scoped() - }) - .await - .map_err(|_| WebError::InternalError(None))? - .ok_or(WebError::Endpoint(OAuthError::DenySilently))?; + let app_name = catch_error!(with_connection!(self.db_pool, |db_conn| { + oauth2_applications::table + .find(client_id) + .select(oauth2_applications::name) + .get_result::(db_conn) + .await + .optional() + })) + .map_err(|_| WebError::InternalError(None))? + .map_err(|_| WebError::InternalError(None))? + .ok_or(WebError::Endpoint(OAuthError::DenySilently))?; let scopes = solicitation .pre_grant() diff --git a/lib/athena/Cargo.toml b/lib/athena/Cargo.toml index 30a877001..5b52874d2 100644 --- a/lib/athena/Cargo.toml +++ b/lib/athena/Cargo.toml @@ -26,7 +26,7 @@ simd-json = "0.13.9" smol_str = "0.2.1" speedy-uuid = { path = "../speedy-uuid", features = ["redis", "serde"] } thiserror = "1.0.58" -tokio = { version = "1.36.0", features = ["macros", "rt", "sync"] } +tokio = { version = "1.37.0", features = ["macros", "rt", "sync"] } tokio-util = { version = "0.7.10", features = ["rt"] } tracing = "0.1.40" typed-builder = "0.18.1" diff --git a/lib/blowocking/Cargo.toml b/lib/blowocking/Cargo.toml index 2ee5184dc..7d6f4b533 100644 --- a/lib/blowocking/Cargo.toml +++ b/lib/blowocking/Cargo.toml @@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0" once_cell = "1.19.0" rayon = "1.10.0" thiserror = "1.0.58" -tokio = { version = "1.36.0", features = ["sync"] } +tokio = { version = "1.37.0", features = ["sync"] } tracing = "0.1.40" [features] diff --git a/lib/cursiv/Cargo.toml b/lib/cursiv/Cargo.toml index 495c5f005..c7bb56220 100644 --- a/lib/cursiv/Cargo.toml +++ b/lib/cursiv/Cargo.toml @@ -11,7 +11,7 @@ blake3 = "1.5.1" cookie = { version = "0.18.1", features = ["percent-encode"] } hex-simd = "0.8.0" http = "1.1.0" -pin-project-lite = "0.2.13" +pin-project-lite = "0.2.14" rand = "0.8.5" tower = { version = "0.4.13", default-features = false } zeroize = { version = "1.7.0", features = ["derive"] } diff --git a/lib/cursiv/src/handle.rs b/lib/cursiv/src/handle.rs index 10c9c0f7c..7c83f7526 100644 --- a/lib/cursiv/src/handle.rs +++ b/lib/cursiv/src/handle.rs @@ -40,8 +40,8 @@ impl CsrfHandle { /// Keep the current signature and message inside the cookie #[inline] pub fn keep_cookie(&self) { - let mut guard = self.inner.lock().unwrap(); - guard.set_data = guard.read_data.clone(); + let inner = &mut *self.inner.lock().unwrap(); + inner.set_data.clone_from(&inner.read_data); } /// Create a signature and store it inside a cookie diff --git a/lib/geomjeungja/Cargo.toml b/lib/geomjeungja/Cargo.toml index 4270ec9c5..547b0ad11 100644 --- a/lib/geomjeungja/Cargo.toml +++ b/lib/geomjeungja/Cargo.toml @@ -16,10 +16,10 @@ tracing = "0.1.40" typed-builder = "0.18.1" [dev-dependencies] -insta = { version = "1.37.0", features = ["json"] } +insta = { version = "1.38.0", features = ["json"] } rand_xorshift = "0.3.0" serde_json = "1.0.115" -tokio = { version = "1.36.0", features = ["macros", "rt"] } +tokio = { version = "1.37.0", features = ["macros", "rt"] } [lints] workspace = true diff --git a/lib/http-signatures/Cargo.toml b/lib/http-signatures/Cargo.toml index e86ab4542..97aff5ecb 100644 --- a/lib/http-signatures/Cargo.toml +++ b/lib/http-signatures/Cargo.toml @@ -38,7 +38,7 @@ tracing = { version = "0.1.40", default-features = false, optional = true } [dev-dependencies] criterion = "0.5.1" proptest = { version = "1.4.0", default-features = false, features = ["std"] } -tokio = { version = "1.36.0", features = ["macros", "rt"] } +tokio = { version = "1.37.0", features = ["macros", "rt"] } [features] default = ["easy"] diff --git a/lib/http-signatures/src/cavage/easy.rs b/lib/http-signatures/src/cavage/easy.rs index eaf4ce9d0..14daf712e 100644 --- a/lib/http-signatures/src/cavage/easy.rs +++ b/lib/http-signatures/src/cavage/easy.rs @@ -123,7 +123,7 @@ pub async fn sign( /// You don't need to supply any more information. The library will figure out the rest. #[inline] #[instrument(skip_all)] -pub async fn verify<'a, B, F, Fut, E>(req: &http::Request, get_key: F) -> Result<(), Error> +pub async fn verify<'a, B, F, Fut, E>(req: &'a http::Request, get_key: F) -> Result<(), Error> where for<'k_id> F: Fn(&'k_id str) -> ScopedFutureWrapper<'k_id, 'a, Fut>, Fut: Future>, diff --git a/lib/just-retry/Cargo.toml b/lib/just-retry/Cargo.toml index 0385142ac..d2b1fdc4f 100644 --- a/lib/just-retry/Cargo.toml +++ b/lib/just-retry/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" [dependencies] chrono = { version = "0.4.37", default-features = false, features = ["std"] } retry-policies = "0.3.0" -tokio = { version = "1.36.0", features = ["time"] } +tokio = { version = "1.37.0", features = ["time"] } tracing = "0.1.40" [lints] diff --git a/lib/masto-id-convert/src/lib.rs b/lib/masto-id-convert/src/lib.rs index 1cac40a1b..bac832be9 100644 --- a/lib/masto-id-convert/src/lib.rs +++ b/lib/masto-id-convert/src/lib.rs @@ -57,9 +57,8 @@ where } #[cfg(test)] +#[allow(clippy::unreadable_literal)] mod test { - #![allow(clippy::unreadable_literal)] - use time::{Month, OffsetDateTime}; // ID nabbed from this post: diff --git a/lib/mrf-manifest/Cargo.toml b/lib/mrf-manifest/Cargo.toml index d5a0011ba..75f2ed1c7 100644 --- a/lib/mrf-manifest/Cargo.toml +++ b/lib/mrf-manifest/Cargo.toml @@ -7,7 +7,6 @@ license = "MIT OR Apache-2.0" [dependencies] leb128 = { version = "0.2.5", optional = true } -miette = { version = "7.2.0", optional = true } olpc-cjson = { version = "0.1.3", optional = true } schemars = { version = "0.8.16", features = ["impl_json_schema", "semver"] } semver = { version = "1.0.22", features = ["serde"] } @@ -19,17 +18,11 @@ wasmparser = { version = "0.202.0", optional = true } [dev-dependencies] serde_json = "1.0.115" -insta = { version = "1.37.0", default-features = false, features = ["json"] } +insta = { version = "1.38.0", default-features = false, features = ["json"] } wat = "1.202.0" [features] -decode = [ - "dep:leb128", - "dep:miette", - "dep:serde_json", - "dep:thiserror", - "dep:wasmparser", -] +decode = ["dep:leb128", "dep:serde_json", "dep:thiserror", "dep:wasmparser"] encode = ["dep:wasm-encoder", "serialise"] serialise = ["dep:olpc-cjson", "dep:serde_json"] diff --git a/lib/mrf-manifest/src/decode.rs b/lib/mrf-manifest/src/decode.rs index 26c93ccc2..d5f20130b 100644 --- a/lib/mrf-manifest/src/decode.rs +++ b/lib/mrf-manifest/src/decode.rs @@ -1,5 +1,4 @@ use crate::{Manifest, SECTION_NAME}; -use miette::Diagnostic; use std::{io, ops::Range}; use thiserror::Error; use wasmparser::Payload; @@ -8,7 +7,7 @@ use wasmparser::Payload; pub type SectionRange = Range; /// Error while decoding the manifest from a WASM module -#[derive(Debug, Diagnostic, Error)] +#[derive(Debug, Error)] pub enum DecodeError { /// Parsing of the JSON manifest failed #[error(transparent)] diff --git a/lib/mrf-tool/Cargo.toml b/lib/mrf-tool/Cargo.toml index 7088deafa..48b58418f 100644 --- a/lib/mrf-tool/Cargo.toml +++ b/lib/mrf-tool/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" [dependencies] clap = { version = "4.5.4", features = ["derive", "wrap_help"] } -miette = { version = "7.2.0", features = ["fancy"] } +color-eyre = "0.6.3" mrf-manifest = { path = "../mrf-manifest", features = [ "decode", "encode", diff --git a/lib/mrf-tool/src/main.rs b/lib/mrf-tool/src/main.rs index 05dca8240..eef0c9259 100644 --- a/lib/mrf-tool/src/main.rs +++ b/lib/mrf-tool/src/main.rs @@ -1,6 +1,6 @@ use self::args::{ManifestSubcommand, ModuleSubcommand, ToolArgs, ToolSubcommand}; use clap::Parser; -use miette::{bail, IntoDiagnostic, Result}; +use color_eyre::{eyre::bail, Result}; use std::{ fs::{self, File}, io::Write, @@ -14,14 +14,14 @@ fn read_manifest(module: &[u8]) -> Result<()> { bail!("missing manifest in module"); }; - let prettified = serde_json::to_string_pretty(&manifest).into_diagnostic()?; + let prettified = serde_json::to_string_pretty(&manifest)?; println!("{prettified}"); Ok(()) } fn remove_manifest(module_path: &Path, output_path: &Path) -> Result<()> { - let module = fs::read(module_path).into_diagnostic()?; + let module = fs::read(module_path)?; let Some((_manifest, section_range)) = mrf_manifest::decode(&module)? else { bail!("missing manifest in module"); }; @@ -30,29 +30,21 @@ fn remove_manifest(module_path: &Path, output_path: &Path) -> Result<()> { .create(true) .truncate(true) .write(true) - .open(output_path) - .into_diagnostic()?; + .open(output_path)?; - module_file - .write_all(&module[..section_range.start]) - .into_diagnostic()?; - module_file - .write_all(&module[section_range.end..]) - .into_diagnostic()?; + module_file.write_all(&module[..section_range.start])?; + module_file.write_all(&module[section_range.end..])?; Ok(()) } fn write_manifest(manifest: &[u8], module_path: &Path) -> Result<()> { // Parse the manifest and re-encode it in canonical JSON - let parsed_manifest = serde_json::from_slice(manifest).into_diagnostic()?; - let custom_section = mrf_manifest::encode(&parsed_manifest).into_diagnostic()?; + let parsed_manifest = serde_json::from_slice(manifest)?; + let custom_section = mrf_manifest::encode(&parsed_manifest)?; - let mut file = File::options() - .append(true) - .open(module_path) - .into_diagnostic()?; - file.write_all(&custom_section).into_diagnostic()?; + let mut file = File::options().append(true).open(module_path)?; + file.write_all(&custom_section)?; Ok(()) } @@ -61,25 +53,25 @@ fn main() -> Result<()> { let args = ToolArgs::parse(); match args.command { ToolSubcommand::Manifest(ManifestSubcommand::Add(args)) => { - let manifest = fs::read(args.manifest_path).into_diagnostic()?; + let manifest = fs::read(args.manifest_path)?; // Only copy if the paths are distinct if args.module_path != args.output { - fs::copy(&args.module_path, &args.output).into_diagnostic()?; + fs::copy(&args.module_path, &args.output)?; } write_manifest(&manifest, &args.output)?; } ToolSubcommand::Manifest(ManifestSubcommand::Read(args)) => { - let data = fs::read(args.module_path).into_diagnostic()?; + let data = fs::read(args.module_path)?; read_manifest(&data)?; } ToolSubcommand::Manifest(ManifestSubcommand::Remove(args)) => { remove_manifest(&args.module_path, &args.output)?; } ToolSubcommand::Module(ModuleSubcommand::Validate(args)) => { - let data = fs::read(args.module_path).into_diagnostic()?; - wasmparser::validate(&data).into_diagnostic()?; + let data = fs::read(args.module_path)?; + wasmparser::validate(&data)?; } } diff --git a/lib/post-process/Cargo.toml b/lib/post-process/Cargo.toml index 645a39d63..8af336bde 100644 --- a/lib/post-process/Cargo.toml +++ b/lib/post-process/Cargo.toml @@ -15,7 +15,7 @@ logos = "0.14.0" [dev-dependencies] criterion = { version = "0.5.1", features = ["async_futures"] } futures = "0.3.30" -insta = { version = "1.37.0", features = ["glob"] } +insta = { version = "1.38.0", features = ["glob"] } pretty_assertions = "1.4.0" [lints] diff --git a/lib/tower-http-digest/Cargo.toml b/lib/tower-http-digest/Cargo.toml index 48e990a5b..46c09fcc8 100644 --- a/lib/tower-http-digest/Cargo.toml +++ b/lib/tower-http-digest/Cargo.toml @@ -11,8 +11,8 @@ bytes = "1.6.0" either = { version = "1.10.0", default-features = false } http = "1.1.0" http-body = "1.0.0" -memchr = "2.7.1" -pin-project-lite = "0.2.13" +memchr = "2.7.2" +pin-project-lite = "0.2.14" sha2 = "0.10.8" subtle = "2.5.0" tower-layer = "0.3.2" diff --git a/lib/tower-x-clacks-overhead/Cargo.toml b/lib/tower-x-clacks-overhead/Cargo.toml index 60a5d0e4d..7ca7e9d44 100644 --- a/lib/tower-x-clacks-overhead/Cargo.toml +++ b/lib/tower-x-clacks-overhead/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" [dependencies] http = "1.1.0" itertools = { version = "0.12.1", default-features = false } -pin-project-lite = "0.2.13" +pin-project-lite = "0.2.14" tower-layer = "0.3.2" tower-service = "0.3.2"