diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index bb45cfafb..547b87870 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -48,7 +48,8 @@ jobs: DATABASE_URL: "postgres://postgres:postgres@localhost/test_db" REDIS_URL: "redis://localhost" - name: Upload coverage to Codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 with: files: lcov.info fail_ci_if_error: true + token: ${{ secrets.CODECOV_TOKEN }} diff --git a/Cargo.lock b/Cargo.lock index ca85b0bc9..359fcaee7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -33,7 +33,7 @@ version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ - "getrandom 0.2.12", + "getrandom 0.2.13", "once_cell", "version_check", ] @@ -45,7 +45,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", - "getrandom 0.2.12", + "getrandom 0.2.13", "once_cell", "version_check", "zerocopy", @@ -77,7 +77,7 @@ checksum = "d1eb7c4fcde1858a6796c18a729b661346d38e05a207e2d9028bce822fc20283" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -213,7 +213,7 @@ dependencies = [ "argh_shared", "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -286,7 +286,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -322,7 +322,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f28243a43d821d11341ab73c80bed182dc015c514b951616cf79bd4af39af0c3" dependencies = [ "concurrent-queue", - "event-listener 5.2.0", + "event-listener 5.3.0", "event-listener-strategy 0.5.1", "futures-core", "pin-project-lite", @@ -330,9 +330,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86a9249d1447a85f95810c620abea82e001fe58a31713fcce614caf52499f905" +checksum = "07dbbf24db18d609b1462965249abdf49129ccad073ec257da372adc83259c60" dependencies = [ "brotli", "flate2", @@ -409,7 +409,7 @@ dependencies = [ "proc-macro2", "quote", "strum", - "syn 2.0.57", + "syn 2.0.58", "thiserror", ] @@ -476,7 +476,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -493,7 +493,7 @@ checksum = "a507401cad91ec6a857ed5513a2073c82a9b9048762b885bb98655b306964681" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -569,7 +569,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -729,7 +729,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -972,9 +972,9 @@ dependencies = [ [[package]] name = "brotli" -version = "3.5.0" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d640d25bc63c50fb1f0b545ffd80207d2e10a4c965530809b40ba3386825c391" +checksum = "125740193d7fee5cc63ab9e16c2fdc4e07c74ba755cc53b327d6ea029e9fc569" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -983,9 +983,9 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "2.5.1" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f" +checksum = "65622a320492e09b5e0ac436b14c54ff68199bac392d0e89a6832c4518eea525" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -1195,9 +1195,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.90" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" +checksum = "1fd97381a8cc6493395a5afc4c691c1084b3768db713b73aa215217aa245d153" [[package]] name = "cfg-if" @@ -1282,7 +1282,7 @@ dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim 0.11.0", + "strsim 0.11.1", "terminal_size", ] @@ -1295,7 +1295,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -1744,7 +1744,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" dependencies = [ "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -1821,7 +1821,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -1869,7 +1869,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.10.0", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -1891,7 +1891,7 @@ checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" dependencies = [ "darling_core 0.20.8", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -1902,9 +1902,9 @@ checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" [[package]] name = "der" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ "const-oid", "pem-rfc7468", @@ -1939,7 +1939,7 @@ dependencies = [ "darling 0.20.8", "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -1949,7 +1949,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b" dependencies = [ "derive_builder_core", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -1982,7 +1982,7 @@ checksum = "2bba3e9872d7c58ce7ef0fcf1844fcc3e23ef2a58377b50df35dd98e42a5726e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -2022,7 +2022,7 @@ dependencies = [ "diesel_table_macro_syntax", "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -2051,7 +2051,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5" dependencies = [ - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -2231,7 +2231,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -2243,7 +2243,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -2299,9 +2299,9 @@ dependencies = [ [[package]] name = "event-listener" -version = "5.2.0" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b5fb89194fa3cad959b833185b3063ba881dbfc7030680b314250779fb4cc91" +checksum = "6d9944b8ca13534cdfb2800775f8dd4902ff3fc75a50101466decadfdf322a24" dependencies = [ "concurrent-queue", "parking", @@ -2324,7 +2324,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "332f51cb23d20b0de8458b86580878211da09bcd4503cb579c225b3d124cabb3" dependencies = [ - "event-listener 5.2.0", + "event-listener 5.3.0", "pin-project-lite", ] @@ -2574,7 +2574,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -2657,7 +2657,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -2711,9 +2711,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +checksum = "a06fddc2749e0528d2813f95e050e87e52c8cbbae56223b9babf73b3e53b0cc6" dependencies = [ "cfg-if", "js-sys", @@ -2784,9 +2784,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51ee2dd2e4f378392eeff5d51618cd9a63166a2513846bbc55f21cfacd9199d4" +checksum = "816ec7294445779408f36fe57bc5b7fc1cf59664059096c65f905c1c61f58069" dependencies = [ "bytes", "fnv", @@ -2803,9 +2803,9 @@ dependencies = [ [[package]] name = "half" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5eceaaeec696539ddaf7b333340f1af35a5aa87ae3e4f3ead0532f72affab2e" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" dependencies = [ "cfg-if", "crunchy", @@ -3161,7 +3161,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.3", + "h2 0.4.4", "http 1.1.0", "http-body 1.0.0", "httparse", @@ -3480,15 +3480,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.12.1" @@ -3563,7 +3554,6 @@ dependencies = [ "clap", "color-eyre", "cursiv", - "der", "diesel", "diesel-async", "fs_extra", @@ -3582,6 +3572,7 @@ dependencies = [ "kitsune-db", "kitsune-email", "kitsune-embed", + "kitsune-error", "kitsune-federation", "kitsune-federation-filter", "kitsune-http-client", @@ -3589,7 +3580,6 @@ dependencies = [ "kitsune-jobs", "kitsune-language", "kitsune-mastodon", - "kitsune-messaging", "kitsune-observability", "kitsune-oidc", "kitsune-scss-compiler", @@ -3621,7 +3611,6 @@ dependencies = [ "speedy-uuid", "strum", "tempfile", - "thiserror", "time", "tokio", "tokio-util", @@ -3631,6 +3620,7 @@ dependencies = [ "tower-stop-using-brave", "tower-x-clacks-overhead", "tracing", + "trials", "typed-builder", "url", "utoipa", @@ -3646,7 +3636,6 @@ dependencies = [ "base64-simd", "diesel", "diesel-async", - "eyre", "futures-util", "headers", "http 1.1.0", @@ -3658,6 +3647,7 @@ dependencies = [ "kitsune-core", "kitsune-db", "kitsune-embed", + "kitsune-error", "kitsune-federation-filter", "kitsune-http-client", "kitsune-language", @@ -3672,12 +3662,10 @@ dependencies = [ "mime", "mime_guess", "pretty_assertions", - "rsa", "serde", "sha2", "simd-json", "speedy-uuid", - "thiserror", "tokio", "tower", "tracing", @@ -3690,12 +3678,12 @@ name = "kitsune-cache" version = "0.0.1-pre.6" dependencies = [ "enum_dispatch", + "kitsune-error", "moka", "multiplex-pool", "redis", "serde", "simd-json", - "thiserror", "tokio", "tracing", "typed-builder", @@ -3707,12 +3695,12 @@ version = "0.0.1-pre.6" dependencies = [ "enum_dispatch", "http 1.1.0", + "kitsune-error", "kitsune-http-client", "serde", "serde_urlencoded", "simd-json", "strum", - "thiserror", "typed-builder", ] @@ -3728,6 +3716,7 @@ dependencies = [ "envy", "kitsune-config", "kitsune-db", + "kitsune-error", "serde", "speedy-uuid", "tokio", @@ -3754,13 +3743,9 @@ version = "0.0.1-pre.6" dependencies = [ "async-trait", "const_format", - "eyre", - "http 1.1.0", "kitsune-db", - "kitsune-messaging", + "kitsune-error", "serde", - "speedy-uuid", - "thiserror", "typed-builder", "vergen", ] @@ -3777,6 +3762,7 @@ dependencies = [ "futures-util", "iso8601-timestamp", "kitsune-config", + "kitsune-error", "kitsune-language", "kitsune-test", "kitsune-type", @@ -3787,12 +3773,12 @@ dependencies = [ "serde", "simd-json", "speedy-uuid", - "thiserror", "tokio", "tokio-postgres", "tokio-postgres-rustls", "tracing", "tracing-log", + "trials", "typed-builder", ] @@ -3805,11 +3791,11 @@ dependencies = [ "diesel", "diesel-async", "kitsune-db", + "kitsune-error", "kitsune-url", "lettre", "mrml", "speedy-uuid", - "thiserror", "typed-builder", ] @@ -3823,14 +3809,27 @@ dependencies = [ "http 1.1.0", "iso8601-timestamp", "kitsune-db", + "kitsune-error", "kitsune-http-client", "once_cell", "scraper", "smol_str", - "thiserror", "typed-builder", ] +[[package]] +name = "kitsune-error" +version = "0.0.1-pre.6" +dependencies = [ + "axum-core 0.4.3", + "eyre", + "garde", + "http 1.1.0", + "simd-json", + "sync_wrapper 1.0.0", + "tracing", +] + [[package]] name = "kitsune-federation" version = "0.0.1-pre.6" @@ -3856,8 +3855,8 @@ version = "0.0.1-pre.6" dependencies = [ "globset", "kitsune-config", + "kitsune-error", "kitsune-type", - "thiserror", "url", ] @@ -3896,6 +3895,7 @@ dependencies = [ "kitsune-core", "kitsune-db", "kitsune-email", + "kitsune-error", "kitsune-federation", "kitsune-federation-filter", "kitsune-jobs", @@ -3919,11 +3919,11 @@ dependencies = [ "derive_more 1.0.0-beta.6", "diesel", "diesel-async", - "eyre", "futures-util", "kitsune-core", "kitsune-db", "kitsune-email", + "kitsune-error", "serde", "speedy-uuid", "tracing", @@ -3947,15 +3947,14 @@ dependencies = [ name = "kitsune-mastodon" version = "0.0.1-pre.6" dependencies = [ - "derive_builder", "diesel", "diesel-async", "futures-util", "iso8601-timestamp", "kitsune-cache", - "kitsune-core", "kitsune-db", "kitsune-embed", + "kitsune-error", "kitsune-service", "kitsune-type", "kitsune-url", @@ -3965,29 +3964,10 @@ dependencies = [ "simd-json", "smol_str", "speedy-uuid", - "thiserror", - "tokio", "tracing", "typed-builder", ] -[[package]] -name = "kitsune-messaging" -version = "0.0.1-pre.6" -dependencies = [ - "ahash 0.8.11", - "derive_more 1.0.0-beta.6", - "futures-util", - "just-retry", - "pin-project-lite", - "redis", - "serde", - "simd-json", - "tokio", - "tokio-stream", - "tracing", -] - [[package]] name = "kitsune-observability" version = "0.0.1-pre.6" @@ -4021,6 +4001,7 @@ dependencies = [ "http 1.1.0", "http-compat", "kitsune-config", + "kitsune-error", "kitsune-http-client", "moka", "multiplex-pool", @@ -4030,7 +4011,6 @@ dependencies = [ "serde", "simd-json", "speedy-uuid", - "thiserror", "url", ] @@ -4041,6 +4021,7 @@ dependencies = [ "bytes", "futures-util", "http 1.1.0", + "kitsune-error", "kitsune-http-client", "kitsune-test", "quick-xml 0.31.0", @@ -4072,12 +4053,12 @@ dependencies = [ "isahc", "kitsune-config", "kitsune-db", + "kitsune-error", "kitsune-language", "meilisearch-sdk", "serde", "speedy-uuid", "strum", - "thiserror", "tracing", "typed-builder", ] @@ -4112,11 +4093,11 @@ dependencies = [ "kitsune-db", "kitsune-email", "kitsune-embed", + "kitsune-error", "kitsune-federation-filter", "kitsune-http-client", "kitsune-jobs", "kitsune-language", - "kitsune-messaging", "kitsune-search", "kitsune-storage", "kitsune-test", @@ -4134,11 +4115,9 @@ dependencies = [ "rsa", "rusty-s3", "serde", - "simd-json", "smol_str", "speedy-uuid", "tempfile", - "thiserror", "tokio", "tower", "tracing", @@ -4154,6 +4133,7 @@ dependencies = [ "bytes", "derive_more 1.0.0-beta.6", "futures-util", + "kitsune-error", "kitsune-s3", "rusty-s3", "tempfile", @@ -4218,7 +4198,7 @@ dependencies = [ "iso8601-timestamp", "kitsune-type", "once_cell", - "pulldown-cmark 0.10.0", + "pulldown-cmark 0.10.2", "rand 0.8.5", "speedy-uuid", "tokio", @@ -4234,6 +4214,7 @@ dependencies = [ "enum_dispatch", "futures-util", "kitsune-config", + "kitsune-error", "kitsune-type", "mrf-manifest", "multiplex-pool", @@ -4243,7 +4224,6 @@ dependencies = [ "sled", "smol_str", "tempfile", - "thiserror", "tokio", "tracing", "tracing-subscriber", @@ -4259,13 +4239,13 @@ version = "0.0.1-pre.6" dependencies = [ "async-trait", "autometrics", - "eyre", "futures-util", "http 1.1.0", "http-body-util", "hyper 1.2.0", "kitsune-cache", "kitsune-core", + "kitsune-error", "kitsune-http-client", "kitsune-type", "kitsune-util", @@ -4322,7 +4302,7 @@ dependencies = [ "percent-encoding", "quoted_printable", "rustls 0.23.4", - "rustls-pemfile 2.1.1", + "rustls-pemfile 2.1.2", "socket2", "tokio", "tokio-rustls 0.26.0", @@ -4462,7 +4442,7 @@ checksum = "adf157a4dc5a29b7b464aa8fe7edeff30076e07e13646a1c3874f58477dc99f8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -4523,7 +4503,7 @@ dependencies = [ "proc-macro2", "quote", "regex-syntax 0.8.3", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -4661,7 +4641,7 @@ dependencies = [ "proc-macro2", "quote", "structmeta", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -4799,7 +4779,7 @@ checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -4937,9 +4917,9 @@ dependencies = [ [[package]] name = "mrml" -version = "3.1.3" +version = "3.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b6f31e38fc879b2dbcca95fd0c03f15351e79e3abcdb8533216cc3bdf14afb5" +checksum = "398abc648681dde5791dc51172e52d95156ec579d2e55a128606d7819d2a471e" dependencies = [ "indexmap 2.2.6", "itertools 0.12.1", @@ -4955,7 +4935,7 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b99915c25e2b56916308ccd9ca090cb0759226271e5f3b8101e7bbdf7fb8d35" dependencies = [ - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -4968,7 +4948,7 @@ dependencies = [ "mrml-common-macros", "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -5082,7 +5062,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -5154,7 +5134,7 @@ checksum = "c38841cdd844847e3e7c8d29cef9dcfed8877f8f56f9071f77843ecf3baf937f" dependencies = [ "base64 0.13.1", "chrono", - "getrandom 0.2.12", + "getrandom 0.2.13", "http 0.2.12", "rand 0.8.5", "serde", @@ -5555,9 +5535,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.8" +version = "2.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f8023d0fb78c8e03784ea1c7f3fa36e68a723138990b8d5a47d916b651e7a8" +checksum = "311fb059dee1a7b802f036316d790138c613a4e8b180c822e3925a662e9f0c95" dependencies = [ "memchr", "thiserror", @@ -5684,7 +5664,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -5731,7 +5711,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -5984,9 +5964,9 @@ dependencies = [ [[package]] name = "prost" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c289cda302b98a28d40c8b3b90498d6e526dd24ac2ecea73e4e491685b94a" +checksum = "d0f5d036824e4761737860779c906171497f6d55681139d8312388f8fe398922" dependencies = [ "bytes", "prost-derive", @@ -5994,15 +5974,15 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efb6c9a1dd1def8e2124d17e83a20af56f1570d6c2d2bd9e266ccb768df3840e" +checksum = "19de2de2a00075bf566bee3bd4db014b11587e84184d3f7a791bc17f1a8e9e48" dependencies = [ "anyhow", - "itertools 0.11.0", + "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -6047,9 +6027,9 @@ dependencies = [ [[package]] name = "pulldown-cmark" -version = "0.10.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce76ce678ffc8e5675b22aa1405de0b7037e2fdf8913fea40d1926c6fe1e6e7" +checksum = "5f0530d13d87d1f549b66a3e8d0c688952abe5994e204ed62615baaf25dc029c" dependencies = [ "bitflags 2.5.0", "memchr", @@ -6201,7 +6181,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.12", + "getrandom 0.2.13", ] [[package]] @@ -6262,9 +6242,9 @@ dependencies = [ [[package]] name = "redis" -version = "0.25.2" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71d64e978fd98a0e6b105d066ba4889a7301fca65aeac850a877d8797343feeb" +checksum = "6472825949c09872e8f2c50bde59fcefc17748b6be5c90fd67cd8b4daca73bfd" dependencies = [ "ahash 0.8.11", "arc-swap", @@ -6278,7 +6258,7 @@ dependencies = [ "pin-project-lite", "rustls 0.22.3", "rustls-native-certs", - "rustls-pemfile 2.1.1", + "rustls-pemfile 2.1.2", "rustls-pki-types", "ryu", "sha1_smol", @@ -6325,7 +6305,7 @@ checksum = "5fddb4f8d99b0a2ebafc65a87a69a7b9875e4b1ae1f00db265d300ef7f28bccc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -6424,7 +6404,7 @@ checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.12", + "getrandom 0.2.13", "libc", "spin 0.9.8", "untrusted", @@ -6564,7 +6544,7 @@ dependencies = [ "proc-macro2", "quote", "rust-embed-utils", - "syn 2.0.57", + "syn 2.0.58", "walkdir", ] @@ -6663,7 +6643,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f1fb85efa936c42c6d5fc28d2629bb51e4b2f4b8a5211e297d599cc5a093792" dependencies = [ "openssl-probe", - "rustls-pemfile 2.1.1", + "rustls-pemfile 2.1.2", "rustls-pki-types", "schannel", "security-framework", @@ -6680,11 +6660,11 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f48172685e6ff52a556baa527774f61fcaa884f59daf3375c62a3f1cd2549dab" +checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" dependencies = [ - "base64 0.21.7", + "base64 0.22.0", "rustls-pki-types", ] @@ -6717,9 +6697,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" [[package]] name = "rusty-s3" @@ -6948,7 +6928,7 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -7084,7 +7064,7 @@ dependencies = [ "darling 0.20.8", "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -7178,7 +7158,7 @@ version = "0.13.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0b84c23a1066e1d650ebc99aa8fb9f8ed0ab96fd36e2e836173c92fc9fb29bc" dependencies = [ - "getrandom 0.2.12", + "getrandom 0.2.13", "halfbrown", "lexical-core", "ref-cast", @@ -7334,7 +7314,7 @@ checksum = "c87e960f4dca2788eeb86bbdde8dd246be8948790b7618d656e68f9b720a86e8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -7441,9 +7421,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "strsim" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "structmeta" @@ -7454,7 +7434,7 @@ dependencies = [ "proc-macro2", "quote", "structmeta-derive", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -7465,7 +7445,7 @@ checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -7488,7 +7468,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -7531,9 +7511,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.57" +version = "2.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11a6ae1e52eb25aab8f3fb9fca13be982a373b8f1157ca14b897a825ba4a2d35" +checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" dependencies = [ "proc-macro2", "quote", @@ -7658,9 +7638,9 @@ dependencies = [ [[package]] name = "testcontainers-modules" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "204d1c7516bfdc8a01bb85d3e30145e5bbeb2351812e5e8aa6971769109b45b5" +checksum = "8debb5e215d9e89ea93255fffff00bf037ea44075d7a2669a21a8a988d6b52fd" dependencies = [ "testcontainers", ] @@ -7705,7 +7685,7 @@ checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -7820,7 +7800,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -7851,8 +7831,9 @@ dependencies = [ [[package]] name = "tokio-postgres-rustls" -version = "0.11.1" -source = "git+https://github.com/jbg/tokio-postgres-rustls.git?rev=b3b59ac2fa1b5823f2426fef78a0fb74c004ec38#b3b59ac2fa1b5823f2426fef78a0fb74c004ec38" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04fb792ccd6bbcd4bba408eb8a292f70fc4a3589e5d793626f45190e6454b6ab" dependencies = [ "ring", "rustls 0.23.4", @@ -7914,7 +7895,6 @@ dependencies = [ "futures-core", "pin-project-lite", "tokio", - "tokio-util", ] [[package]] @@ -8025,7 +8005,7 @@ dependencies = [ "pin-project", "prost", "rustls-native-certs", - "rustls-pemfile 2.1.1", + "rustls-pemfile 2.1.2", "rustls-pki-types", "tokio", "tokio-rustls 0.25.0", @@ -8166,7 +8146,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -8242,6 +8222,23 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "trials" +version = "0.0.1-pre.6" +dependencies = [ + "futures-test", + "trials-macros", +] + +[[package]] +name = "trials-macros" +version = "0.0.1-pre.6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + [[package]] name = "triomphe" version = "0.1.11" @@ -8290,7 +8287,7 @@ checksum = "563b3b88238ec95680aef36bdece66896eaa7ce3c0f1b4f39d38fb2435261352" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -8423,7 +8420,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.57", + "syn 2.0.58", "uuid", ] @@ -8450,7 +8447,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" dependencies = [ "atomic", - "getrandom 0.2.12", + "getrandom 0.2.13", "rand 0.8.5", "serde", ] @@ -8578,7 +8575,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", "wasm-bindgen-shared", ] @@ -8612,7 +8609,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -8747,7 +8744,7 @@ dependencies = [ "anyhow", "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", "wasmtime-component-util", "wasmtime-wit-bindgen", "wit-parser 0.201.0", @@ -8906,14 +8903,14 @@ checksum = "f1d81c092a61ca1667013e2eb08fed7c6c53e496dbbaa32d5685dc5152b0a772" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] name = "wasmtime-wasi" -version = "19.0.0" +version = "19.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "371d828b6849ea06d598ae7dd1c316e8dd9e99b76f77d93d5886cb25c7f8e188" +checksum = "af3ebe8aeb34f49342db2e8fbb1992796e6c7d9f7005b7298ca0dceede532e9d" dependencies = [ "anyhow", "async-trait", @@ -9054,9 +9051,9 @@ dependencies = [ [[package]] name = "widestring" -version = "1.0.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8" +checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" [[package]] name = "winapi" @@ -9286,9 +9283,9 @@ dependencies = [ [[package]] name = "wit-bindgen" -version = "0.23.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "041a3276f25dce240b10f5b34d9d490b007f18e8ead05984ae7948b283b4059e" +checksum = "9fb4e7653763780be47e38f479e9aa83c768aa6a3b2ed086dc2826fdbbb7e7f5" dependencies = [ "wit-bindgen-rt", "wit-bindgen-rust-macro", @@ -9296,9 +9293,9 @@ dependencies = [ [[package]] name = "wit-bindgen-core" -version = "0.23.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0834150cd852e64e1eddcff4fea9524b788161b4111d83a94c9eda715f8f442" +checksum = "9b67e11c950041849a10828c7600ea62a4077c01e8af72e8593253575428f91b" dependencies = [ "anyhow", "wit-parser 0.202.0", @@ -9306,18 +9303,18 @@ dependencies = [ [[package]] name = "wit-bindgen-rt" -version = "0.23.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6ccd4c6a69667c75474ddb30c36773c5e70cdca099ec2e293005458886ac81b" +checksum = "3b0780cf7046630ed70f689a098cd8d56c5c3b22f2a7379bbdb088879963ff96" dependencies = [ "bitflags 2.5.0", ] [[package]] name = "wit-bindgen-rust" -version = "0.23.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60620df421d4c787e2660f0159fd58f2ae6998dc42ccf2e09b8d9d96d16885a9" +checksum = "30acbe8fb708c3a830a33c4cb705df82659bf831b492ec6ca1a17a369cfeeafb" dependencies = [ "anyhow", "heck 0.4.1", @@ -9329,14 +9326,14 @@ dependencies = [ [[package]] name = "wit-bindgen-rust-macro" -version = "0.23.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96e875e7dd09a0d2acc1a27df6a4b0586288741d940b40a717e2daed3bc3d979" +checksum = "2b1b06eae85feaecdf9f2854f7cac124e00d5a6e5014bfb02eb1ecdeb5f265b9" dependencies = [ "anyhow", "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", "wit-bindgen-core", "wit-bindgen-rust", ] @@ -9475,7 +9472,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] @@ -9495,7 +9492,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.57", + "syn 2.0.58", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 277b8ccf7..417f59ea5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,13 +24,13 @@ members = [ "crates/kitsune-db", "crates/kitsune-email", "crates/kitsune-embed", + "crates/kitsune-error", "crates/kitsune-federation", "crates/kitsune-federation-filter", "crates/kitsune-http-client", "crates/kitsune-jobs", "crates/kitsune-language", "crates/kitsune-mastodon", - "crates/kitsune-messaging", "crates/kitsune-observability", "crates/kitsune-oidc", "crates/kitsune-s3", @@ -65,6 +65,8 @@ members = [ "lib/tower-http-digest", "lib/tower-stop-using-brave", "lib/tower-x-clacks-overhead", + "lib/trials", + "lib/trials/macros", "xtask", ] resolver = "2" @@ -117,4 +119,3 @@ 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/crates/kitsune-activitypub/Cargo.toml b/crates/kitsune-activitypub/Cargo.toml index 504734144..68daf8475 100644 --- a/crates/kitsune-activitypub/Cargo.toml +++ b/crates/kitsune-activitypub/Cargo.toml @@ -11,7 +11,6 @@ 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" @@ -21,6 +20,7 @@ kitsune-config = { path = "../kitsune-config" } kitsune-core = { path = "../kitsune-core" } kitsune-db = { path = "../kitsune-db" } kitsune-embed = { path = "../kitsune-embed" } +kitsune-error = { path = "../kitsune-error" } kitsune-federation-filter = { path = "../kitsune-federation-filter" } kitsune-http-client = { path = "../kitsune-http-client" } kitsune-language = { path = "../kitsune-language" } @@ -32,12 +32,10 @@ kitsune-util = { path = "../kitsune-util" } kitsune-wasm-mrf = { path = "../kitsune-wasm-mrf" } mime = "0.3.17" mime_guess = { version = "2.0.4", default-features = false } -rsa = "0.9.6" serde = "1.0.197" sha2 = "0.10.8" simd-json = "0.13.9" speedy-uuid = { path = "../../lib/speedy-uuid" } -thiserror = "1.0.58" tracing = "0.1.40" typed-builder = "0.18.1" url = "2.5.0" diff --git a/crates/kitsune-activitypub/src/deliverer/core.rs b/crates/kitsune-activitypub/src/deliverer/core.rs index e2c56ad33..00f05b4d0 100644 --- a/crates/kitsune-activitypub/src/deliverer/core.rs +++ b/crates/kitsune-activitypub/src/deliverer/core.rs @@ -1,9 +1,9 @@ -use crate::error::{Error, Result}; use autometrics::autometrics; use futures_util::{stream::FuturesUnordered, Stream, StreamExt}; use http::{Method, Request}; use kitsune_core::consts::USER_AGENT; use kitsune_db::model::{account::Account, user::User}; +use kitsune_error::{Error, Result}; use kitsune_federation_filter::FederationFilter; use kitsune_http_client::Client; use kitsune_type::ap::Activity; diff --git a/crates/kitsune-activitypub/src/deliverer/mod.rs b/crates/kitsune-activitypub/src/deliverer/mod.rs index 5e7266ef8..f51c1104a 100644 --- a/crates/kitsune-activitypub/src/deliverer/mod.rs +++ b/crates/kitsune-activitypub/src/deliverer/mod.rs @@ -1,5 +1,4 @@ use crate::{ - error::Result, mapping::{self, IntoActivity}, InboxResolver, }; @@ -17,6 +16,7 @@ use kitsune_db::{ schema::{accounts, posts, users}, with_connection, PgPool, }; +use kitsune_error::Result; use kitsune_service::attachment::AttachmentService; use kitsune_type::ap::{ap_context, Activity, ActivityType, ObjectField}; use kitsune_url::UrlService; @@ -390,7 +390,7 @@ impl Deliverer { #[async_trait] impl DelivererTrait for Deliverer { - async fn deliver(&self, action: Action) -> eyre::Result<()> { + async fn deliver(&self, action: Action) -> 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 deleted file mode 100644 index 51b5cfb87..000000000 --- a/crates/kitsune-activitypub/src/error.rs +++ /dev/null @@ -1,87 +0,0 @@ -use diesel_async::pooled_connection::bb8; -use rsa::pkcs8::der; -use std::{convert::Infallible, fmt::Debug}; -use thiserror::Error; - -pub type Result = std::result::Result; - -#[derive(Debug, Error)] -pub enum Error { - #[error("Instance is blocked")] - BlockedInstance, - - #[error(transparent)] - Cache(#[from] kitsune_cache::Error), - - #[error(transparent)] - DatabasePool(#[from] bb8::RunError), - - #[error(transparent)] - Der(#[from] der::Error), - - #[error(transparent)] - Diesel(#[from] diesel::result::Error), - - #[error(transparent)] - Embed(#[from] kitsune_embed::Error), - - #[error(transparent)] - FederationFilter(#[from] kitsune_federation_filter::error::Error), - - #[error(transparent)] - FetchAccount(eyre::Report), - - #[error(transparent)] - FetchEmoji(eyre::Report), - - #[error(transparent)] - FetchPost(eyre::Report), - - #[error(transparent)] - Http(#[from] http::Error), - - #[error(transparent)] - HttpClient(#[from] kitsune_http_client::Error), - - #[error("Invalid ActivityPub document")] - InvalidDocument, - - #[error("Invalid ActivityPub response")] - InvalidResponse, - - #[error(transparent)] - InvalidUri(#[from] http::uri::InvalidUri), - - #[error("Missing host")] - MissingHost, - - #[error(transparent)] - Mrf(#[from] kitsune_wasm_mrf::Error), - - #[error("Not found")] - NotFound, - - #[error(transparent)] - Resolver(eyre::Report), - - #[error(transparent)] - Search(#[from] kitsune_search::Error), - - #[error(transparent)] - Service(#[from] kitsune_service::error::Error), - - #[error(transparent)] - SimdJson(#[from] simd_json::Error), - - #[error("Unsupported media type")] - UnsupportedMediaType, - - #[error(transparent)] - UrlParse(#[from] url::ParseError), -} - -impl From for Error { - fn from(err: Infallible) -> Self { - match err {} - } -} diff --git a/crates/kitsune-activitypub/src/fetcher/actor.rs b/crates/kitsune-activitypub/src/fetcher/actor.rs index 472e6b318..1b96f2991 100644 --- a/crates/kitsune-activitypub/src/fetcher/actor.rs +++ b/crates/kitsune-activitypub/src/fetcher/actor.rs @@ -1,8 +1,5 @@ use super::Fetcher; -use crate::{ - error::{Error, Result}, - process_attachments, -}; +use crate::process_attachments; use autometrics::autometrics; use diesel::{ExpressionMethods, OptionalExtension, QueryDsl, SelectableHelper}; use diesel_async::RunQueryDsl; @@ -13,6 +10,7 @@ use kitsune_db::{ schema::accounts, with_connection, with_transaction, }; +use kitsune_error::{kitsune_error, Error, Result}; use kitsune_search::SearchBackend; use kitsune_type::ap::actor::Actor; use kitsune_util::{convert::timestamp_to_uuid, sanitize::CleanHtmlExt}; @@ -55,7 +53,10 @@ impl Fetcher { return Ok(None); }; - let mut domain = url.host_str().ok_or(Error::MissingHost)?; + let mut domain = url + .host_str() + .ok_or_else(|| kitsune_error!("missing host component"))?; + let domain_buf; let try_resolver = opts .acct @@ -65,8 +66,7 @@ impl Fetcher { match self .resolver .resolve_account(&actor.preferred_username, domain) - .await - .map_err(Error::Resolver)? + .await? { Some(resource) if resource.uri == actor.id => { actor.preferred_username = resource.username; @@ -85,7 +85,9 @@ impl Fetcher { if !used_resolver && actor.id != url.as_str() { url = Url::parse(&actor.id)?; - domain = url.host_str().ok_or(Error::MissingHost)?; + domain = url + .host_str() + .ok_or_else(|| kitsune_error!("missing host component"))?; } actor.clean_html(); diff --git a/crates/kitsune-activitypub/src/fetcher/emoji.rs b/crates/kitsune-activitypub/src/fetcher/emoji.rs index ba340b299..40dfad542 100644 --- a/crates/kitsune-activitypub/src/fetcher/emoji.rs +++ b/crates/kitsune-activitypub/src/fetcher/emoji.rs @@ -1,5 +1,4 @@ use super::Fetcher; -use crate::error::{Error, Result}; use diesel::{ExpressionMethods, OptionalExtension, QueryDsl, SelectableHelper}; use diesel_async::RunQueryDsl; use iso8601_timestamp::Timestamp; @@ -11,6 +10,7 @@ use kitsune_db::{ schema::{custom_emojis, media_attachments}, with_connection, with_transaction, }; +use kitsune_error::{kitsune_error, Error, Result}; use kitsune_type::ap::emoji::Emoji; use speedy_uuid::Uuid; use url::Url; @@ -35,11 +35,15 @@ impl Fetcher { return Ok(None); }; - let mut domain = url.host_str().ok_or(Error::MissingHost)?; + let mut domain = url + .host_str() + .ok_or_else(|| kitsune_error!("missing host component"))?; if emoji.id != url.as_str() { url = Url::parse(&emoji.id)?; - domain = url.host_str().ok_or(Error::MissingHost)?; + domain = url + .host_str() + .ok_or_else(|| kitsune_error!("missing host component"))?; } let content_type = emoji @@ -47,7 +51,7 @@ impl Fetcher { .media_type .as_deref() .or_else(|| mime_guess::from_path(&emoji.icon.url).first_raw()) - .ok_or(Error::InvalidDocument)?; + .ok_or_else(|| kitsune_error!("failed to guess content-type"))?; let name_pure = emoji.name.replace(':', ""); diff --git a/crates/kitsune-activitypub/src/fetcher/mod.rs b/crates/kitsune-activitypub/src/fetcher/mod.rs index d481c078c..97c737c88 100644 --- a/crates/kitsune-activitypub/src/fetcher/mod.rs +++ b/crates/kitsune-activitypub/src/fetcher/mod.rs @@ -1,4 +1,3 @@ -use crate::error::{Error, Result}; use async_trait::async_trait; use headers::{ContentType, HeaderMapExt}; use http::HeaderValue; @@ -16,6 +15,7 @@ use kitsune_db::{ PgPool, }; use kitsune_embed::Client as EmbedClient; +use kitsune_error::{bail, Error, Result}; use kitsune_federation_filter::FederationFilter; use kitsune_http_client::Client; use kitsune_type::jsonld::RdfNode; @@ -70,7 +70,7 @@ impl Fetcher { { let url = url.try_into()?; if !self.federation_filter.is_url_allowed(&url)? { - return Err(Error::BlockedInstance); + bail!("instance is blocked"); } let response = self.client.get(url.as_str()).await?; @@ -84,7 +84,7 @@ impl Fetcher { .typed_get::() .map(Mime::from) else { - return Err(Error::InvalidResponse); + bail!("invalid content-type header in response"); }; let is_json_ld_activitystreams = || { @@ -108,7 +108,7 @@ impl Fetcher { }; if !is_json_ld_activitystreams() && !is_activity_json() { - return Err(Error::InvalidResponse); + bail!("invalid content-type: isnt either ld+json or activity+json"); } let response = response.jsonld().await?; @@ -123,15 +123,15 @@ impl FetcherTrait for Fetcher { Arc::new(self.resolver.clone()) } - async fn fetch_account(&self, opts: AccountFetchOptions<'_>) -> eyre::Result> { + async fn fetch_account(&self, opts: AccountFetchOptions<'_>) -> Result> { Ok(self.fetch_actor(opts).await?) } - async fn fetch_emoji(&self, url: &str) -> eyre::Result> { + async fn fetch_emoji(&self, url: &str) -> Result> { Ok(self.fetch_emoji(url).await?) } - async fn fetch_post(&self, opts: PostFetchOptions<'_>) -> eyre::Result> { + async fn fetch_post(&self, opts: PostFetchOptions<'_>) -> 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 3b7817e4e..8698f0ca2 100644 --- a/crates/kitsune-activitypub/src/fetcher/object.rs +++ b/crates/kitsune-activitypub/src/fetcher/object.rs @@ -1,10 +1,11 @@ use super::Fetcher; -use crate::{error::Result, process_new_object, ProcessNewObject}; +use crate::{process_new_object, ProcessNewObject}; 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, with_connection}; +use kitsune_error::Result; // 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 diff --git a/crates/kitsune-activitypub/src/inbox_resolver.rs b/crates/kitsune-activitypub/src/inbox_resolver.rs index 6fc2a7259..af32fb71e 100644 --- a/crates/kitsune-activitypub/src/inbox_resolver.rs +++ b/crates/kitsune-activitypub/src/inbox_resolver.rs @@ -1,4 +1,3 @@ -use crate::error::{Error, Result}; use diesel::{ result::Error as DieselError, BelongingToDsl, BoolExpressionMethods, ExpressionMethods, JoinOnDsl, QueryDsl, SelectableHelper, @@ -15,6 +14,7 @@ use kitsune_db::{ schema::{accounts, accounts_follows}, with_connection, PgPool, }; +use kitsune_error::{Error, Result}; pub struct InboxResolver { db_pool: PgPool, diff --git a/crates/kitsune-activitypub/src/lib.rs b/crates/kitsune-activitypub/src/lib.rs index 76923d0bd..a39cc1fbf 100644 --- a/crates/kitsune-activitypub/src/lib.rs +++ b/crates/kitsune-activitypub/src/lib.rs @@ -1,7 +1,6 @@ #[macro_use] extern crate tracing; -use crate::error::{Error, Result}; use diesel::{ExpressionMethods, SelectableHelper}; use diesel_async::{AsyncPgConnection, RunQueryDsl}; use futures_util::{stream, StreamExt, TryStreamExt}; @@ -23,6 +22,7 @@ use kitsune_db::{ with_transaction, PgPool, }; use kitsune_embed::Client as EmbedClient; +use kitsune_error::{kitsune_error, Error, Result}; use kitsune_language::Language; use kitsune_search::{AnySearchBackend, SearchBackend}; use kitsune_type::ap::{object::MediaAttachment, Object, Tag, TagType}; @@ -31,7 +31,6 @@ use speedy_uuid::Uuid; use typed_builder::TypedBuilder; pub mod deliverer; -pub mod error; pub mod fetcher; pub mod inbox_resolver; pub mod mapping; @@ -104,8 +103,7 @@ async fn handle_custom_emojis( emoji_text: emoji_tag.name.clone(), }) .try_collect::>() - .await - .map_err(Error::FetchEmoji)?; + .await?; diesel::insert_into(posts_custom_emojis::table) .values(emojis) @@ -206,15 +204,14 @@ async fn preprocess_object( if Uri::try_from(&object.attributed_to)?.authority() != Uri::try_from(&object.id)?.authority() { - return Err(Error::InvalidDocument); + return Err(kitsune_error!("invalid document")); } let Some(author) = fetcher .fetch_account(object.attributed_to.as_str().into()) - .await - .map_err(Error::FetchAccount)? + .await? else { - return Err(Error::NotFound); + return Err(kitsune_error!("account not found")); }; CowBox::boxed(author) @@ -229,8 +226,7 @@ async fn preprocess_object( .call_depth(call_depth + 1) .build(), ) - .await - .map_err(Error::FetchPost)? + .await? .map(|post| post.id) } else { None diff --git a/crates/kitsune-activitypub/src/mapping/activity.rs b/crates/kitsune-activitypub/src/mapping/activity.rs index 87d152964..914278511 100644 --- a/crates/kitsune-activitypub/src/mapping/activity.rs +++ b/crates/kitsune-activitypub/src/mapping/activity.rs @@ -1,5 +1,4 @@ use super::{IntoObject, State}; -use crate::error::Result; use diesel::QueryDsl; use diesel_async::RunQueryDsl; use iso8601_timestamp::Timestamp; @@ -8,6 +7,7 @@ use kitsune_db::{ schema::{accounts, posts}, with_connection, }; +use kitsune_error::Result; use kitsune_type::ap::{ap_context, Activity, ActivityType, ObjectField}; use kitsune_util::try_join; use std::future::Future; diff --git a/crates/kitsune-activitypub/src/mapping/object.rs b/crates/kitsune-activitypub/src/mapping/object.rs index e099fd46a..e2239ec89 100644 --- a/crates/kitsune-activitypub/src/mapping/object.rs +++ b/crates/kitsune-activitypub/src/mapping/object.rs @@ -1,5 +1,4 @@ use super::{util::BaseToCc, State}; -use crate::error::{Error, Result}; use diesel::{BelongingToDsl, ExpressionMethods, QueryDsl, SelectableHelper}; use diesel_async::RunQueryDsl; use futures_util::{future::OptionFuture, FutureExt, TryFutureExt, TryStreamExt}; @@ -14,6 +13,7 @@ use kitsune_db::{ schema::{accounts, custom_emojis, media_attachments, posts, posts_custom_emojis}, with_connection, }; +use kitsune_error::{bail, kitsune_error, Error, ErrorType, Result}; use kitsune_type::ap::{ actor::{Actor, PublicKey}, ap_context, @@ -35,12 +35,19 @@ impl IntoObject for DbMediaAttachment { type Output = MediaAttachment; async fn into_object(self, state: State<'_>) -> Result { - let mime = Mime::from_str(&self.content_type).map_err(|_| Error::UnsupportedMediaType)?; + let mime = Mime::from_str(&self.content_type).map_err( + |_| kitsune_error!(type = ErrorType::UnsupportedMediaType, "unsupported media type"), + )?; + let r#type = match mime.type_() { mime::AUDIO => MediaAttachmentType::Audio, mime::IMAGE => MediaAttachmentType::Image, mime::VIDEO => MediaAttachmentType::Video, - _ => return Err(Error::UnsupportedMediaType), + _ => { + return Err( + kitsune_error!(type = ErrorType::UnsupportedMediaType, "unsupported media type"), + ) + } }; let url = state.service.attachment.get_url(self.id).await?; @@ -98,7 +105,7 @@ impl IntoObject for Post { // Therefore it's also not an object // We just return en error here if self.reposted_post_id.is_some() { - return Err(Error::NotFound); + bail!("post not found"); } let (account, in_reply_to, mentions, emojis, attachment_stream) = @@ -255,7 +262,7 @@ impl IntoObject for CustomEmoji { // Let's pretend we're not home and do not answer let name = match self.domain { None => Ok(format!(":{}:", self.shortcode)), - Some(_) => Err(Error::NotFound), + Some(_) => Err(kitsune_error!("custom emoji not found")), }?; let icon = with_connection!(state.db_pool, |db_conn| { diff --git a/crates/kitsune-activitypub/tests/fetcher/filter.rs b/crates/kitsune-activitypub/tests/fetcher/filter.rs index 1cd2cdf1d..8a32c7e29 100644 --- a/crates/kitsune-activitypub/tests/fetcher/filter.rs +++ b/crates/kitsune-activitypub/tests/fetcher/filter.rs @@ -1,18 +1,24 @@ use super::handle::handle; use http_body_util::Empty; use hyper::{body::Bytes, Request, Response}; -use kitsune_activitypub::{error::Error, Fetcher}; +use kitsune_activitypub::Fetcher; use kitsune_cache::NoopCache; use kitsune_config::instance::FederationFilterConfiguration; use kitsune_core::traits::Fetcher as _; use kitsune_federation_filter::FederationFilter; use kitsune_http_client::Client; use kitsune_search::NoopSearchService; -use kitsune_test::{database_test, language_detection_config}; +use kitsune_test::{assert_display_eq, database_test, language_detection_config}; use kitsune_webfinger::Webfinger; use std::{convert::Infallible, sync::Arc}; use tower::service_fn; +macro_rules! assert_blocked { + ($error:expr) => { + assert_display_eq!($error, "instance is blocked") + }; +} + #[tokio::test] async fn federation_allow() { database_test(|db_pool| async move { @@ -46,25 +52,15 @@ async fn federation_allow() { ))) .build(); - assert!(matches!( - *fetcher - .fetch_post("https://example.com/fakeobject".into()) - .await - .unwrap_err() - .downcast_ref() - .unwrap(), - Error::BlockedInstance - )); + assert_blocked!(fetcher + .fetch_post("https://example.com/fakeobject".into()) + .await + .unwrap_err()); - assert!(matches!( - *fetcher - .fetch_post("https://other.badstuff.com/otherfake".into()) - .await - .unwrap_err() - .downcast_ref() - .unwrap(), - Error::BlockedInstance - )); + assert_blocked!(fetcher + .fetch_post("https://other.badstuff.com/otherfake".into()) + .await + .unwrap_err()); let client = Client::builder().service(service_fn(handle)); let fetcher = builder @@ -118,24 +114,15 @@ async fn federation_deny() { .post_cache(Arc::new(NoopCache.into())) .build(); - assert!(matches!( - fetcher - .fetch_post("https://example.com/fakeobject".into()) - .await - .unwrap_err() - .downcast_ref() - .unwrap(), - Error::BlockedInstance - )); - assert!(matches!( - *fetcher - .fetch_post("https://other.badstuff.com/otherfake".into()) - .await - .unwrap_err() - .downcast_ref() - .unwrap(), - Error::BlockedInstance - )); + assert_blocked!(fetcher + .fetch_post("https://example.com/fakeobject".into()) + .await + .unwrap_err()); + + assert_blocked!(fetcher + .fetch_post("https://other.badstuff.com/otherfake".into()) + .await + .unwrap_err()); }) .await; } diff --git a/crates/kitsune-activitypub/tests/fetcher/origin.rs b/crates/kitsune-activitypub/tests/fetcher/origin.rs index 2588cf281..6523660c7 100644 --- a/crates/kitsune-activitypub/tests/fetcher/origin.rs +++ b/crates/kitsune-activitypub/tests/fetcher/origin.rs @@ -1,14 +1,14 @@ use super::handle::handle; use http::{header::CONTENT_TYPE, uri::PathAndQuery}; use hyper::Request; -use kitsune_activitypub::{error::Error, Fetcher}; +use kitsune_activitypub::Fetcher; use kitsune_cache::NoopCache; use kitsune_config::instance::FederationFilterConfiguration; use kitsune_core::traits::Fetcher as _; use kitsune_federation_filter::FederationFilter; use kitsune_http_client::Client; use kitsune_search::NoopSearchService; -use kitsune_test::{database_test, language_detection_config}; +use kitsune_test::{assert_display_eq, database_test, language_detection_config}; use kitsune_webfinger::Webfinger; use std::{convert::Infallible, sync::Arc}; use tower::service_fn; @@ -108,15 +108,13 @@ async fn check_ap_content_type() { .post_cache(Arc::new(NoopCache.into())) .build(); - assert!(matches!( - *fetcher + assert_display_eq!( + fetcher .fetch_post("https://corteximplant.com/users/0x0".into()) .await - .unwrap_err() - .downcast_ref() - .unwrap(), - Error::InvalidResponse - )); + .unwrap_err(), + "invalid content-type header in response" + ); }) .await; } diff --git a/crates/kitsune-cache/Cargo.toml b/crates/kitsune-cache/Cargo.toml index 7d04b258e..9eaad874e 100644 --- a/crates/kitsune-cache/Cargo.toml +++ b/crates/kitsune-cache/Cargo.toml @@ -7,15 +7,15 @@ license.workspace = true [dependencies] enum_dispatch = "0.3.13" +kitsune-error = { path = "../kitsune-error" } moka = { version = "0.12.5", features = ["future"] } multiplex-pool = { path = "../../lib/multiplex-pool" } -redis = { version = "0.25.2", default-features = false, features = [ +redis = { version = "0.25.3", default-features = false, features = [ "connection-manager", "tokio-comp", ] } serde = "1.0.197" simd-json = "0.13.9" -thiserror = "1.0.58" tracing = "0.1.40" typed-builder = "0.18.1" diff --git a/crates/kitsune-cache/src/error.rs b/crates/kitsune-cache/src/error.rs deleted file mode 100644 index c489648e4..000000000 --- a/crates/kitsune-cache/src/error.rs +++ /dev/null @@ -1,11 +0,0 @@ -use redis::RedisError; -use thiserror::Error; - -#[derive(Debug, Error)] -pub enum Error { - #[error(transparent)] - Redis(#[from] RedisError), - - #[error(transparent)] - SimdJson(#[from] simd_json::Error), -} diff --git a/crates/kitsune-cache/src/in_memory.rs b/crates/kitsune-cache/src/in_memory.rs index 55cde709c..53ef33c94 100644 --- a/crates/kitsune-cache/src/in_memory.rs +++ b/crates/kitsune-cache/src/in_memory.rs @@ -1,4 +1,5 @@ -use crate::{CacheBackend, CacheResult}; +use crate::CacheBackend; +use kitsune_error::Result; use moka::future::Cache; use std::{fmt::Display, marker::PhantomData, time::Duration}; @@ -34,16 +35,16 @@ where K: Display + Send + Sync + ?Sized, V: Clone + Send + Sync + 'static, { - async fn delete(&self, key: &K) -> CacheResult<()> { + async fn delete(&self, key: &K) -> Result<()> { self.inner.remove(&key.to_string()).await; Ok(()) } - async fn get(&self, key: &K) -> CacheResult> { + async fn get(&self, key: &K) -> Result> { Ok(self.inner.get(&key.to_string()).await) } - async fn set(&self, key: &K, value: &V) -> CacheResult<()> { + async fn set(&self, key: &K, value: &V) -> Result<()> { self.inner.insert(key.to_string(), value.clone()).await; Ok(()) } diff --git a/crates/kitsune-cache/src/lib.rs b/crates/kitsune-cache/src/lib.rs index ca6fc966d..6b4e8dfb9 100644 --- a/crates/kitsune-cache/src/lib.rs +++ b/crates/kitsune-cache/src/lib.rs @@ -2,19 +2,16 @@ extern crate tracing; use enum_dispatch::enum_dispatch; +use kitsune_error::Result; use serde::{de::DeserializeOwned, Serialize}; use std::{fmt::Display, sync::Arc}; -pub use self::error::Error; pub use self::in_memory::InMemory as InMemoryCache; pub use self::redis::Redis as RedisCache; -mod error; mod in_memory; mod redis; -type CacheResult = Result; - pub type ArcCache = Arc>; #[enum_dispatch(CacheBackend)] @@ -34,9 +31,9 @@ pub trait CacheBackend: Send + Sync where K: ?Sized, { - async fn delete(&self, key: &K) -> CacheResult<()>; - async fn get(&self, key: &K) -> CacheResult>; - async fn set(&self, key: &K, value: &V) -> CacheResult<()>; + async fn delete(&self, key: &K) -> Result<()>; + async fn get(&self, key: &K) -> Result>; + async fn set(&self, key: &K, value: &V) -> Result<()>; } #[derive(Clone)] @@ -47,15 +44,15 @@ where K: Send + Sync + ?Sized, V: Send + Sync, { - async fn delete(&self, _key: &K) -> CacheResult<()> { + async fn delete(&self, _key: &K) -> Result<()> { Ok(()) } - async fn get(&self, _key: &K) -> CacheResult> { + async fn get(&self, _key: &K) -> Result> { Ok(None) } - async fn set(&self, _key: &K, _value: &V) -> CacheResult<()> { + async fn set(&self, _key: &K, _value: &V) -> Result<()> { Ok(()) } } diff --git a/crates/kitsune-cache/src/redis.rs b/crates/kitsune-cache/src/redis.rs index aa8daf9ca..208092311 100644 --- a/crates/kitsune-cache/src/redis.rs +++ b/crates/kitsune-cache/src/redis.rs @@ -1,4 +1,5 @@ -use super::{CacheBackend, CacheResult}; +use super::CacheBackend; +use kitsune_error::Result; use redis::{aio::ConnectionManager, AsyncCommands}; use serde::{de::DeserializeOwned, Serialize}; use std::{fmt::Display, marker::PhantomData, time::Duration}; @@ -53,7 +54,7 @@ where V: Serialize + DeserializeOwned + Send + Sync, { #[instrument(skip_all, fields(%key))] - async fn delete(&self, key: &K) -> CacheResult<()> { + async fn delete(&self, key: &K) -> Result<()> { let mut conn = self.redis_conn.get(); let key = self.compute_key(key); @@ -64,7 +65,7 @@ where } #[instrument(skip_all, fields(%key))] - async fn get(&self, key: &K) -> CacheResult> { + async fn get(&self, key: &K) -> Result> { let mut conn = self.redis_conn.get(); let key = self.compute_key(key); @@ -79,7 +80,7 @@ where } #[instrument(skip_all, fields(%key))] - async fn set(&self, key: &K, value: &V) -> CacheResult<()> { + async fn set(&self, key: &K, value: &V) -> Result<()> { let mut conn = self.redis_conn.get(); let key = self.compute_key(key); let serialised = simd_json::to_string(value)?; diff --git a/crates/kitsune-captcha/Cargo.toml b/crates/kitsune-captcha/Cargo.toml index 35d915212..6519a5706 100644 --- a/crates/kitsune-captcha/Cargo.toml +++ b/crates/kitsune-captcha/Cargo.toml @@ -8,12 +8,12 @@ license.workspace = true [dependencies] enum_dispatch = "0.3.13" http = "1.1.0" +kitsune-error = { path = "../kitsune-error" } kitsune-http-client = { path = "../kitsune-http-client" } serde = { version = "1.0.197", features = ["derive"] } serde_urlencoded = "0.7.1" simd-json = "0.13.9" strum = { version = "0.26.2", features = ["derive"] } -thiserror = "1.0.58" typed-builder = "0.18.1" [lints] diff --git a/crates/kitsune-captcha/src/error.rs b/crates/kitsune-captcha/src/error.rs index 2518b8f96..abce561e4 100644 --- a/crates/kitsune-captcha/src/error.rs +++ b/crates/kitsune-captcha/src/error.rs @@ -1,8 +1,7 @@ use serde::{Deserialize, Serialize}; use strum::{Display, EnumString}; -use thiserror::Error; -#[derive(Debug, PartialEq, Display, Serialize, Deserialize, EnumString, Error)] +#[derive(Debug, PartialEq, Display, Serialize, Deserialize, EnumString)] pub enum CaptchaVerification { #[strum(serialize = "missing-input-secret")] MissingInputSecret, @@ -28,21 +27,3 @@ pub enum CaptchaVerification { #[strum(default)] Other(String), } - -#[derive(Debug, Error)] -pub enum Error { - #[error(transparent)] - CaptchaVerification(#[from] CaptchaVerification), - - #[error(transparent)] - SimdJson(#[from] simd_json::Error), - - #[error(transparent)] - Http(#[from] http::Error), - - #[error(transparent)] - HttpClient(#[from] kitsune_http_client::Error), - - #[error(transparent)] - HttpForm(#[from] serde_urlencoded::ser::Error), -} diff --git a/crates/kitsune-captcha/src/lib.rs b/crates/kitsune-captcha/src/lib.rs index a798869db..f42ca89bb 100644 --- a/crates/kitsune-captcha/src/lib.rs +++ b/crates/kitsune-captcha/src/lib.rs @@ -2,16 +2,12 @@ use self::error::CaptchaVerification; use enum_dispatch::enum_dispatch; +use kitsune_error::Result; pub mod error; pub mod hcaptcha; pub mod mcaptcha; -pub use self::error::Error; - -/// Result alias where the error defaults to [`BoxError`] -pub type Result = std::result::Result; - /// Status of the captcha challenge verification #[derive(PartialEq)] pub enum ChallengeStatus { diff --git a/crates/kitsune-core/Cargo.toml b/crates/kitsune-core/Cargo.toml index ad8fa39bc..d28250abd 100644 --- a/crates/kitsune-core/Cargo.toml +++ b/crates/kitsune-core/Cargo.toml @@ -9,13 +9,9 @@ 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" } +kitsune-error = { path = "../kitsune-error" } serde = { version = "1.0.197", features = ["derive"] } -speedy-uuid = { path = "../../lib/speedy-uuid", features = ["diesel"] } -thiserror = "1.0.58" typed-builder = "0.18.1" [build-dependencies] diff --git a/crates/kitsune-core/src/error.rs b/crates/kitsune-core/src/error.rs deleted file mode 100644 index 093083db4..000000000 --- a/crates/kitsune-core/src/error.rs +++ /dev/null @@ -1,48 +0,0 @@ -use http::StatusCode; -use std::borrow::Cow; -use thiserror::Error; - -macro_rules! http_error { - ($($variant_name:ident => $status_code:path),*$(,)?) => { - #[derive(Debug, Error)] - pub enum HttpError { - $( - #[doc = stringify!($variant_name)] - #[error("{}", self.as_str())] - $variant_name, - )* - } - - impl HttpError { - #[inline] - pub fn as_str(&self) -> Cow<'static, str> { - let status_code = self.status_code(); - - status_code - .canonical_reason() - .map_or_else( - || Cow::Owned(status_code.as_str().to_string()), - Cow::Borrowed, - ) - } - - #[inline] - #[must_use] - pub fn status_code(&self) -> ::http::StatusCode { - match self { - $( - Self::$variant_name => $status_code, - )* - } - } - } - } -} - -http_error! { - BadRequest => StatusCode::NOT_FOUND, - InternalServerError => StatusCode::INTERNAL_SERVER_ERROR, - NotFound => StatusCode::NOT_FOUND, - Unauthorised => StatusCode::UNAUTHORIZED, - UnsupportedMediaType => StatusCode::UNSUPPORTED_MEDIA_TYPE, -} diff --git a/crates/kitsune-core/src/event/mod.rs b/crates/kitsune-core/src/event/mod.rs deleted file mode 100644 index f19abb0d3..000000000 --- a/crates/kitsune-core/src/event/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -use kitsune_messaging::{MessageConsumer, MessageEmitter}; - -pub use self::post::PostEvent; - -pub mod post; - -pub type PostEventConsumer = MessageConsumer; -pub type PostEventEmitter = MessageEmitter; diff --git a/crates/kitsune-core/src/event/post.rs b/crates/kitsune-core/src/event/post.rs deleted file mode 100644 index a1bccd8c8..000000000 --- a/crates/kitsune-core/src/event/post.rs +++ /dev/null @@ -1,16 +0,0 @@ -use serde::{Deserialize, Serialize}; -use speedy_uuid::Uuid; - -#[derive(Clone, Copy, Deserialize, Serialize)] -#[serde(rename_all = "lowercase")] -pub enum EventType { - Create, - Delete, - Update, -} - -#[derive(Clone, Copy, Deserialize, Serialize)] -pub struct PostEvent { - pub r#type: EventType, - pub post_id: Uuid, -} diff --git a/crates/kitsune-core/src/lib.rs b/crates/kitsune-core/src/lib.rs index 702b5b3b2..90b1e3c14 100644 --- a/crates/kitsune-core/src/lib.rs +++ b/crates/kitsune-core/src/lib.rs @@ -1,4 +1,2 @@ pub mod consts; -pub mod error; -pub mod event; pub mod traits; diff --git a/crates/kitsune-core/src/traits/deliverer.rs b/crates/kitsune-core/src/traits/deliverer.rs index 72120d58d..29e8694a1 100644 --- a/crates/kitsune-core/src/traits/deliverer.rs +++ b/crates/kitsune-core/src/traits/deliverer.rs @@ -1,6 +1,6 @@ use async_trait::async_trait; -use eyre::Result; use kitsune_db::model::{account::Account, favourite::Favourite, follower::Follow, post::Post}; +use kitsune_error::Result; use serde::{Deserialize, Serialize}; use std::sync::Arc; diff --git a/crates/kitsune-core/src/traits/fetcher.rs b/crates/kitsune-core/src/traits/fetcher.rs index 1849dd91f..1912ccd9a 100644 --- a/crates/kitsune-core/src/traits/fetcher.rs +++ b/crates/kitsune-core/src/traits/fetcher.rs @@ -1,7 +1,7 @@ use super::Resolver; use async_trait::async_trait; -use eyre::Result; use kitsune_db::model::{account::Account, custom_emoji::CustomEmoji, post::Post}; +use kitsune_error::Result; use std::sync::Arc; use typed_builder::TypedBuilder; diff --git a/crates/kitsune-core/src/traits/resolver.rs b/crates/kitsune-core/src/traits/resolver.rs index 6458d7efd..addaa1565 100644 --- a/crates/kitsune-core/src/traits/resolver.rs +++ b/crates/kitsune-core/src/traits/resolver.rs @@ -1,5 +1,5 @@ use async_trait::async_trait; -use eyre::Result; +use kitsune_error::Result; use serde::{Deserialize, Serialize}; use std::sync::Arc; diff --git a/crates/kitsune-db/Cargo.toml b/crates/kitsune-db/Cargo.toml index 687fc80f5..eb4534549 100644 --- a/crates/kitsune-db/Cargo.toml +++ b/crates/kitsune-db/Cargo.toml @@ -22,6 +22,7 @@ futures-util = { version = "0.3.30", default-features = false, features = [ ] } iso8601-timestamp = { version = "0.2.17", features = ["diesel-pg"] } kitsune-config = { path = "../kitsune-config" } +kitsune-error = { path = "../kitsune-error" } kitsune-language = { path = "../kitsune-language" } kitsune-type = { path = "../kitsune-type" } num-derive = "0.4.2" @@ -36,12 +37,12 @@ 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.37.0", features = ["rt"] } tokio-postgres = "0.7.10" -tokio-postgres-rustls = "0.11.1" +tokio-postgres-rustls = "0.12.0" tracing = "0.1.40" tracing-log = "0.2.0" +trials = { path = "../../lib/trials" } typed-builder = "0.18.1" [dev-dependencies] diff --git a/crates/kitsune-db/src/error.rs b/crates/kitsune-db/src/error.rs index 12daf1669..a7f0634dc 100644 --- a/crates/kitsune-db/src/error.rs +++ b/crates/kitsune-db/src/error.rs @@ -1,10 +1,5 @@ use core::fmt; -use diesel_async::pooled_connection::bb8; use std::error::Error as StdError; -use thiserror::Error; - -pub type BoxError = Box; -pub type Result = std::result::Result; #[derive(Debug)] pub struct EnumConversionError(pub i32); @@ -35,21 +30,3 @@ impl fmt::Display for IsoCodeConversionError { } impl StdError for IsoCodeConversionError {} - -#[derive(Debug, Error)] -pub enum Error { - #[error(transparent)] - Blocking(#[from] blowocking::Error), - - #[error(transparent)] - Diesel(#[from] diesel::result::Error), - - #[error(transparent)] - DieselConnection(#[from] diesel::result::ConnectionError), - - #[error(transparent)] - Migration(BoxError), - - #[error(transparent)] - Pool(#[from] bb8::RunError), -} diff --git a/crates/kitsune-db/src/lib.rs b/crates/kitsune-db/src/lib.rs index a9e851a86..396db7123 100644 --- a/crates/kitsune-db/src/lib.rs +++ b/crates/kitsune-db/src/lib.rs @@ -9,13 +9,13 @@ use diesel_async::{ }; use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness}; use kitsune_config::database::Configuration as DatabaseConfig; +use kitsune_error::{Error, Result}; use tracing_log::LogTracer; pub type PgPool = Pool; -pub use crate::error::{Error, Result}; #[doc(hidden)] -pub use diesel_async; +pub use {diesel_async, kitsune_error, trials}; mod error; mod pool; @@ -45,7 +45,7 @@ pub async fn connect(config: &DatabaseConfig) -> Result { migration_conn .run_pending_migrations(MIGRATIONS) - .map_err(Error::Migration)?; + .map_err(Error::msg)?; Ok::<_, Error>(()) } diff --git a/crates/kitsune-db/src/pool.rs b/crates/kitsune-db/src/pool.rs index 2201c9758..bd8662843 100644 --- a/crates/kitsune-db/src/pool.rs +++ b/crates/kitsune-db/src/pool.rs @@ -7,20 +7,13 @@ macro_rules! with_connection { }}; } -#[macro_export] -macro_rules! catch_error { - ($($tt:tt)*) => {{ - let result: ::std::result::Result<_, ::diesel_async::pooled_connection::bb8::RunError> = async { - Ok({ $($tt)* }) - }.await; - result - }}; -} - #[macro_export] macro_rules! with_connection_panicky { ($pool:expr, $($other:tt)*) => {{ - $crate::catch_error!($crate::with_connection!($pool, $($other)*)).unwrap() + let result: $crate::kitsune_error::Result<_> = $crate::trials::attempt! { async + $crate::with_connection!($pool, $($other)*) + }; + result.unwrap() }}; } diff --git a/crates/kitsune-email/Cargo.toml b/crates/kitsune-email/Cargo.toml index 34bc33664..0c98349d2 100644 --- a/crates/kitsune-email/Cargo.toml +++ b/crates/kitsune-email/Cargo.toml @@ -14,6 +14,7 @@ askama_axum = "0.4.0" # Damn it, cargo. Because "kitsune" uses "askama" with the diesel = "2.1.5" diesel-async = "0.4.1" kitsune-db = { path = "../kitsune-db" } +kitsune-error = { path = "../kitsune-error" } kitsune-url = { path = "../kitsune-url" } lettre = { version = "0.11.6", default-features = false, features = [ "builder", @@ -24,13 +25,12 @@ lettre = { version = "0.11.6", default-features = false, features = [ "tokio1-rustls-tls", "tracing", ] } -mrml = { version = "3.1.3", default-features = false, features = [ +mrml = { version = "3.1.4", default-features = false, features = [ "orderedmap", "parse", "render", ] } speedy-uuid = { path = "../../lib/speedy-uuid" } -thiserror = "1.0.58" typed-builder = "0.18.1" [lints] diff --git a/crates/kitsune-email/src/error.rs b/crates/kitsune-email/src/error.rs deleted file mode 100644 index 3c3c3176c..000000000 --- a/crates/kitsune-email/src/error.rs +++ /dev/null @@ -1,33 +0,0 @@ -use diesel_async::pooled_connection::bb8::RunError as DatabasePoolError; -use std::{error::Error as StdError, fmt::Debug}; -use thiserror::Error; - -pub type BoxError = Box; -pub type Result = std::result::Result; - -#[derive(Debug, Error)] -pub enum Error { - #[error(transparent)] - Address(#[from] lettre::address::AddressError), - - #[error(transparent)] - DatabasePool(#[from] DatabasePoolError), - - #[error(transparent)] - Diesel(#[from] diesel::result::Error), - - #[error(transparent)] - Lettre(#[from] lettre::error::Error), - - #[error(transparent)] - Templating(#[from] askama::Error), - - #[error(transparent)] - Transport(BoxError), - - #[error(transparent)] - RenderParsing(#[from] mrml::prelude::parser::Error), - - #[error(transparent)] - Rendering(#[from] mrml::prelude::render::Error), -} diff --git a/crates/kitsune-email/src/lib.rs b/crates/kitsune-email/src/lib.rs index c25cbd164..f0d2ec3f4 100644 --- a/crates/kitsune-email/src/lib.rs +++ b/crates/kitsune-email/src/lib.rs @@ -1,7 +1,5 @@ -use crate::{ - error::{BoxError, Error, Result}, - traits::RenderableEmail, -}; +use crate::traits::RenderableEmail; +use kitsune_error::{Error, Result}; use lettre::{ message::{Mailbox, MultiPart}, AsyncTransport, Message, @@ -12,7 +10,6 @@ use typed_builder::TypedBuilder; pub use self::service::Mailing as MailingService; pub use lettre; -pub mod error; pub mod mails; pub mod service; pub mod traits; @@ -27,7 +24,7 @@ pub struct MailSender { impl MailSender where B: AsyncTransport + Sync, - ::Error: Into, + Error: From<::Error>, { pub async fn send<'a, I, M>(&self, mailboxes: I, email: &M) -> Result<()> where @@ -46,10 +43,7 @@ where rendered_email.body.clone(), ))?; - self.backend - .send(message) - .await - .map_err(|err| Error::Transport(err.into()))?; + self.backend.send(message).await?; } Ok(()) diff --git a/crates/kitsune-email/src/mails/confirm_account.rs b/crates/kitsune-email/src/mails/confirm_account.rs index 39c3bdec1..da6f731a9 100644 --- a/crates/kitsune-email/src/mails/confirm_account.rs +++ b/crates/kitsune-email/src/mails/confirm_account.rs @@ -1,8 +1,6 @@ -use crate::{ - error::Result, - traits::{RenderableEmail, RenderedEmail}, -}; +use crate::traits::{RenderableEmail, RenderedEmail}; use askama::Template; +use kitsune_error::Result; use mrml::{mjml::Mjml, prelude::render::RenderOptions}; use typed_builder::TypedBuilder; diff --git a/crates/kitsune-email/src/service.rs b/crates/kitsune-email/src/service.rs index fc844c735..d98e333e4 100644 --- a/crates/kitsune-email/src/service.rs +++ b/crates/kitsune-email/src/service.rs @@ -1,7 +1,8 @@ -use crate::{error::Result, mails::confirm_account::ConfirmAccount, MailSender}; +use crate::{mails::confirm_account::ConfirmAccount, MailSender}; use diesel::{ExpressionMethods, NullableExpressionMethods, QueryDsl}; use diesel_async::RunQueryDsl; use kitsune_db::{function::now, model::user::User, schema::users, with_connection, PgPool}; +use kitsune_error::Result; use kitsune_url::UrlService; use lettre::{AsyncSmtpTransport, Tokio1Executor}; use speedy_uuid::Uuid; diff --git a/crates/kitsune-email/src/traits.rs b/crates/kitsune-email/src/traits.rs index 555e3ac79..7279d3d3b 100644 --- a/crates/kitsune-email/src/traits.rs +++ b/crates/kitsune-email/src/traits.rs @@ -1,4 +1,4 @@ -use crate::error::Result; +use kitsune_error::Result; #[derive(Debug)] pub struct RenderedEmail { diff --git a/crates/kitsune-embed/Cargo.toml b/crates/kitsune-embed/Cargo.toml index 700a64e41..0fd5f4646 100644 --- a/crates/kitsune-embed/Cargo.toml +++ b/crates/kitsune-embed/Cargo.toml @@ -12,11 +12,11 @@ embed-sdk = { git = "https://github.com/Lantern-chat/embed-service.git", rev = " http = "1.1.0" iso8601-timestamp = "0.2.17" kitsune-db = { path = "../kitsune-db" } +kitsune-error = { path = "../kitsune-error" } kitsune-http-client = { path = "../kitsune-http-client" } once_cell = "1.19.0" scraper = { version = "0.19.0", default-features = false } smol_str = "0.2.1" -thiserror = "1.0.58" typed-builder = "0.18.1" [lints] diff --git a/crates/kitsune-embed/src/lib.rs b/crates/kitsune-embed/src/lib.rs index 806a13d1d..a0f774e2d 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, RunQueryDsl}; +use diesel_async::RunQueryDsl; use embed_sdk::EmbedWithExpire; use http::{Method, Request}; use iso8601_timestamp::Timestamp; @@ -9,18 +9,16 @@ use kitsune_db::{ schema::link_previews, with_connection, PgPool, }; +use kitsune_error::Result; use kitsune_http_client::Client as HttpClient; use once_cell::sync::Lazy; use scraper::{Html, Selector}; use smol_str::SmolStr; -use std::fmt::Debug; use typed_builder::TypedBuilder; pub use embed_sdk; pub use embed_sdk::Embed; -type Result = std::result::Result; - static LINK_SELECTOR: Lazy = Lazy::new(|| { Selector::parse("a:not(.mention, .hashtag)").expect("[Bug] Failed to parse link HTML selector") }); @@ -34,18 +32,6 @@ fn first_link_from_fragment(fragment: &str) -> Option { .and_then(|element| element.value().attr("href").map(ToString::to_string)) } -#[derive(Debug, thiserror::Error)] -pub enum Error { - #[error(transparent)] - Diesel(#[from] diesel::result::Error), - - #[error(transparent)] - Http(#[from] kitsune_http_client::Error), - - #[error(transparent)] - Pool(#[from] bb8::RunError), -} - #[derive(Clone, TypedBuilder)] pub struct Client { db_pool: PgPool, diff --git a/crates/kitsune-error/Cargo.toml b/crates/kitsune-error/Cargo.toml new file mode 100644 index 000000000..1af759c7b --- /dev/null +++ b/crates/kitsune-error/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "kitsune-error" +authors.workspace = true +edition.workspace = true +version.workspace = true +license.workspace = true + +[dependencies] +axum-core = "0.4.3" +eyre = "0.6.12" +garde = { version = "0.18.0", default-features = false, features = ["serde"] } +http = "1.1.0" +simd-json = "0.13.9" +sync_wrapper = "1.0.0" +tracing = "0.1.40" + +[lints] +workspace = true diff --git a/crates/kitsune-messaging/LICENSE-AGPL-3.0 b/crates/kitsune-error/LICENSE-AGPL-3.0 similarity index 100% rename from crates/kitsune-messaging/LICENSE-AGPL-3.0 rename to crates/kitsune-error/LICENSE-AGPL-3.0 diff --git a/crates/kitsune-error/src/axum.rs b/crates/kitsune-error/src/axum.rs new file mode 100644 index 000000000..8c04c9e31 --- /dev/null +++ b/crates/kitsune-error/src/axum.rs @@ -0,0 +1,48 @@ +use crate::{Error, ErrorType}; +use axum_core::response::{IntoResponse, Response}; +use http::StatusCode; + +#[inline] +fn to_response(status_code: StatusCode, maybe_body: Option) -> Response +where + B: IntoResponse, +{ + maybe_body.map_or_else( + || status_code.into_response(), + |body| (status_code, body).into_response(), + ) +} + +impl From for Response { + #[inline] + fn from(value: Error) -> Self { + value.into_response() + } +} + +impl IntoResponse for Error { + fn into_response(self) -> Response { + debug!(error = ?self.inner); + + if let Some(garde_report) = self.inner.downcast_ref::() { + let body = match simd_json::to_string(&garde_report) { + Ok(body) => body, + Err(error) => return Error::from(error).into_response(), + }; + + return to_response(StatusCode::BAD_REQUEST, Some(body)); + } + + let maybe_body = self.ctx.body.into_inner(); + match self.ctx.ty { + ErrorType::BadRequest => to_response(StatusCode::BAD_REQUEST, maybe_body), + ErrorType::Forbidden => to_response(StatusCode::FORBIDDEN, maybe_body), + ErrorType::NotFound => to_response(StatusCode::NOT_FOUND, maybe_body), + ErrorType::Unauthorized => to_response(StatusCode::UNAUTHORIZED, maybe_body), + ErrorType::UnsupportedMediaType => { + to_response(StatusCode::UNSUPPORTED_MEDIA_TYPE, maybe_body) + } + ErrorType::Other => to_response(StatusCode::INTERNAL_SERVER_ERROR, maybe_body), + } + } +} diff --git a/crates/kitsune-error/src/ext.rs b/crates/kitsune-error/src/ext.rs new file mode 100644 index 000000000..79ba34e79 --- /dev/null +++ b/crates/kitsune-error/src/ext.rs @@ -0,0 +1,21 @@ +use crate::{Error, ErrorContext}; + +mod sealed { + pub trait Sealed {} + + impl Sealed for Result {} +} + +pub trait ResultExt: sealed::Sealed { + fn with_error_context(self, ty: ErrorContext) -> Result; +} + +impl ResultExt for Result +where + E: Into, +{ + #[inline] + fn with_error_context(self, ctx: ErrorContext) -> Result { + self.map_err(|err| err.into().with_context(ctx)) + } +} diff --git a/crates/kitsune-error/src/lib.rs b/crates/kitsune-error/src/lib.rs new file mode 100644 index 000000000..51b4dde14 --- /dev/null +++ b/crates/kitsune-error/src/lib.rs @@ -0,0 +1,139 @@ +#[macro_use] +extern crate tracing; + +use axum_core::response::{IntoResponse, Response}; +use std::fmt::{self, Debug, Display}; +use sync_wrapper::SyncWrapper; + +pub use self::ext::ResultExt; + +mod axum; +mod ext; + +pub type BoxError = Box; +pub type Result = std::result::Result; + +#[macro_export] +macro_rules! bail { + ($(type = $type:expr,)? $msg:expr) => { + return Err($crate::kitsune_error!($(type = $type,)? $msg).into()); + }; +} + +#[macro_export] +macro_rules! kitsune_error { + (type = $type:expr, $msg:expr) => { + $crate::Error::msg($msg).with_context({ $type }.into()) + }; + ($msg:expr) => { + $crate::kitsune_error!(type = $crate::ErrorType::Other, $msg) + }; +} + +#[derive(Clone, Debug)] +pub enum ErrorType { + BadRequest, + Forbidden, + NotFound, + Unauthorized, + UnsupportedMediaType, + Other, +} + +impl ErrorType { + #[must_use] + pub fn with_body(self, body: B) -> ErrorContext + where + B: IntoResponse, + { + ErrorContext { + ty: self, + body: Some(body.into_response()).into(), + } + } +} + +impl From for ErrorContext { + fn from(value: ErrorType) -> Self { + Self { + ty: value, + body: SyncWrapper::new(None), + } + } +} + +#[derive(Debug)] +pub struct ErrorContext { + ty: ErrorType, + body: SyncWrapper>, +} + +#[derive(Debug)] +pub struct Error { + ctx: ErrorContext, + inner: eyre::Report, +} + +impl Error { + #[inline] + pub fn new(ctx: ErrorContext, err: E) -> Self + where + E: Into, + { + Self { + ctx, + inner: err.into(), + } + } + + #[inline] + pub fn msg(msg: M) -> Self + where + M: Debug + Display + Send + Sync + 'static, + { + eyre::Report::msg(msg).into() + } + + #[must_use] + pub fn context(&self) -> &ErrorContext { + &self.ctx + } + + pub fn error(&self) -> &eyre::Report { + &self.inner + } + + pub fn into_error(self) -> eyre::Report { + self.inner + } + + #[must_use] + pub fn with_context(self, ctx: ErrorContext) -> Self { + Self { ctx, ..self } + } +} + +impl From for Error +where + T: Into, +{ + fn from(value: T) -> Self { + Self { + ctx: ErrorType::Other.into(), + inner: value.into(), + } + } +} + +impl From for BoxError { + fn from(value: Error) -> Self { + BoxError::from(value.inner) + } +} + +impl fmt::Display for Error { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + ::fmt(&self.inner, f) + } +} diff --git a/crates/kitsune-federation-filter/Cargo.toml b/crates/kitsune-federation-filter/Cargo.toml index 069ac0102..fd385f953 100644 --- a/crates/kitsune-federation-filter/Cargo.toml +++ b/crates/kitsune-federation-filter/Cargo.toml @@ -8,8 +8,8 @@ license.workspace = true [dependencies] globset = "0.4.14" kitsune-config = { path = "../kitsune-config" } +kitsune-error = { path = "../kitsune-error" } kitsune-type = { path = "../kitsune-type" } -thiserror = "1.0.58" url = "2.5.0" [lints] diff --git a/crates/kitsune-federation-filter/src/error.rs b/crates/kitsune-federation-filter/src/error.rs deleted file mode 100644 index 99d0991e5..000000000 --- a/crates/kitsune-federation-filter/src/error.rs +++ /dev/null @@ -1,15 +0,0 @@ -use thiserror::Error; - -pub type Result = std::result::Result; - -#[derive(Debug, Error)] -pub enum Error { - #[error(transparent)] - Glob(#[from] globset::Error), - - #[error("Host missing from URL")] - HostMissing, - - #[error(transparent)] - UrlParse(#[from] url::ParseError), -} diff --git a/crates/kitsune-federation-filter/src/lib.rs b/crates/kitsune-federation-filter/src/lib.rs index 3c05f6572..1dd0c566a 100644 --- a/crates/kitsune-federation-filter/src/lib.rs +++ b/crates/kitsune-federation-filter/src/lib.rs @@ -1,12 +1,10 @@ -use crate::error::{Error, Result}; use globset::{Glob, GlobSet, GlobSetBuilder}; use kitsune_config::instance::FederationFilterConfiguration; +use kitsune_error::{kitsune_error, Result}; use kitsune_type::ap::{actor::Actor, Activity, Object}; use std::sync::Arc; use url::Url; -pub mod error; - pub trait Entity { fn id(&self) -> &str; } @@ -58,7 +56,9 @@ impl FederationFilter { } pub fn is_url_allowed(&self, url: &Url) -> Result { - let host = url.host_str().ok_or(Error::HostMissing)?; + let host = url + .host_str() + .ok_or_else(|| kitsune_error!("missing host component"))?; let allowed = match self.filter { FilterMode::Allow { .. } => self.domains.is_match(host), diff --git a/crates/kitsune-jobs/Cargo.toml b/crates/kitsune-jobs/Cargo.toml index 0ceccd7ef..0b6ac0121 100644 --- a/crates/kitsune-jobs/Cargo.toml +++ b/crates/kitsune-jobs/Cargo.toml @@ -10,11 +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" } +kitsune-error = { path = "../kitsune-error" } 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 b4f0863ca..70b5a2d35 100644 --- a/crates/kitsune-jobs/src/deliver/accept.rs +++ b/crates/kitsune-jobs/src/deliver/accept.rs @@ -13,7 +13,7 @@ pub struct DeliverAccept { impl Runnable for DeliverAccept { type Context = JobRunnerContext; - type Error = eyre::Report; + type Error = kitsune_error::Error; #[instrument(skip_all, fields(follow_id = %self.follow_id))] async fn run(&self, ctx: &Self::Context) -> Result<(), Self::Error> { diff --git a/crates/kitsune-jobs/src/deliver/create.rs b/crates/kitsune-jobs/src/deliver/create.rs index 24eb7bd0c..046ed075d 100644 --- a/crates/kitsune-jobs/src/deliver/create.rs +++ b/crates/kitsune-jobs/src/deliver/create.rs @@ -14,7 +14,7 @@ pub struct DeliverCreate { impl Runnable for DeliverCreate { type Context = JobRunnerContext; - type Error = eyre::Report; + type Error = kitsune_error::Error; #[instrument(skip_all, fields(post_id = %self.post_id))] async fn run(&self, ctx: &Self::Context) -> Result<(), Self::Error> { diff --git a/crates/kitsune-jobs/src/deliver/delete.rs b/crates/kitsune-jobs/src/deliver/delete.rs index 7c7a2131c..6089fa4eb 100644 --- a/crates/kitsune-jobs/src/deliver/delete.rs +++ b/crates/kitsune-jobs/src/deliver/delete.rs @@ -14,7 +14,7 @@ pub struct DeliverDelete { impl Runnable for DeliverDelete { type Context = JobRunnerContext; - type Error = eyre::Report; + type Error = kitsune_error::Error; #[instrument(skip_all, fields(post_id = %self.post_id))] async fn run(&self, ctx: &Self::Context) -> Result<(), Self::Error> { diff --git a/crates/kitsune-jobs/src/deliver/favourite.rs b/crates/kitsune-jobs/src/deliver/favourite.rs index 46a7b2d96..57eb15e40 100644 --- a/crates/kitsune-jobs/src/deliver/favourite.rs +++ b/crates/kitsune-jobs/src/deliver/favourite.rs @@ -14,7 +14,7 @@ pub struct DeliverFavourite { impl Runnable for DeliverFavourite { type Context = JobRunnerContext; - type Error = eyre::Report; + type Error = kitsune_error::Error; #[instrument(skip_all, fields(favourite_id = %self.favourite_id))] async fn run(&self, ctx: &Self::Context) -> Result<(), Self::Error> { diff --git a/crates/kitsune-jobs/src/deliver/follow.rs b/crates/kitsune-jobs/src/deliver/follow.rs index 1465999a6..08368a565 100644 --- a/crates/kitsune-jobs/src/deliver/follow.rs +++ b/crates/kitsune-jobs/src/deliver/follow.rs @@ -14,7 +14,7 @@ pub struct DeliverFollow { impl Runnable for DeliverFollow { type Context = JobRunnerContext; - type Error = eyre::Report; + type Error = kitsune_error::Error; #[instrument(skip_all, fields(follow_id = %self.follow_id))] async fn run(&self, ctx: &Self::Context) -> Result<(), Self::Error> { diff --git a/crates/kitsune-jobs/src/deliver/reject.rs b/crates/kitsune-jobs/src/deliver/reject.rs index 050431acf..bc5411b70 100644 --- a/crates/kitsune-jobs/src/deliver/reject.rs +++ b/crates/kitsune-jobs/src/deliver/reject.rs @@ -13,7 +13,7 @@ pub struct DeliverReject { impl Runnable for DeliverReject { type Context = JobRunnerContext; - type Error = eyre::Report; + type Error = kitsune_error::Error; #[instrument(skip_all, fields(follow_id = %self.follow_id))] async fn run(&self, ctx: &Self::Context) -> Result<(), Self::Error> { diff --git a/crates/kitsune-jobs/src/deliver/unfavourite.rs b/crates/kitsune-jobs/src/deliver/unfavourite.rs index 046c000de..bf7ed6258 100644 --- a/crates/kitsune-jobs/src/deliver/unfavourite.rs +++ b/crates/kitsune-jobs/src/deliver/unfavourite.rs @@ -14,7 +14,7 @@ pub struct DeliverUnfavourite { impl Runnable for DeliverUnfavourite { type Context = JobRunnerContext; - type Error = eyre::Report; + type Error = kitsune_error::Error; #[instrument(skip_all, fields(favourite_id = %self.favourite_id))] async fn run(&self, ctx: &Self::Context) -> Result<(), Self::Error> { diff --git a/crates/kitsune-jobs/src/deliver/unfollow.rs b/crates/kitsune-jobs/src/deliver/unfollow.rs index cfa1db94a..6d3237cd9 100644 --- a/crates/kitsune-jobs/src/deliver/unfollow.rs +++ b/crates/kitsune-jobs/src/deliver/unfollow.rs @@ -14,7 +14,7 @@ pub struct DeliverUnfollow { impl Runnable for DeliverUnfollow { type Context = JobRunnerContext; - type Error = eyre::Report; + type Error = kitsune_error::Error; async fn run(&self, ctx: &Self::Context) -> Result<(), Self::Error> { let follow = with_connection!(ctx.db_pool, |db_conn| { diff --git a/crates/kitsune-jobs/src/deliver/update.rs b/crates/kitsune-jobs/src/deliver/update.rs index ae605b86b..30ff91241 100644 --- a/crates/kitsune-jobs/src/deliver/update.rs +++ b/crates/kitsune-jobs/src/deliver/update.rs @@ -25,7 +25,7 @@ pub struct DeliverUpdate { impl Runnable for DeliverUpdate { type Context = JobRunnerContext; - type Error = eyre::Report; + type Error = kitsune_error::Error; async fn run(&self, ctx: &Self::Context) -> Result<(), Self::Error> { let action = match self.entity { diff --git a/crates/kitsune-jobs/src/lib.rs b/crates/kitsune-jobs/src/lib.rs index 315fe863e..e7901b3d0 100644 --- a/crates/kitsune-jobs/src/lib.rs +++ b/crates/kitsune-jobs/src/lib.rs @@ -55,7 +55,7 @@ pub enum Job { impl Runnable for Job { type Context = JobRunnerContext; - type Error = eyre::Report; + type Error = kitsune_error::Error; async fn run(&self, ctx: &Self::Context) -> Result<(), Self::Error> { match self { @@ -87,7 +87,7 @@ impl KitsuneContextRepo { impl JobContextRepository for KitsuneContextRepo { type JobContext = Job; - type Error = eyre::Report; + type Error = kitsune_error::Error; type Stream = BoxStream<'static, Result<(Uuid, Self::JobContext), Self::Error>>; async fn fetch_context(&self, job_ids: I) -> Result @@ -103,7 +103,7 @@ impl JobContextRepository for KitsuneContextRepo { Ok(stream .map_ok(|ctx| (ctx.id, ctx.context.0)) - .map_err(eyre::Report::from) + .map_err(kitsune_error::Error::from) .boxed()) } diff --git a/crates/kitsune-jobs/src/mailing/confirmation.rs b/crates/kitsune-jobs/src/mailing/confirmation.rs index 62de30776..722f910b4 100644 --- a/crates/kitsune-jobs/src/mailing/confirmation.rs +++ b/crates/kitsune-jobs/src/mailing/confirmation.rs @@ -13,7 +13,7 @@ pub struct SendConfirmationMail { impl Runnable for SendConfirmationMail { type Context = JobRunnerContext; - type Error = eyre::Report; + type Error = kitsune_error::Error; async fn run(&self, ctx: &Self::Context) -> Result<(), Self::Error> { let mailing_service = &ctx.service.mailing; diff --git a/crates/kitsune-mastodon/Cargo.toml b/crates/kitsune-mastodon/Cargo.toml index 557ca1e05..fc967efc5 100644 --- a/crates/kitsune-mastodon/Cargo.toml +++ b/crates/kitsune-mastodon/Cargo.toml @@ -6,15 +6,14 @@ version.workspace = true license.workspace = true [dependencies] -derive_builder = "0.20.0" diesel = "2.1.5" diesel-async = "0.4.1" futures-util = "0.3.30" iso8601-timestamp = "0.2.17" kitsune-cache = { path = "../kitsune-cache" } -kitsune-core = { path = "../kitsune-core" } kitsune-db = { path = "../kitsune-db" } kitsune-embed = { path = "../kitsune-embed" } +kitsune-error = { path = "../kitsune-error" } kitsune-service = { path = "../kitsune-service" } kitsune-type = { path = "../kitsune-type" } kitsune-url = { path = "../kitsune-url" } @@ -24,8 +23,6 @@ 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.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 deleted file mode 100644 index a82188284..000000000 --- a/crates/kitsune-mastodon/src/error.rs +++ /dev/null @@ -1,22 +0,0 @@ -use diesel_async::pooled_connection::bb8; -use std::fmt::Debug; - -pub type Result = std::result::Result; - -#[derive(Debug, thiserror::Error)] -pub enum Error { - #[error(transparent)] - Cache(#[from] kitsune_cache::Error), - - #[error(transparent)] - DatabasePool(#[from] bb8::RunError), - - #[error(transparent)] - Diesel(#[from] diesel::result::Error), - - #[error(transparent)] - Embed(#[from] kitsune_embed::Error), - - #[error(transparent)] - Service(#[from] kitsune_service::error::Error), -} diff --git a/crates/kitsune-mastodon/src/lib.rs b/crates/kitsune-mastodon/src/lib.rs index 9d243c7cf..ceec34aef 100644 --- a/crates/kitsune-mastodon/src/lib.rs +++ b/crates/kitsune-mastodon/src/lib.rs @@ -2,13 +2,10 @@ extern crate tracing; use self::sealed::{IntoMastodon, MapperState}; -use crate::error::Result; -use derive_builder::Builder; -use futures_util::StreamExt; use kitsune_cache::{ArcCache, CacheBackend}; -use kitsune_core::event::{post::EventType, PostEventConsumer}; use kitsune_db::PgPool; use kitsune_embed::Client as EmbedClient; +use kitsune_error::Result; use kitsune_service::attachment::AttachmentService; use kitsune_url::UrlService; use serde::Deserialize; @@ -18,71 +15,12 @@ use typed_builder::TypedBuilder; mod sealed; -pub mod error; - pub trait MapperMarker: IntoMastodon {} impl MapperMarker for T where T: IntoMastodon {} -#[derive(TypedBuilder)] -struct CacheInvalidationActor { - cache: ArcCache, - event_consumer: PostEventConsumer, -} - -impl CacheInvalidationActor { - async fn run(mut self) { - loop { - while let Some(event) = self.event_consumer.next().await { - let event = match event { - Ok(event) => event, - Err(err) => { - error!(error = %err, "Failed to receive status event"); - continue; - } - }; - - if matches!(event.r#type, EventType::Delete | EventType::Update) { - if let Err(err) = self.cache.delete(&event.post_id).await { - error!(error = %err, "Failed to remove entry from cache"); - } - } - } - - if let Err(err) = self.event_consumer.reconnect().await { - error!(error = %err, "Failed to reconnect to event source"); - } - } - } - - pub fn spawn(self) { - tokio::spawn(self.run()); - } -} - -#[derive(Builder, Clone)] -#[builder(pattern = "owned")] -#[allow(clippy::used_underscore_binding)] +#[derive(Clone, TypedBuilder)] pub struct MastodonMapper { - #[builder( - field( - ty = "Option", - build = "CacheInvalidationActor::builder() - .cache( - self.mastodon_cache - .clone() - .ok_or(MastodonMapperBuilderError::UninitializedField(\"mastodon_cache\"))? - ) - .event_consumer( - self._cache_invalidator - .ok_or(MastodonMapperBuilderError::UninitializedField(\"cache_invalidator\"))? - ) - .build() - .spawn();", - ), - setter(name = "cache_invalidator", strip_option) - )] - _cache_invalidator: (), attachment_service: AttachmentService, db_pool: PgPool, embed_client: Option, @@ -91,11 +29,6 @@ pub struct MastodonMapper { } impl MastodonMapper { - #[must_use] - pub fn builder() -> MastodonMapperBuilder { - MastodonMapperBuilder::default() - } - /// Return a reference to a mapper state /// /// Passed down to the concrete mapping implementations diff --git a/crates/kitsune-mastodon/src/sealed.rs b/crates/kitsune-mastodon/src/sealed.rs index f70c7b759..9ed57b5f7 100644 --- a/crates/kitsune-mastodon/src/sealed.rs +++ b/crates/kitsune-mastodon/src/sealed.rs @@ -1,4 +1,3 @@ -use crate::error::{Error, Result}; use diesel::{ BelongingToDsl, BoolExpressionMethods, ExpressionMethods, JoinOnDsl, NullableExpressionMethods, OptionalExtension, QueryDsl, SelectableHelper, @@ -28,6 +27,7 @@ use kitsune_db::{ }; use kitsune_embed::Client as EmbedClient; use kitsune_embed::{embed_sdk::EmbedType, Embed}; +use kitsune_error::{Error, Result}; use kitsune_service::attachment::AttachmentService; use kitsune_type::mastodon::{ account::Source, diff --git a/crates/kitsune-messaging/Cargo.toml b/crates/kitsune-messaging/Cargo.toml deleted file mode 100644 index 86cd059d1..000000000 --- a/crates/kitsune-messaging/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "kitsune-messaging" -version.workspace = true -authors.workspace = true -edition.workspace = true -license.workspace = true - -[dependencies] -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.14" -redis = { version = "0.25.2", features = ["connection-manager", "tokio-comp"] } -serde = "1.0.197" -simd-json = "0.13.9" -tokio = { version = "1.37.0", features = ["macros", "rt", "sync"] } -tokio-stream = { version = "0.1.15", features = ["sync"] } -tracing = "0.1.40" - -[lints] -workspace = true diff --git a/crates/kitsune-messaging/README.md b/crates/kitsune-messaging/README.md deleted file mode 100644 index 5324b057b..000000000 --- a/crates/kitsune-messaging/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# kitsune-messaging - -Messaging backend abstraction for Kitsune - -## About - -Some common infrastructure built on a trait that abstracts over message delivery backends such as Redis PubSub, RabbitMQ, etc. diff --git a/crates/kitsune-messaging/examples/simple_redis.rs b/crates/kitsune-messaging/examples/simple_redis.rs deleted file mode 100644 index 117fd6b9b..000000000 --- a/crates/kitsune-messaging/examples/simple_redis.rs +++ /dev/null @@ -1,48 +0,0 @@ -use futures_util::StreamExt; -use kitsune_messaging::{redis::RedisMessagingBackend, MessagingHub}; -use simd_json::{json, OwnedValue}; -use std::time::Duration; - -#[tokio::main(flavor = "current_thread")] -async fn main() { - let redis_backend = RedisMessagingBackend::new("redis://localhost") - .await - .unwrap(); - let hub = MessagingHub::new(redis_backend); - - let consumer = hub.consumer::("test".into()).await.unwrap(); - tokio::spawn(consumer.for_each(|msg| async move { println!("Consumer 1: {msg:?}]") })); - - let consumer = hub.consumer::("test".into()).await.unwrap(); - tokio::spawn(consumer.for_each(|msg| async move { println!("Consumer 2: {msg:?}]") })); - - let consumer = hub.consumer::("test2".into()).await.unwrap(); - tokio::spawn(consumer.for_each(|msg| async move { println!("Consumer 3: {msg:?}]") })); - - let consumer = hub.consumer::("test2".into()).await.unwrap(); - tokio::spawn(consumer.for_each(|msg| async move { println!("Consumer 4: {msg:?}]") })); - - for i in 1..=3 { - let emitter = hub.emitter("test".into()); - emitter - .emit(json!({ - "hello": "world", - "who": ["are", "you", "?"], - "message": i, - })) - .await - .unwrap(); - - let emitter = hub.emitter("test2".into()); - emitter - .emit(json!({ - "hello": "world", - "who": ["are", "you", "?"], - "message": i, - })) - .await - .unwrap(); - } - - tokio::time::sleep(Duration::from_secs(1)).await; -} diff --git a/crates/kitsune-messaging/examples/simple_tokio_broadcast.rs b/crates/kitsune-messaging/examples/simple_tokio_broadcast.rs deleted file mode 100644 index 1be4f8e5a..000000000 --- a/crates/kitsune-messaging/examples/simple_tokio_broadcast.rs +++ /dev/null @@ -1,46 +0,0 @@ -use futures_util::StreamExt; -use kitsune_messaging::{tokio_broadcast::TokioBroadcastMessagingBackend, MessagingHub}; -use simd_json::{json, OwnedValue}; -use std::time::Duration; - -#[tokio::main(flavor = "current_thread")] -async fn main() { - let tokio_broadcast_backend = TokioBroadcastMessagingBackend::default(); - let hub = MessagingHub::new(tokio_broadcast_backend); - - let consumer = hub.consumer::("test".into()).await.unwrap(); - tokio::spawn(consumer.for_each(|msg| async move { println!("Consumer 1: {msg:?}]") })); - - let consumer = hub.consumer::("test".into()).await.unwrap(); - tokio::spawn(consumer.for_each(|msg| async move { println!("Consumer 2: {msg:?}]") })); - - let consumer = hub.consumer::("test2".into()).await.unwrap(); - tokio::spawn(consumer.for_each(|msg| async move { println!("Consumer 3: {msg:?}]") })); - - let consumer = hub.consumer::("test2".into()).await.unwrap(); - tokio::spawn(consumer.for_each(|msg| async move { println!("Consumer 4: {msg:?}]") })); - - for i in 1..=3 { - let emitter = hub.emitter("test".into()); - emitter - .emit(json!({ - "hello": "world", - "who": ["are", "you", "?"], - "message": i, - })) - .await - .unwrap(); - - let emitter = hub.emitter("test2".into()); - emitter - .emit(json!({ - "hello": "world", - "who": ["are", "you", "?"], - "message": i, - })) - .await - .unwrap(); - } - - tokio::time::sleep(Duration::from_secs(1)).await; -} diff --git a/crates/kitsune-messaging/src/lib.rs b/crates/kitsune-messaging/src/lib.rs deleted file mode 100644 index 3b8c3ef7e..000000000 --- a/crates/kitsune-messaging/src/lib.rs +++ /dev/null @@ -1,257 +0,0 @@ -#![doc = include_str!("../README.md")] -#![forbid(missing_docs)] - -#[macro_use] -extern crate tracing; - -use derive_more::From; -use futures_util::{stream::BoxStream, Stream, StreamExt}; -use pin_project_lite::pin_project; -use serde::{de::DeserializeOwned, Serialize}; -use std::{ - error::Error, - future::Future, - marker::PhantomData, - pin::Pin, - sync::Arc, - task::{self, ready, Poll}, -}; - -/// Boxed error -pub type BoxError = Box; - -/// Type alias for Result, defaulting to [`BoxError`] on the error branch -pub type Result = std::result::Result; - -pub mod redis; -pub mod tokio_broadcast; - -/// Enum dispatch over all supported backends -#[derive(From)] -pub enum AnyMessagingBackend { - /// Redis backend - Redis(redis::RedisMessagingBackend), - - /// Tokio broadcast backend - Tokio(tokio_broadcast::TokioBroadcastMessagingBackend), -} - -impl MessagingBackend for AnyMessagingBackend { - async fn enqueue(&self, channel_name: &str, message: Vec) -> Result<()> { - match self { - Self::Redis(redis) => redis.enqueue(channel_name, message).await, - Self::Tokio(tokio) => tokio.enqueue(channel_name, message).await, - } - } - - async fn message_stream( - &self, - channel_name: String, - ) -> Result>> + 'static> { - match self { - Self::Redis(redis) => redis - .message_stream(channel_name) - .await - .map(StreamExt::left_stream), - Self::Tokio(tokio) => tokio - .message_stream(channel_name) - .await - .map(StreamExt::right_stream), - } - } -} - -/// Messaging backend -/// -/// This is the trait that lets the message hub create emitters and consumers. -/// The backend just needs to be able to transport bytes, that's all. -/// -/// The trait is designed to be object-safe since it's internally stored inside an `Arc` -/// and supposed to be type-erased for ease of testing. -pub trait MessagingBackend { - /// Enqueue a new message onto the backend - fn enqueue(&self, channel_name: &str, message: Vec) -> impl Future>; - - /// Open a new stream of messages from the backend - fn message_stream( - &self, - channel_name: String, - ) -> impl Future>> + 'static>>; -} - -pin_project! { - /// Consumer of messages - pub struct MessageConsumer { - backend: Arc, - channel_name: String, - #[pin] - inner: BoxStream<'static, Result>>, - _ty: PhantomData, - } -} - -impl MessageConsumer -where - M: DeserializeOwned + Serialize, -{ - /// Duplicate the message consumer - /// - /// This is essentially just creating another consumer. - /// Useful if you don't have access to the backend nor an emitter - /// - /// # Errors - /// - /// - Failed to create another consumer - /// - /// For more details, check [`MessagingHub::consumer`] - pub async fn duplicate(&self) -> Result { - MessagingHub { - backend: self.backend.clone(), - } - .consumer(self.channel_name.clone()) - .await - } - - /// Create an emitter that emits messages to this consumer - #[must_use] - pub fn emitter(&self) -> MessageEmitter { - MessagingHub { - backend: self.backend.clone(), - } - .emitter(self.channel_name.clone()) - } - - /// Reconnect the message consumer - /// - /// Use this if the stream ever ends and you think it really shouldn't - /// - /// # Errors - /// - /// - Reconnection failed - pub async fn reconnect(&mut self) -> Result<()> { - self.inner = self - .backend - .message_stream(self.channel_name.clone()) - .await? - .boxed(); - - Ok(()) - } -} - -impl Stream for MessageConsumer -where - M: DeserializeOwned, -{ - type Item = Result; - - fn poll_next(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll> { - let this = self.project(); - match ready!(this.inner.poll_next(cx)) { - Some(Ok(mut msg)) => { - Poll::Ready(Some(simd_json::from_slice(&mut msg).map_err(Into::into))) - } - Some(Err(err)) => Poll::Ready(Some(Err(err))), - None => Poll::Ready(None), - } - } -} - -/// Message emitter -/// -/// This is cheaply clonable. Internally it is a string for the channel name and an `Arc` referencing the backend. -#[derive(Clone)] -pub struct MessageEmitter { - backend: Arc, - channel_name: String, - _ty: PhantomData, -} - -impl MessageEmitter -where - M: DeserializeOwned + Serialize, -{ - /// Create a new consumer from the emitter - /// - /// # Errors - /// - /// - Failed to create consumer - pub async fn consumer(&self) -> Result> { - MessagingHub { - backend: self.backend.clone(), - } - .consumer(self.channel_name.clone()) - .await - } - - /// Emit a new message - /// - /// # Errors - /// - /// - Message failed to serialise - /// - Message failed to enqueue - pub async fn emit(&self, message: M) -> Result<()> { - let message = simd_json::to_vec(&message)?; - self.backend.enqueue(&self.channel_name, message).await - } -} - -/// Central hub for messaging -/// -/// Allows for the registration of new emitters and consumers -/// -/// Using the same backend instance ensures that channels with the same name are connected. -/// When using two distinct backend instances it depends on the backend. -/// -/// For example, the Redis backend, when connected to the same Redis server, will connect channels with the same name across two different instances. -pub struct MessagingHub { - backend: Arc, -} - -impl MessagingHub { - /// Create a new messaging hub - pub fn new(backend: B) -> Self - where - B: Into, - { - Self { - backend: Arc::new(backend.into()), - } - } - - /// Create a new consumer of messages emitted to the channel - /// - /// # Errors - /// - /// - Consumer failed to be created - pub async fn consumer(&self, channel_name: String) -> Result> - where - M: DeserializeOwned + Serialize, - { - let message_stream = self - .backend - .message_stream(channel_name.clone()) - .await? - .boxed(); - - Ok(MessageConsumer { - backend: self.backend.clone(), - channel_name, - inner: message_stream, - _ty: PhantomData, - }) - } - - /// Create a new emitter for a channel - #[must_use] - pub fn emitter(&self, channel_name: String) -> MessageEmitter - where - M: DeserializeOwned + Serialize, - { - MessageEmitter { - channel_name, - backend: self.backend.clone(), - _ty: PhantomData, - } - } -} diff --git a/crates/kitsune-messaging/src/redis.rs b/crates/kitsune-messaging/src/redis.rs deleted file mode 100644 index fe87b58ed..000000000 --- a/crates/kitsune-messaging/src/redis.rs +++ /dev/null @@ -1,172 +0,0 @@ -//! -//! Redis implementation -//! - -use crate::{MessagingBackend, Result}; -use ahash::AHashMap; -use futures_util::{future, Stream, StreamExt, TryStreamExt}; -use just_retry::RetryExt; -use redis::{ - aio::{ConnectionManager, PubSub}, - AsyncCommands, RedisError, -}; -use std::fmt::Debug; -use tokio::sync::{broadcast, mpsc, oneshot}; -use tokio_stream::wrappers::BroadcastStream; - -const BROADCAST_CAPACITY: usize = 10; -const REGISTRATION_QUEUE_SIZE: usize = 50; - -macro_rules! handle_err { - ($result:expr, $msg:literal $(,)?) => {{ - if let Err(error) = { $result } { - error!(?error, $msg); - } - }}; - ($result:expr $(,)?) => { - handle_err!($result, ""); - }; -} - -#[derive(Debug)] -struct RegistrationMessage { - channel_pattern: String, - responder: oneshot::Sender>>, -} - -struct MultiplexActor { - client: redis::Client, - conn: PubSub, - mapping: AHashMap>>, - registration_queue: mpsc::Receiver, -} - -impl MultiplexActor { - async fn run(mut self) { - loop { - tokio::select! { - Some(msg) = self.registration_queue.recv() => { - let receiver = if let Some(sender) = self.mapping.get(&msg.channel_pattern) { - sender.subscribe() - } else { - let (sender, receiver) = broadcast::channel(BROADCAST_CAPACITY); - - handle_err!(self.conn.psubscribe( - msg.channel_pattern.as_str()).await, - "Failed to subscribe to pattern", - ); - - self.mapping.insert(msg.channel_pattern, sender); - receiver - }; - drop(msg.responder.send(receiver)); - } - msg = future::poll_fn(|ctx| self.conn.on_message().poll_next_unpin(ctx)) => { - if let Some(msg) = msg { - let pattern: String = msg.get_pattern().unwrap(); - - if let Some(sender) = self.mapping.get(&pattern) { - if sender.send(msg.get_payload_bytes().to_vec()).is_err() { - // According to the tokio docs, this case only occurs when all receivers have been dropped - handle_err!( - self.conn.punsubscribe(pattern.as_str()).await, - "Failed to unsubscribe from pattern", - ); - self.mapping.remove(&pattern); - } - } else { - debug!(%pattern, "Failed to find correct receiver"); - } - } else { - self.conn = (|| { - let client = self.client.clone(); - async move { - client - .get_async_pubsub() - .await - } - }) - .retry(just_retry::backoff_policy()) - .await - .unwrap(); - - for key in self.mapping.keys() { - handle_err!( - self.conn.psubscribe(key).await, - "Failed to subscribe to pattern", - ); - } - } - } - } - } - } - - pub async fn spawn( - client: redis::Client, - ) -> Result, RedisError> { - let (sender, receiver) = mpsc::channel(REGISTRATION_QUEUE_SIZE); - - let actor = Self { - mapping: AHashMap::new(), - conn: client.get_async_pubsub().await?, - client, - registration_queue: receiver, - }; - tokio::spawn(actor.run()); - - Ok(sender) - } -} - -/// Implementation of the [`MessagingBackend`] trait for Redis PubSub -/// -/// Note: Channel names, when passed to the `message_stream` function, are interpreted as channel patterns. -pub struct RedisMessagingBackend { - pub_connection: ConnectionManager, - sub_actor: mpsc::Sender, -} - -impl RedisMessagingBackend { - /// Create a new Redis PubSub backend - /// - /// # Errors - /// - /// - Failed to connect to the Redis instance - pub async fn new(conn_string: &str) -> Result { - let client = redis::Client::open(conn_string)?; - let sub_actor = MultiplexActor::spawn(client.clone()).await?; - let pub_connection = ConnectionManager::new(client).await?; - - Ok(Self { - pub_connection, - sub_actor, - }) - } -} - -impl MessagingBackend for RedisMessagingBackend { - async fn enqueue(&self, channel_name: &str, message: Vec) -> Result<()> { - self.pub_connection - .clone() - .publish(channel_name, message) - .await - .map_err(Into::into) - } - - async fn message_stream( - &self, - channel_name: String, - ) -> Result>> + 'static> { - let (sender, receiver) = oneshot::channel(); - self.sub_actor - .send(RegistrationMessage { - channel_pattern: channel_name, - responder: sender, - }) - .await?; - let broadcast_receiver = receiver.await?; - - Ok(BroadcastStream::new(broadcast_receiver).map_err(Into::into)) - } -} diff --git a/crates/kitsune-messaging/src/tokio_broadcast.rs b/crates/kitsune-messaging/src/tokio_broadcast.rs deleted file mode 100644 index 29bd8def7..000000000 --- a/crates/kitsune-messaging/src/tokio_broadcast.rs +++ /dev/null @@ -1,54 +0,0 @@ -//! -//! Implementation over a Tokio broadcast channel -//! - -use crate::{MessagingBackend, Result}; -use futures_util::{Stream, TryStreamExt}; -use std::{collections::HashMap, sync::RwLock}; -use tokio::sync::broadcast; -use tokio_stream::wrappers::BroadcastStream; - -const BROADCAST_CAPACITY: usize = 50; - -/// Messaging backend implementation based on Tokio's broadcast channel -pub struct TokioBroadcastMessagingBackend { - registry: RwLock>>>, -} - -impl Default for TokioBroadcastMessagingBackend { - fn default() -> Self { - Self { - registry: RwLock::new(HashMap::new()), - } - } -} - -impl MessagingBackend for TokioBroadcastMessagingBackend { - async fn enqueue(&self, channel_name: &str, message: Vec) -> Result<()> { - let guard = self.registry.read().unwrap(); - if let Some(sender) = guard.get(channel_name) { - sender.send(message)?; - } - - Ok(()) - } - - async fn message_stream( - &self, - channel_name: String, - ) -> Result>> + 'static> { - let guard = self.registry.read().unwrap(); - let receiver = if let Some(sender) = guard.get(&channel_name) { - sender.subscribe() - } else { - drop(guard); - - let mut guard = self.registry.write().unwrap(); - let (sender, receiver) = broadcast::channel(BROADCAST_CAPACITY); - guard.insert(channel_name, sender); - receiver - }; - - Ok(BroadcastStream::new(receiver).map_err(Into::into)) - } -} diff --git a/crates/kitsune-oidc/Cargo.toml b/crates/kitsune-oidc/Cargo.toml index f3c6c1416..0361e4344 100644 --- a/crates/kitsune-oidc/Cargo.toml +++ b/crates/kitsune-oidc/Cargo.toml @@ -10,6 +10,7 @@ enum_dispatch = "0.3.13" http = "1.1.0" http-compat = { path = "../../lib/http-compat" } kitsune-config = { path = "../kitsune-config" } +kitsune-error = { path = "../kitsune-error" } kitsune-http-client = { path = "../kitsune-http-client" } moka = { version = "0.12.5", features = ["future"] } multiplex-pool = { path = "../../lib/multiplex-pool" } @@ -19,14 +20,13 @@ openidconnect = { version = "3.5.0", default-features = false, features = [ "accept-rfc3339-timestamps", "accept-string-booleans", ] } -redis = { version = "0.25.2", default-features = false, features = [ +redis = { version = "0.25.3", default-features = false, features = [ "connection-manager", "tokio-comp", ] } serde = { version = "1.0.197", features = ["derive"] } simd-json = "0.13.9" speedy-uuid = { path = "../../lib/speedy-uuid", features = ["serde"] } -thiserror = "1.0.58" url = "2.5.0" [lints] diff --git a/crates/kitsune-oidc/src/error.rs b/crates/kitsune-oidc/src/error.rs deleted file mode 100644 index de10ab221..000000000 --- a/crates/kitsune-oidc/src/error.rs +++ /dev/null @@ -1,55 +0,0 @@ -use openidconnect::{ - core::CoreErrorResponseType, ClaimsVerificationError, DiscoveryError, RequestTokenError, - SigningError, StandardErrorResponse, -}; -use thiserror::Error; - -pub type Result = std::result::Result; - -#[derive(Debug, Error)] -pub enum Error { - #[error(transparent)] - ClaimsVerification(#[from] ClaimsVerificationError), - - #[error(transparent)] - Discovery(#[from] DiscoveryError), - - #[error(transparent)] - JsonParse(#[from] simd_json::Error), - - #[error("Missing Email address")] - MissingEmail, - - #[error("Mismatching hash")] - MismatchingHash, - - #[error("Missing ID token")] - MissingIdToken, - - #[error("Missing login state")] - MissingLoginState, - - #[error("Missing username")] - MissingUsername, - - #[error(transparent)] - Redis(#[from] redis::RedisError), - - #[error(transparent)] - RequestToken( - #[from] - RequestTokenError< - kitsune_http_client::Error, - StandardErrorResponse, - >, - ), - - #[error(transparent)] - Signing(#[from] SigningError), - - #[error("Unknown CSRF token")] - UnknownCsrfToken, - - #[error(transparent)] - UrlParse(#[from] url::ParseError), -} diff --git a/crates/kitsune-oidc/src/lib.rs b/crates/kitsune-oidc/src/lib.rs index 1edb03399..77b6922ed 100644 --- a/crates/kitsune-oidc/src/lib.rs +++ b/crates/kitsune-oidc/src/lib.rs @@ -1,11 +1,9 @@ -use crate::{ - error::Result, - state::{ - store::{InMemory as InMemoryStore, Redis as RedisStore}, - LoginState, OAuth2LoginState, Store, - }, +use crate::state::{ + store::{InMemory as InMemoryStore, Redis as RedisStore}, + LoginState, OAuth2LoginState, Store, }; use kitsune_config::oidc::{Configuration, StoreConfiguration}; +use kitsune_error::{bail, kitsune_error, Result}; use multiplex_pool::RoundRobinStrategy; use openidconnect::{ core::{CoreAuthenticationFlow, CoreClient, CoreProviderMetadata}, @@ -15,9 +13,6 @@ use openidconnect::{ use speedy_uuid::Uuid; use url::Url; -pub use self::error::Error; - -mod error; mod state; pub mod http; @@ -135,7 +130,9 @@ impl OidcService { .request_async(self::http::async_client) .await?; - let id_token = token_response.id_token().ok_or(Error::MissingIdToken)?; + let id_token = token_response + .id_token() + .ok_or_else(|| kitsune_error!("missing id token"))?; let claims = id_token.claims(&self.client.id_token_verifier(), &nonce)?; if let Some(expected_hash) = claims.access_token_hash() { @@ -145,7 +142,7 @@ impl OidcService { )?; if actual_hash != *expected_hash { - return Err(Error::MismatchingHash); + bail!("hash mismatch"); } } @@ -153,9 +150,12 @@ impl OidcService { subject: claims.subject().to_string(), username: claims .preferred_username() - .ok_or(Error::MissingUsername)? + .ok_or_else(|| kitsune_error!("missing username"))? + .to_string(), + email: claims + .email() + .ok_or_else(|| kitsune_error!("missing email address"))? .to_string(), - email: claims.email().ok_or(Error::MissingEmail)?.to_string(), oauth2: OAuth2Info { application_id: oauth2.application_id, scope: oauth2.scope, diff --git a/crates/kitsune-oidc/src/state/store/in_memory.rs b/crates/kitsune-oidc/src/state/store/in_memory.rs index 18731c94d..e0ee0afef 100644 --- a/crates/kitsune-oidc/src/state/store/in_memory.rs +++ b/crates/kitsune-oidc/src/state/store/in_memory.rs @@ -1,8 +1,6 @@ use super::Store; -use crate::{ - error::{Error, Result}, - state::LoginState, -}; +use crate::state::LoginState; +use kitsune_error::{kitsune_error, ErrorType, Result}; use moka::future::Cache; #[derive(Clone)] @@ -20,7 +18,10 @@ impl InMemory { impl Store for InMemory { async fn get_and_remove(&self, key: &str) -> Result { - self.inner.remove(key).await.ok_or(Error::MissingLoginState) + self.inner + .remove(key) + .await + .ok_or_else(|| kitsune_error!(type = ErrorType::BadRequest, "missing login state")) } async fn set(&self, key: &str, value: LoginState) -> Result<()> { diff --git a/crates/kitsune-oidc/src/state/store/mod.rs b/crates/kitsune-oidc/src/state/store/mod.rs index 3bc1104c0..261a358c1 100644 --- a/crates/kitsune-oidc/src/state/store/mod.rs +++ b/crates/kitsune-oidc/src/state/store/mod.rs @@ -1,6 +1,6 @@ use super::LoginState; -use crate::error::Result; use enum_dispatch::enum_dispatch; +use kitsune_error::Result; pub use self::{in_memory::InMemory, redis::Redis}; diff --git a/crates/kitsune-oidc/src/state/store/redis.rs b/crates/kitsune-oidc/src/state/store/redis.rs index 535cc1e2e..4c1ff8461 100644 --- a/crates/kitsune-oidc/src/state/store/redis.rs +++ b/crates/kitsune-oidc/src/state/store/redis.rs @@ -1,5 +1,6 @@ use super::Store; -use crate::{error::Result, state::LoginState}; +use crate::state::LoginState; +use kitsune_error::Result; use redis::{aio::ConnectionManager, AsyncCommands}; const REDIS_PREFIX: &str = "OIDC-LOGIN-STATE"; diff --git a/crates/kitsune-s3/Cargo.toml b/crates/kitsune-s3/Cargo.toml index b861c5940..72ab5b5f0 100644 --- a/crates/kitsune-s3/Cargo.toml +++ b/crates/kitsune-s3/Cargo.toml @@ -9,6 +9,7 @@ license.workspace = true bytes = "1.6.0" futures-util = { version = "0.3.30", default-features = false } http = "1.1.0" +kitsune-error = { path = "../kitsune-error" } kitsune-http-client = { path = "../kitsune-http-client" } quick-xml = { version = "0.31.0", features = ["serialize"] } rusty-s3 = "0.5.0" diff --git a/crates/kitsune-s3/src/lib.rs b/crates/kitsune-s3/src/lib.rs index f98325f48..0d333ed04 100644 --- a/crates/kitsune-s3/src/lib.rs +++ b/crates/kitsune-s3/src/lib.rs @@ -4,15 +4,13 @@ use http::{ header::{CONTENT_LENGTH, ETAG}, Request, }; +use kitsune_error::{bail, Error, Result}; use kitsune_http_client::{Body, Client as HttpClient, Response}; use rusty_s3::{actions::CreateMultipartUpload, Bucket, Credentials, S3Action}; use serde::Serialize; use std::{ops::Deref, time::Duration}; use typed_builder::TypedBuilder; -type BoxError = Box; -type Result = std::result::Result; - const TWO_MINUTES: Duration = Duration::from_secs(2 * 60); #[derive(Serialize)] @@ -49,7 +47,7 @@ async fn execute_request(client: &HttpClient, req: Request) -> Result(&self, path: &str, stream: S) -> Result<()> where S: Stream> + Send + Sync + 'static, - E: Into, + E: Into, { let create_multipart_upload = self .bucket @@ -171,7 +169,7 @@ impl Client { let response = execute_request(&self.http_client, request).await?; let Some(etag_header) = response.headers().get(ETAG) else { - return Err(Box::from("missing etag header")); + bail!("missing etag header"); }; etags.push(etag_header.to_str()?.to_string()); @@ -228,8 +226,9 @@ impl Client { #[cfg(test)] mod test { - use crate::{BoxError, CreateBucketConfiguration}; + use crate::CreateBucketConfiguration; use futures_util::{future, stream, TryStreamExt}; + use kitsune_error::{kitsune_error, Error}; use kitsune_test::minio_test; const TEST_DATA: &[u8] = b"https://open.spotify.com/track/6VNNakpjSH8LNBX7fSGhUv"; @@ -255,7 +254,7 @@ mod test { client .put_object( "good song", - stream::once(future::ok::<_, BoxError>(TEST_DATA.into())), + stream::once(future::ok::<_, Error>(TEST_DATA.into())), ) .await .unwrap(); @@ -287,7 +286,7 @@ mod test { let result = client .put_object( "this will break horribly", - stream::once(future::err(BoxError::from("hehe"))), + stream::once(future::err(kitsune_error!("hehe"))), ) .await; diff --git a/crates/kitsune-search/Cargo.toml b/crates/kitsune-search/Cargo.toml index 427b45a4f..57ea74888 100644 --- a/crates/kitsune-search/Cargo.toml +++ b/crates/kitsune-search/Cargo.toml @@ -16,11 +16,11 @@ enum_dispatch = "0.3.13" futures-util = "0.3.30" kitsune-config = { path = "../kitsune-config" } kitsune-db = { path = "../kitsune-db" } +kitsune-error = { path = "../kitsune-error" } kitsune-language = { path = "../kitsune-language" } serde = { version = "1.0.197", features = ["derive"] } speedy-uuid = { path = "../../lib/speedy-uuid" } strum = { version = "0.26.2", features = ["derive"] } -thiserror = "1.0.58" tracing = "0.1.40" typed-builder = "0.18.1" diff --git a/crates/kitsune-search/src/error.rs b/crates/kitsune-search/src/error.rs deleted file mode 100644 index dc94a6652..000000000 --- a/crates/kitsune-search/src/error.rs +++ /dev/null @@ -1,16 +0,0 @@ -use diesel_async::pooled_connection::bb8; -use std::fmt::Debug; -use thiserror::Error; - -#[derive(Debug, Error)] -pub enum Error { - #[error(transparent)] - Database(#[from] diesel::result::Error), - - #[error(transparent)] - DatabasePool(#[from] bb8::RunError), - - #[cfg(feature = "meilisearch")] - #[error(transparent)] - Meilisearch(#[from] meilisearch_sdk::errors::Error), -} diff --git a/crates/kitsune-search/src/lib.rs b/crates/kitsune-search/src/lib.rs index d434ee859..740ac29cc 100644 --- a/crates/kitsune-search/src/lib.rs +++ b/crates/kitsune-search/src/lib.rs @@ -3,22 +3,19 @@ extern crate tracing; use enum_dispatch::enum_dispatch; use kitsune_db::model::{account::Account as DbAccount, post::Post as DbPost}; +use kitsune_error::Result; use serde::{Deserialize, Serialize}; use speedy_uuid::Uuid; use strum::{AsRefStr, EnumIter}; -mod error; #[cfg(feature = "meilisearch")] mod meilisearch; mod sql; -pub use self::error::Error; #[cfg(feature = "meilisearch")] pub use self::meilisearch::MeiliSearchService; pub use self::sql::SearchService as SqlSearchService; -type Result = std::result::Result; - #[derive(Clone)] #[enum_dispatch(SearchBackend)] pub enum AnySearchBackend { diff --git a/crates/kitsune-service/Cargo.toml b/crates/kitsune-service/Cargo.toml index c119fddbb..9543b8f1f 100644 --- a/crates/kitsune-service/Cargo.toml +++ b/crates/kitsune-service/Cargo.toml @@ -34,10 +34,10 @@ kitsune-core = { path = "../kitsune-core" } kitsune-db = { path = "../kitsune-db" } kitsune-email = { path = "../kitsune-email" } kitsune-embed = { path = "../kitsune-embed" } +kitsune-error = { path = "../kitsune-error" } kitsune-http-client = { path = "../kitsune-http-client" } kitsune-jobs = { path = "../kitsune-jobs" } kitsune-language = { path = "../kitsune-language" } -kitsune-messaging = { path = "../kitsune-messaging" } kitsune-search = { path = "../kitsune-search" } kitsune-storage = { path = "../kitsune-storage" } kitsune-url = { path = "../kitsune-url" } @@ -48,17 +48,15 @@ password-hash = { version = "0.5.0", features = ["std"] } pkcs8 = "0.10.2" post-process = { path = "../../lib/post-process" } rand = "0.8.5" -redis = { version = "0.25.2", default-features = false, features = [ +redis = { version = "0.25.3", default-features = false, features = [ "connection-manager", "tokio-comp", ] } rsa = "0.9.6" rusty-s3 = { version = "0.5.0", default-features = false } 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.37.0", features = ["macros", "sync"] } tracing = "0.1.40" typed-builder = "0.18.1" diff --git a/crates/kitsune-service/src/account.rs b/crates/kitsune-service/src/account.rs index 3bbac8f1d..08112ed4d 100644 --- a/crates/kitsune-service/src/account.rs +++ b/crates/kitsune-service/src/account.rs @@ -3,7 +3,6 @@ use super::{ job::{Enqueue, JobService}, LimitContext, }; -use crate::error::{Error, Result}; use bytes::Bytes; use derive_builder::Builder; use diesel::{ @@ -28,6 +27,7 @@ use kitsune_db::{ schema::{accounts, accounts_follows, accounts_preferences, notifications, posts}, with_connection, PgPool, }; +use kitsune_error::{Error, Result}; use kitsune_jobs::deliver::{ accept::DeliverAccept, follow::DeliverFollow, @@ -299,8 +299,7 @@ impl AccountService { let Some(webfinger_actor) = self .resolver .resolve_account(get_user.username, domain) - .await - .map_err(Error::Resolver)? + .await? else { return Ok(None); }; @@ -310,10 +309,7 @@ impl AccountService { .url(&webfinger_actor.uri) .build(); - self.fetcher - .fetch_account(opts) - .await - .map_err(Error::Fetcher) + self.fetcher.fetch_account(opts).await } else { with_connection!(self.db_pool, |db_conn| { accounts::table @@ -577,8 +573,8 @@ impl AccountService { pub async fn update(&self, mut update: Update) -> Result where - A: Stream> + Send + Sync + 'static, - H: Stream> + Send + Sync + 'static, + A: Stream> + Send + Sync + 'static, + H: Stream> + Send + Sync + 'static, { update.validate(&())?; diff --git a/crates/kitsune-service/src/attachment.rs b/crates/kitsune-service/src/attachment.rs index 400e1d3d9..f31e7aa09 100644 --- a/crates/kitsune-service/src/attachment.rs +++ b/crates/kitsune-service/src/attachment.rs @@ -1,4 +1,3 @@ -use crate::error::{AttachmentError, Error, Result}; use bytes::{Bytes, BytesMut}; use derive_builder::Builder; use diesel::{BoolExpressionMethods, ExpressionMethods, QueryDsl}; @@ -12,8 +11,9 @@ use kitsune_db::{ schema::media_attachments, with_connection, PgPool, }; +use kitsune_error::{kitsune_error, Error, ErrorType, Result}; use kitsune_http_client::Client; -use kitsune_storage::{AnyStorageBackend, BoxError, StorageBackend}; +use kitsune_storage::{AnyStorageBackend, StorageBackend}; use kitsune_url::UrlService; use speedy_uuid::Uuid; use typed_builder::TypedBuilder; @@ -125,13 +125,9 @@ impl AttachmentService { ) -> Result> + 'static> { // TODO: Find way to avoid boxing the streams here if let Some(ref file_path) = media_attachment.file_path { - let stream = self - .storage_backend - .get(file_path.as_str()) - .await - .map_err(Error::Storage)?; + let stream = self.storage_backend.get(file_path.as_str()).await?; - Ok(stream.map_err(Error::Storage).boxed()) + Ok(stream.map_err(Error::from).boxed()) } else if self.media_proxy_enabled { Ok(self .client @@ -141,7 +137,7 @@ impl AttachmentService { .map_err(Into::into) .boxed()) } else { - Err(AttachmentError::NotFound.into()) + Err(kitsune_error!(type = ErrorType::NotFound, "attachment not found")) } } @@ -172,7 +168,7 @@ impl AttachmentService { pub async fn upload(&self, upload: Upload) -> Result where - S: Stream> + Send + Sync + 'static, + S: Stream> + Send + Sync + 'static, { upload.validate(&())?; @@ -182,32 +178,26 @@ impl AttachmentService { pin_mut!(stream); let mut img_bytes = BytesMut::new(); - while let Some(chunk) = stream - .next() - .await - .transpose() - .map_err(AttachmentError::StreamError)? - { + while let Some(chunk) = stream.next().await.transpose()? { img_bytes.extend_from_slice(&chunk); } let img_bytes = img_bytes.freeze(); - let final_bytes = DynImage::from_bytes(img_bytes) - .map_err(AttachmentError::ImageProcessingError)? + let final_bytes = DynImage::from_bytes(img_bytes)? .ok_or(img_parts::Error::WrongSignature) .map(|mut image| { image.set_exif(None); image.encoder().bytes() - }) - .map_err(AttachmentError::ImageProcessingError)?; + })?; self.storage_backend .put(&upload.path, stream::once(async { Ok(final_bytes) })) - .await + .await?; } else { - self.storage_backend.put(&upload.path, upload.stream).await + self.storage_backend + .put(&upload.path, upload.stream) + .await?; } - .map_err(Error::Storage)?; let media_attachment = with_connection!(self.db_pool, |db_conn| { diesel::insert_into(media_attachments::table) diff --git a/crates/kitsune-service/src/captcha.rs b/crates/kitsune-service/src/captcha.rs index 2a13467d0..2f61abae2 100644 --- a/crates/kitsune-service/src/captcha.rs +++ b/crates/kitsune-service/src/captcha.rs @@ -1,5 +1,5 @@ -use crate::error::Result; use kitsune_captcha::{AnyCaptcha, CaptchaBackend, ChallengeStatus}; +use kitsune_error::Result; use typed_builder::TypedBuilder; #[derive(Clone, TypedBuilder)] diff --git a/crates/kitsune-service/src/custom_emoji.rs b/crates/kitsune-service/src/custom_emoji.rs index 2a0aa74d0..f48be5bad 100644 --- a/crates/kitsune-service/src/custom_emoji.rs +++ b/crates/kitsune-service/src/custom_emoji.rs @@ -1,5 +1,4 @@ use super::attachment::{AttachmentService, Upload}; -use crate::error::{BoxError, Error, Result}; use bytes::Bytes; use diesel::{ BoolExpressionMethods, ExpressionMethods, JoinOnDsl, NullableExpressionMethods, @@ -15,6 +14,7 @@ use kitsune_db::{ schema::{custom_emojis, media_attachments, posts, posts_custom_emojis}, with_connection, PgPool, }; +use kitsune_error::{Error, Result}; use kitsune_url::UrlService; use speedy_uuid::Uuid; use typed_builder::TypedBuilder; @@ -134,7 +134,7 @@ impl CustomEmojiService { pub async fn add_emoji(&self, emoji_upload: EmojiUpload) -> Result where - S: Stream> + Send + Sync + 'static, + S: Stream> + Send + Sync + 'static, { emoji_upload.validate(&())?; diff --git a/crates/kitsune-service/src/error.rs b/crates/kitsune-service/src/error.rs deleted file mode 100644 index c22ca6977..000000000 --- a/crates/kitsune-service/src/error.rs +++ /dev/null @@ -1,129 +0,0 @@ -use diesel_async::pooled_connection::bb8; -use std::{error::Error as StdError, fmt::Debug}; -use thiserror::Error; - -pub type BoxError = Box; -pub type Result = std::result::Result; - -#[derive(Debug, Error)] -pub enum AttachmentError { - #[error(transparent)] - ImageProcessingError(#[from] img_parts::Error), - - #[error("Not found")] - NotFound, - - #[error(transparent)] - StreamError(#[from] BoxError), -} - -#[derive(Debug, Error)] -pub enum PostError { - #[error("Bad request")] - BadRequest, - - #[error("Unauthorised")] - Unauthorised, -} - -#[derive(Debug, Error)] -pub enum UserError { - #[error("Invalid captcha")] - InvalidCaptcha, - - #[error("Registrations closed")] - RegistrationsClosed, -} - -#[derive(Debug, Error)] -pub enum Error { - #[error(transparent)] - Attachment(#[from] AttachmentError), - - #[error(transparent)] - Blocking(#[from] blowocking::Error), - - #[error(transparent)] - Cache(#[from] kitsune_cache::Error), - - #[error(transparent)] - Captcha(#[from] kitsune_captcha::Error), - - #[error(transparent)] - DatabasePool(#[from] bb8::RunError), - - #[error(transparent)] - Der(#[from] pkcs8::der::Error), - - #[error(transparent)] - Diesel(#[from] diesel::result::Error), - - #[error(transparent)] - Email(#[from] kitsune_email::error::Error), - - #[error(transparent)] - Embed(#[from] kitsune_embed::Error), - - #[error(transparent)] - Event(kitsune_messaging::BoxError), - - #[error(transparent)] - Fetcher(eyre::Report), - - #[error(transparent)] - Http(#[from] http::Error), - - #[error(transparent)] - HttpClient(#[from] kitsune_http_client::Error), - - #[error(transparent)] - HttpHeaderToStr(#[from] http::header::ToStrError), - - #[error(transparent)] - JobQueue(#[from] athena::Error), - - #[error(transparent)] - Mime(#[from] mime::FromStrError), - - #[error(transparent)] - PasswordHash(#[from] password_hash::Error), - - #[error(transparent)] - Pkcs8(#[from] pkcs8::Error), - - #[error(transparent)] - Post(#[from] PostError), - - #[error(transparent)] - PostProcessing(post_process::BoxError), - - #[error(transparent)] - Resolver(eyre::Report), - - #[error(transparent)] - Rsa(#[from] rsa::Error), - - #[error(transparent)] - Search(#[from] kitsune_search::Error), - - #[error(transparent)] - SimdJson(#[from] simd_json::Error), - - #[error(transparent)] - Spki(#[from] pkcs8::spki::Error), - - #[error(transparent)] - Storage(kitsune_storage::BoxError), - - #[error(transparent)] - UriInvalid(#[from] http::uri::InvalidUri), - - #[error(transparent)] - UrlParse(#[from] url::ParseError), - - #[error(transparent)] - User(#[from] UserError), - - #[error(transparent)] - Validate(#[from] garde::Report), -} diff --git a/crates/kitsune-service/src/instance.rs b/crates/kitsune-service/src/instance.rs index 2e3fb34e8..30188506e 100644 --- a/crates/kitsune-service/src/instance.rs +++ b/crates/kitsune-service/src/instance.rs @@ -1,10 +1,10 @@ -use crate::error::{Error, Result}; use diesel::{ExpressionMethods, QueryDsl}; use diesel_async::RunQueryDsl; use kitsune_db::{ schema::{accounts, posts, users}, with_connection, PgPool, }; +use kitsune_error::{Error, Result}; use smol_str::SmolStr; use typed_builder::TypedBuilder; diff --git a/crates/kitsune-service/src/job.rs b/crates/kitsune-service/src/job.rs index 21f386326..6177cecd0 100644 --- a/crates/kitsune-service/src/job.rs +++ b/crates/kitsune-service/src/job.rs @@ -1,6 +1,6 @@ -use crate::error::Result; use athena::{JobDetails, JobQueue}; use iso8601_timestamp::Timestamp; +use kitsune_error::Result; use kitsune_jobs::{Job, KitsuneContextRepo}; use typed_builder::TypedBuilder; diff --git a/crates/kitsune-service/src/lib.rs b/crates/kitsune-service/src/lib.rs index 9f3db12f4..eb8a0c02b 100644 --- a/crates/kitsune-service/src/lib.rs +++ b/crates/kitsune-service/src/lib.rs @@ -7,7 +7,6 @@ pub mod account; pub mod attachment; pub mod captcha; pub mod custom_emoji; -pub mod error; pub mod instance; pub mod job; pub mod notification; diff --git a/crates/kitsune-service/src/notification.rs b/crates/kitsune-service/src/notification.rs index dc885fd18..2171f281f 100644 --- a/crates/kitsune-service/src/notification.rs +++ b/crates/kitsune-service/src/notification.rs @@ -1,5 +1,4 @@ use super::LimitContext; -use crate::error::{Error, Result}; use diesel::{ BoolExpressionMethods, ExpressionMethods, JoinOnDsl, OptionalExtension, QueryDsl, SelectableHelper, @@ -12,6 +11,7 @@ use kitsune_db::{ schema::{accounts, accounts_follows, accounts_preferences, notifications, posts}, with_connection, PgPool, }; +use kitsune_error::{Error, Result}; use speedy_uuid::Uuid; use typed_builder::TypedBuilder; diff --git a/crates/kitsune-service/src/post/mod.rs b/crates/kitsune-service/src/post/mod.rs index fa217b233..d980ae1eb 100644 --- a/crates/kitsune-service/src/post/mod.rs +++ b/crates/kitsune-service/src/post/mod.rs @@ -4,7 +4,6 @@ use super::{ notification::NotificationService, LimitContext, }; -use crate::error::{Error, PostError, Result}; use async_stream::try_stream; use diesel::{ BelongingToDsl, BoolExpressionMethods, ExpressionMethods, JoinOnDsl, OptionalExtension, @@ -15,7 +14,6 @@ use futures_util::{stream::BoxStream, Stream, StreamExt, TryStreamExt}; use garde::Validate; use iso8601_timestamp::Timestamp; use kitsune_config::language_detection::Configuration as LanguageDetectionConfig; -use kitsune_core::event::{post::EventType, PostEvent, PostEventEmitter}; use kitsune_db::{ model::{ account::Account, @@ -36,6 +34,7 @@ use kitsune_db::{ with_connection, with_transaction, PgPool, }; use kitsune_embed::Client as EmbedClient; +use kitsune_error::{bail, Error, ErrorType, Result}; use kitsune_jobs::deliver::{ create::DeliverCreate, delete::DeliverDelete, @@ -304,7 +303,6 @@ pub struct PostService { language_detection_config: LanguageDetectionConfig, post_resolver: PostResolver, search_backend: kitsune_search::AnySearchBackend, - status_event_emitter: PostEventEmitter, url_service: UrlService, } @@ -325,7 +323,7 @@ impl PostService { .await? != media_attachment_ids.len() as i64 { - return Err(PostError::BadRequest.into()); + bail!(type = ErrorType::BadRequest, "tried to attach unknown attachment ids"); } diesel::insert_into(posts_media_attachments::table) @@ -534,14 +532,6 @@ impl PostService { .await?; } - self.status_event_emitter - .emit(PostEvent { - r#type: EventType::Create, - post_id: post.id, - }) - .await - .map_err(Error::Event)?; - Ok(post) } @@ -567,14 +557,6 @@ impl PostService { ) .await?; - self.status_event_emitter - .emit(PostEvent { - r#type: EventType::Delete, - post_id: post.id, - }) - .await - .map_err(Error::Event)?; - self.search_backend.remove_from_index(&post.into()).await?; Ok(()) @@ -682,14 +664,6 @@ impl PostService { .await?; } - self.status_event_emitter - .emit(PostEvent { - r#type: EventType::Update, - post_id: post.id, - }) - .await - .map_err(Error::Event)?; - Ok(post) } @@ -774,14 +748,6 @@ impl PostService { ) .await?; - self.status_event_emitter - .emit(PostEvent { - r#type: EventType::Create, - post_id: repost.id, - }) - .await - .map_err(Error::Event)?; - Ok(repost) } @@ -816,14 +782,6 @@ impl PostService { ) .await?; - self.status_event_emitter - .emit(PostEvent { - r#type: EventType::Delete, - post_id: post.id, - }) - .await - .map_err(Error::Event)?; - Ok(post) } @@ -1175,10 +1133,10 @@ impl PostService { })?; if admin_role_count == 0 { - return Err(PostError::Unauthorised.into()); + bail!(type = ErrorType::Unauthorized, "unauthorised (not an admin)"); } } else { - return Err(PostError::Unauthorised.into()); + bail!(type = ErrorType::Unauthorized, "unauthorised (not logged in)"); } } diff --git a/crates/kitsune-service/src/post/resolver.rs b/crates/kitsune-service/src/post/resolver.rs index ecd404a1a..0bc508b78 100644 --- a/crates/kitsune-service/src/post/resolver.rs +++ b/crates/kitsune-service/src/post/resolver.rs @@ -1,8 +1,8 @@ use crate::{ account::{AccountService, GetUser}, custom_emoji::{CustomEmojiService, GetEmoji}, - error::{Error, Result}, }; +use kitsune_error::{Error, Result}; use post_process::{BoxError, Element, Html, Render}; use speedy_uuid::Uuid; use std::{borrow::Cow, sync::mpsc}; @@ -94,7 +94,7 @@ impl PostResolver { ) }) .await - .map_err(Error::PostProcessing)?; + .map_err(Error::msg)?; Ok(ResolvedPost { mentioned_accounts: mentioned_account_ids.try_iter().collect(), diff --git a/crates/kitsune-service/src/prepare.rs b/crates/kitsune-service/src/prepare.rs index 155ab644e..edd612ab8 100644 --- a/crates/kitsune-service/src/prepare.rs +++ b/crates/kitsune-service/src/prepare.rs @@ -1,16 +1,12 @@ -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}; -use kitsune_config::{cache, captcha, email, language_detection, messaging, search, storage}; +use kitsune_config::{cache, captcha, email, language_detection, search, storage}; use kitsune_db::PgPool; use kitsune_email::{ lettre::{message::Mailbox, AsyncSmtpTransport, Tokio1Executor}, MailSender, }; -use kitsune_messaging::{ - redis::RedisMessagingBackend, tokio_broadcast::TokioBroadcastMessagingBackend, MessagingHub, -}; use kitsune_search::{AnySearchBackend, NoopSearchService, SqlSearchService}; use kitsune_storage::{fs::Storage as FsStorage, s3::Storage as S3Storage, AnyStorageBackend}; use multiplex_pool::RoundRobinStrategy; @@ -127,23 +123,6 @@ pub fn mail_sender( .build()) } -pub async fn messaging(config: &messaging::Configuration) -> eyre::Result { - let backend = match config { - messaging::Configuration::InProcess => { - MessagingHub::new(TokioBroadcastMessagingBackend::default()) - } - messaging::Configuration::Redis(ref redis_config) => { - let redis_messaging_backend = RedisMessagingBackend::new(&redis_config.url) - .await - .wrap_err("Failed to initialise Redis messaging backend")?; - - MessagingHub::new(redis_messaging_backend) - } - }; - - Ok(backend) -} - #[allow(clippy::unused_async)] // "async" is only unused when none of the more advanced searches are compiled in pub async fn search( search_config: &search::Configuration, @@ -157,10 +136,15 @@ pub async fn search( #[cfg(feature = "meilisearch")] #[allow(clippy::used_underscore_binding)] - kitsune_search::MeiliSearchService::new(&_config.instance_url, &_config.api_key) - .await - .wrap_err("Failed to connect to Meilisearch")? - .into() + { + use eyre::WrapErr; + + kitsune_search::MeiliSearchService::new(&_config.instance_url, &_config.api_key) + .await + .map_err(kitsune_error::Error::into_error) + .wrap_err("Failed to connect to Meilisearch")? + .into() + } } search::Configuration::Sql => SqlSearchService::builder() .db_pool(db_pool.clone()) diff --git a/crates/kitsune-service/src/search.rs b/crates/kitsune-service/src/search.rs index a1d6d1a9f..401818f3f 100644 --- a/crates/kitsune-service/src/search.rs +++ b/crates/kitsune-service/src/search.rs @@ -1,4 +1,3 @@ -use crate::error::Result; use ahash::AHashSet; use diesel::{QueryDsl, SelectableHelper}; use diesel_async::RunQueryDsl; @@ -10,6 +9,7 @@ use kitsune_db::{ schema::{accounts, posts}, with_connection, PgPool, }; +use kitsune_error::Result; use kitsune_search::{SearchBackend, SearchIndex}; use speedy_uuid::Uuid; use std::sync::Arc; diff --git a/crates/kitsune-service/src/timeline.rs b/crates/kitsune-service/src/timeline.rs index 49f783214..a7a1f3292 100644 --- a/crates/kitsune-service/src/timeline.rs +++ b/crates/kitsune-service/src/timeline.rs @@ -1,5 +1,4 @@ use super::LimitContext; -use crate::error::{Error, Result}; use diesel::{BoolExpressionMethods, ExpressionMethods, QueryDsl, SelectableHelper}; use diesel_async::RunQueryDsl; use futures_util::{Stream, TryStreamExt}; @@ -10,6 +9,7 @@ use kitsune_db::{ schema::{accounts_follows, posts, posts_mentions}, with_connection, PgPool, }; +use kitsune_error::{Error, Result}; use speedy_uuid::Uuid; use typed_builder::TypedBuilder; diff --git a/crates/kitsune-service/src/user.rs b/crates/kitsune-service/src/user.rs index ea615b4f5..743f0147d 100644 --- a/crates/kitsune-service/src/user.rs +++ b/crates/kitsune-service/src/user.rs @@ -2,7 +2,6 @@ use super::{ captcha::CaptchaService, job::{Enqueue, JobService}, }; -use crate::error::{Error, Result, UserError}; use argon2::{password_hash::SaltString, Argon2, PasswordHasher}; use diesel_async::RunQueryDsl; use futures_util::future::OptionFuture; @@ -17,6 +16,7 @@ use kitsune_db::{ schema::{accounts, accounts_preferences, users}, with_transaction, PgPool, }; +use kitsune_error::{bail, kitsune_error, Error, ErrorType, Result}; use kitsune_jobs::mailing::confirmation::SendConfirmationMail; use kitsune_url::UrlService; use kitsune_util::{generate_secret, try_join}; @@ -126,7 +126,7 @@ impl UserService { #[allow(clippy::too_many_lines)] // TODO: Refactor to get under the limit pub async fn register(&self, register: Register) -> Result { if !self.registrations_open && !register.force_registration { - return Err(UserError::RegistrationsClosed.into()); + bail!(type = ErrorType::Forbidden.with_body("registrations closed"), "registrations closed"); } register.validate(&RegisterContext { @@ -134,10 +134,12 @@ impl UserService { })?; if self.captcha_service.enabled() { - let token = register.captcha_token.ok_or(UserError::InvalidCaptcha)?; + let invalid_captcha = || kitsune_error!(type = ErrorType::BadRequest.with_body("invalid captcha"), "invalid captcha"); + + let token = register.captcha_token.ok_or_else(invalid_captcha)?; let result = self.captcha_service.verify_token(&token).await?; if result != ChallengeStatus::Verified { - return Err(UserError::InvalidCaptcha.into()); + return Err(invalid_captcha()); } } diff --git a/crates/kitsune-storage/Cargo.toml b/crates/kitsune-storage/Cargo.toml index c79320c78..7419a0934 100644 --- a/crates/kitsune-storage/Cargo.toml +++ b/crates/kitsune-storage/Cargo.toml @@ -9,6 +9,7 @@ license.workspace = true bytes = "1.6.0" derive_more = { version = "1.0.0-beta.6", features = ["from"] } futures-util = "0.3.30" +kitsune-error = { path = "../kitsune-error" } kitsune-s3 = { path = "../kitsune-s3" } rusty-s3 = { version = "0.5.0", default-features = false } tokio = { version = "1.37.0", features = ["fs", "io-util"] } diff --git a/crates/kitsune-storage/src/fs.rs b/crates/kitsune-storage/src/fs.rs index 3f776c8a1..0f5e29292 100644 --- a/crates/kitsune-storage/src/fs.rs +++ b/crates/kitsune-storage/src/fs.rs @@ -2,9 +2,10 @@ //! File system backed implementation of the [`StorageBackend`] trait //! -use crate::{Result, StorageBackend}; +use crate::StorageBackend; use bytes::Bytes; use futures_util::{pin_mut, Stream, StreamExt, TryStreamExt}; +use kitsune_error::Result; use std::path::PathBuf; use tokio::{ fs::{self, File}, diff --git a/crates/kitsune-storage/src/lib.rs b/crates/kitsune-storage/src/lib.rs index 03bec6fba..0d1de83cb 100644 --- a/crates/kitsune-storage/src/lib.rs +++ b/crates/kitsune-storage/src/lib.rs @@ -3,17 +3,12 @@ use bytes::Bytes; use derive_more::From; use futures_util::{Stream, StreamExt}; -use std::{error::Error, future::Future}; +use kitsune_error::Result; +use std::future::Future; pub mod fs; pub mod s3; -/// Boxed error -pub type BoxError = Box; - -/// Result alias where the error defaults to [`BoxError`] -pub type Result = std::result::Result; - /// Trait abstraction over storage backends pub trait StorageBackend: Clone + Send + Sync { /// Delete something from the object storage diff --git a/crates/kitsune-storage/src/s3.rs b/crates/kitsune-storage/src/s3.rs index 77489d859..05809db33 100644 --- a/crates/kitsune-storage/src/s3.rs +++ b/crates/kitsune-storage/src/s3.rs @@ -2,9 +2,10 @@ //! An S3 backed implementation of the [`StorageBackend`] trait //! -use crate::{Result, StorageBackend}; +use crate::StorageBackend; use bytes::Bytes; use futures_util::{Stream, StreamExt}; +use kitsune_error::Result; use rusty_s3::{Bucket, Credentials}; use std::sync::Arc; diff --git a/crates/kitsune-test/Cargo.toml b/crates/kitsune-test/Cargo.toml index 1282d83f4..2aad43763 100644 --- a/crates/kitsune-test/Cargo.toml +++ b/crates/kitsune-test/Cargo.toml @@ -18,13 +18,13 @@ kitsune-s3 = { path = "../kitsune-s3" } multiplex-pool = { path = "../../lib/multiplex-pool" } pin-project-lite = "0.2.14" rand = "0.8.5" -redis = { version = "0.25.2", default-features = false, features = [ +redis = { version = "0.25.3", default-features = false, features = [ "connection-manager", "tokio-rustls-comp", ] } rusty-s3 = { version = "0.5.0", default-features = false } testcontainers = "0.15.0" -testcontainers-modules = { version = "0.3.6", features = [ +testcontainers-modules = { version = "0.3.7", features = [ "minio", "postgres", "redis", diff --git a/crates/kitsune-test/src/lib.rs b/crates/kitsune-test/src/lib.rs index e0aaa23c6..5a0225ce9 100644 --- a/crates/kitsune-test/src/lib.rs +++ b/crates/kitsune-test/src/lib.rs @@ -18,6 +18,7 @@ use uuid::Uuid; mod catch_panic; mod container; +mod macros; mod redis; mod resource; diff --git a/crates/kitsune-test/src/macros.rs b/crates/kitsune-test/src/macros.rs new file mode 100644 index 000000000..72ac8643c --- /dev/null +++ b/crates/kitsune-test/src/macros.rs @@ -0,0 +1,10 @@ +#[macro_export] +macro_rules! assert_display_eq { + ($left:expr, $right:expr $(, $msg:literal)?) => { + assert_eq!( + $left.to_string(), + $right.to_string() + $(, $msg)? + ) + }; +} diff --git a/crates/kitsune-util/Cargo.toml b/crates/kitsune-util/Cargo.toml index ad79444ab..fa5534809 100644 --- a/crates/kitsune-util/Cargo.toml +++ b/crates/kitsune-util/Cargo.toml @@ -10,7 +10,7 @@ bubble-bath = "0.1.2" iso8601-timestamp = "0.2.17" kitsune-type = { path = "../kitsune-type" } once_cell = "1.19.0" -pulldown-cmark = { version = "0.10.0", default-features = false, features = [ +pulldown-cmark = { version = "0.10.2", default-features = false, features = [ "html", "simd", ] } diff --git a/crates/kitsune-util/src/convert.rs b/crates/kitsune-util/src/convert.rs index df22fb12d..1006a797c 100644 --- a/crates/kitsune-util/src/convert.rs +++ b/crates/kitsune-util/src/convert.rs @@ -7,8 +7,10 @@ pub fn timestamp_to_uuid(timestamp: Timestamp) -> Uuid { let seconds = timestamp .duration_since(Timestamp::UNIX_EPOCH) .whole_seconds(); - let uuid_timestamp = - uuid::Timestamp::from_unix(uuid::NoContext, seconds as u64, timestamp.nanosecond()); - Uuid::new_v7(uuid_timestamp) + Uuid::new_v7(uuid::Timestamp::from_unix( + uuid::NoContext, + seconds as u64, + timestamp.nanosecond(), + )) } diff --git a/crates/kitsune-wasm-mrf/Cargo.toml b/crates/kitsune-wasm-mrf/Cargo.toml index 434635a50..9898c04a9 100644 --- a/crates/kitsune-wasm-mrf/Cargo.toml +++ b/crates/kitsune-wasm-mrf/Cargo.toml @@ -15,10 +15,11 @@ futures-util = { version = "0.3.30", default-features = false, features = [ "alloc", ] } kitsune-config = { path = "../kitsune-config" } +kitsune-error = { path = "../kitsune-error" } kitsune-type = { path = "../kitsune-type" } mrf-manifest = { path = "../../lib/mrf-manifest", features = ["decode"] } multiplex-pool = { path = "../../lib/multiplex-pool" } -redis = { version = "0.25.2", default-features = false, features = [ +redis = { version = "0.25.3", default-features = false, features = [ "connection-manager", "tokio-rustls-comp", ] } @@ -26,7 +27,6 @@ simd-json = "0.13.9" slab = "0.4.9" sled = "0.34.7" smol_str = "0.2.1" -thiserror = "1.0.58" tokio = { version = "1.37.0", features = ["fs"] } tracing = "0.1.40" typed-builder = "0.18.1" @@ -40,7 +40,7 @@ wasmtime = { version = "19.0.1", default-features = false, features = [ "pooling-allocator", "runtime", ] } -wasmtime-wasi = { version = "19.0.0", default-features = false } +wasmtime-wasi = { version = "19.0.1", default-features = false } [dev-dependencies] tempfile = "3.10.1" diff --git a/crates/kitsune-wasm-mrf/example-mrf/Cargo.toml b/crates/kitsune-wasm-mrf/example-mrf/Cargo.toml index 5a31e10f5..7bbad746b 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.23.0" +wit-bindgen = "0.24.0" [lints] workspace = true diff --git a/crates/kitsune-wasm-mrf/src/error.rs b/crates/kitsune-wasm-mrf/src/error.rs deleted file mode 100644 index db7f9690c..000000000 --- a/crates/kitsune-wasm-mrf/src/error.rs +++ /dev/null @@ -1,13 +0,0 @@ -use thiserror::Error; - -#[derive(Debug, Error)] -pub enum Error { - #[error(transparent)] - Json(#[from] simd_json::Error), - - #[error(transparent)] - ManifestParse(#[from] mrf_manifest::DecodeError), - - #[error(transparent)] - Runtime(wasmtime::Error), -} diff --git a/crates/kitsune-wasm-mrf/src/lib.rs b/crates/kitsune-wasm-mrf/src/lib.rs index cda5bdc6b..34dd73e17 100644 --- a/crates/kitsune-wasm-mrf/src/lib.rs +++ b/crates/kitsune-wasm-mrf/src/lib.rs @@ -10,6 +10,7 @@ use futures_util::{stream::FuturesUnordered, Stream, TryFutureExt, TryStreamExt} use kitsune_config::mrf::{ Configuration as MrfConfiguration, FsKvStorage, KvStorage, RedisKvStorage, }; +use kitsune_error::Error; use kitsune_type::ap::Activity; use mrf_manifest::{Manifest, ManifestV1}; use smol_str::SmolStr; @@ -28,10 +29,7 @@ use wasmtime::{ Config, Engine, InstanceAllocationStrategy, }; -pub use self::error::Error; - mod ctx; -mod error; mod logging; mod mrf_wit; @@ -210,14 +208,14 @@ impl MrfService { let (mrf, _) = mrf_wit::v1::Mrf::instantiate_async(&mut store, &module.component, &self.linker) .await - .map_err(Error::Runtime)?; + .map_err(Error::msg)?; store.data_mut().kv_ctx.module_name = Some(module.manifest.name.to_string()); let result = mrf .call_transform(&mut store, &module.config, direction, &activity) .await - .map_err(Error::Runtime)?; + .map_err(Error::msg)?; match result { Ok(transformed) => { diff --git a/crates/kitsune-webfinger/Cargo.toml b/crates/kitsune-webfinger/Cargo.toml index 26dfee60a..9f91ff96a 100644 --- a/crates/kitsune-webfinger/Cargo.toml +++ b/crates/kitsune-webfinger/Cargo.toml @@ -8,16 +8,16 @@ 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" } kitsune-core = { path = "../kitsune-core" } +kitsune-error = { path = "../kitsune-error" } kitsune-http-client = { path = "../kitsune-http-client" } kitsune-type = { path = "../kitsune-type" } kitsune-util = { path = "../kitsune-util" } multiplex-pool = { path = "../../lib/multiplex-pool" } -redis = { version = "0.25.2", default-features = false, features = [ +redis = { version = "0.25.3", default-features = false, features = [ "connection-manager", "tokio-comp", ] } diff --git a/crates/kitsune-webfinger/src/lib.rs b/crates/kitsune-webfinger/src/lib.rs index cd2c02aa8..0c5d0f8a4 100644 --- a/crates/kitsune-webfinger/src/lib.rs +++ b/crates/kitsune-webfinger/src/lib.rs @@ -10,6 +10,7 @@ use kitsune_core::{ consts::USER_AGENT, traits::{resolver::AccountResource, Resolver}, }; +use kitsune_error::Result; use kitsune_http_client::Client; use kitsune_type::webfinger::Resource; use kitsune_util::try_join; @@ -72,7 +73,7 @@ impl Resolver for Webfinger { &self, username: &str, domain: &str, - ) -> eyre::Result> { + ) -> 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/kitsune-cli/Cargo.toml b/kitsune-cli/Cargo.toml index d0408b62c..9a3cbd77f 100644 --- a/kitsune-cli/Cargo.toml +++ b/kitsune-cli/Cargo.toml @@ -21,6 +21,7 @@ dotenvy = "0.15.7" envy = "0.4.2" kitsune-config = { path = "../crates/kitsune-config" } kitsune-db = { path = "../crates/kitsune-db" } +kitsune-error = { path = "../crates/kitsune-error" } serde = { version = "1.0.197", features = ["derive"] } speedy-uuid = { path = "../lib/speedy-uuid" } tokio = { version = "1.37.0", features = ["full"] } diff --git a/kitsune-cli/src/main.rs b/kitsune-cli/src/main.rs index add367524..bef612589 100644 --- a/kitsune-cli/src/main.rs +++ b/kitsune-cli/src/main.rs @@ -33,7 +33,8 @@ async fn main() -> Result<()> { max_connections: 1, use_tls: config.database_use_tls, }) - .await?; + .await + .map_err(kitsune_error::Error::into_error)?; let cmd = App::parse(); diff --git a/kitsune-job-runner/Cargo.toml b/kitsune-job-runner/Cargo.toml index fde6425a2..3f9968c20 100644 --- a/kitsune-job-runner/Cargo.toml +++ b/kitsune-job-runner/Cargo.toml @@ -20,6 +20,7 @@ kitsune-config = { path = "../crates/kitsune-config" } kitsune-core = { path = "../crates/kitsune-core" } kitsune-db = { path = "../crates/kitsune-db" } kitsune-email = { path = "../crates/kitsune-email" } +kitsune-error = { path = "../crates/kitsune-error" } kitsune-federation = { path = "../crates/kitsune-federation" } kitsune-federation-filter = { path = "../crates/kitsune-federation-filter" } kitsune-jobs = { path = "../crates/kitsune-jobs" } @@ -29,7 +30,7 @@ kitsune-url = { path = "../crates/kitsune-url" } kitsune-wasm-mrf = { path = "../crates/kitsune-wasm-mrf" } mimalloc = "0.1.39" multiplex-pool = { path = "../lib/multiplex-pool" } -redis = { version = "0.25.2", default-features = false, features = [ +redis = { version = "0.25.3", default-features = false, features = [ "aio", "connection-manager", "tokio-rustls-comp", diff --git a/kitsune-job-runner/src/main.rs b/kitsune-job-runner/src/main.rs index 8912bc4af..7fee9eb73 100644 --- a/kitsune-job-runner/src/main.rs +++ b/kitsune-job-runner/src/main.rs @@ -30,7 +30,10 @@ async fn main() -> eyre::Result<()> { kitsune_observability::initialise(env!("CARGO_PKG_NAME"), &config)?; - let db_pool = kitsune_db::connect(&config.database).await?; + let db_pool = kitsune_db::connect(&config.database) + .await + .map_err(kitsune_error::Error::into_error)?; + let job_queue = kitsune_job_runner::prepare_job_queue(db_pool.clone(), &config.job_queue).await?; @@ -46,7 +49,8 @@ async fn main() -> eyre::Result<()> { .storage_backend(kitsune_service::prepare::storage(&config.storage)?) .url_service(url_service.clone()) .build(); - let federation_filter = FederationFilter::new(&config.instance.federation_filter)?; + let federation_filter = FederationFilter::new(&config.instance.federation_filter) + .map_err(kitsune_error::Error::into_error)?; let state = JobDispatcherState::builder() .attachment_service(attachment_service) diff --git a/kitsune/Cargo.toml b/kitsune/Cargo.toml index 0e61ddb57..748dbb0c2 100644 --- a/kitsune/Cargo.toml +++ b/kitsune/Cargo.toml @@ -34,8 +34,8 @@ blowocking = { path = "../lib/blowocking" } bytes = "1.6.0" chrono = { version = "0.4.37", default-features = false } clap = { version = "4.5.4", features = ["derive", "wrap_help"] } +color-eyre = "0.6.3" cursiv = { path = "../lib/cursiv", features = ["axum"] } -der = { version = "0.7.8", features = ["std"] } diesel = "2.1.5" diesel-async = "0.4.1" futures-util = "0.3.30" @@ -53,12 +53,12 @@ kitsune-core = { path = "../crates/kitsune-core" } kitsune-db = { path = "../crates/kitsune-db" } kitsune-email = { path = "../crates/kitsune-email" } kitsune-embed = { path = "../crates/kitsune-embed" } +kitsune-error = { path = "../crates/kitsune-error" } kitsune-federation = { path = "../crates/kitsune-federation" } kitsune-federation-filter = { path = "../crates/kitsune-federation-filter" } kitsune-job-runner = { path = "../kitsune-job-runner" } kitsune-jobs = { path = "../crates/kitsune-jobs" } kitsune-language = { path = "../crates/kitsune-language" } -kitsune-messaging = { path = "../crates/kitsune-messaging" } kitsune-observability = { path = "../crates/kitsune-observability" } kitsune-search = { path = "../crates/kitsune-search" } kitsune-service = { path = "../crates/kitsune-service" } @@ -75,7 +75,7 @@ mime_guess = { version = "2.0.4", default-features = false } oxide-auth = "0.5.4" oxide-auth-async = "0.1.1" oxide-auth-axum = "0.4.0" -redis = { version = "0.25.2", default-features = false, features = [ +redis = { version = "0.25.3", default-features = false, features = [ "tokio-rustls-comp", ] } rust-embed = { version = "8.3.0", features = ["include-exclude"] } @@ -87,7 +87,6 @@ simdutf8 = { version = "0.1.4", features = ["aarch64_neon"] } speedy-uuid = { path = "../lib/speedy-uuid" } strum = { version = "0.26.2", features = ["derive", "phf"] } tempfile = "3.10.1" -thiserror = "1.0.58" time = "0.3.34" tokio = { version = "1.37.0", features = ["full"] } tokio-util = { version = "0.7.10", features = ["compat"] } @@ -104,6 +103,7 @@ tower-http = { version = "0.5.2", features = [ ] } tower-http-digest = { path = "../lib/tower-http-digest" } tracing = "0.1.40" +trials = { path = "../lib/trials" } typed-builder = "0.18.1" url = "2.5.0" utoipa = { version = "4.2.0", features = ["axum_extras", "uuid"] } @@ -127,7 +127,6 @@ 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" @@ -139,7 +138,7 @@ kitsune-http-client = { path = "../crates/kitsune-http-client" } kitsune-test = { path = "../crates/kitsune-test" } multiplex-pool = { path = "../lib/multiplex-pool" } pretty_assertions = "1.4.0" -redis = { version = "0.25.2", default-features = false, features = [ +redis = { version = "0.25.3", default-features = false, features = [ "connection-manager", "tokio-comp", ] } diff --git a/kitsune/src/error.rs b/kitsune/src/error.rs deleted file mode 100644 index 53452374f..000000000 --- a/kitsune/src/error.rs +++ /dev/null @@ -1,149 +0,0 @@ -use crate::http::extractor::Json; -use argon2::password_hash; -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::HttpError; -use kitsune_service::error::{Error as ServiceError, PostError}; -use std::{fmt::Debug, str::ParseBoolError}; -use thiserror::Error; - -pub type Result = std::result::Result; - -#[derive(Debug, Error)] -#[non_exhaustive] -pub enum Error { - #[error(transparent)] - ActivityPub(#[from] kitsune_activitypub::error::Error), - - #[error(transparent)] - Blocking(#[from] blowocking::Error), - - #[error(transparent)] - Cache(#[from] kitsune_cache::Error), - - #[error(transparent)] - CoreHttp(#[from] kitsune_core::error::HttpError), - - #[error(transparent)] - Database(#[from] diesel::result::Error), - - #[error(transparent)] - DatabasePool(#[from] bb8::RunError), - - #[error(transparent)] - Der(#[from] der::Error), - - #[error(transparent)] - Fetcher(eyre::Report), - - #[error(transparent)] - Http(#[from] http::Error), - - #[error(transparent)] - Mailing(#[from] kitsune_email::error::Error), - - #[cfg(feature = "mastodon-api")] - #[error(transparent)] - Mastodon(#[from] kitsune_mastodon::error::Error), - - #[error(transparent)] - Messaging(kitsune_messaging::BoxError), - - #[error(transparent)] - Mrf(#[from] kitsune_wasm_mrf::Error), - - #[error(transparent)] - Multipart(#[from] MultipartError), - - #[error(transparent)] - OAuth2(#[from] OAuth2Error), - - #[cfg(feature = "oidc")] - #[error(transparent)] - Oidc(#[from] kitsune_oidc::Error), - - #[error(transparent)] - ParseBool(#[from] ParseBoolError), - - #[error(transparent)] - PasswordHash(#[from] password_hash::Error), - - #[error("Password mismatch")] - PasswordMismatch, - - #[error(transparent)] - Service(#[from] ServiceError), - - #[error(transparent)] - SimdJson(#[from] simd_json::Error), - - #[error(transparent)] - TokioJoin(#[from] tokio::task::JoinError), - - #[error("Unconfirmed email address. Check your inbox!")] - UnconfirmedEmailAddress, - - #[error(transparent)] - UrlParse(#[from] url::ParseError), - - #[error(transparent)] - Uuid(#[from] speedy_uuid::Error), -} - -#[derive(Debug, Error)] -pub enum OAuth2Error { - #[error("Missing grant type")] - MissingGrantType, - - #[error(transparent)] - OxideAuth(#[from] oxide_auth::endpoint::OAuthError), - - #[error(transparent)] - ParseScope(#[from] oxide_auth::primitives::scope::ParseScopeErr), - - #[error("Unknown grant type")] - UnknownGrantType, - - #[error(transparent)] - Web(#[from] oxide_auth_axum::WebError), -} - -impl From for Response { - fn from(err: Error) -> Response { - err.into_response() - } -} - -impl IntoResponse for Error { - fn into_response(self) -> Response { - match self { - Self::Database(diesel::result::Error::NotFound) => { - StatusCode::NOT_FOUND.into_response() - } - Self::Service(ServiceError::Validate(report)) => { - (StatusCode::BAD_REQUEST, Json(report)).into_response() - } - err @ Self::CoreHttp(HttpError::NotFound) => { - (StatusCode::NOT_FOUND, err.to_string()).into_response() - } - err @ Self::Service(ServiceError::Post(PostError::BadRequest)) => { - (StatusCode::BAD_REQUEST, err.to_string()).into_response() - } - err @ Self::Service(ServiceError::Post(PostError::Unauthorised)) => { - (StatusCode::UNAUTHORIZED, err.to_string()).into_response() - } - err @ Self::Service(_) => { - (StatusCode::UNSUPPORTED_MEDIA_TYPE, err.to_string()).into_response() - } - error => { - error!(?error, "Error occurred in handler"); - StatusCode::INTERNAL_SERVER_ERROR.into_response() - } - } - } -} diff --git a/kitsune/src/http/extractor/auth.rs b/kitsune/src/http/extractor/auth.rs index 7101c993c..acd586ceb 100644 --- a/kitsune/src/http/extractor/auth.rs +++ b/kitsune/src/http/extractor/auth.rs @@ -1,4 +1,4 @@ -use crate::{error::Error, state::Zustand}; +use crate::state::Zustand; use async_trait::async_trait; use axum::{ extract::FromRequestParts, @@ -11,12 +11,13 @@ 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 kitsune_error::{Error, Result}; use time::OffsetDateTime; +use trials::attempt; /// Mastodon-specific auth extractor alias /// @@ -63,14 +64,17 @@ impl FromRequestParts .filter(oauth2_access_tokens::expires_at.gt(OffsetDateTime::now_utc())); } - 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)??; + let result: Result<(User, Account)> = attempt! { async + with_connection!(state.db_pool, |db_conn| { + user_account_query + .select(<(User, Account)>::as_select()) + .get_result(db_conn) + .await + .map_err(Error::from) + })? + }; + + let (user, account) = result?; Ok(Self(UserData { account, user })) } diff --git a/kitsune/src/http/extractor/mod.rs b/kitsune/src/http/extractor/mod.rs index 0fe4d48ff..557bd4db9 100644 --- a/kitsune/src/http/extractor/mod.rs +++ b/kitsune/src/http/extractor/mod.rs @@ -1,4 +1,3 @@ -use crate::error::Result; use async_trait::async_trait; use axum::{ body::Body, @@ -9,6 +8,7 @@ use axum::{ use axum_extra::TypedHeader; use headers::ContentType; use http::StatusCode; +use kitsune_error::Result; use mime::Mime; use serde::de::DeserializeOwned; diff --git a/kitsune/src/http/extractor/signed_activity.rs b/kitsune/src/http/extractor/signed_activity.rs index 62b2bae42..f2845c49d 100644 --- a/kitsune/src/http/extractor/signed_activity.rs +++ b/kitsune/src/http/extractor/signed_activity.rs @@ -1,7 +1,4 @@ -use crate::{ - error::{Error, Result}, - state::Zustand, -}; +use crate::state::Zustand; use async_trait::async_trait; use axum::{ body::Body, @@ -14,8 +11,9 @@ use diesel::{ExpressionMethods, QueryDsl, SelectableHelper}; use diesel_async::RunQueryDsl; use http::StatusCode; use http_body_util::BodyExt; -use kitsune_core::{error::HttpError, traits::fetcher::AccountFetchOptions}; +use kitsune_core::traits::fetcher::AccountFetchOptions; use kitsune_db::{model::account::Account, schema::accounts, with_connection, PgPool}; +use kitsune_error::{bail, Error, ErrorType, Result}; use kitsune_type::ap::Activity; use kitsune_wasm_mrf::Outcome; use scoped_futures::ScopedFutureExt; @@ -79,13 +77,8 @@ impl FromRequest for SignedActivity { }; let ap_id = activity.actor.as_str(); - let Some(remote_user) = state - .fetcher - .fetch_account(ap_id.into()) - .await - .map_err(Error::Fetcher)? - else { - return Err(Error::CoreHttp(HttpError::BadRequest).into()); + let Some(remote_user) = state.fetcher.fetch_account(ap_id.into()).await? else { + bail!(type = ErrorType::BadRequest, "failed to fetch remote account"); }; let request = http::Request::from_parts(parts, ()); @@ -96,13 +89,8 @@ impl FromRequest for SignedActivity { .url(ap_id) .build(); - let Some(remote_user) = state - .fetcher - .fetch_account(opts) - .await - .map_err(Error::Fetcher)? - else { - return Err(Error::CoreHttp(HttpError::BadRequest).into()); + let Some(remote_user) = state.fetcher.fetch_account(opts).await? else { + bail!(type = ErrorType::BadRequest, "failed to fetch remote account"); }; if !verify_signature(&request, &state.db_pool, Some(&remote_user)).await? { @@ -135,7 +123,7 @@ async fn verify_signature( // Otherwise a random person with a key that's known to the database could start signing activities willy-nilly and the server would accept it. if let Some(expected_account) = expected_account { if expected_account.url != remote_user.url { - return Err(HttpError::Unauthorised.into()); + bail!(type = ErrorType::Unauthorized, "remote account isn't the author of the activity"); } } diff --git a/kitsune/src/http/graphql/mutation/mod.rs b/kitsune/src/http/graphql/mutation/mod.rs index d0f55adcb..4c0966360 100644 --- a/kitsune/src/http/graphql/mutation/mod.rs +++ b/kitsune/src/http/graphql/mutation/mod.rs @@ -4,7 +4,6 @@ use async_graphql::{Context, Error, MergedObject, Result, Upload}; use bytes::Bytes; use futures_util::{Stream, TryStreamExt}; use kitsune_service::attachment; -use kitsune_storage::BoxError; use mime::Mime; use std::str::FromStr; use tokio_util::{compat::FuturesAsyncReadCompatExt, io::ReaderStream}; @@ -18,7 +17,9 @@ fn handle_upload( ctx: &Context<'_>, file: Upload, description: Option, -) -> Result> + Send + 'static>> { +) -> Result< + attachment::Upload> + Send + 'static>, +> { let user_data = ctx.user_data()?; let value = file.value(ctx)?; let content_type = value diff --git a/kitsune/src/http/handler/confirm_account.rs b/kitsune/src/http/handler/confirm_account.rs index 56f3ca33a..67b060baa 100644 --- a/kitsune/src/http/handler/confirm_account.rs +++ b/kitsune/src/http/handler/confirm_account.rs @@ -1,10 +1,11 @@ -use crate::{error::Result, state::Zustand}; +use crate::state::Zustand; use axum::{ debug_handler, extract::{Path, State}, routing, Router, }; use kitsune_email::MailingService; +use kitsune_error::Result; use serde::Deserialize; #[derive(Deserialize)] diff --git a/kitsune/src/http/handler/custom_emojis.rs b/kitsune/src/http/handler/custom_emojis.rs index d96da6c5a..e5931c527 100644 --- a/kitsune/src/http/handler/custom_emojis.rs +++ b/kitsune/src/http/handler/custom_emojis.rs @@ -1,6 +1,7 @@ -use crate::{error::Result, http::responder::ActivityPubJson, state::Zustand}; +use crate::{http::responder::ActivityPubJson, state::Zustand}; use axum::{debug_handler, extract::Path, extract::State, routing, Router}; use kitsune_activitypub::mapping::IntoObject; +use kitsune_error::Result; use kitsune_service::custom_emoji::CustomEmojiService; use kitsune_type::ap::emoji::Emoji; use speedy_uuid::Uuid; diff --git a/kitsune/src/http/handler/mastodon/api/v1/accounts/follow.rs b/kitsune/src/http/handler/mastodon/api/v1/accounts/follow.rs index dc72dbafc..36cb59b3f 100644 --- a/kitsune/src/http/handler/mastodon/api/v1/accounts/follow.rs +++ b/kitsune/src/http/handler/mastodon/api/v1/accounts/follow.rs @@ -1,13 +1,10 @@ -use crate::{ - error::Result, - http::extractor::{AgnosticForm, AuthExtractor, MastodonAuthExtractor}, -}; +use crate::http::extractor::{AgnosticForm, AuthExtractor, MastodonAuthExtractor}; use axum::{ debug_handler, extract::{Path, State}, Json, }; -use kitsune_core::error::HttpError; +use kitsune_error::{bail, ErrorType, Result}; use kitsune_mastodon::MastodonMapper; use kitsune_service::account::{AccountService, Follow}; use kitsune_type::mastodon::relationship::Relationship; @@ -41,7 +38,7 @@ pub async fn post( follow_body: Option>, ) -> Result> { if user_data.account.id == id { - return Err(HttpError::BadRequest.into()); + bail!(type = ErrorType::BadRequest, "user tried to follow themselves"); } let follow = Follow::builder() diff --git a/kitsune/src/http/handler/mastodon/api/v1/accounts/lookup.rs b/kitsune/src/http/handler/mastodon/api/v1/accounts/lookup.rs index 35c8d3556..43d3936b1 100644 --- a/kitsune/src/http/handler/mastodon/api/v1/accounts/lookup.rs +++ b/kitsune/src/http/handler/mastodon/api/v1/accounts/lookup.rs @@ -1,10 +1,10 @@ -use crate::{error::Result, http::extractor::MastodonAuthExtractor}; +use crate::http::extractor::MastodonAuthExtractor; use axum::{ debug_handler, extract::{Query, State}, Json, }; -use kitsune_core::error::HttpError; +use kitsune_error::{kitsune_error, ErrorType, Result}; use kitsune_mastodon::MastodonMapper; use kitsune_service::account::{AccountService, GetUser}; use kitsune_type::mastodon::Account; @@ -49,7 +49,7 @@ pub async fn get( let account = account_service .get(get_user) .await? - .ok_or(HttpError::NotFound)?; + .ok_or_else(|| kitsune_error!(type = ErrorType::NotFound, "account not found"))?; Ok(Json(mastodon_mapper.map(account).await?)) } diff --git a/kitsune/src/http/handler/mastodon/api/v1/accounts/mod.rs b/kitsune/src/http/handler/mastodon/api/v1/accounts/mod.rs index 6ec8ee48b..39299fe7f 100644 --- a/kitsune/src/http/handler/mastodon/api/v1/accounts/mod.rs +++ b/kitsune/src/http/handler/mastodon/api/v1/accounts/mod.rs @@ -1,9 +1,9 @@ -use crate::{error::Result, state::Zustand}; +use crate::state::Zustand; use axum::{ extract::{Path, State}, routing, Json, Router, }; -use kitsune_core::error::HttpError; +use kitsune_error::{kitsune_error, ErrorType, Result}; use kitsune_mastodon::MastodonMapper; use kitsune_service::account::AccountService; use kitsune_type::mastodon; @@ -33,7 +33,7 @@ async fn get( let account = account_service .get_by_id(id) .await? - .ok_or(HttpError::NotFound)?; + .ok_or_else(|| kitsune_error!(type = ErrorType::NotFound, "account not found"))?; Ok(Json(mastodon_mapper.map(account).await?)) } 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 b0f4ac7bb..30fa92cf5 100644 --- a/kitsune/src/http/handler/mastodon/api/v1/accounts/relationships.rs +++ b/kitsune/src/http/handler/mastodon/api/v1/accounts/relationships.rs @@ -1,13 +1,11 @@ -use crate::{ - error::Result, - http::extractor::{AuthExtractor, MastodonAuthExtractor}, -}; +use crate::http::extractor::{AuthExtractor, MastodonAuthExtractor}; use axum::{debug_handler, extract::State, Json}; 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, with_connection, PgPool}; +use kitsune_error::Result; use kitsune_mastodon::MastodonMapper; use kitsune_type::mastodon::relationship::Relationship; use serde::Deserialize; diff --git a/kitsune/src/http/handler/mastodon/api/v1/accounts/statuses.rs b/kitsune/src/http/handler/mastodon/api/v1/accounts/statuses.rs index 2b7367ae8..a9802bb8f 100644 --- a/kitsune/src/http/handler/mastodon/api/v1/accounts/statuses.rs +++ b/kitsune/src/http/handler/mastodon/api/v1/accounts/statuses.rs @@ -1,6 +1,5 @@ use crate::{ consts::default_limit, - error::{Error, Result}, http::{ extractor::{AuthExtractor, MastodonAuthExtractor}, pagination::{LinkHeader, PaginatedJsonResponse}, @@ -11,6 +10,7 @@ use axum::{ Json, }; use futures_util::{FutureExt, TryFutureExt, TryStreamExt}; +use kitsune_error::{Error, Result}; use kitsune_mastodon::MastodonMapper; use kitsune_service::account::{AccountService, GetPosts}; use kitsune_type::mastodon::Status; diff --git a/kitsune/src/http/handler/mastodon/api/v1/accounts/unfollow.rs b/kitsune/src/http/handler/mastodon/api/v1/accounts/unfollow.rs index 65a4a0719..68284f121 100644 --- a/kitsune/src/http/handler/mastodon/api/v1/accounts/unfollow.rs +++ b/kitsune/src/http/handler/mastodon/api/v1/accounts/unfollow.rs @@ -1,13 +1,10 @@ -use crate::{ - error::Result, - http::extractor::{AuthExtractor, MastodonAuthExtractor}, -}; +use crate::http::extractor::{AuthExtractor, MastodonAuthExtractor}; use axum::{ debug_handler, extract::{Path, State}, Json, }; -use kitsune_core::error::HttpError; +use kitsune_error::{bail, ErrorType, Result}; use kitsune_mastodon::MastodonMapper; use kitsune_service::account::{AccountService, Unfollow}; use kitsune_type::mastodon::relationship::Relationship; @@ -21,7 +18,7 @@ pub async fn post( Path(id): Path, ) -> Result> { if user_data.account.id == id { - return Err(HttpError::BadRequest.into()); + bail!(type = ErrorType::BadRequest, "user tried to unfollow themselves"); } let unfollow = Unfollow::builder() diff --git a/kitsune/src/http/handler/mastodon/api/v1/accounts/update_credentials.rs b/kitsune/src/http/handler/mastodon/api/v1/accounts/update_credentials.rs index e632512c2..687caf532 100644 --- a/kitsune/src/http/handler/mastodon/api/v1/accounts/update_credentials.rs +++ b/kitsune/src/http/handler/mastodon/api/v1/accounts/update_credentials.rs @@ -1,15 +1,12 @@ -use crate::{ - error::Result, - http::{ - extractor::{AuthExtractor, MastodonAuthExtractor}, - util::buffer_multipart_to_tempfile, - }, +use crate::http::{ + extractor::{AuthExtractor, MastodonAuthExtractor}, + util::buffer_multipart_to_tempfile, }; use axum::{ extract::{Multipart, State}, Json, }; -use kitsune_core::error::HttpError; +use kitsune_error::{bail, kitsune_error, ErrorType, Result}; use kitsune_mastodon::MastodonMapper; use kitsune_service::{ account::{AccountService, Update}, @@ -45,7 +42,7 @@ pub async fn patch( "note" => update.note(field.text().await?), "avatar" => { let Some(content_type) = field.content_type().map(ToString::to_string) else { - return Err(HttpError::BadRequest.into()); + bail!(type = ErrorType::BadRequest, "invalid content-type"); }; let stream = buffer_multipart_to_tempfile(&mut field).await?; @@ -54,13 +51,13 @@ pub async fn patch( .content_type(content_type) .stream(stream) .build() - .map_err(|_| HttpError::BadRequest)?; + .unwrap(); update.avatar(upload) } "header" => { let Some(content_type) = field.content_type().map(ToString::to_string) else { - return Err(HttpError::BadRequest.into()); + bail!(type = ErrorType::BadRequest, "invalid content-type"); }; let stream = buffer_multipart_to_tempfile(&mut field).await?; @@ -69,7 +66,7 @@ pub async fn patch( .content_type(content_type) .stream(stream) .build() - .map_err(|_| HttpError::BadRequest)?; + .unwrap(); update.header(upload) } @@ -78,7 +75,12 @@ pub async fn patch( }; } - let update = update.build().map_err(|_| HttpError::BadRequest)?; + let update = update.build().map_err(|err| { + kitsune_error!( + type = ErrorType::BadRequest.with_body(err.to_string()), + "missing upload field" + ) + })?; let account = account_service.update(update).await?; Ok(Json(mastodon_mapper.map(account).await?)) diff --git a/kitsune/src/http/handler/mastodon/api/v1/accounts/verify_credentials.rs b/kitsune/src/http/handler/mastodon/api/v1/accounts/verify_credentials.rs index d64645700..1e967beaa 100644 --- a/kitsune/src/http/handler/mastodon/api/v1/accounts/verify_credentials.rs +++ b/kitsune/src/http/handler/mastodon/api/v1/accounts/verify_credentials.rs @@ -1,8 +1,6 @@ -use crate::{ - error::Result, - http::extractor::{AuthExtractor, MastodonAuthExtractor}, -}; +use crate::http::extractor::{AuthExtractor, MastodonAuthExtractor}; use axum::{extract::State, Json}; +use kitsune_error::Result; use kitsune_mastodon::MastodonMapper; use kitsune_type::mastodon::Account; diff --git a/kitsune/src/http/handler/mastodon/api/v1/apps.rs b/kitsune/src/http/handler/mastodon/api/v1/apps.rs index 9fcc1c9f3..15e9f4541 100644 --- a/kitsune/src/http/handler/mastodon/api/v1/apps.rs +++ b/kitsune/src/http/handler/mastodon/api/v1/apps.rs @@ -1,10 +1,10 @@ use crate::state::Zustand; use crate::{ - error::Result, http::extractor::AgnosticForm, oauth2::{CreateApp, OAuth2Service}, }; use axum::{extract::State, routing, Json, Router}; +use kitsune_error::Result; use kitsune_type::mastodon::App; use serde::Deserialize; use utoipa::ToSchema; diff --git a/kitsune/src/http/handler/mastodon/api/v1/custom_emojis.rs b/kitsune/src/http/handler/mastodon/api/v1/custom_emojis.rs index 29f0d9825..d9ef601d4 100644 --- a/kitsune/src/http/handler/mastodon/api/v1/custom_emojis.rs +++ b/kitsune/src/http/handler/mastodon/api/v1/custom_emojis.rs @@ -1,10 +1,7 @@ -use crate::{ - error::{Error, Result}, - http::extractor::MastodonAuthExtractor, - state::Zustand, -}; +use crate::{http::extractor::MastodonAuthExtractor, state::Zustand}; use axum::{debug_handler, extract::State, routing, Json, Router}; use futures_util::{TryFutureExt, TryStreamExt}; +use kitsune_error::{Error, Result}; use kitsune_mastodon::MastodonMapper; use kitsune_service::custom_emoji::{CustomEmojiService, GetEmojiList}; use kitsune_type::mastodon::CustomEmoji; diff --git a/kitsune/src/http/handler/mastodon/api/v1/follow_requests/accept.rs b/kitsune/src/http/handler/mastodon/api/v1/follow_requests/accept.rs index 34883d808..d37760662 100644 --- a/kitsune/src/http/handler/mastodon/api/v1/follow_requests/accept.rs +++ b/kitsune/src/http/handler/mastodon/api/v1/follow_requests/accept.rs @@ -1,13 +1,10 @@ -use crate::{ - error::Result, - http::extractor::{AuthExtractor, MastodonAuthExtractor}, -}; +use crate::http::extractor::{AuthExtractor, MastodonAuthExtractor}; use axum::{ debug_handler, extract::{Path, State}, Json, }; -use kitsune_core::error::HttpError; +use kitsune_error::{bail, ErrorType, Result}; use kitsune_mastodon::MastodonMapper; use kitsune_service::account::{AccountService, FollowRequest}; use kitsune_type::mastodon::relationship::Relationship; @@ -32,7 +29,7 @@ pub async fn post( Path(id): Path, ) -> Result> { if user_data.account.id == id { - return Err(HttpError::BadRequest.into()); + bail!(type = ErrorType::BadRequest, "user tried to accept a follow to themselves"); } let follow_request = FollowRequest::builder() @@ -51,6 +48,6 @@ pub async fn post( .await?, )) } else { - Err(HttpError::BadRequest.into()) + bail!(type = ErrorType::BadRequest, "follow request wasn't found in the database"); } } diff --git a/kitsune/src/http/handler/mastodon/api/v1/follow_requests/mod.rs b/kitsune/src/http/handler/mastodon/api/v1/follow_requests/mod.rs index 630a8bc8a..91519fce5 100644 --- a/kitsune/src/http/handler/mastodon/api/v1/follow_requests/mod.rs +++ b/kitsune/src/http/handler/mastodon/api/v1/follow_requests/mod.rs @@ -1,6 +1,5 @@ use crate::{ consts::default_limit, - error::{Error, Result}, http::{ extractor::{AuthExtractor, MastodonAuthExtractor}, pagination::{LinkHeader, PaginatedJsonResponse}, @@ -14,6 +13,7 @@ use axum::{ }; use axum_extra::extract::Query; use futures_util::{TryFutureExt, TryStreamExt}; +use kitsune_error::{Error, Result}; use kitsune_mastodon::MastodonMapper; use kitsune_service::account::{AccountService, GetFollowRequests}; use kitsune_type::mastodon::Account; diff --git a/kitsune/src/http/handler/mastodon/api/v1/follow_requests/reject.rs b/kitsune/src/http/handler/mastodon/api/v1/follow_requests/reject.rs index 18b278964..b387f09ba 100644 --- a/kitsune/src/http/handler/mastodon/api/v1/follow_requests/reject.rs +++ b/kitsune/src/http/handler/mastodon/api/v1/follow_requests/reject.rs @@ -1,13 +1,10 @@ -use crate::{ - error::Result, - http::extractor::{AuthExtractor, MastodonAuthExtractor}, -}; +use crate::http::extractor::{AuthExtractor, MastodonAuthExtractor}; use axum::{ debug_handler, extract::{Path, State}, Json, }; -use kitsune_core::error::HttpError; +use kitsune_error::{bail, ErrorType, Result}; use kitsune_mastodon::MastodonMapper; use kitsune_service::account::{AccountService, FollowRequest}; use kitsune_type::mastodon::relationship::Relationship; @@ -32,7 +29,7 @@ pub async fn post( Path(id): Path, ) -> Result> { if user_data.account.id == id { - return Err(HttpError::BadRequest.into()); + bail!(type = ErrorType::BadRequest, "user tried to reject a follow to themselves"); } let follow_request = FollowRequest::builder() @@ -51,6 +48,6 @@ pub async fn post( .await?, )) } else { - Err(HttpError::BadRequest.into()) + bail!(type = ErrorType::BadRequest, "follow request wasn't found in the database"); } } diff --git a/kitsune/src/http/handler/mastodon/api/v1/instance.rs b/kitsune/src/http/handler/mastodon/api/v1/instance.rs index a37cda014..b7d15fdd0 100644 --- a/kitsune/src/http/handler/mastodon/api/v1/instance.rs +++ b/kitsune/src/http/handler/mastodon/api/v1/instance.rs @@ -1,6 +1,7 @@ -use crate::{error::Result, state::Zustand}; +use crate::state::Zustand; use axum::{extract::State, routing, Json, Router}; use kitsune_core::consts::VERSION; +use kitsune_error::Result; use kitsune_service::instance::InstanceService; use kitsune_type::mastodon::{ instance::{Stats, Urls}, diff --git a/kitsune/src/http/handler/mastodon/api/v1/media.rs b/kitsune/src/http/handler/mastodon/api/v1/media.rs index 90e3c7240..b7dd8b267 100644 --- a/kitsune/src/http/handler/mastodon/api/v1/media.rs +++ b/kitsune/src/http/handler/mastodon/api/v1/media.rs @@ -1,5 +1,4 @@ use crate::{ - error::{Error, Result}, http::{ extractor::{AgnosticForm, AuthExtractor, MastodonAuthExtractor}, util::buffer_multipart_to_tempfile, @@ -12,7 +11,7 @@ use axum::{ routing, Json, Router, }; use futures_util::TryFutureExt; -use kitsune_core::error::HttpError; +use kitsune_error::{kitsune_error, Error, ErrorType, Result}; use kitsune_mastodon::MastodonMapper; use kitsune_service::attachment::{AttachmentService, Update, Upload}; use kitsune_type::mastodon::MediaAttachment; @@ -20,6 +19,7 @@ use serde::Deserialize; use speedy_uuid::Uuid; use utoipa::ToSchema; +#[allow(dead_code)] #[derive(ToSchema)] pub struct CreateAttachment { pub description: Option, @@ -87,7 +87,13 @@ pub async fn post( } } - let upload = upload.build().map_err(|_| HttpError::BadRequest)?; + let upload = upload.build().map_err(|err| { + kitsune_error!( + type = ErrorType::BadRequest.with_body(err.to_string()), + "not all fields were filled" + ) + })?; + let media_attachment = attachment_service.upload(upload).await?; Ok(Json(mastodon_mapper.map(media_attachment).await?)) } diff --git a/kitsune/src/http/handler/mastodon/api/v1/notifications/clear.rs b/kitsune/src/http/handler/mastodon/api/v1/notifications/clear.rs index 14508732e..6d94a1d95 100644 --- a/kitsune/src/http/handler/mastodon/api/v1/notifications/clear.rs +++ b/kitsune/src/http/handler/mastodon/api/v1/notifications/clear.rs @@ -1,9 +1,7 @@ -use crate::{ - error::Result, - http::extractor::{AuthExtractor, MastodonAuthExtractor}, -}; +use crate::http::extractor::{AuthExtractor, MastodonAuthExtractor}; use axum::{debug_handler, extract::State}; use http::StatusCode; +use kitsune_error::Result; use kitsune_service::notification::NotificationService; #[debug_handler(state = crate::state::Zustand)] diff --git a/kitsune/src/http/handler/mastodon/api/v1/notifications/dismiss.rs b/kitsune/src/http/handler/mastodon/api/v1/notifications/dismiss.rs index 62df5931e..6bb537041 100644 --- a/kitsune/src/http/handler/mastodon/api/v1/notifications/dismiss.rs +++ b/kitsune/src/http/handler/mastodon/api/v1/notifications/dismiss.rs @@ -1,12 +1,10 @@ -use crate::{ - error::Result, - http::extractor::{AuthExtractor, MastodonAuthExtractor}, -}; +use crate::http::extractor::{AuthExtractor, MastodonAuthExtractor}; use axum::{ debug_handler, extract::{Path, State}, }; use http::StatusCode; +use kitsune_error::Result; use kitsune_service::notification::NotificationService; use speedy_uuid::Uuid; diff --git a/kitsune/src/http/handler/mastodon/api/v1/notifications/mod.rs b/kitsune/src/http/handler/mastodon/api/v1/notifications/mod.rs index 9536042b7..e059c5df7 100644 --- a/kitsune/src/http/handler/mastodon/api/v1/notifications/mod.rs +++ b/kitsune/src/http/handler/mastodon/api/v1/notifications/mod.rs @@ -1,6 +1,5 @@ use crate::{ consts::default_limit, - error::{Error, Result}, http::{ extractor::{AuthExtractor, MastodonAuthExtractor}, pagination::{LinkHeader, PaginatedJsonResponse}, @@ -14,7 +13,7 @@ use axum::{ }; use axum_extra::extract::Query; use futures_util::{TryFutureExt, TryStreamExt}; -use kitsune_core::error::HttpError; +use kitsune_error::{kitsune_error, Error, ErrorType, Result}; use kitsune_mastodon::MastodonMapper; use kitsune_service::notification::{GetNotifications, NotificationService}; use kitsune_type::mastodon::{notification::NotificationType, Notification}; @@ -121,7 +120,7 @@ pub async fn get_by_id( let notification = notification_service .get_notification_by_id(id, user_data.account.id) .await? - .ok_or(HttpError::NotFound)?; + .ok_or_else(|| kitsune_error!(type = ErrorType::NotFound, "notification not found"))?; Ok(Json(mastodon_mapper.map(notification).await?)) } diff --git a/kitsune/src/http/handler/mastodon/api/v1/statuses/context.rs b/kitsune/src/http/handler/mastodon/api/v1/statuses/context.rs index 215c0f39f..9df69d470 100644 --- a/kitsune/src/http/handler/mastodon/api/v1/statuses/context.rs +++ b/kitsune/src/http/handler/mastodon/api/v1/statuses/context.rs @@ -1,13 +1,11 @@ -use crate::{ - error::{Error, Result}, - http::extractor::MastodonAuthExtractor, -}; +use crate::http::extractor::MastodonAuthExtractor; use axum::{ debug_handler, extract::{Path, State}, Json, }; use futures_util::{TryFutureExt, TryStreamExt}; +use kitsune_error::{Error, Result}; use kitsune_mastodon::MastodonMapper; use kitsune_service::post::PostService; use kitsune_type::mastodon::status::Context; diff --git a/kitsune/src/http/handler/mastodon/api/v1/statuses/favourite.rs b/kitsune/src/http/handler/mastodon/api/v1/statuses/favourite.rs index 5b7b566a7..ab3b9c848 100644 --- a/kitsune/src/http/handler/mastodon/api/v1/statuses/favourite.rs +++ b/kitsune/src/http/handler/mastodon/api/v1/statuses/favourite.rs @@ -1,12 +1,10 @@ -use crate::{ - error::Result, - http::extractor::{AuthExtractor, MastodonAuthExtractor}, -}; +use crate::http::extractor::{AuthExtractor, MastodonAuthExtractor}; use axum::{ debug_handler, extract::{Path, State}, Json, }; +use kitsune_error::Result; use kitsune_mastodon::MastodonMapper; use kitsune_service::post::PostService; use kitsune_type::mastodon::Status; diff --git a/kitsune/src/http/handler/mastodon/api/v1/statuses/favourited_by.rs b/kitsune/src/http/handler/mastodon/api/v1/statuses/favourited_by.rs index 26ffef46b..e0f43048b 100644 --- a/kitsune/src/http/handler/mastodon/api/v1/statuses/favourited_by.rs +++ b/kitsune/src/http/handler/mastodon/api/v1/statuses/favourited_by.rs @@ -1,6 +1,5 @@ use crate::{ consts::default_limit, - error::{Error, Result}, http::{ extractor::MastodonAuthExtractor, pagination::{LinkHeader, PaginatedJsonResponse}, @@ -13,6 +12,7 @@ use axum::{ }; use axum_extra::extract::Query; use futures_util::{TryFutureExt, TryStreamExt}; +use kitsune_error::{Error, Result}; use kitsune_mastodon::MastodonMapper; use kitsune_service::post::{GetAccountsInteractingWithPost, PostService}; use kitsune_type::mastodon::Account; diff --git a/kitsune/src/http/handler/mastodon/api/v1/statuses/mod.rs b/kitsune/src/http/handler/mastodon/api/v1/statuses/mod.rs index 1c7cccfbf..af5f28e25 100644 --- a/kitsune/src/http/handler/mastodon/api/v1/statuses/mod.rs +++ b/kitsune/src/http/handler/mastodon/api/v1/statuses/mod.rs @@ -1,5 +1,4 @@ use crate::{ - error::Result, http::extractor::{AgnosticForm, AuthExtractor, MastodonAuthExtractor}, state::Zustand, }; @@ -9,6 +8,7 @@ use axum::{ routing, Json, Router, }; use http::StatusCode; +use kitsune_error::Result; use kitsune_mastodon::MastodonMapper; use kitsune_service::post::{CreatePost, DeletePost, PostService, UpdatePost}; use kitsune_type::mastodon::{status::Visibility, Status}; diff --git a/kitsune/src/http/handler/mastodon/api/v1/statuses/reblog.rs b/kitsune/src/http/handler/mastodon/api/v1/statuses/reblog.rs index 200ff9654..fc3d49253 100644 --- a/kitsune/src/http/handler/mastodon/api/v1/statuses/reblog.rs +++ b/kitsune/src/http/handler/mastodon/api/v1/statuses/reblog.rs @@ -1,12 +1,10 @@ -use crate::{ - error::Result, - http::extractor::{AgnosticForm, AuthExtractor, MastodonAuthExtractor}, -}; +use crate::http::extractor::{AgnosticForm, AuthExtractor, MastodonAuthExtractor}; use axum::{ debug_handler, extract::{Path, State}, Json, }; +use kitsune_error::Result; use kitsune_mastodon::MastodonMapper; use kitsune_service::post::{PostService, RepostPost}; use kitsune_type::mastodon::{status::Visibility, Status}; diff --git a/kitsune/src/http/handler/mastodon/api/v1/statuses/reblogged_by.rs b/kitsune/src/http/handler/mastodon/api/v1/statuses/reblogged_by.rs index 0b176d7d4..da0682df2 100644 --- a/kitsune/src/http/handler/mastodon/api/v1/statuses/reblogged_by.rs +++ b/kitsune/src/http/handler/mastodon/api/v1/statuses/reblogged_by.rs @@ -1,6 +1,5 @@ use crate::{ consts::default_limit, - error::{Error, Result}, http::{ extractor::MastodonAuthExtractor, pagination::{LinkHeader, PaginatedJsonResponse}, @@ -13,6 +12,7 @@ use axum::{ }; use axum_extra::extract::Query; use futures_util::{TryFutureExt, TryStreamExt}; +use kitsune_error::{Error, Result}; use kitsune_mastodon::MastodonMapper; use kitsune_service::post::{GetAccountsInteractingWithPost, PostService}; use kitsune_type::mastodon::Account; diff --git a/kitsune/src/http/handler/mastodon/api/v1/statuses/source.rs b/kitsune/src/http/handler/mastodon/api/v1/statuses/source.rs index f5576c163..b56e05bed 100644 --- a/kitsune/src/http/handler/mastodon/api/v1/statuses/source.rs +++ b/kitsune/src/http/handler/mastodon/api/v1/statuses/source.rs @@ -1,11 +1,9 @@ -use crate::{ - error::Result, - http::extractor::{Json, MastodonAuthExtractor}, -}; +use crate::http::extractor::{Json, MastodonAuthExtractor}; use axum::{ debug_handler, extract::{Path, State}, }; +use kitsune_error::Result; use kitsune_mastodon::MastodonMapper; use kitsune_service::post::PostService; use kitsune_type::mastodon::status::StatusSource; diff --git a/kitsune/src/http/handler/mastodon/api/v1/statuses/unfavourite.rs b/kitsune/src/http/handler/mastodon/api/v1/statuses/unfavourite.rs index f2448fc9b..a7f0ba012 100644 --- a/kitsune/src/http/handler/mastodon/api/v1/statuses/unfavourite.rs +++ b/kitsune/src/http/handler/mastodon/api/v1/statuses/unfavourite.rs @@ -1,12 +1,10 @@ -use crate::{ - error::Result, - http::extractor::{AuthExtractor, MastodonAuthExtractor}, -}; +use crate::http::extractor::{AuthExtractor, MastodonAuthExtractor}; use axum::{ debug_handler, extract::{Path, State}, Json, }; +use kitsune_error::Result; use kitsune_mastodon::MastodonMapper; use kitsune_service::post::PostService; use kitsune_type::mastodon::Status; diff --git a/kitsune/src/http/handler/mastodon/api/v1/statuses/unreblog.rs b/kitsune/src/http/handler/mastodon/api/v1/statuses/unreblog.rs index 623860b0e..6bab52630 100644 --- a/kitsune/src/http/handler/mastodon/api/v1/statuses/unreblog.rs +++ b/kitsune/src/http/handler/mastodon/api/v1/statuses/unreblog.rs @@ -1,12 +1,10 @@ -use crate::{ - error::Result, - http::extractor::{AuthExtractor, MastodonAuthExtractor}, -}; +use crate::http::extractor::{AuthExtractor, MastodonAuthExtractor}; use axum::{ debug_handler, extract::{Path, State}, Json, }; +use kitsune_error::Result; use kitsune_mastodon::MastodonMapper; use kitsune_service::post::{PostService, UnrepostPost}; use kitsune_type::mastodon::Status; diff --git a/kitsune/src/http/handler/mastodon/api/v1/timelines/home.rs b/kitsune/src/http/handler/mastodon/api/v1/timelines/home.rs index ac2b69b40..8f5545364 100644 --- a/kitsune/src/http/handler/mastodon/api/v1/timelines/home.rs +++ b/kitsune/src/http/handler/mastodon/api/v1/timelines/home.rs @@ -1,6 +1,5 @@ use crate::{ consts::default_limit, - error::{Error, Result}, http::{ extractor::{AuthExtractor, MastodonAuthExtractor}, pagination::{LinkHeader, PaginatedJsonResponse}, @@ -11,6 +10,7 @@ use axum::{ Json, }; use futures_util::{TryFutureExt, TryStreamExt}; +use kitsune_error::{Error, Result}; use kitsune_mastodon::MastodonMapper; use kitsune_service::timeline::{GetHome, TimelineService}; use kitsune_type::mastodon::Status; diff --git a/kitsune/src/http/handler/mastodon/api/v1/timelines/public.rs b/kitsune/src/http/handler/mastodon/api/v1/timelines/public.rs index bf2f289e5..f203a2ef2 100644 --- a/kitsune/src/http/handler/mastodon/api/v1/timelines/public.rs +++ b/kitsune/src/http/handler/mastodon/api/v1/timelines/public.rs @@ -1,6 +1,5 @@ use crate::{ consts::default_limit, - error::{Error, Result}, http::{ extractor::{AuthExtractor, MastodonAuthExtractor}, pagination::{LinkHeader, PaginatedJsonResponse}, @@ -11,6 +10,7 @@ use axum::{ Json, }; use futures_util::{FutureExt, TryFutureExt, TryStreamExt}; +use kitsune_error::{Error, Result}; use kitsune_mastodon::MastodonMapper; use kitsune_service::timeline::{GetPublic, TimelineService}; use kitsune_type::mastodon::Status; diff --git a/kitsune/src/http/handler/mastodon/api/v2/search.rs b/kitsune/src/http/handler/mastodon/api/v2/search.rs index d783e50f7..681595e68 100644 --- a/kitsune/src/http/handler/mastodon/api/v2/search.rs +++ b/kitsune/src/http/handler/mastodon/api/v2/search.rs @@ -1,6 +1,5 @@ use crate::{ consts::default_limit, - error::Result, http::extractor::{AuthExtractor, MastodonAuthExtractor}, state::Zustand, }; @@ -8,6 +7,7 @@ use axum::{debug_handler, extract::State, routing, Json, Router}; use axum_extra::{either::Either, extract::Query}; use http::StatusCode; use kitsune_core::consts::API_MAX_LIMIT; +use kitsune_error::Result; use kitsune_mastodon::MastodonMapper; use kitsune_search::SearchIndex; use kitsune_service::search::{Search, SearchResult, SearchService}; diff --git a/kitsune/src/http/handler/media.rs b/kitsune/src/http/handler/media.rs index 9a406a071..99920a84f 100644 --- a/kitsune/src/http/handler/media.rs +++ b/kitsune/src/http/handler/media.rs @@ -1,4 +1,4 @@ -use crate::{error::Result, state::Zustand}; +use crate::state::Zustand; use axum::{ body::Body, extract::{Path, State}, @@ -6,6 +6,7 @@ use axum::{ routing, Router, }; use http::header::CONTENT_TYPE; +use kitsune_error::Result; use kitsune_service::attachment::AttachmentService; use speedy_uuid::Uuid; diff --git a/kitsune/src/http/handler/nodeinfo/two_one.rs b/kitsune/src/http/handler/nodeinfo/two_one.rs index 9983c2450..b9b6b647a 100644 --- a/kitsune/src/http/handler/nodeinfo/two_one.rs +++ b/kitsune/src/http/handler/nodeinfo/two_one.rs @@ -1,4 +1,4 @@ -use crate::{error::Result, state::Zustand}; +use crate::state::Zustand; use axum::{debug_handler, extract::State, routing, Json, Router}; use diesel::{ExpressionMethods, QueryDsl}; use diesel_async::RunQueryDsl; @@ -7,6 +7,7 @@ use kitsune_db::{ schema::{posts, users}, with_connection, PgPool, }; +use kitsune_error::Result; use kitsune_service::user::UserService; use kitsune_type::nodeinfo::two_one::{ Protocol, Services, Software, TwoOne, Usage, UsageUsers, Version, diff --git a/kitsune/src/http/handler/oauth/authorize.rs b/kitsune/src/http/handler/oauth/authorize.rs index 78ae3b376..0555941f9 100644 --- a/kitsune/src/http/handler/oauth/authorize.rs +++ b/kitsune/src/http/handler/oauth/authorize.rs @@ -1,4 +1,3 @@ -use crate::error::{Error, Result}; use crate::oauth2::{OAuthEndpoint, OAuthOwnerSolicitor}; use argon2::{Argon2, PasswordHash, PasswordVerifier}; use askama::Template; @@ -19,13 +18,16 @@ 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 kitsune_db::{model::user::User, schema::users, with_connection, PgPool}; +use kitsune_error::{Error, Result}; use oxide_auth_async::endpoint::authorization::AuthorizationFlow; use oxide_auth_axum::{OAuthRequest, OAuthResponse}; use serde::Deserialize; use speedy_uuid::Uuid; +const UNCONFIRMED_EMAIL_ADDRESS: &str = "Email address is unconfirmed. Check your inbox!"; +const WRONG_EMAIL_OR_PASSWORD: &str = "Entered wrong email or password"; + #[cfg(feature = "oidc")] use { axum::extract::Query, @@ -130,14 +132,14 @@ pub async fn post( let Some(user) = user else { return Ok(Either::E2(( - flash.error(Error::PasswordMismatch.to_string()), + flash.error(WRONG_EMAIL_OR_PASSWORD), Redirect::to(redirect_to), ))); }; if user.confirmed_at.is_none() { return Ok(Either::E2(( - flash.error(Error::UnconfirmedEmailAddress.to_string()), + flash.error(UNCONFIRMED_EMAIL_ADDRESS), Redirect::to(redirect_to), ))); } @@ -156,7 +158,7 @@ pub async fn post( if !is_valid { return Ok(Either::E2(( - flash.error(Error::PasswordMismatch.to_string()), + flash.error(WRONG_EMAIL_OR_PASSWORD), Redirect::to(redirect_to), ))); } diff --git a/kitsune/src/http/handler/oauth/token.rs b/kitsune/src/http/handler/oauth/token.rs index 7d5ceeb18..7c8392676 100644 --- a/kitsune/src/http/handler/oauth/token.rs +++ b/kitsune/src/http/handler/oauth/token.rs @@ -1,8 +1,6 @@ -use crate::{ - error::{Error, OAuth2Error, Result}, - oauth2::OAuthEndpoint, -}; +use crate::oauth2::OAuthEndpoint; use axum::{debug_handler, extract::State}; +use kitsune_error::{kitsune_error, Error, ErrorType, Result}; use oxide_auth::endpoint::QueryParameter; use oxide_auth_async::endpoint::{ access_token::AccessTokenFlow, client_credentials::ClientCredentialsFlow, refresh::RefreshFlow, @@ -18,7 +16,7 @@ pub async fn post( let grant_type = oauth_req .body() .and_then(|body| body.unique_value("grant_type")) - .ok_or(OAuth2Error::MissingGrantType)?; + .ok_or_else(|| kitsune_error!(type = ErrorType::BadRequest, "missing grant type"))?; match grant_type.as_ref() { "authorization_code" => { @@ -35,7 +33,10 @@ pub async fn post( let mut flow = RefreshFlow::prepare(oauth_endpoint)?; RefreshFlow::execute(&mut flow, oauth_req).await } - _ => Err(OAuth2Error::UnknownGrantType), + _ => Err(kitsune_error!( + type = ErrorType::BadRequest.with_body("unknown grant type"), + format!("unknown grant type: {grant_type}") + )), } .map_err(Error::from) } diff --git a/kitsune/src/http/handler/oidc/callback.rs b/kitsune/src/http/handler/oidc/callback.rs index 0f92faaa7..ecd5606ed 100644 --- a/kitsune/src/http/handler/oidc/callback.rs +++ b/kitsune/src/http/handler/oidc/callback.rs @@ -1,18 +1,15 @@ -use crate::{ - error::{OAuth2Error, Result}, - oauth2::{AuthorisationCode, OAuth2Service}, -}; +use crate::oauth2::{AuthorisationCode, OAuth2Service}; use axum::{ extract::{Query, State}, response::Response, }; use diesel::{ExpressionMethods, OptionalExtension, QueryDsl}; use diesel_async::RunQueryDsl; -use kitsune_core::error::HttpError; use kitsune_db::{ schema::{oauth2_applications, users}, with_connection, PgPool, }; +use kitsune_error::{bail, ErrorType, Result}; use kitsune_oidc::OidcService; use kitsune_service::user::{Register, UserService}; use serde::Deserialize; @@ -31,7 +28,7 @@ pub async fn get( Query(query): Query, ) -> Result { let Some(oidc_service) = oidc_service else { - return Err(HttpError::BadRequest.into()); + bail!(type = ErrorType::BadRequest, "oidc not configured"); }; let user_info = oidc_service.get_user_info(query.state, query.code).await?; @@ -67,7 +64,7 @@ pub async fn get( .application(application) .state(user_info.oauth2.state) .user_id(user.id) - .scopes(user_info.oauth2.scope.parse().map_err(OAuth2Error::from)?) + .scopes(user_info.oauth2.scope.parse()?) .build(); oauth_service diff --git a/kitsune/src/http/handler/posts/activity.rs b/kitsune/src/http/handler/posts/activity.rs index 448bf0518..b776ff0ef 100644 --- a/kitsune/src/http/handler/posts/activity.rs +++ b/kitsune/src/http/handler/posts/activity.rs @@ -1,9 +1,10 @@ -use crate::{error::Result, http::responder::ActivityPubJson, state::Zustand}; +use crate::{http::responder::ActivityPubJson, state::Zustand}; use axum::{ debug_handler, extract::{Path, State}, }; use kitsune_activitypub::mapping::IntoActivity; +use kitsune_error::Result; use kitsune_type::ap::Activity; use speedy_uuid::Uuid; diff --git a/kitsune/src/http/handler/posts/mod.rs b/kitsune/src/http/handler/posts/mod.rs index 1161d4aa9..7b4a29f25 100644 --- a/kitsune/src/http/handler/posts/mod.rs +++ b/kitsune/src/http/handler/posts/mod.rs @@ -1,6 +1,7 @@ -use crate::{error::Result, http::responder::ActivityPubJson, state::Zustand}; +use crate::{http::responder::ActivityPubJson, state::Zustand}; use axum::{debug_handler, extract::Path, extract::State, routing, Router}; use kitsune_activitypub::mapping::IntoObject; +use kitsune_error::Result; use kitsune_service::post::PostService; use kitsune_type::ap::Object; use speedy_uuid::Uuid; diff --git a/kitsune/src/http/handler/users/followers.rs b/kitsune/src/http/handler/users/followers.rs index a1732373f..55b1b4016 100644 --- a/kitsune/src/http/handler/users/followers.rs +++ b/kitsune/src/http/handler/users/followers.rs @@ -1,4 +1,4 @@ -use crate::{error::Result, http::responder::ActivityPubJson, state::Zustand}; +use crate::{http::responder::ActivityPubJson, state::Zustand}; use axum::extract::{OriginalUri, Path, State}; use diesel::{BoolExpressionMethods, ExpressionMethods, JoinOnDsl, QueryDsl}; use diesel_async::RunQueryDsl; @@ -6,6 +6,7 @@ use kitsune_db::{ schema::{accounts, accounts_follows}, with_connection, }; +use kitsune_error::Result; use kitsune_type::ap::{ ap_context, collection::{Collection, CollectionType}, diff --git a/kitsune/src/http/handler/users/following.rs b/kitsune/src/http/handler/users/following.rs index 478c01390..39cc10ca6 100644 --- a/kitsune/src/http/handler/users/following.rs +++ b/kitsune/src/http/handler/users/following.rs @@ -1,4 +1,4 @@ -use crate::{error::Result, http::responder::ActivityPubJson, state::Zustand}; +use crate::{http::responder::ActivityPubJson, state::Zustand}; use axum::extract::{OriginalUri, Path, State}; use diesel::{BoolExpressionMethods, ExpressionMethods, JoinOnDsl, QueryDsl}; use diesel_async::RunQueryDsl; @@ -6,6 +6,7 @@ use kitsune_db::{ schema::{accounts, accounts_follows}, with_connection, }; +use kitsune_error::Result; use kitsune_type::ap::{ ap_context, collection::{Collection, CollectionType}, diff --git a/kitsune/src/http/handler/users/inbox.rs b/kitsune/src/http/handler/users/inbox.rs index 3e383080a..a72cc59ef 100644 --- a/kitsune/src/http/handler/users/inbox.rs +++ b/kitsune/src/http/handler/users/inbox.rs @@ -1,19 +1,9 @@ -use crate::{ - error::{Error, Result}, - http::extractor::SignedActivity, - state::Zustand, -}; +use crate::{http::extractor::SignedActivity, state::Zustand}; use axum::{debug_handler, extract::State}; use diesel::{BoolExpressionMethods, ExpressionMethods, QueryDsl, SelectableHelper}; use diesel_async::RunQueryDsl; use iso8601_timestamp::Timestamp; -use kitsune_activitypub::{ - error::Error as ApError, process_new_object, update_object, ProcessNewObject, -}; -use kitsune_core::{ - error::HttpError, - event::{post::EventType, PostEvent}, -}; +use kitsune_activitypub::{process_new_object, update_object, ProcessNewObject}; use kitsune_db::{ model::{ account::Account, @@ -27,6 +17,7 @@ use kitsune_db::{ schema::{accounts_follows, accounts_preferences, notifications, posts, posts_favourites}, with_connection, }; +use kitsune_error::{bail, Error, ErrorType, Result}; use kitsune_federation_filter::FederationFilter; use kitsune_jobs::deliver::accept::DeliverAccept; use kitsune_service::job::Enqueue; @@ -47,13 +38,8 @@ async fn accept_activity(state: &Zustand, activity: Activity) -> Result<()> { } async fn announce_activity(state: &Zustand, author: Account, activity: Activity) -> Result<()> { - let Some(reposted_post) = state - .fetcher - .fetch_post(activity.object().into()) - .await - .map_err(Error::Fetcher)? - else { - return Err(HttpError::BadRequest.into()); + let Some(reposted_post) = state.fetcher.fetch_post(activity.object().into()).await? else { + bail!(type = ErrorType::BadRequest, "announced post couldn't be fetched"); }; with_connection!(state.db_pool, |db_conn| { @@ -92,47 +78,22 @@ async fn create_activity(state: &Zustand, author: Account, activity: Activity) - .object(Box::new(object)) .search_backend(state.service.search.backend()) .build(); - let new_post = process_new_object(process_data).await?; - - state - .event_emitter - .post - .emit(PostEvent { - r#type: EventType::Create, - post_id: new_post.id, - }) - .await - .map_err(Error::Messaging)?; + process_new_object(process_data).await?; } Ok(()) } async fn delete_activity(state: &Zustand, author: Account, activity: Activity) -> Result<()> { - 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?; - - diesel::delete(posts::table.find(post_id)) - .execute(db_conn) - .await?; - - Ok::<_, Error>(post_id) - })?; - - state - .event_emitter - .post - .emit(PostEvent { - r#type: EventType::Delete, - post_id, - }) + with_connection!(state.db_pool, |db_conn| { + diesel::delete( + posts::table + .filter(posts::account_id.eq(author.id)) + .filter(posts::url.eq(activity.object())), + ) + .execute(db_conn) .await - .map_err(Error::Messaging)?; + })?; Ok(()) } @@ -141,10 +102,9 @@ async fn follow_activity(state: &Zustand, author: Account, activity: Activity) - let Some(followed_user) = state .fetcher .fetch_account(activity.object().into()) - .await - .map_err(Error::Fetcher)? + .await? else { - return Err(HttpError::BadRequest.into()); + bail!(type = ErrorType::BadRequest, "followed account couldn't be fetched"); }; let approved_at = followed_user.locked.not().then(Timestamp::now_utc); @@ -298,17 +258,8 @@ async fn update_activity(state: &Zustand, author: Account, activity: Activity) - .object(Box::new(object)) .search_backend(state.service.search.backend()) .build(); - let modified_post = update_object(process_data).await?; - state - .event_emitter - .post - .emit(PostEvent { - r#type: EventType::Update, - post_id: modified_post.id, - }) - .await - .map_err(Error::Messaging)?; + update_object(process_data).await?; } Ok(()) @@ -329,10 +280,7 @@ pub async fn post( let counter = counter!("received_activities"); counter.increment(1); - if !federation_filter - .is_entity_allowed(&activity) - .map_err(ApError::from)? - { + if !federation_filter.is_entity_allowed(&activity)? { return Ok(()); } diff --git a/kitsune/src/http/handler/users/mod.rs b/kitsune/src/http/handler/users/mod.rs index 2d9e5e0f5..e70af5d1d 100644 --- a/kitsune/src/http/handler/users/mod.rs +++ b/kitsune/src/http/handler/users/mod.rs @@ -1,10 +1,10 @@ -use crate::{error::Result, http::responder::ActivityPubJson, state::Zustand}; +use crate::{http::responder::ActivityPubJson, state::Zustand}; use axum::{ extract::{Path, State}, routing, Router, }; use kitsune_activitypub::mapping::IntoObject; -use kitsune_core::error::HttpError; +use kitsune_error::{kitsune_error, ErrorType, Result}; use kitsune_service::account::AccountService; use kitsune_type::ap::actor::Actor; use speedy_uuid::Uuid; @@ -23,7 +23,7 @@ async fn get( let account = account_service .get_by_id(account_id) .await? - .ok_or(HttpError::NotFound)?; + .ok_or_else(|| kitsune_error!(type = ErrorType::NotFound, "account not found"))?; Ok(ActivityPubJson( account.into_object(state.ap_state()).await?, diff --git a/kitsune/src/http/handler/users/outbox.rs b/kitsune/src/http/handler/users/outbox.rs index ab8a713af..3217e97bf 100644 --- a/kitsune/src/http/handler/users/outbox.rs +++ b/kitsune/src/http/handler/users/outbox.rs @@ -1,4 +1,4 @@ -use crate::{error::Result, http::responder::ActivityPubJson, state::Zustand}; +use crate::{http::responder::ActivityPubJson, state::Zustand}; use axum::extract::{OriginalUri, Path, Query, State}; use axum_extra::either::Either; use diesel::{BelongingToDsl, ExpressionMethods, QueryDsl, SelectableHelper}; @@ -10,6 +10,7 @@ use kitsune_db::{ schema::accounts, with_connection, }; +use kitsune_error::Result; use kitsune_service::account::GetPosts; use kitsune_type::ap::{ ap_context, diff --git a/kitsune/src/http/handler/well_known/webfinger.rs b/kitsune/src/http/handler/well_known/webfinger.rs index 01a5f0049..97cced1c0 100644 --- a/kitsune/src/http/handler/well_known/webfinger.rs +++ b/kitsune/src/http/handler/well_known/webfinger.rs @@ -1,10 +1,11 @@ -use crate::{error::Result, state::Zustand}; +use crate::state::Zustand; use axum::{ extract::{Query, State}, routing, Json, Router, }; use axum_extra::either::Either; use http::StatusCode; +use kitsune_error::Result; use kitsune_service::account::{AccountService, GetUser}; use kitsune_type::webfinger::{Link, Resource}; use kitsune_url::UrlService; @@ -66,7 +67,6 @@ pub fn routes() -> Router { #[cfg(test)] mod tests { use super::{get, WebfingerQuery}; - use crate::error::Error; use athena::JobQueue; use axum::{ extract::{Query, State}, @@ -85,6 +85,7 @@ mod tests { schema::accounts, with_connection_panicky, PgPool, }; + use kitsune_error::Error; use kitsune_federation_filter::FederationFilter; use kitsune_http_client::Client; use kitsune_jobs::KitsuneContextRepo; diff --git a/kitsune/src/http/pagination.rs b/kitsune/src/http/pagination.rs index 113765b18..f40c60b56 100644 --- a/kitsune/src/http/pagination.rs +++ b/kitsune/src/http/pagination.rs @@ -3,10 +3,9 @@ use axum::{ Json, }; use http::{Error as HttpError, HeaderValue}; +use kitsune_error::Error; use std::{borrow::Cow, fmt::Display}; -use crate::error::Error; - pub type PaginatedJsonResponse = ( Option>>, Json>, diff --git a/kitsune/src/http/util.rs b/kitsune/src/http/util.rs index ffbf56c8a..4bcbb3cb2 100644 --- a/kitsune/src/http/util.rs +++ b/kitsune/src/http/util.rs @@ -1,9 +1,8 @@ -use crate::error::Result; use axum::extract::multipart; use bytes::Bytes; +use color_eyre::eyre::Context; use futures_util::{Stream, TryStreamExt}; -use kitsune_core::error::HttpError; -use kitsune_storage::BoxError; +use kitsune_error::Result; use std::io::SeekFrom; use tempfile::tempfile; use tokio::{ @@ -15,18 +14,18 @@ use tokio_util::io::ReaderStream; #[allow(dead_code)] // Not used when the Mastodon API feature is deactivated pub async fn buffer_multipart_to_tempfile( field: &mut multipart::Field<'_>, -) -> Result> + Send + 'static> { +) -> Result> + Send + 'static> { let tempfile = tempfile().unwrap(); let mut tempfile = File::from_std(tempfile); while let Some(chunk) = field.chunk().await? { - if let Err(error) = tempfile.write_all(&chunk).await { - error!(?error, "Failed to write chunk to tempfile"); - return Err(HttpError::InternalServerError.into()); - } + tempfile + .write_all(&chunk) + .await + .wrap_err("failed to write chunk to tempfile")?; } - tempfile.seek(SeekFrom::Start(0)).await.unwrap(); + tempfile.seek(SeekFrom::Start(0)).await?; Ok(ReaderStream::new(tempfile).map_err(Into::into)) } diff --git a/kitsune/src/lib.rs b/kitsune/src/lib.rs index 1da99707d..c728505cc 100644 --- a/kitsune/src/lib.rs +++ b/kitsune/src/lib.rs @@ -5,7 +5,6 @@ extern crate metrics; extern crate tracing; pub mod consts; -pub mod error; pub mod http; pub mod oauth2; pub mod signal; @@ -13,7 +12,7 @@ pub mod state; use self::{ oauth2::{OAuth2Service, OAuthEndpoint}, - state::{EventEmitter, Service, SessionConfig, Zustand, ZustandInner}, + state::{Service, SessionConfig, Zustand, ZustandInner}, }; use athena::JobQueue; use color_eyre::eyre; @@ -52,9 +51,6 @@ pub async fn initialise_state( db_pool: PgPool, job_queue: JobQueue, ) -> eyre::Result { - let messaging_hub = prepare::messaging(&config.messaging).await?; - let status_event_emitter = messaging_hub.emitter("event.status".into()); - let url_service = UrlService::builder() .domain(config.url.domain.clone()) .scheme(config.url.scheme.clone()) @@ -68,7 +64,8 @@ pub async fn initialise_state( .url_service(url_service.clone()) .build(); - let federation_filter = FederationFilter::new(&config.instance.federation_filter)?; + let federation_filter = FederationFilter::new(&config.instance.federation_filter) + .map_err(kitsune_error::Error::into_error)?; let job_service = JobService::builder().job_queue(job_queue).build(); @@ -141,18 +138,11 @@ pub async fn initialise_state( #[cfg(feature = "mastodon-api")] let mastodon_mapper = kitsune_mastodon::MastodonMapper::builder() .attachment_service(attachment_service.clone()) - .cache_invalidator( - status_event_emitter - .consumer() - .await - .expect("Failed to register status event consumer"), - ) .db_pool(db_pool.clone()) .embed_client(embed_client.clone()) .mastodon_cache(prepare::cache(&config.cache, "MASTODON-CACHE").await?) .url_service(url_service.clone()) - .build() - .unwrap(); + .build(); let mrf_service = MrfService::from_config(&config.mrf).await?; @@ -166,7 +156,8 @@ pub async fn initialise_state( OidcService::initialise(oidc_config, url_service.oidc_redirect_uri()) })) .await - .transpose()?; + .transpose() + .map_err(kitsune_error::Error::into_error)?; let oauth2_service = OAuth2Service::builder() .db_pool(db_pool.clone()) @@ -186,7 +177,6 @@ pub async fn initialise_state( .language_detection_config(config.language_detection) .post_resolver(post_resolver) .search_backend(search_backend.clone()) - .status_event_emitter(status_event_emitter.clone()) .url_service(url_service.clone()) .build(); @@ -210,9 +200,6 @@ pub async fn initialise_state( Ok(ZustandInner { db_pool: db_pool.clone(), embed_client, - event_emitter: EventEmitter { - post: status_event_emitter, - }, federation_filter, fetcher, language_detection_config: config.language_detection, diff --git a/kitsune/src/main.rs b/kitsune/src/main.rs index 79f17be8a..107ca69d3 100644 --- a/kitsune/src/main.rs +++ b/kitsune/src/main.rs @@ -27,6 +27,7 @@ async fn boot() -> eyre::Result<()> { let conn = kitsune_db::connect(&config.database) .await + .map_err(kitsune_error::Error::into_error) .wrap_err("Failed to connect to and migrate the database")?; let job_queue = kitsune_job_runner::prepare_job_queue(conn.clone(), &config.job_queue) diff --git a/kitsune/src/oauth2/authorizer.rs b/kitsune/src/oauth2/authorizer.rs index 9c4184413..dbc541a57 100644 --- a/kitsune/src/oauth2/authorizer.rs +++ b/kitsune/src/oauth2/authorizer.rs @@ -3,14 +3,15 @@ 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}, with_connection, PgPool, }; +use kitsune_error::Result; use kitsune_util::generate_secret; use oxide_auth::primitives::grant::{Extensions, Grant}; use oxide_auth_async::primitives::Authorizer; +use trials::attempt; #[derive(Clone)] pub struct OAuthAuthorizer { @@ -19,56 +20,61 @@ pub struct OAuthAuthorizer { #[async_trait] impl Authorizer for OAuthAuthorizer { + #[instrument(skip_all)] async fn authorize(&mut self, grant: Grant) -> Result { - let application_id = grant.client_id.parse().map_err(|_| ())?; - let user_id = grant.owner_id.parse().map_err(|_| ())?; - let scopes = grant.scope.to_string(); - let secret = generate_secret(); - let expires_at = chrono_to_timestamp(grant.until); + let result: Result<_> = attempt! { async + let application_id = grant.client_id.parse()?; + let user_id = grant.owner_id.parse()?; + let scopes = grant.scope.to_string(); + let secret = generate_secret(); + let expires_at = chrono_to_timestamp(grant.until); - 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(|_| ()) + 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 + })? + }; + + result.map_err(|error| debug!(?error, "authorize failed")) } + #[instrument(skip_all)] async fn extract(&mut self, authorization_code: &str) -> Result, ()> { - 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 result: Result<_> = attempt! { async + let oauth_data = 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() + })?; - let oauth_data = oauth_data.map(|(code, app)| { - let scope = app.scopes.parse().unwrap(); - let redirect_uri = app.redirect_uri.parse().unwrap(); + oauth_data + .map(|(code, app)| { + let scope = app.scopes.parse().unwrap(); + let redirect_uri = app.redirect_uri.parse().unwrap(); - Grant { - owner_id: code.user_id.to_string(), - client_id: code.application_id.to_string(), - scope, - redirect_uri, - until: timestamp_to_chrono(code.expires_at), - extensions: Extensions::default(), - } - }); + Grant { + owner_id: code.user_id.to_string(), + client_id: code.application_id.to_string(), + scope, + redirect_uri, + until: timestamp_to_chrono(code.expires_at), + extensions: Extensions::default(), + } + }) + }; - Ok(oauth_data) + result.map_err(|error| debug!(?error, "extract failed")) } } diff --git a/kitsune/src/oauth2/endpoint.rs b/kitsune/src/oauth2/endpoint.rs index f1a5b8b8a..d9d212761 100644 --- a/kitsune/src/oauth2/endpoint.rs +++ b/kitsune/src/oauth2/endpoint.rs @@ -1,7 +1,6 @@ use super::{ authorizer::OAuthAuthorizer, issuer::OAuthIssuer, registrar::OAuthRegistrar, OAuthScope, }; -use crate::error::OAuth2Error; use kitsune_db::PgPool; use oxide_auth::endpoint::{OAuthError, OwnerConsent, Scope, Scopes, Solicitation, WebRequest}; use oxide_auth_async::{ @@ -65,7 +64,7 @@ impl Endpoint for OAuthEndpoint where S: OwnerSolicitor + Send, { - type Error = OAuth2Error; + type Error = kitsune_error::Error; fn registrar(&self) -> Option<&(dyn Registrar + Sync)> { Some(&self.registrar) @@ -116,6 +115,7 @@ where _req: &mut T, _solicitation: Solicitation<'_>, ) -> OwnerConsent { + error!("called the \"vacant\" owner solicitor. this is most likely a bug!"); OwnerConsent::Denied } } diff --git a/kitsune/src/oauth2/issuer.rs b/kitsune/src/oauth2/issuer.rs index 4074a675b..ba0f4ba95 100644 --- a/kitsune/src/oauth2/issuer.rs +++ b/kitsune/src/oauth2/issuer.rs @@ -1,14 +1,13 @@ use super::{chrono_to_timestamp, timestamp_to_chrono}; -use crate::error::Error; 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}, with_connection, with_transaction, PgPool, }; +use kitsune_error::{Error, Result}; use kitsune_util::generate_secret; use oxide_auth::primitives::{ grant::{Extensions, Grant}, @@ -16,6 +15,7 @@ use oxide_auth::primitives::{ prelude::IssuedToken, }; use oxide_auth_async::primitives::Issuer; +use trials::attempt; #[derive(Clone)] pub struct OAuthIssuer { @@ -24,161 +24,167 @@ pub struct OAuthIssuer { #[async_trait] impl Issuer for OAuthIssuer { + #[instrument(skip_all)] async fn issue(&mut self, grant: Grant) -> Result { - let application_id = grant.client_id.parse().map_err(|_| ())?; - let user_id = grant.owner_id.parse().map_err(|_| ())?; - let scopes = grant.scope.to_string(); - let expires_at = chrono_to_timestamp(grant.until); - - 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, - refresh: Some(refresh_token.token), - until: grant.until, - token_type: TokenType::Bearer, - }) + let result: Result<_> = attempt! { async + let application_id = grant.client_id.parse()?; + let user_id = grant.owner_id.parse()?; + let scopes = grant.scope.to_string(); + let expires_at = chrono_to_timestamp(grant.until); + + let (access_token, refresh_token) = 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)) + })?; + + IssuedToken { + token: access_token.token, + refresh: Some(refresh_token.token), + until: grant.until, + token_type: TokenType::Bearer, + } + }; + + result.map_err(|error| debug!(?error, "failed to issue token")) } + #[instrument(skip_all)] async fn refresh(&mut self, refresh_token: &str, grant: Grant) -> Result { - 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) - .await - })) - .map_err(|_| ())? - .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, - refresh: Some(refresh_token.token), - until: timestamp_to_chrono(access_token.expires_at), - token_type: TokenType::Bearer, - }) + let result: Result<_> = attempt! { async + let (refresh_token, access_token) = + 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) + .await + })?; + + let (access_token, refresh_token) = 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)) + })?; + + RefreshedToken { + token: access_token.token, + refresh: Some(refresh_token.token), + until: timestamp_to_chrono(access_token.expires_at), + token_type: TokenType::Bearer, + } + }; + + result.map_err(|error| debug!(?error, "failed to refresh token")) } + #[instrument(skip_all)] async fn recover_token(&mut self, access_token: &str) -> Result, ()> { - 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(); - let redirect_uri = app.redirect_uri.parse().unwrap(); - let until = timestamp_to_chrono(access_token.expires_at); - - Grant { - owner_id: access_token - .user_id - .as_ref() - .map(ToString::to_string) - .unwrap_or_default(), - client_id: app.id.to_string(), - scope, - redirect_uri, - until, - extensions: Extensions::default(), - } - }); - - Ok(oauth_data) + let result: Result<_> = attempt! { async + let oauth_data = 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() + })?; + + oauth_data.map(|(access_token, app)| { + let scope = app.scopes.parse().unwrap(); + let redirect_uri = app.redirect_uri.parse().unwrap(); + let until = timestamp_to_chrono(access_token.expires_at); + + Grant { + owner_id: access_token + .user_id + .as_ref() + .map(ToString::to_string) + .unwrap_or_default(), + client_id: app.id.to_string(), + scope, + redirect_uri, + until, + extensions: Extensions::default(), + } + }) + }; + + result.map_err(|error| debug!(?error, "failed to recover token grant")) } + #[instrument(skip_all)] async fn recover_refresh(&mut self, refresh_token: &str) -> Result, ()> { - 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(); - let redirect_uri = app.redirect_uri.parse().unwrap(); - let until = chrono::NaiveDateTime::MAX.and_utc(); - - Grant { - owner_id: access_token - .user_id - .as_ref() - .map(ToString::to_string) - .unwrap_or_default(), - client_id: app.id.to_string(), - scope, - redirect_uri, - until, - extensions: Extensions::default(), - } - }); - - Ok(oauth_data) + let result: Result<_> = attempt! { async + let oauth_data = 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() + })?; + + oauth_data.map(|(access_token, app)| { + let scope = access_token.scopes.parse().unwrap(); + let redirect_uri = app.redirect_uri.parse().unwrap(); + let until = chrono::NaiveDateTime::MAX.and_utc(); + + Grant { + owner_id: access_token + .user_id + .as_ref() + .map(ToString::to_string) + .unwrap_or_default(), + client_id: app.id.to_string(), + scope, + redirect_uri, + until, + extensions: Extensions::default(), + } + }) + }; + + result.map_err(|error| debug!(?error, "failed to recover refresh grant")) } } diff --git a/kitsune/src/oauth2/mod.rs b/kitsune/src/oauth2/mod.rs index f171ecb3c..b195e223d 100644 --- a/kitsune/src/oauth2/mod.rs +++ b/kitsune/src/oauth2/mod.rs @@ -1,4 +1,3 @@ -use crate::error::{Error, Result}; use askama::Template; use askama_axum::IntoResponse; use axum::response::{Redirect, Response}; @@ -10,6 +9,7 @@ use kitsune_db::{ schema::{oauth2_applications, oauth2_authorization_codes}, with_connection, PgPool, }; +use kitsune_error::{Error, Result}; use kitsune_url::UrlService; use kitsune_util::generate_secret; use oxide_auth::endpoint::Scope; diff --git a/kitsune/src/oauth2/registrar.rs b/kitsune/src/oauth2/registrar.rs index d6f567974..57d6f1765 100644 --- a/kitsune/src/oauth2/registrar.rs +++ b/kitsune/src/oauth2/registrar.rs @@ -1,9 +1,8 @@ use async_trait::async_trait; use diesel::{ExpressionMethods, OptionalExtension, QueryDsl}; use diesel_async::RunQueryDsl; -use kitsune_db::{ - catch_error, model::oauth2, schema::oauth2_applications, with_connection, PgPool, -}; +use kitsune_db::{model::oauth2, schema::oauth2_applications, with_connection, PgPool}; +use kitsune_error::Result; use oxide_auth::{ endpoint::{PreGrant, Scope}, primitives::registrar::{BoundClient, ClientUrl, ExactUrl, RegisteredUrl, RegistrarError}, @@ -14,6 +13,7 @@ use std::{ borrow::Cow, str::{self, FromStr}, }; +use trials::attempt; use super::OAuthScope; @@ -24,6 +24,7 @@ pub struct OAuthRegistrar { #[async_trait] impl Registrar for OAuthRegistrar { + #[instrument(skip_all)] async fn bound_redirect<'a>( &self, bound: ClientUrl<'a>, @@ -38,6 +39,7 @@ impl Registrar for OAuthRegistrar { } } + #[instrument(skip_all)] async fn negotiate<'a>( &self, client: BoundClient<'a>, @@ -48,17 +50,20 @@ impl Registrar for OAuthRegistrar { .parse() .map_err(|_| RegistrarError::PrimitiveError)?; - 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_result: Result<_> = attempt! { async + 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() + })? + }; + + let client = client_result + .map_err(|_| RegistrarError::PrimitiveError)? + .ok_or(RegistrarError::Unspecified)?; let client_id = client.id.to_string(); let redirect_uri = ExactUrl::new(client.redirect_uri) @@ -91,6 +96,7 @@ impl Registrar for OAuthRegistrar { }) } + #[instrument(skip_all)] async fn check( &self, client_id: &str, @@ -107,16 +113,19 @@ impl Registrar for OAuthRegistrar { client_query = client_query.filter(oauth2_applications::secret.eq(passphrase)); } - 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) + let result: Result<_> = attempt! { async + with_connection!(self.db_pool, |db_conn| { + client_query + .select(oauth2_applications::id) + .execute(db_conn) + .await + .optional() + })? + }; + + result + .map_err(|_| RegistrarError::PrimitiveError)? + .map(|_| ()) + .ok_or(RegistrarError::Unspecified) } } diff --git a/kitsune/src/oauth2/solicitor.rs b/kitsune/src/oauth2/solicitor.rs index a2b0371ed..7468e8f0a 100644 --- a/kitsune/src/oauth2/solicitor.rs +++ b/kitsune/src/oauth2/solicitor.rs @@ -4,15 +4,15 @@ use async_trait::async_trait; use cursiv::CsrfHandle; use diesel::{OptionalExtension, QueryDsl}; use diesel_async::RunQueryDsl; -use kitsune_db::{ - catch_error, model::user::User, schema::oauth2_applications, with_connection, PgPool, -}; +use kitsune_db::{model::user::User, schema::oauth2_applications, with_connection, PgPool}; +use kitsune_error::Result; use oxide_auth::endpoint::{OAuthError, OwnerConsent, QueryParameter, Solicitation, WebRequest}; use oxide_auth_async::endpoint::OwnerSolicitor; use oxide_auth_axum::{OAuthRequest, OAuthResponse, WebError}; use speedy_uuid::Uuid; use std::{borrow::Cow, str::FromStr}; use strum::EnumMessage; +use trials::attempt; use typed_builder::TypedBuilder; #[derive(Template)] @@ -55,6 +55,7 @@ pub struct OAuthOwnerSolicitor { } impl OAuthOwnerSolicitor { + #[instrument(skip_all)] async fn check_consent( &self, login_consent: Option<&str>, @@ -81,17 +82,20 @@ impl OAuthOwnerSolicitor { .parse() .map_err(|_| WebError::Endpoint(OAuthError::BadRequest))?; - 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 app_name_result: Result> = attempt! { async + with_connection!(self.db_pool, |db_conn| { + oauth2_applications::table + .find(client_id) + .select(oauth2_applications::name) + .get_result::(db_conn) + .await + .optional() + })? + }; + + let app_name = app_name_result + .map_err(|_| WebError::InternalError(None))? + .ok_or(WebError::Endpoint(OAuthError::DenySilently))?; let scopes = solicitation .pre_grant() @@ -129,6 +133,7 @@ impl OAuthOwnerSolicitor { #[async_trait] impl OwnerSolicitor for OAuthOwnerSolicitor { + #[instrument(skip_all)] async fn check_consent( &mut self, req: &mut OAuthRequest, diff --git a/kitsune/src/state.rs b/kitsune/src/state.rs index 6a895f09a..4414128f8 100644 --- a/kitsune/src/state.rs +++ b/kitsune/src/state.rs @@ -1,7 +1,7 @@ use crate::oauth2::{OAuth2Service, OAuthEndpoint}; use axum_extra::extract::cookie; use kitsune_config::language_detection::Configuration as LanguageDetectionConfig; -use kitsune_core::{event::PostEventEmitter, traits::Fetcher}; +use kitsune_core::traits::Fetcher; use kitsune_db::PgPool; use kitsune_email::MailingService; use kitsune_embed::Client as EmbedClient; @@ -71,13 +71,6 @@ impl_from_ref! { ] } -impl_from_ref! { - Zustand; - [ - PostEventEmitter => |input: &Zustand| input.event_emitter.post.clone() - ] -} - #[derive(Clone)] pub struct SessionConfig { pub cookie_key: cookie::Key, @@ -124,14 +117,6 @@ impl_from_ref! { ] } -/// Emitter collection -/// -/// This contains all the "emitters" that can emit events inside of Kitsune. -/// Something like "a post has been created" or "an account has been followed". -pub struct EventEmitter { - pub post: PostEventEmitter, -} - /// Service collection /// /// This contains all the "services" that Kitsune consists of. @@ -156,7 +141,6 @@ pub struct Service { pub struct ZustandInner { pub db_pool: PgPool, pub embed_client: Option, - pub event_emitter: EventEmitter, pub federation_filter: FederationFilter, pub fetcher: Arc, pub language_detection_config: LanguageDetectionConfig, diff --git a/lib/athena/Cargo.toml b/lib/athena/Cargo.toml index 5b52874d2..04cfab1be 100644 --- a/lib/athena/Cargo.toml +++ b/lib/athena/Cargo.toml @@ -14,7 +14,7 @@ just-retry = { path = "../just-retry" } multiplex-pool = { path = "../multiplex-pool" } once_cell = "1.19.0" rand = "0.8.5" -redis = { version = "0.25.2", default-features = false, features = [ +redis = { version = "0.25.3", default-features = false, features = [ "ahash", "connection-manager", "script", @@ -32,7 +32,7 @@ tracing = "0.1.40" typed-builder = "0.18.1" [dev-dependencies] -redis = { version = "0.25.2", features = ["connection-manager"] } +redis = { version = "0.25.3", features = ["connection-manager"] } tracing-subscriber = "0.3.18" [lints] diff --git a/lib/athena/src/queue/util.rs b/lib/athena/src/queue/util.rs index 62275b7c1..df33de218 100644 --- a/lib/athena/src/queue/util.rs +++ b/lib/athena/src/queue/util.rs @@ -3,9 +3,7 @@ use redis::{streams::StreamId, FromRedisValue, RedisResult}; #[derive(Clone, Debug)] pub struct StreamAutoClaimReply { - pub start_stream_id: String, pub claimed_ids: Vec, - pub deleted_ids: Vec, } impl FromRedisValue for StreamAutoClaimReply { @@ -16,7 +14,7 @@ impl FromRedisValue for StreamAutoClaimReply { Vec, ); - let (start_stream_id, claimed_ids, deleted_ids): AutoClaimReturnValue = + let (_start_stream_id, claimed_ids, _deleted_ids): AutoClaimReturnValue = redis::from_redis_value(v)?; let claimed_ids: Vec = claimed_ids @@ -24,10 +22,6 @@ impl FromRedisValue for StreamAutoClaimReply { .flat_map(|row| row.into_iter().map(|(id, map)| StreamId { id, map })) .collect(); - Ok(Self { - start_stream_id, - claimed_ids, - deleted_ids, - }) + Ok(Self { claimed_ids }) } } diff --git a/lib/speedy-uuid/Cargo.toml b/lib/speedy-uuid/Cargo.toml index 59e208e81..22b4465a6 100644 --- a/lib/speedy-uuid/Cargo.toml +++ b/lib/speedy-uuid/Cargo.toml @@ -11,7 +11,7 @@ diesel = { version = "2.1.5", features = [ "postgres_backend", "uuid", ], optional = true } -redis = { version = "0.25.2", default-features = false, optional = true } +redis = { version = "0.25.3", default-features = false, optional = true } serde = { version = "1.0.197", optional = true } thiserror = "1.0.58" uuid = { version = "1.8.0", features = ["fast-rng", "v7"] } diff --git a/lib/trials/Cargo.toml b/lib/trials/Cargo.toml new file mode 100644 index 000000000..324979218 --- /dev/null +++ b/lib/trials/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "trials" +authors.workspace = true +edition.workspace = true +version.workspace = true +license = "MIT OR Apache-2.0" + +[dependencies] +trials-macros = { path = "macros", optional = true } + +[features] +proc-macro = ["dep:trials-macros"] + +[dev-dependencies] +futures-test = "0.3.30" + +[lints] +workspace = true diff --git a/lib/trials/macros/Cargo.toml b/lib/trials/macros/Cargo.toml new file mode 100644 index 000000000..c5bdd5fb1 --- /dev/null +++ b/lib/trials/macros/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "trials-macros" +authors.workspace = true +edition.workspace = true +version.workspace = true +license = "MIT OR Apache-2.0" + +[lib] +proc-macro = true + +[dependencies] +proc-macro2 = "1.0.79" +quote = "1.0.35" +syn = { version = "2.0.58", features = ["full", "visit-mut"] } + +[lints] +workspace = true diff --git a/lib/trials/macros/src/lib.rs b/lib/trials/macros/src/lib.rs new file mode 100644 index 000000000..8e2ab4a67 --- /dev/null +++ b/lib/trials/macros/src/lib.rs @@ -0,0 +1,122 @@ +use proc_macro2::TokenStream; +use quote::ToTokens; +use syn::{ + parse_quote, + visit_mut::{visit_expr_mut, visit_file_mut, VisitMut}, + Expr, ImplItemFn, ItemFn, TraitItemFn, +}; + +struct TryBlockVisitor { + use_async_strategy: bool, +} + +impl VisitMut for TryBlockVisitor { + fn visit_expr_mut(&mut self, expr: &mut syn::Expr) { + let Expr::TryBlock(ref try_block) = expr else { + visit_expr_mut(self, expr); + return; + }; + + let body = &try_block.block; + let replacement: Expr = if self.use_async_strategy { + parse_quote!((async { Ok(#body) }).await) + } else { + parse_quote!((|| { Ok(#body) })()) + }; + + *expr = replacement; + visit_expr_mut(self, expr); + } +} + +macro_rules! impl_visit { + ($($fn_name:ident => $ty:ty),+$(,)?) => { + $( + fn $fn_name(&mut self, func: &mut $ty) { + let use_async_strategy = func.sig.asyncness.is_some(); + let mut visitor = TryBlockVisitor { use_async_strategy }; + ::syn::visit_mut::$fn_name(&mut visitor, func); + } + )+ + }; +} + +struct FunctionVisitor; + +impl VisitMut for FunctionVisitor { + impl_visit! { + visit_impl_item_fn_mut => ImplItemFn, + visit_item_fn_mut => ItemFn, + visit_trait_item_fn_mut => TraitItemFn, + } +} + +fn trials_impl(input: TokenStream) -> TokenStream { + let mut code: syn::File = match syn::parse2(input) { + Ok(func) => func, + Err(err) => return err.into_compile_error(), + }; + + let mut visitor = FunctionVisitor; + visit_file_mut(&mut visitor, &mut code); + + code.to_token_stream() +} + +/// Usage: +/// +/// ``` +/// # use trials::trials_stable; +/// trials_stable! { +/// pub fn fallible() { +/// let result: Result<_, ()> = try { +/// # let fallible_operation = move || Err::<(), ()>(()); +/// fallible_operation()?; +/// Ok(()) +/// }; +/// +/// assert!(result.is_err()); +/// } +/// } +#[proc_macro] +pub fn trials_stable(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + trials_impl(input.into()).into() +} + +/// ## ⚠ HUGE DISCLAIMER +/// +/// Since this is an attribute macro, it still goes through the Rust parser, meaning if (for some reason) `try` blocks +/// are fully removed from the Rust parser, compilation will start failing. +/// +/// Only use this if you really don't care about your code potentially breaking (and about the large warning block rustc emits) +/// +/// --- +/// +/// Usage: +/// +/// ``` +/// # use trials::trials; +/// #[trials] +/// pub fn fallible() { +/// let result: Result<_, ()> = try { +/// # let fallible_operation = move || Err::<(), ()>(()); +/// fallible_operation()?; +/// Ok(()) +/// }; +/// +/// assert!(result.is_err()); +/// } +/// ``` +#[proc_macro_attribute] +pub fn trials( + attr: proc_macro::TokenStream, + input: proc_macro::TokenStream, +) -> proc_macro::TokenStream { + if !attr.is_empty() { + return syn::Error::new_spanned(TokenStream::from(attr), "expected no arguments") + .into_compile_error() + .into(); + } + + trials_impl(input.into()).into() +} diff --git a/lib/trials/src/lib.rs b/lib/trials/src/lib.rs new file mode 100644 index 000000000..936640a75 --- /dev/null +++ b/lib/trials/src/lib.rs @@ -0,0 +1,12 @@ +#[cfg(feature = "proc-macro")] +pub use trials_macros::{trials, trials_stable}; + +#[macro_export] +macro_rules! attempt { + (async $($tt:tt)*) => { + async { Ok({ $($tt)* }) }.await + }; + ($($tt:tt)*) => { + (|| Ok({ $($tt)* }))() + }; +} diff --git a/lib/trials/tests/basic.rs b/lib/trials/tests/basic.rs new file mode 100644 index 000000000..96fcd67fd --- /dev/null +++ b/lib/trials/tests/basic.rs @@ -0,0 +1,74 @@ +use trials::attempt; + +#[test] +#[cfg(feature = "proc-macro")] +fn does_catch() { + trials::trials_stable! { + fn is_odd(num: i32) -> bool { + let result: Result<(), ()> = try { + if num % 2 == 0 { + Err(())?; + } + }; + + result.is_ok() + } + } + + assert!(is_odd(3)); + assert!(!is_odd(2)); +} + +#[test] +fn works_declarative() { + let result: Result<(), ()> = attempt! { + Err(())?; + }; + assert!(result.is_err()); +} + +#[futures_test::test] +async fn works_declarative_async() { + let result: Result<(), ()> = attempt! { async + async { Err(()) }.await?; + }; + assert!(result.is_err()); +} + +#[test] +#[cfg(feature = "proc-macro")] +fn works_on_impl_block() { + struct Test; + + #[trials::trials] + impl Test { + fn erroring() { + let result: Result<(), ()> = try { + let fallible_op = || Err(()); + fallible_op()?; + }; + + assert!(result.is_err()); + } + } + + Test::erroring(); +} + +#[futures_test::test] +#[cfg(feature = "proc-macro")] +async fn does_catch_async() { + #[trials::trials] + async fn is_odd(num: i32) -> bool { + let result: Result<(), ()> = try { + if num % 2 == 0 { + Err(())?; + } + }; + + result.is_ok() + } + + assert!(is_odd(3).await); + assert!(!is_odd(2).await); +}