# It is not intended for manual editing.
version = 3
+[[package]]
+name = "activitypub_federation"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78b34a144dc98c419543690aa8f182d8675ebe0610775982b8fdee84a00f70fe"
+dependencies = [
+ "activitypub_federation_derive",
+ "actix-web",
+ "anyhow",
+ "async-trait",
+ "background-jobs",
+ "base64",
+ "chrono",
+ "derive_builder 0.11.2",
+ "http",
+ "http-signature-normalization-actix",
+ "http-signature-normalization-reqwest",
+ "once_cell",
+ "openssl",
+ "reqwest",
+ "reqwest-middleware",
+ "serde",
+ "serde_json",
+ "sha2",
+ "thiserror",
+ "tracing",
+ "url",
+]
+
+[[package]]
+name = "activitypub_federation_derive"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a2aaf58676b669d3b0dedf6bbb44fa518b5a6657b2959561d77899c668dec2a"
+dependencies = [
+ "proc-macro2 1.0.39",
+ "quote 1.0.18",
+ "syn 1.0.95",
+]
+
[[package]]
name = "activitystreams-kinds"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "465a6172cf69b960917811022d8f29bc0b7fa1398bc4f78b3c466673db1213b6"
dependencies = [
- "quote 1.0.17",
- "syn 1.0.90",
+ "quote 1.0.18",
+ "syn 1.0.95",
]
[[package]]
checksum = "7525bedf54704abb1d469e88d7e7e9226df73778798a69cea5022d53b2ae91bc"
dependencies = [
"actix-router",
- "proc-macro2 1.0.36",
- "quote 1.0.17",
- "syn 1.0.90",
+ "proc-macro2 1.0.39",
+ "quote 1.0.18",
+ "syn 1.0.95",
]
[[package]]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d44b8fee1ced9671ba043476deddef739dd0959bf77030b26b738cc591737a7"
dependencies = [
- "proc-macro2 1.0.36",
- "quote 1.0.17",
- "syn 1.0.90",
+ "proc-macro2 1.0.39",
+ "quote 1.0.18",
+ "syn 1.0.95",
]
[[package]]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27"
dependencies = [
- "proc-macro2 1.0.36",
- "quote 1.0.17",
- "syn 1.0.90",
+ "proc-macro2 1.0.39",
+ "quote 1.0.18",
+ "syn 1.0.95",
]
[[package]]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed6aa3524a2dfcf9fe180c51eae2b58738348d819517ceadf95789c51fff7600"
dependencies = [
- "proc-macro2 1.0.36",
- "quote 1.0.17",
- "syn 1.0.90",
+ "proc-macro2 1.0.39",
+ "quote 1.0.18",
+ "syn 1.0.95",
]
[[package]]
checksum = "21fb6a0b39c6517edafe46f8137e53c51742425a4dae1c73ee12264a37ad7541"
dependencies = [
"chrono",
- "derive_builder",
+ "derive_builder 0.10.2",
"diligent-date-parser",
"never",
"quick-xml",
"darling_macro 0.13.1",
]
+[[package]]
+name = "darling"
+version = "0.14.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4529658bdda7fd6769b8614be250cdcfc3aeb0ee72fe66f9e41e5e5eb73eac02"
+dependencies = [
+ "darling_core 0.14.1",
+ "darling_macro 0.14.1",
+]
+
[[package]]
name = "darling_core"
version = "0.12.4"
dependencies = [
"fnv",
"ident_case",
- "proc-macro2 1.0.36",
- "quote 1.0.17",
+ "proc-macro2 1.0.39",
+ "quote 1.0.18",
"strsim",
- "syn 1.0.90",
+ "syn 1.0.95",
]
[[package]]
dependencies = [
"fnv",
"ident_case",
- "proc-macro2 1.0.36",
- "quote 1.0.17",
+ "proc-macro2 1.0.39",
+ "quote 1.0.18",
+ "strsim",
+ "syn 1.0.95",
+]
+
+[[package]]
+name = "darling_core"
+version = "0.14.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "649c91bc01e8b1eac09fb91e8dbc7d517684ca6be8ebc75bb9cafc894f9fdb6f"
+dependencies = [
+ "fnv",
+ "ident_case",
+ "proc-macro2 1.0.39",
+ "quote 1.0.18",
"strsim",
- "syn 1.0.90",
+ "syn 1.0.95",
]
[[package]]
checksum = "29b5acf0dea37a7f66f7b25d2c5e93fd46f8f6968b1a5d7a3e02e97768afc95a"
dependencies = [
"darling_core 0.12.4",
- "quote 1.0.17",
- "syn 1.0.90",
+ "quote 1.0.18",
+ "syn 1.0.95",
]
[[package]]
checksum = "72c41b3b7352feb3211a0d743dc5700a4e3b60f51bd2b368892d1e0f9a95f44b"
dependencies = [
"darling_core 0.13.1",
- "quote 1.0.17",
- "syn 1.0.90",
+ "quote 1.0.18",
+ "syn 1.0.95",
+]
+
+[[package]]
+name = "darling_macro"
+version = "0.14.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ddfc69c5bfcbd2fc09a0f38451d2daf0e372e367986a83906d1b0dbc88134fb5"
+dependencies = [
+ "darling_core 0.14.1",
+ "quote 1.0.18",
+ "syn 1.0.95",
]
[[package]]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d13202debe11181040ae9063d739fa32cfcaaebe2275fe387703460ae2365b30"
dependencies = [
- "derive_builder_macro",
+ "derive_builder_macro 0.10.2",
+]
+
+[[package]]
+name = "derive_builder"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d07adf7be193b71cc36b193d0f5fe60b918a3a9db4dad0449f57bcfd519704a3"
+dependencies = [
+ "derive_builder_macro 0.11.2",
]
[[package]]
checksum = "66e616858f6187ed828df7c64a6d71720d83767a7f19740b2d1b6fe6327b36e5"
dependencies = [
"darling 0.12.4",
- "proc-macro2 1.0.36",
- "quote 1.0.17",
- "syn 1.0.90",
+ "proc-macro2 1.0.39",
+ "quote 1.0.18",
+ "syn 1.0.95",
+]
+
+[[package]]
+name = "derive_builder_core"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f91d4cfa921f1c05904dc3c57b4a32c38aed3340cce209f3a6fd1478babafc4"
+dependencies = [
+ "darling 0.14.1",
+ "proc-macro2 1.0.39",
+ "quote 1.0.18",
+ "syn 1.0.95",
]
[[package]]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58a94ace95092c5acb1e97a7e846b310cfbd499652f72297da7493f618a98d73"
dependencies = [
- "derive_builder_core",
- "syn 1.0.90",
+ "derive_builder_core 0.10.2",
+ "syn 1.0.95",
+]
+
+[[package]]
+name = "derive_builder_macro"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f0314b72bed045f3a68671b3c86328386762c93f82d98c65c3cb5e5f573dd68"
+dependencies = [
+ "derive_builder_core 0.11.2",
+ "syn 1.0.95",
]
[[package]]
checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
dependencies = [
"convert_case",
- "proc-macro2 1.0.36",
- "quote 1.0.17",
+ "proc-macro2 1.0.39",
+ "quote 1.0.18",
"rustc_version",
- "syn 1.0.90",
+ "syn 1.0.95",
]
[[package]]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45f5098f628d02a7a0f68ddba586fb61e80edec3bdc1be3b921f4ceec60858d3"
dependencies = [
- "proc-macro2 1.0.36",
- "quote 1.0.17",
- "syn 1.0.90",
+ "proc-macro2 1.0.39",
+ "quote 1.0.18",
+ "syn 1.0.95",
]
[[package]]
"winapi",
]
-[[package]]
-name = "dissimilar"
-version = "1.0.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "31ad93652f40969dead8d4bf897a41e9462095152eb21c56e5830537e41179dd"
-
[[package]]
name = "doku"
version = "0.11.0"
checksum = "603fe9f91b4d0e11036df029aeaeffa90b8f97e700104d5d24abb053bf9ba858"
dependencies = [
"darling 0.13.1",
- "proc-macro2 1.0.36",
- "quote 1.0.17",
- "syn 1.0.90",
+ "proc-macro2 1.0.39",
+ "quote 1.0.18",
+ "syn 1.0.95",
]
[[package]]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512"
dependencies = [
- "proc-macro2 1.0.36",
- "quote 1.0.17",
- "syn 1.0.90",
+ "proc-macro2 1.0.39",
+ "quote 1.0.18",
+ "syn 1.0.95",
]
[[package]]
"wasi 0.10.0+wasi-snapshot-preview1",
]
-[[package]]
-name = "glob"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
-
[[package]]
name = "h2"
version = "0.3.12"
"log",
"mac",
"markup5ever",
- "proc-macro2 1.0.36",
- "quote 1.0.17",
- "syn 1.0.90",
+ "proc-macro2 1.0.39",
+ "quote 1.0.18",
+ "syn 1.0.95",
]
[[package]]
name = "lemmy_api"
version = "0.16.5"
dependencies = [
+ "activitypub_federation",
"actix-web",
"anyhow",
"async-trait",
"diesel",
"lemmy_api_common",
"lemmy_apub",
- "lemmy_apub_lib",
"lemmy_db_schema",
"lemmy_db_views",
"lemmy_db_views_actor",
name = "lemmy_api_crud"
version = "0.16.5"
dependencies = [
+ "activitypub_federation",
"actix-web",
"async-trait",
"bcrypt",
"lemmy_api_common",
"lemmy_apub",
- "lemmy_apub_lib",
"lemmy_db_schema",
"lemmy_db_views",
"lemmy_db_views_actor",
name = "lemmy_apub"
version = "0.16.5"
dependencies = [
+ "activitypub_federation",
"activitystreams-kinds",
"actix",
"actix-rt",
"http-signature-normalization-actix",
"itertools",
"lemmy_api_common",
- "lemmy_apub_lib",
"lemmy_db_schema",
"lemmy_db_views",
"lemmy_db_views_actor",
"uuid",
]
-[[package]]
-name = "lemmy_apub_lib"
-version = "0.16.5"
-dependencies = [
- "actix-web",
- "anyhow",
- "async-trait",
- "background-jobs",
- "base64",
- "chrono",
- "diesel",
- "http",
- "http-signature-normalization-actix",
- "http-signature-normalization-reqwest",
- "lemmy_apub_lib_derive",
- "lemmy_utils",
- "once_cell",
- "openssl",
- "reqwest",
- "reqwest-middleware",
- "serde",
- "serde_json",
- "sha2",
- "tracing",
- "url",
-]
-
-[[package]]
-name = "lemmy_apub_lib_derive"
-version = "0.16.5"
-dependencies = [
- "proc-macro2 1.0.36",
- "quote 1.0.17",
- "syn 1.0.90",
- "trybuild",
-]
-
[[package]]
name = "lemmy_db_schema"
version = "0.16.5"
dependencies = [
+ "activitypub_federation",
"bcrypt",
"chrono",
"diesel",
"diesel-derive-newtype",
"diesel_migrations",
- "lemmy_apub_lib",
"lemmy_utils",
"once_cell",
"regex",
name = "lemmy_server"
version = "0.16.5"
dependencies = [
+ "activitypub_federation",
"actix",
"actix-web",
"clokwerk",
"lemmy_api_common",
"lemmy_api_crud",
"lemmy_apub",
- "lemmy_apub_lib",
"lemmy_db_schema",
"lemmy_routes",
"lemmy_utils",
"parking_lot 0.12.0",
"reqwest",
"reqwest-middleware",
+ "reqwest-retry",
"reqwest-tracing",
"serde",
"tracing",
checksum = "9753f12909fd8d923f75ae5c3258cae1ed3c8ec052e1b38c93c21a6d157f789c"
dependencies = [
"migrations_internals",
- "proc-macro2 1.0.36",
- "quote 1.0.17",
- "syn 1.0.90",
+ "proc-macro2 1.0.39",
+ "quote 1.0.18",
+ "syn 1.0.95",
]
[[package]]
dependencies = [
"pest",
"pest_meta",
- "proc-macro2 1.0.36",
- "quote 1.0.17",
- "syn 1.0.90",
+ "proc-macro2 1.0.39",
+ "quote 1.0.18",
+ "syn 1.0.95",
]
[[package]]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "744b6f092ba29c3650faf274db506afd39944f48420f6c86b17cfe0ee1cb36bb"
dependencies = [
- "proc-macro2 1.0.36",
- "quote 1.0.17",
- "syn 1.0.90",
+ "proc-macro2 1.0.39",
+ "quote 1.0.18",
+ "syn 1.0.95",
]
[[package]]
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
- "proc-macro2 1.0.36",
- "quote 1.0.17",
- "syn 1.0.90",
+ "proc-macro2 1.0.39",
+ "quote 1.0.18",
+ "syn 1.0.95",
"version_check",
]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
- "proc-macro2 1.0.36",
- "quote 1.0.17",
+ "proc-macro2 1.0.39",
+ "quote 1.0.18",
"version_check",
]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
dependencies = [
- "unicode-xid 0.1.0",
+ "unicode-xid",
]
[[package]]
name = "proc-macro2"
-version = "1.0.36"
+version = "1.0.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
+checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f"
dependencies = [
- "unicode-xid 0.2.2",
+ "unicode-ident",
]
[[package]]
dependencies = [
"anyhow",
"itertools",
- "proc-macro2 1.0.36",
- "quote 1.0.17",
- "syn 1.0.90",
+ "proc-macro2 1.0.39",
+ "quote 1.0.18",
+ "syn 1.0.95",
]
[[package]]
[[package]]
name = "quote"
-version = "1.0.17"
+version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "632d02bff7f874a36f33ea8bb416cd484b90cc66c1194b1a1110d067a7013f58"
+checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
dependencies = [
- "proc-macro2 1.0.36",
+ "proc-macro2 1.0.39",
]
[[package]]
[[package]]
name = "reqwest-middleware"
-version = "0.1.5"
+version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b58621b8223cfc85b63d38b8d335c69b96a666d9b7561aa30a3b070ce1df31c"
+checksum = "69539cea4148dce683bec9dc95be3f0397a9bb2c248a49c8296a9d21659a8cdd"
dependencies = [
"anyhow",
"async-trait",
"thiserror",
]
+[[package]]
+name = "reqwest-retry"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce246a729eaa6aff5e215aee42845bf5fed9893cc6cd51aeeb712f34e04dd9f3"
+dependencies = [
+ "anyhow",
+ "async-trait",
+ "chrono",
+ "futures",
+ "http",
+ "hyper",
+ "reqwest",
+ "reqwest-middleware",
+ "retry-policies",
+ "task-local-extensions",
+ "tokio",
+ "tracing",
+]
+
[[package]]
name = "reqwest-tracing"
version = "0.2.1"
"tracing-opentelemetry 0.16.0",
]
+[[package]]
+name = "retry-policies"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "47f9e19b18c6cdd796cc70aea8a9ea5ee7b813be611c6589e3624fcdbfd05f9d"
+dependencies = [
+ "anyhow",
+ "chrono",
+ "rand 0.8.5",
+]
+
[[package]]
name = "rgb"
version = "0.8.32"
dependencies = [
"convert_case",
"lazy_static",
- "proc-macro2 1.0.36",
- "quote 1.0.17",
+ "proc-macro2 1.0.39",
+ "quote 1.0.18",
"regex",
"tinyjson",
]
checksum = "36e19e299f301be17927a7c05b8fa1c621e3227e6c3a0da65492701642901ff7"
dependencies = [
"atom_syndication",
- "derive_builder",
+ "derive_builder 0.10.2",
"never",
"quick-xml",
]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9"
dependencies = [
- "proc-macro2 1.0.36",
- "quote 1.0.17",
- "syn 1.0.90",
+ "proc-macro2 1.0.39",
+ "quote 1.0.18",
+ "syn 1.0.95",
]
[[package]]
checksum = "12e47be9471c72889ebafb5e14d5ff930d89ae7a67bbdb5f8abb564f845a927e"
dependencies = [
"darling 0.13.1",
- "proc-macro2 1.0.36",
- "quote 1.0.17",
- "syn 1.0.90",
+ "proc-macro2 1.0.39",
+ "quote 1.0.18",
+ "syn 1.0.95",
]
[[package]]
checksum = "2881bccd7d60fb32dfa3d7b3136385312f8ad75e2674aab2852867a09790cae8"
dependencies = [
"proc-macro-error",
- "proc-macro2 1.0.36",
- "quote 1.0.17",
+ "proc-macro2 1.0.39",
+ "quote 1.0.18",
"rustversion",
- "syn 1.0.90",
+ "syn 1.0.95",
]
[[package]]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "133659a15339456eeeb07572eb02a91c91e9815e9cbc89566944d2c8d3efdbf6"
dependencies = [
- "proc-macro2 1.0.36",
- "quote 1.0.17",
- "syn 1.0.90",
+ "proc-macro2 1.0.39",
+ "quote 1.0.18",
+ "syn 1.0.95",
]
[[package]]
dependencies = [
"phf_generator 0.10.0",
"phf_shared 0.10.0",
- "proc-macro2 1.0.36",
- "quote 1.0.17",
+ "proc-macro2 1.0.39",
+ "quote 1.0.18",
]
[[package]]
checksum = "6878079b17446e4d3eba6192bb0a2950d5b14f0ed8424b852310e5a94345d0ef"
dependencies = [
"heck 0.4.0",
- "proc-macro2 1.0.36",
- "quote 1.0.17",
+ "proc-macro2 1.0.39",
+ "quote 1.0.18",
"rustversion",
- "syn 1.0.90",
+ "syn 1.0.95",
]
[[package]]
dependencies = [
"proc-macro2 0.4.30",
"quote 0.6.13",
- "unicode-xid 0.1.0",
+ "unicode-xid",
]
[[package]]
name = "syn"
-version = "1.0.90"
+version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "704df27628939572cd88d33f171cd6f896f4eaca85252c6e0a72d8d8287ee86f"
+checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942"
dependencies = [
- "proc-macro2 1.0.36",
- "quote 1.0.17",
- "unicode-xid 0.2.2",
+ "proc-macro2 1.0.39",
+ "quote 1.0.18",
+ "unicode-ident",
]
[[package]]
"utf-8",
]
-[[package]]
-name = "termcolor"
-version = "1.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
-dependencies = [
- "winapi-util",
-]
-
[[package]]
name = "thiserror"
-version = "1.0.30"
+version = "1.0.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
+checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
-version = "1.0.30"
+version = "1.0.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
+checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
dependencies = [
- "proc-macro2 1.0.36",
- "quote 1.0.17",
- "syn 1.0.90",
+ "proc-macro2 1.0.39",
+ "quote 1.0.18",
+ "syn 1.0.95",
]
[[package]]
[[package]]
name = "tokio"
-version = "1.17.0"
+version = "1.18.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2af73ac49756f3f7c01172e34a23e5d0216f6c32333757c2c61feb2bbff5a5ee"
+checksum = "4903bf0427cf68dddd5aa6a93220756f8be0c34fcfa9f5e6191e103e15a31395"
dependencies = [
"bytes",
"libc",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7"
dependencies = [
- "proc-macro2 1.0.36",
- "quote 1.0.17",
- "syn 1.0.90",
+ "proc-macro2 1.0.39",
+ "quote 1.0.18",
+ "syn 1.0.95",
]
[[package]]
"tracing",
]
-[[package]]
-name = "toml"
-version = "0.5.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
-dependencies = [
- "serde",
-]
-
[[package]]
name = "tonic"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9403f1bafde247186684b230dc6f38b5cd514584e8bec1dd32514be4745fa757"
dependencies = [
- "proc-macro2 1.0.36",
+ "proc-macro2 1.0.39",
"prost-build",
- "quote 1.0.17",
- "syn 1.0.90",
+ "quote 1.0.18",
+ "syn 1.0.95",
]
[[package]]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e65ce065b4b5c53e73bb28912318cb8c9e9ad3921f1d669eb0e68b4c8143a2b"
dependencies = [
- "proc-macro2 1.0.36",
- "quote 1.0.17",
- "syn 1.0.90",
+ "proc-macro2 1.0.39",
+ "quote 1.0.18",
+ "syn 1.0.95",
]
[[package]]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
-[[package]]
-name = "trybuild"
-version = "1.0.57"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2ae8c4cee9b97b861a6e3be1d5acb6f50a86bbb68b1f3a896db8342fb6d0f94c"
-dependencies = [
- "dissimilar",
- "glob",
- "once_cell",
- "serde",
- "serde_derive",
- "serde_json",
- "termcolor",
- "toml",
-]
-
[[package]]
name = "twoway"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f"
+[[package]]
+name = "unicode-ident"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
+
[[package]]
name = "unicode-normalization"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
-[[package]]
-name = "unicode-xid"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
-
[[package]]
name = "unicode_categories"
version = "0.1.1"
"bumpalo",
"lazy_static",
"log",
- "proc-macro2 1.0.36",
- "quote 1.0.17",
- "syn 1.0.90",
+ "proc-macro2 1.0.39",
+ "quote 1.0.18",
+ "syn 1.0.95",
"wasm-bindgen-shared",
]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01"
dependencies = [
- "quote 1.0.17",
+ "quote 1.0.18",
"wasm-bindgen-macro-support",
]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc"
dependencies = [
- "proc-macro2 1.0.36",
- "quote 1.0.17",
- "syn 1.0.90",
+ "proc-macro2 1.0.39",
+ "quote 1.0.18",
+ "syn 1.0.95",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
"crates/api",
"crates/api_crud",
"crates/api_common",
- "crates/apub_lib",
- "crates/apub_lib_derive",
"crates/apub",
"crates/utils",
"crates/db_schema",
lemmy_api = { version = "=0.16.5", path = "./crates/api" }
lemmy_api_crud = { version = "=0.16.5", path = "./crates/api_crud" }
lemmy_apub = { version = "=0.16.5", path = "./crates/apub" }
-lemmy_apub_lib = { version = "=0.16.5", path = "./crates/apub_lib" }
lemmy_utils = { version = "=0.16.5", path = "./crates/utils" }
lemmy_db_schema = { version = "=0.16.5", path = "./crates/db_schema" }
lemmy_api_common = { version = "=0.16.5", path = "crates/api_common" }
lemmy_websocket = { version = "=0.16.5", path = "./crates/websocket" }
lemmy_routes = { version = "=0.16.5", path = "./crates/routes" }
+activitypub_federation = "0.1.0"
diesel = "1.4.8"
diesel_migrations = "1.4.0"
serde = { version = "1.0.136", features = ["derive"] }
clokwerk = "0.3.5"
doku = "0.11.0"
parking_lot = "0.12.0"
+reqwest-retry = "0.1.5"
console-subscriber = { version = "0.1.3", optional = true }
opentelemetry = { version = "0.17.0", features = ["rt-tokio"], optional = true }
opentelemetry-otlp = { version = "0.10.0", optional = true }
});
test('Remove a comment from admin and community on different instance', async () => {
- let alphaUser = await registerUser(alpha);
+ let alpha_user = await registerUser(alpha);
let newAlphaApi: API = {
client: alpha.client,
- auth: alphaUser.jwt,
+ auth: alpha_user.jwt,
};
// New alpha user creates a community, post, and comment.
// create a test user
let alphaUserJwt = await registerUser(alpha);
expect(alphaUserJwt).toBeDefined();
- let alphaUser: API = {
+ let alpha_user: API = {
client: alpha.client,
auth: alphaUserJwt.jwt,
};
- let alphaUserActorId = (await getSite(alphaUser)).my_user.local_user_view.person.actor_id;
+ let alphaUserActorId = (await getSite(alpha_user)).my_user.local_user_view.person.actor_id;
expect(alphaUserActorId).toBeDefined();
- let alphaPerson = (await resolvePerson(alphaUser, alphaUserActorId)).person;
+ let alphaPerson = (await resolvePerson(alpha_user, alphaUserActorId)).person;
expect(alphaPerson).toBeDefined();
// alpha makes post in beta community, it federates to beta instance
- let postRes1 = await createPost(alphaUser, betaCommunity.community.id);
+ let postRes1 = await createPost(alpha_user, betaCommunity.community.id);
let searchBeta1 = await searchPostLocal(beta, postRes1.post_view.post);
expect(searchBeta1.posts[0]).toBeDefined();
expect(unBanAlpha.banned).toBe(false);
// alpha makes new post in beta community, it federates
- let postRes2 = await createPost(alphaUser, betaCommunity.community.id);
+ let postRes2 = await createPost(alpha_user, betaCommunity.community.id);
let searchBeta3 = await searchPostLocal(beta, postRes2.post_view.post);
expect(searchBeta3.posts[0]).toBeDefined();
[dependencies]
lemmy_apub = { version = "=0.16.5", path = "../apub" }
-lemmy_apub_lib = { version = "=0.16.5", path = "../apub_lib" }
lemmy_utils = { version = "=0.16.5", path = "../utils" }
lemmy_db_schema = { version = "=0.16.5", path = "../db_schema", features = ["full"] }
lemmy_db_views = { version = "=0.16.5", path = "../db_views", features = ["full"] }
lemmy_db_views_actor = { version = "=0.16.5", path = "../db_views_actor", features = ["full"] }
lemmy_api_common = { version = "=0.16.5", path = "../api_common", features = ["full"] }
lemmy_websocket = { version = "=0.16.5", path = "../websocket" }
+activitypub_federation = "0.1.0"
diesel = "1.4.8"
bcrypt = "0.12.1"
chrono = { version = "0.4.19", features = ["serde"], default-features = false }
traits::Likeable,
};
use lemmy_db_views::structs::{CommentView, LocalUserView};
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::{send::send_comment_ws_message, LemmyContext, UserOperation};
use std::convert::TryInto;
};
use lemmy_db_schema::source::comment::Comment;
use lemmy_db_views::structs::CommentView;
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::LemmyContext;
#[async_trait::async_trait(?Send)]
traits::Saveable,
};
use lemmy_db_views::structs::CommentView;
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::LemmyContext;
#[async_trait::async_trait(?Send)]
use crate::Perform;
+use activitypub_federation::core::object_id::ObjectId;
use actix_web::web::Data;
use lemmy_api_common::{
comment::{CommentReportResponse, CreateCommentReport},
utils::{blocking, check_community_ban, get_local_user_view_from_jwt},
};
use lemmy_apub::protocol::activities::community::report::Report;
-use lemmy_apub_lib::object_id::ObjectId;
use lemmy_db_schema::{
source::comment_report::{CommentReport, CommentReportForm},
traits::Reportable,
};
use lemmy_db_views::structs::{CommentReportView, CommentView};
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::{messages::SendModRoomMessage, LemmyContext, UserOperation};
/// Creates a comment report and notifies the moderators of the community
utils::{blocking, get_local_user_view_from_jwt},
};
use lemmy_db_views::comment_report_view::CommentReportQueryBuilder;
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::LemmyContext;
/// Lists comment reports for a community if an id is supplied
};
use lemmy_db_schema::{source::comment_report::CommentReport, traits::Reportable};
use lemmy_db_views::structs::CommentReportView;
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::{messages::SendModRoomMessage, LemmyContext, UserOperation};
/// Resolves or unresolves a comment report and notifies the moderators of the community
traits::{Crud, Joinable},
};
use lemmy_db_views_actor::structs::CommunityModeratorView;
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::{messages::SendCommunityRoomMessage, LemmyContext, UserOperation};
#[async_trait::async_trait(?Send)]
traits::{Bannable, Crud, Followable},
};
use lemmy_db_views_actor::structs::PersonViewSafe;
-use lemmy_utils::{utils::naive_from_unix, ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, utils::naive_from_unix, ConnectionId};
use lemmy_websocket::{messages::SendCommunityRoomMessage, LemmyContext, UserOperation};
#[async_trait::async_trait(?Send)]
traits::{Blockable, Crud, Followable},
};
use lemmy_db_views_actor::structs::CommunityView;
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::LemmyContext;
#[async_trait::async_trait(?Send)]
traits::{Crud, Followable},
};
use lemmy_db_views_actor::structs::CommunityView;
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::LemmyContext;
#[async_trait::async_trait(?Send)]
traits::Crud,
utils::naive_now,
};
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::{send::send_community_ws_message, LemmyContext, UserOperationCrud};
#[async_trait::async_trait(?Send)]
traits::{Crud, Joinable},
};
use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView, PersonViewSafe};
-use lemmy_utils::{location_info, ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, location_info, ConnectionId};
use lemmy_websocket::LemmyContext;
// TODO: we dont do anything for federation here, it should be updated the next time the community
use actix_web::{web, web::Data};
use captcha::Captcha;
use lemmy_api_common::{comment::*, community::*, person::*, post::*, site::*, websocket::*};
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::{serialize_websocket_message, LemmyContext, UserOperation};
use serde::Deserialize;
traits::Crud,
};
use lemmy_db_views_actor::structs::PersonViewSafe;
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::{messages::SendAllMessage, LemmyContext, UserOperation};
#[async_trait::async_trait(?Send)]
traits::Crud,
};
use lemmy_db_views_actor::structs::PersonViewSafe;
-use lemmy_utils::{utils::naive_from_unix, ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, utils::naive_from_unix, ConnectionId};
use lemmy_websocket::{messages::SendAllMessage, LemmyContext, UserOperation};
#[async_trait::async_trait(?Send)]
traits::Blockable,
};
use lemmy_db_views_actor::structs::PersonViewSafe;
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::LemmyContext;
#[async_trait::async_trait(?Send)]
utils::{blocking, get_local_user_view_from_jwt, password_length_check},
};
use lemmy_db_schema::source::local_user::LocalUser;
-use lemmy_utils::{claims::Claims, ConnectionId, LemmyError};
+use lemmy_utils::{claims::Claims, error::LemmyError, ConnectionId};
use lemmy_websocket::LemmyContext;
#[async_trait::async_trait(?Send)]
local_user::LocalUser,
password_reset_request::PasswordResetRequest,
};
-use lemmy_utils::{claims::Claims, ConnectionId, LemmyError};
+use lemmy_utils::{claims::Claims, error::LemmyError, ConnectionId};
use lemmy_websocket::LemmyContext;
#[async_trait::async_trait(?Send)]
use chrono::Duration;
use lemmy_api_common::person::{CaptchaResponse, GetCaptcha, GetCaptchaResponse};
use lemmy_db_schema::utils::naive_now;
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::{messages::CaptchaItem, LemmyContext};
#[async_trait::async_trait(?Send)]
utils::{blocking, get_local_user_view_from_jwt, is_admin},
};
use lemmy_db_views_actor::structs::PersonViewSafe;
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::LemmyContext;
#[async_trait::async_trait(?Send)]
};
use lemmy_db_schema::source::site::Site;
use lemmy_db_views::structs::LocalUserView;
-use lemmy_utils::{claims::Claims, ConnectionId, LemmyError};
+use lemmy_utils::{claims::Claims, error::LemmyError, ConnectionId};
use lemmy_websocket::LemmyContext;
#[async_trait::async_trait(?Send)]
utils::{blocking, get_local_user_view_from_jwt},
};
use lemmy_db_views_actor::person_mention_view::PersonMentionQueryBuilder;
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::LemmyContext;
#[async_trait::async_trait(?Send)]
utils::{blocking, get_local_user_view_from_jwt},
};
use lemmy_db_views::comment_view::CommentQueryBuilder;
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::LemmyContext;
#[async_trait::async_trait(?Send)]
private_message::PrivateMessage,
};
use lemmy_db_views::comment_view::CommentQueryBuilder;
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::LemmyContext;
#[async_trait::async_trait(?Send)]
};
use lemmy_db_schema::{source::person_mention::PersonMention, traits::Crud};
use lemmy_db_views_actor::structs::PersonMentionView;
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::LemmyContext;
#[async_trait::async_trait(?Send)]
};
use lemmy_db_views::structs::{CommentView, PrivateMessageView};
use lemmy_db_views_actor::structs::PersonMentionView;
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::LemmyContext;
#[async_trait::async_trait(?Send)]
utils::{blocking, get_local_user_view_from_jwt},
};
use lemmy_db_views::structs::{CommentReportView, PostReportView};
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::LemmyContext;
#[async_trait::async_trait(?Send)]
utils::{blocking, send_password_reset_email},
};
use lemmy_db_views::structs::LocalUserView;
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::LemmyContext;
#[async_trait::async_trait(?Send)]
};
use lemmy_utils::{
claims::Claims,
+ error::LemmyError,
utils::{is_valid_display_name, is_valid_matrix_id},
ConnectionId,
- LemmyError,
};
use lemmy_websocket::LemmyContext;
traits::Crud,
};
use lemmy_db_views::structs::LocalUserView;
-use lemmy_utils::LemmyError;
+use lemmy_utils::error::LemmyError;
use lemmy_websocket::LemmyContext;
#[async_trait::async_trait(?Send)]
post::{GetSiteMetadata, GetSiteMetadataResponse},
request::fetch_site_metadata,
};
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::LemmyContext;
#[async_trait::async_trait(?Send)]
source::post::{Post, PostLike, PostLikeForm},
traits::{Crud, Likeable},
};
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperation};
#[async_trait::async_trait(?Send)]
},
traits::Crud,
};
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperation};
#[async_trait::async_trait(?Send)]
utils::{blocking, get_local_user_view_from_jwt, mark_post_as_read, mark_post_as_unread},
};
use lemmy_db_views::structs::PostView;
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::LemmyContext;
#[async_trait::async_trait(?Send)]
traits::Saveable,
};
use lemmy_db_views::structs::PostView;
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::LemmyContext;
#[async_trait::async_trait(?Send)]
},
traits::Crud,
};
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperation};
#[async_trait::async_trait(?Send)]
use crate::Perform;
+use activitypub_federation::core::object_id::ObjectId;
use actix_web::web::Data;
use lemmy_api_common::{
post::{CreatePostReport, PostReportResponse},
utils::{blocking, check_community_ban, get_local_user_view_from_jwt},
};
use lemmy_apub::protocol::activities::community::report::Report;
-use lemmy_apub_lib::object_id::ObjectId;
use lemmy_db_schema::{
source::post_report::{PostReport, PostReportForm},
traits::Reportable,
};
use lemmy_db_views::structs::{PostReportView, PostView};
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::{messages::SendModRoomMessage, LemmyContext, UserOperation};
/// Creates a post report and notifies the moderators of the community
utils::{blocking, get_local_user_view_from_jwt},
};
use lemmy_db_views::post_report_view::PostReportQueryBuilder;
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::LemmyContext;
/// Lists post reports for a community if an id is supplied
};
use lemmy_db_schema::{source::post_report::PostReport, traits::Reportable};
use lemmy_db_views::structs::PostReportView;
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::{messages::SendModRoomMessage, LemmyContext, UserOperation};
/// Resolves or unresolves a post report and notifies the moderators of the community
utils::{blocking, get_local_user_view_from_jwt},
};
use lemmy_db_schema::{source::private_message::PrivateMessage, traits::Crud};
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperation};
#[async_trait::async_trait(?Send)]
site::{GetSiteConfig, GetSiteConfigResponse},
utils::{get_local_user_view_from_jwt, is_admin},
};
-use lemmy_utils::{settings::structs::Settings, ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, settings::structs::Settings, ConnectionId};
use lemmy_websocket::LemmyContext;
#[async_trait::async_trait(?Send)]
site::{GetSiteConfigResponse, SaveSiteConfig},
utils::{get_local_user_view_from_jwt, is_admin},
};
-use lemmy_utils::{settings::structs::Settings, ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, settings::structs::Settings, ConnectionId};
use lemmy_websocket::LemmyContext;
#[async_trait::async_trait(?Send)]
};
use lemmy_db_views::structs::SiteView;
use lemmy_db_views_actor::structs::PersonViewSafe;
-use lemmy_utils::{version, ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, version, ConnectionId};
use lemmy_websocket::LemmyContext;
#[async_trait::async_trait(?Send)]
ModStickyPostView,
ModTransferCommunityView,
};
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::LemmyContext;
#[async_trait::async_trait(?Send)]
utils::diesel_option_overwrite,
};
use lemmy_db_views::structs::{LocalUserView, RegistrationApplicationView};
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::LemmyContext;
#[async_trait::async_trait(?Send)]
};
use lemmy_db_schema::source::site::Site;
use lemmy_db_views::registration_application_view::RegistrationApplicationQueryBuilder;
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::LemmyContext;
/// Lists registration applications, filterable by undenied only.
};
use lemmy_db_schema::source::site::Site;
use lemmy_db_views::structs::RegistrationApplicationView;
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::LemmyContext;
#[async_trait::async_trait(?Send)]
use lemmy_db_schema::{newtypes::PersonId, utils::DbPool};
use lemmy_db_views::structs::{CommentView, PostView};
use lemmy_db_views_actor::structs::{CommunityView, PersonViewSafe};
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::LemmyContext;
#[async_trait::async_trait(?Send)]
community_view::CommunityQueryBuilder,
person_view::PersonQueryBuilder,
};
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::LemmyContext;
#[async_trait::async_trait(?Send)]
use crate::Perform;
use actix_web::web::Data;
use lemmy_api_common::{utils::get_local_user_view_from_jwt, websocket::*};
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::{
messages::{JoinCommunityRoom, JoinModRoom, JoinPostRoom, JoinUserRoom},
LemmyContext,
use crate::post::SiteMetadata;
use encoding::{all::encodings, DecoderTrap};
-use lemmy_utils::{settings::structs::Settings, version::VERSION, LemmyError, REQWEST_TIMEOUT};
+use lemmy_utils::{error::LemmyError, settings::structs::Settings, version::VERSION};
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
use reqwest_middleware::ClientWithMiddleware;
use serde::Deserialize;
url: &Url,
) -> Result<SiteMetadata, LemmyError> {
info!("Fetching site metadata for url: {}", url);
- let response = client
- .get(url.as_str())
- .timeout(REQWEST_TIMEOUT)
- .send()
- .await?;
+ let response = client.get(url.as_str()).send().await?;
// Can't use .text() here, because it only checks the content header, not the actual bytes
// https://github.com/LemmyNet/lemmy/issues/1964
utf8_percent_encode(image_url.as_str(), NON_ALPHANUMERIC) // TODO this might not be needed
);
- let response = client
- .get(&fetch_url)
- .timeout(REQWEST_TIMEOUT)
- .send()
- .await?;
+ let response = client.get(&fetch_url).send().await?;
let response: PictrsResponse = response.json().await.map_err(LemmyError::from)?;
#[tracing::instrument(skip_all)]
async fn is_image_content_type(client: &ClientWithMiddleware, url: &Url) -> Result<(), LemmyError> {
- let response = client
- .get(url.as_str())
- .timeout(REQWEST_TIMEOUT)
- .send()
- .await?;
+ let response = client.get(url.as_str()).send().await?;
if response
.headers()
.get("Content-Type")
use lemmy_utils::{
claims::Claims,
email::{send_email, translations::Lang},
+ error::LemmyError,
settings::structs::Settings,
utils::generate_random_string,
- LemmyError,
};
use rosetta_i18n::{Language, LanguageId};
use tracing::warn;
[dependencies]
lemmy_apub = { version = "=0.16.5", path = "../apub" }
-lemmy_apub_lib = { version = "=0.16.5", path = "../apub_lib" }
lemmy_utils = { version = "=0.16.5", path = "../utils" }
lemmy_db_schema = { version = "=0.16.5", path = "../db_schema", features = ["full"] }
lemmy_db_views = { version = "=0.16.5", path = "../db_views", features = ["full"] }
lemmy_db_views_actor = { version = "=0.16.5", path = "../db_views_actor", features = ["full"] }
lemmy_api_common = { version = "=0.16.5", path = "../api_common", features = ["full"] }
lemmy_websocket = { version = "=0.16.5", path = "../websocket" }
+activitypub_federation = "0.1.0"
bcrypt = "0.12.1"
serde_json = { version = "1.0.79", features = ["preserve_order"] }
serde = { version = "1.0.136", features = ["derive"] }
};
use lemmy_db_views::structs::CommentView;
use lemmy_utils::{
+ error::LemmyError,
utils::{remove_slurs, scrape_text_for_mentions},
ConnectionId,
- LemmyError,
};
use lemmy_websocket::{
send::{send_comment_ws_message, send_local_notifs},
traits::Crud,
};
use lemmy_db_views::structs::CommentView;
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::{
send::{send_comment_ws_message, send_local_notifs},
LemmyContext,
use lemmy_apub::{fetcher::resolve_actor_identifier, objects::community::ApubCommunity};
use lemmy_db_schema::{source::community::Community, traits::DeleteableOrRemoveable};
use lemmy_db_views::comment_view::CommentQueryBuilder;
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::LemmyContext;
#[async_trait::async_trait(?Send)]
utils::{blocking, check_private_instance, get_local_user_view_from_jwt_opt},
};
use lemmy_db_views::structs::CommentView;
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::LemmyContext;
#[async_trait::async_trait(?Send)]
traits::Crud,
};
use lemmy_db_views::structs::CommentView;
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::{
send::{send_comment_ws_message, send_local_notifs},
LemmyContext,
use lemmy_db_schema::source::comment::Comment;
use lemmy_db_views::structs::CommentView;
use lemmy_utils::{
+ error::LemmyError,
utils::{remove_slurs, scrape_text_for_mentions},
ConnectionId,
- LemmyError,
};
use lemmy_websocket::{
send::{send_comment_ws_message, send_local_notifs},
use crate::PerformCrud;
+use activitypub_federation::core::{object_id::ObjectId, signatures::generate_actor_keypair};
use actix_web::web::Data;
use lemmy_api_common::{
community::{CommunityResponse, CreateCommunity},
objects::community::ApubCommunity,
EndpointType,
};
-use lemmy_apub_lib::object_id::ObjectId;
use lemmy_db_schema::{
source::{
community::{
};
use lemmy_db_views_actor::structs::CommunityView;
use lemmy_utils::{
- apub::generate_actor_keypair,
+ error::LemmyError,
utils::{check_slurs, check_slurs_opt, is_valid_actor_name},
ConnectionId,
- LemmyError,
};
use lemmy_websocket::LemmyContext;
&context.settings().get_protocol_and_hostname(),
)?;
let community_actor_id_wrapped = ObjectId::<ApubCommunity>::new(community_actor_id.clone());
- let community_dupe = community_actor_id_wrapped.dereference_local(context).await;
+ let community_dupe = community_actor_id_wrapped
+ .dereference_local::<LemmyError>(context)
+ .await;
if community_dupe.is_ok() {
return Err(LemmyError::from_message("community_already_exists"));
}
use lemmy_apub::activities::deletion::{send_apub_delete_in_community, DeletableObjects};
use lemmy_db_schema::source::community::Community;
use lemmy_db_views_actor::structs::CommunityModeratorView;
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::{send::send_community_ws_message, LemmyContext, UserOperationCrud};
#[async_trait::async_trait(?Send)]
};
use lemmy_db_schema::traits::DeleteableOrRemoveable;
use lemmy_db_views_actor::community_view::CommunityQueryBuilder;
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::LemmyContext;
#[async_trait::async_trait(?Send)]
traits::DeleteableOrRemoveable,
};
use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView};
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::{messages::GetCommunityUsersOnline, LemmyContext};
#[async_trait::async_trait(?Send)]
},
traits::Crud,
};
-use lemmy_utils::{utils::naive_from_unix, ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, utils::naive_from_unix, ConnectionId};
use lemmy_websocket::{send::send_community_ws_message, LemmyContext, UserOperationCrud};
#[async_trait::async_trait(?Send)]
utils::{diesel_option_overwrite_to_url, naive_now},
};
use lemmy_db_views_actor::structs::CommunityModeratorView;
-use lemmy_utils::{utils::check_slurs_opt, ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, utils::check_slurs_opt, ConnectionId};
use lemmy_websocket::{send::send_community_ws_message, LemmyContext, UserOperationCrud};
#[async_trait::async_trait(?Send)]
use actix_web::{web, web::Data};
use lemmy_api_common::{comment::*, community::*, person::*, post::*, site::*};
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::{serialize_websocket_message, LemmyContext, UserOperationCrud};
use serde::Deserialize;
};
use lemmy_db_views_actor::structs::CommunityView;
use lemmy_utils::{
+ error::LemmyError,
utils::{
check_slurs,
check_slurs_opt,
is_valid_post_title,
},
ConnectionId,
- LemmyError,
};
use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperationCrud};
use tracing::{warn, Instrument};
source::{community::Community, post::Post},
traits::Crud,
};
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperationCrud};
#[async_trait::async_trait(?Send)]
ListingType,
};
use lemmy_db_views::post_view::PostQueryBuilder;
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::LemmyContext;
use std::str::FromStr;
use lemmy_db_schema::traits::DeleteableOrRemoveable;
use lemmy_db_views::{comment_view::CommentQueryBuilder, structs::PostView};
use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView};
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::{messages::GetPostUsersOnline, LemmyContext};
#[async_trait::async_trait(?Send)]
},
traits::Crud,
};
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperationCrud};
#[async_trait::async_trait(?Send)]
utils::naive_now,
};
use lemmy_utils::{
+ error::LemmyError,
utils::{check_slurs_opt, clean_optional_text, clean_url_params, is_valid_post_title},
ConnectionId,
- LemmyError,
};
use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperationCrud};
traits::Crud,
};
use lemmy_db_views::structs::LocalUserView;
-use lemmy_utils::{utils::remove_slurs, ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, utils::remove_slurs, ConnectionId};
use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud};
#[async_trait::async_trait(?Send)]
};
use lemmy_apub::activities::deletion::send_apub_delete_private_message;
use lemmy_db_schema::{source::private_message::PrivateMessage, traits::Crud};
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud};
#[async_trait::async_trait(?Send)]
};
use lemmy_db_schema::traits::DeleteableOrRemoveable;
use lemmy_db_views::private_message_view::PrivateMessageQueryBuilder;
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::LemmyContext;
#[async_trait::async_trait(?Send)]
CreateOrUpdateType,
};
use lemmy_db_schema::{source::private_message::PrivateMessage, traits::Crud};
-use lemmy_utils::{utils::remove_slurs, ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, utils::remove_slurs, ConnectionId};
use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud};
#[async_trait::async_trait(?Send)]
use crate::PerformCrud;
+use activitypub_federation::core::signatures::generate_actor_keypair;
use actix_web::web::Data;
use lemmy_api_common::{
site::{CreateSite, SiteResponse},
};
use lemmy_db_views::structs::SiteView;
use lemmy_utils::{
- apub::generate_actor_keypair,
+ error::LemmyError,
settings::structs::Settings,
utils::{check_slurs, check_slurs_opt},
ConnectionId,
- LemmyError,
};
use lemmy_websocket::LemmyContext;
use url::Url;
PersonBlockView,
PersonViewSafe,
};
-use lemmy_utils::{version, ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, version, ConnectionId};
use lemmy_websocket::{messages::GetUsersOnline, LemmyContext};
use tracing::info;
ListingType,
};
use lemmy_db_views::structs::SiteView;
-use lemmy_utils::{utils::check_slurs_opt, ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, utils::check_slurs_opt, ConnectionId};
use lemmy_websocket::{messages::SendAllMessage, LemmyContext, UserOperationCrud};
use std::{default::Default, str::FromStr};
use crate::PerformCrud;
+use activitypub_federation::core::signatures::generate_actor_keypair;
use actix_web::web::Data;
use lemmy_api_common::{
person::{LoginResponse, Register},
use lemmy_db_views::structs::LocalUserView;
use lemmy_db_views_actor::structs::PersonViewSafe;
use lemmy_utils::{
- apub::generate_actor_keypair,
claims::Claims,
+ error::LemmyError,
utils::{check_slurs, is_valid_actor_name},
ConnectionId,
- LemmyError,
};
use lemmy_websocket::{messages::CheckCaptcha, LemmyContext};
utils::{delete_user_account, get_local_user_view_from_jwt},
};
use lemmy_apub::protocol::activities::deletion::delete_user::DeleteUser;
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::LemmyContext;
#[async_trait::async_trait(?Send)]
use lemmy_db_schema::source::person::Person;
use lemmy_db_views::{comment_view::CommentQueryBuilder, post_view::PostQueryBuilder};
use lemmy_db_views_actor::structs::{CommunityModeratorView, PersonViewSafe};
-use lemmy_utils::{ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::LemmyContext;
#[async_trait::async_trait(?Send)]
[dependencies]
lemmy_utils = { version = "=0.16.5", path = "../utils" }
-lemmy_apub_lib = { version = "=0.16.5", path = "../apub_lib" }
lemmy_db_schema = { version = "=0.16.5", path = "../db_schema", features = ["full"] }
lemmy_db_views = { version = "=0.16.5", path = "../db_views", features = ["full"] }
lemmy_db_views_actor = { version = "=0.16.5", path = "../db_views_actor", features = ["full"] }
lemmy_api_common = { version = "=0.16.5", path = "../api_common", features = ["full"] }
lemmy_websocket = { version = "=0.16.5", path = "../websocket" }
+activitypub_federation = "0.1.0"
diesel = "1.4.8"
activitystreams-kinds = "0.2.1"
chrono = { version = "0.4.19", features = ["serde"], default-features = false }
{
"actor": "http://ds9.lemmy.ml/u/lemmy_alpha",
- "to": [
- "http://enterprise.lemmy.ml/c/main"
- ],
+ "to": "http://enterprise.lemmy.ml/c/main",
"object": "http://enterprise.lemmy.ml/post/7",
"summary": "report this post",
"type": "Flag",
{
"id": "http://enterprise.lemmy.ml/activities/create/987d05fa-f637-46d7-85be-13d112bc269f",
"actor": "http://enterprise.lemmy.ml/u/lemmy_beta",
- "to": [
- "http://ds9.lemmy.ml/u/lemmy_alpha"
- ],
+ "to": "http://ds9.lemmy.ml/u/lemmy_alpha",
"object": {
"type": "ChatMessage",
"id": "http://enterprise.lemmy.ml/private_message/1",
"attributedTo": "http://enterprise.lemmy.ml/u/lemmy_beta",
- "to": [
- "http://ds9.lemmy.ml/u/lemmy_alpha"
- ],
+ "to": "http://ds9.lemmy.ml/u/lemmy_alpha",
"content": "hello",
"mediaType": "text/html",
"source": {
"id": "https://enterprise.lemmy.ml/private_message/1621",
"type": "ChatMessage",
"attributedTo": "https://enterprise.lemmy.ml/u/picard",
- "to": [
- "https://queer.hacktivis.me/users/lanodan"
- ],
+ "to": "https://queer.hacktivis.me/users/lanodan",
"content": "<p>Hello hello, testing</p>\n",
"mediaType": "text/html",
"source": {
community::{announce::GetCommunity, send_activity_in_community},
generate_activity_id,
send_lemmy_activity,
- verify_activity,
verify_is_public,
verify_mod_action,
verify_person_in_community,
},
activity_lists::AnnouncableActivities,
+ local_instance,
objects::{community::ApubCommunity, person::ApubPerson},
protocol::activities::block::block_user::BlockUser,
+ ActorType,
+};
+use activitypub_federation::{
+ core::object_id::ObjectId,
+ data::Data,
+ traits::ActivityHandler,
+ utils::verify_domains_match,
};
use activitystreams_kinds::{activity::BlockType, public};
use anyhow::anyhow;
use chrono::NaiveDateTime;
use lemmy_api_common::utils::{blocking, remove_user_data, remove_user_data_in_community};
-use lemmy_apub_lib::{
- data::Data,
- object_id::ObjectId,
- traits::{ActivityHandler, ActorType},
- verify::verify_domains_match,
-};
use lemmy_db_schema::{
source::{
community::{
},
traits::{Bannable, Crud, Followable},
};
-use lemmy_utils::{settings::structs::Settings, utils::convert_datetime, LemmyError};
+use lemmy_utils::{error::LemmyError, settings::structs::Settings, utils::convert_datetime};
use lemmy_websocket::LemmyContext;
+use url::Url;
impl BlockUser {
pub(in crate::activities::block) async fn new(
#[async_trait::async_trait(?Send)]
impl ActivityHandler for BlockUser {
type DataType = LemmyContext;
+ type Error = LemmyError;
+
+ fn id(&self) -> &Url {
+ &self.id
+ }
+
+ fn actor(&self) -> &Url {
+ self.actor.inner()
+ }
#[tracing::instrument(skip_all)]
async fn verify(
request_counter: &mut i32,
) -> Result<(), LemmyError> {
verify_is_public(&self.to, &self.cc)?;
- verify_activity(&self.id, self.actor.inner(), &context.settings())?;
match self
.target
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, local_instance(context), request_counter)
.await?
{
SiteOrCommunity::Site(site) => {
let expires = self.expires.map(|u| u.naive_local());
let mod_person = self
.actor
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, local_instance(context), request_counter)
.await?;
let blocked_person = self
.object
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, local_instance(context), request_counter)
.await?;
let target = self
.target
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, local_instance(context), request_counter)
.await?;
match target {
SiteOrCommunity::Site(_site) => {
) -> Result<ApubCommunity, LemmyError> {
let target = self
.target
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, local_instance(context), request_counter)
.await?;
match target {
SiteOrCommunity::Community(c) => Ok(c),
use crate::{
objects::{community::ApubCommunity, instance::ApubSite, person::ApubPerson},
protocol::objects::{group::Group, instance::Instance},
+ ActorType,
};
+use activitypub_federation::{core::object_id::ObjectId, traits::ApubObject};
use chrono::NaiveDateTime;
use lemmy_api_common::utils::blocking;
-use lemmy_apub_lib::{
- object_id::ObjectId,
- traits::{ActorType, ApubObject},
-};
use lemmy_db_schema::{source::site::Site, utils::DbPool};
-use lemmy_utils::LemmyError;
+use lemmy_utils::error::LemmyError;
use lemmy_websocket::LemmyContext;
use serde::Deserialize;
use url::Url;
type DataType = LemmyContext;
type ApubType = InstanceOrGroup;
type DbType = ();
- type TombstoneType = ();
+ type Error = LemmyError;
#[tracing::instrument(skip_all)]
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
unimplemented!()
}
- fn to_tombstone(&self) -> Result<Self::TombstoneType, LemmyError> {
- unimplemented!()
- }
-
#[tracing::instrument(skip_all)]
async fn verify(
apub: &Self::ApubType,
community::{announce::GetCommunity, send_activity_in_community},
generate_activity_id,
send_lemmy_activity,
- verify_activity,
verify_is_public,
},
activity_lists::AnnouncableActivities,
+ local_instance,
objects::{community::ApubCommunity, person::ApubPerson},
protocol::activities::block::{block_user::BlockUser, undo_block_user::UndoBlockUser},
+ ActorType,
};
-use activitystreams_kinds::{activity::UndoType, public};
-use lemmy_api_common::utils::blocking;
-use lemmy_apub_lib::{
+use activitypub_federation::{
+ core::object_id::ObjectId,
data::Data,
- object_id::ObjectId,
- traits::{ActivityHandler, ActorType},
- verify::verify_domains_match,
+ traits::ActivityHandler,
+ utils::verify_domains_match,
};
+use activitystreams_kinds::{activity::UndoType, public};
+use lemmy_api_common::utils::blocking;
use lemmy_db_schema::{
source::{
community::{CommunityPersonBan, CommunityPersonBanForm},
},
traits::{Bannable, Crud},
};
-use lemmy_utils::LemmyError;
+use lemmy_utils::error::LemmyError;
use lemmy_websocket::LemmyContext;
+use url::Url;
impl UndoBlockUser {
#[tracing::instrument(skip_all)]
#[async_trait::async_trait(?Send)]
impl ActivityHandler for UndoBlockUser {
type DataType = LemmyContext;
+ type Error = LemmyError;
+
+ fn id(&self) -> &Url {
+ &self.id
+ }
+
+ fn actor(&self) -> &Url {
+ self.actor.inner()
+ }
#[tracing::instrument(skip_all)]
async fn verify(
request_counter: &mut i32,
) -> Result<(), LemmyError> {
verify_is_public(&self.to, &self.cc)?;
- verify_activity(&self.id, self.actor.inner(), &context.settings())?;
verify_domains_match(self.actor.inner(), self.object.actor.inner())?;
self.object.verify(context, request_counter).await?;
Ok(())
context: &Data<LemmyContext>,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
+ let instance = local_instance(context);
let expires = self.object.expires.map(|u| u.naive_local());
let mod_person = self
.actor
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, instance, request_counter)
.await?;
let blocked_person = self
.object
.object
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, instance, request_counter)
.await?;
match self
.object
.target
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, instance, request_counter)
.await?
{
SiteOrCommunity::Site(_site) => {
send_activity_in_community,
},
generate_activity_id,
- verify_activity,
verify_add_remove_moderator_target,
verify_is_public,
verify_mod_action,
},
activity_lists::AnnouncableActivities,
generate_moderators_url,
+ local_instance,
objects::{community::ApubCommunity, person::ApubPerson},
protocol::activities::community::add_mod::AddMod,
+ ActorType,
};
+use activitypub_federation::{core::object_id::ObjectId, data::Data, traits::ActivityHandler};
use activitystreams_kinds::{activity::AddType, public};
use lemmy_api_common::utils::blocking;
-use lemmy_apub_lib::{
- data::Data,
- object_id::ObjectId,
- traits::{ActivityHandler, ActorType},
-};
use lemmy_db_schema::{
source::{
community::{CommunityModerator, CommunityModeratorForm},
},
traits::{Crud, Joinable},
};
-use lemmy_utils::LemmyError;
+use lemmy_utils::error::LemmyError;
use lemmy_websocket::LemmyContext;
+use url::Url;
impl AddMod {
#[tracing::instrument(skip_all)]
#[async_trait::async_trait(?Send)]
impl ActivityHandler for AddMod {
type DataType = LemmyContext;
+ type Error = LemmyError;
+
+ fn id(&self) -> &Url {
+ &self.id
+ }
+
+ fn actor(&self) -> &Url {
+ self.actor.inner()
+ }
#[tracing::instrument(skip_all)]
async fn verify(
request_counter: &mut i32,
) -> Result<(), LemmyError> {
verify_is_public(&self.to, &self.cc)?;
- verify_activity(&self.id, self.actor.inner(), &context.settings())?;
let community = self.get_community(context, request_counter).await?;
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
verify_mod_action(
let community = self.get_community(context, request_counter).await?;
let new_mod = self
.object
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, local_instance(context), request_counter)
.await?;
// If we had to refetch the community while parsing the activity, then the new mod has already
// write mod log
let actor = self
.actor
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, local_instance(context), request_counter)
.await?;
let form = ModAddCommunityForm {
mod_person_id: actor.id,
use crate::{
- activities::{generate_activity_id, send_lemmy_activity, verify_activity, verify_is_public},
+ activities::{generate_activity_id, send_lemmy_activity, verify_is_public},
activity_lists::AnnouncableActivities,
- http::ActivityCommonFields,
insert_activity,
objects::community::ApubCommunity,
protocol::{
activities::{community::announce::AnnounceActivity, CreateOrUpdateType},
IdOrNestedObject,
},
+ ActorType,
};
+use activitypub_federation::{core::object_id::ObjectId, data::Data, traits::ActivityHandler};
use activitystreams_kinds::{activity::AnnounceType, public};
-use lemmy_apub_lib::{
- data::Data,
- object_id::ObjectId,
- traits::{ActivityHandler, ActorType},
-};
-use lemmy_utils::LemmyError;
+use lemmy_utils::error::LemmyError;
use lemmy_websocket::LemmyContext;
use tracing::debug;
+use url::Url;
#[async_trait::async_trait(?Send)]
pub(crate) trait GetCommunity {
#[async_trait::async_trait(?Send)]
impl ActivityHandler for AnnounceActivity {
type DataType = LemmyContext;
+ type Error = LemmyError;
+
+ fn id(&self) -> &Url {
+ &self.id
+ }
+
+ fn actor(&self) -> &Url {
+ self.actor.inner()
+ }
#[tracing::instrument(skip_all)]
async fn verify(
&self,
- context: &Data<LemmyContext>,
+ _context: &Data<LemmyContext>,
_request_counter: &mut i32,
) -> Result<(), LemmyError> {
verify_is_public(&self.to, &self.cc)?;
- verify_activity(&self.id, self.actor.inner(), &context.settings())?;
Ok(())
}
AnnouncableActivities::Page(_) => {}
_ => {
let object_value = serde_json::to_value(&object)?;
- let object_data: ActivityCommonFields = serde_json::from_value(object_value.to_owned())?;
-
let insert =
- insert_activity(&object_data.id, object_value, false, true, context.pool()).await?;
+ insert_activity(object.id(), object_value, false, true, context.pool()).await?;
if !insert {
debug!(
"Received duplicate activity in announce {}",
- object_data.id.to_string()
+ object.id().to_string()
);
return Ok(());
}
use crate::{
activities::send_lemmy_activity,
activity_lists::AnnouncableActivities,
+ local_instance,
objects::community::ApubCommunity,
protocol::activities::community::announce::AnnounceActivity,
+ ActorType,
};
-use lemmy_apub_lib::{object_id::ObjectId, traits::ActorType};
-use lemmy_utils::LemmyError;
+use activitypub_federation::core::object_id::ObjectId;
+use lemmy_utils::error::LemmyError;
use lemmy_websocket::LemmyContext;
use url::Url;
) -> Result<ApubCommunity, LemmyError> {
let community_id = Url::parse(&moderators.to_string().replace("/moderators", ""))?;
ObjectId::new(community_id)
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, local_instance(context), request_counter)
.await
}
send_activity_in_community,
},
generate_activity_id,
- verify_activity,
verify_add_remove_moderator_target,
verify_is_public,
verify_mod_action,
},
activity_lists::AnnouncableActivities,
generate_moderators_url,
+ local_instance,
objects::{community::ApubCommunity, person::ApubPerson},
protocol::activities::community::remove_mod::RemoveMod,
+ ActorType,
};
+use activitypub_federation::{core::object_id::ObjectId, data::Data, traits::ActivityHandler};
use activitystreams_kinds::{activity::RemoveType, public};
use lemmy_api_common::utils::blocking;
-use lemmy_apub_lib::{
- data::Data,
- object_id::ObjectId,
- traits::{ActivityHandler, ActorType},
-};
use lemmy_db_schema::{
source::{
community::{CommunityModerator, CommunityModeratorForm},
},
traits::{Crud, Joinable},
};
-use lemmy_utils::LemmyError;
+use lemmy_utils::error::LemmyError;
use lemmy_websocket::LemmyContext;
+use url::Url;
impl RemoveMod {
#[tracing::instrument(skip_all)]
#[async_trait::async_trait(?Send)]
impl ActivityHandler for RemoveMod {
type DataType = LemmyContext;
+ type Error = LemmyError;
+
+ fn id(&self) -> &Url {
+ &self.id
+ }
+
+ fn actor(&self) -> &Url {
+ self.actor.inner()
+ }
#[tracing::instrument(skip_all)]
async fn verify(
request_counter: &mut i32,
) -> Result<(), LemmyError> {
verify_is_public(&self.to, &self.cc)?;
- verify_activity(&self.id, self.actor.inner(), &context.settings())?;
let community = self.get_community(context, request_counter).await?;
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
verify_mod_action(
let community = self.get_community(context, request_counter).await?;
let remove_mod = self
.object
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, local_instance(context), request_counter)
.await?;
let form = CommunityModeratorForm {
// write mod log
let actor = self
.actor
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, local_instance(context), request_counter)
.await?;
let form = ModAddCommunityForm {
mod_person_id: actor.id,
use crate::{
- activities::{
- generate_activity_id,
- send_lemmy_activity,
- verify_activity,
- verify_person_in_community,
- },
+ activities::{generate_activity_id, send_lemmy_activity, verify_person_in_community},
+ local_instance,
objects::{community::ApubCommunity, person::ApubPerson},
protocol::activities::community::report::Report,
+ ActorType,
PostOrComment,
};
+use activitypub_federation::{core::object_id::ObjectId, data::Data, traits::ActivityHandler};
use activitystreams_kinds::activity::FlagType;
use lemmy_api_common::{comment::CommentReportResponse, post::PostReportResponse, utils::blocking};
-use lemmy_apub_lib::{
- data::Data,
- object_id::ObjectId,
- traits::{ActivityHandler, ActorType},
-};
use lemmy_db_schema::{
source::{
comment_report::{CommentReport, CommentReportForm},
traits::Reportable,
};
use lemmy_db_views::structs::{CommentReportView, PostReportView};
-use lemmy_utils::LemmyError;
+use lemmy_utils::error::LemmyError;
use lemmy_websocket::{messages::SendModRoomMessage, LemmyContext, UserOperation};
+use url::Url;
impl Report {
#[tracing::instrument(skip_all)]
reason: String,
context: &LemmyContext,
) -> Result<(), LemmyError> {
- let community = community_id.dereference_local(context).await?;
+ let community = community_id
+ .dereference_local::<LemmyError>(context)
+ .await?;
let kind = FlagType::Flag;
let id = generate_activity_id(
kind.clone(),
)?;
let report = Report {
actor: ObjectId::new(actor.actor_id()),
- to: [ObjectId::new(community.actor_id())],
+ to: ObjectId::new(community.actor_id()),
object: object_id,
summary: reason,
kind,
#[async_trait::async_trait(?Send)]
impl ActivityHandler for Report {
type DataType = LemmyContext;
+ type Error = LemmyError;
+
+ fn id(&self) -> &Url {
+ &self.id
+ }
+
+ fn actor(&self) -> &Url {
+ self.actor.inner()
+ }
#[tracing::instrument(skip_all)]
async fn verify(
context: &Data<LemmyContext>,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
- verify_activity(&self.id, self.actor.inner(), &context.settings())?;
- let community = self.to[0]
- .dereference(context, context.client(), request_counter)
+ let community = self
+ .to
+ .dereference::<LemmyError>(context, local_instance(context), request_counter)
.await?;
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
Ok(())
) -> Result<(), LemmyError> {
let actor = self
.actor
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, local_instance(context), request_counter)
.await?;
match self
.object
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, local_instance(context), request_counter)
.await?
{
PostOrComment::Post(post) => {
activities::{
community::{announce::GetCommunity, send_activity_in_community},
generate_activity_id,
- verify_activity,
verify_is_public,
verify_mod_action,
verify_person_in_community,
},
activity_lists::AnnouncableActivities,
+ local_instance,
objects::{community::ApubCommunity, person::ApubPerson},
protocol::activities::community::update::UpdateCommunity,
+ ActorType,
};
-use activitystreams_kinds::{activity::UpdateType, public};
-use lemmy_api_common::utils::blocking;
-use lemmy_apub_lib::{
+use activitypub_federation::{
+ core::object_id::ObjectId,
data::Data,
- object_id::ObjectId,
- traits::{ActivityHandler, ActorType, ApubObject},
+ traits::{ActivityHandler, ApubObject},
};
+use activitystreams_kinds::{activity::UpdateType, public};
+use lemmy_api_common::utils::blocking;
use lemmy_db_schema::{
source::community::{Community, CommunityForm},
traits::Crud,
};
-use lemmy_utils::LemmyError;
+use lemmy_utils::error::LemmyError;
use lemmy_websocket::{send::send_community_ws_message, LemmyContext, UserOperationCrud};
+use url::Url;
impl UpdateCommunity {
#[tracing::instrument(skip_all)]
#[async_trait::async_trait(?Send)]
impl ActivityHandler for UpdateCommunity {
type DataType = LemmyContext;
+ type Error = LemmyError;
+
+ fn id(&self) -> &Url {
+ &self.id
+ }
+
+ fn actor(&self) -> &Url {
+ self.actor.inner()
+ }
#[tracing::instrument(skip_all)]
async fn verify(
request_counter: &mut i32,
) -> Result<(), LemmyError> {
verify_is_public(&self.to, &self.cc)?;
- verify_activity(&self.id, self.actor.inner(), &context.settings())?;
let community = self.get_community(context, request_counter).await?;
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
verify_mod_action(
) -> Result<ApubCommunity, LemmyError> {
let cid = ObjectId::new(self.object.id.clone());
cid
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, local_instance(context), request_counter)
.await
}
}
community::{announce::GetCommunity, send_activity_in_community},
create_or_update::get_comment_notif_recipients,
generate_activity_id,
- verify_activity,
verify_is_public,
verify_person_in_community,
},
activity_lists::AnnouncableActivities,
+ local_instance,
mentions::MentionOrValue,
objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson},
protocol::activities::{create_or_update::comment::CreateOrUpdateComment, CreateOrUpdateType},
+ ActorType,
};
-use activitystreams_kinds::public;
-use lemmy_api_common::utils::{blocking, check_post_deleted_or_removed};
-use lemmy_apub_lib::{
+use activitypub_federation::{
+ core::object_id::ObjectId,
data::Data,
- object_id::ObjectId,
- traits::{ActivityHandler, ActorType, ApubObject},
- verify::verify_domains_match,
+ traits::{ActivityHandler, ApubObject},
+ utils::verify_domains_match,
};
+use activitystreams_kinds::public;
+use lemmy_api_common::utils::{blocking, check_post_deleted_or_removed};
use lemmy_db_schema::{
source::{
comment::{CommentLike, CommentLikeForm},
},
traits::{Crud, Likeable},
};
-use lemmy_utils::LemmyError;
+use lemmy_utils::error::LemmyError;
use lemmy_websocket::{send::send_comment_ws_message, LemmyContext, UserOperationCrud};
+use url::Url;
impl CreateOrUpdateComment {
#[tracing::instrument(skip(comment, actor, kind, context))]
let mut inboxes = vec![];
for t in tagged_users {
let person = t
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, local_instance(context), request_counter)
.await?;
inboxes.push(person.shared_inbox_or_inbox_url());
}
#[async_trait::async_trait(?Send)]
impl ActivityHandler for CreateOrUpdateComment {
type DataType = LemmyContext;
+ type Error = LemmyError;
+
+ fn id(&self) -> &Url {
+ &self.id
+ }
+
+ fn actor(&self) -> &Url {
+ self.actor.inner()
+ }
#[tracing::instrument(skip_all)]
async fn verify(
let post = self.object.get_parents(context, request_counter).await?.0;
let community = self.get_community(context, request_counter).await?;
- verify_activity(&self.id, self.actor.inner(), &context.settings())?;
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
verify_domains_match(self.actor.inner(), self.object.id.inner())?;
check_community_deleted_or_removed(&community)?;
-use crate::objects::person::ApubPerson;
+use crate::{local_instance, objects::person::ApubPerson};
+use activitypub_federation::core::object_id::ObjectId;
use lemmy_api_common::utils::blocking;
-use lemmy_apub_lib::object_id::ObjectId;
use lemmy_db_schema::{
newtypes::LocalUserId,
source::{comment::Comment, post::Post},
traits::Crud,
};
-use lemmy_utils::{utils::scrape_text_for_mentions, LemmyError};
+use lemmy_utils::{error::LemmyError, utils::scrape_text_for_mentions};
use lemmy_websocket::{send::send_local_notifs, LemmyContext};
pub mod comment;
let post_id = comment.post_id;
let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
let actor = actor
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, local_instance(context), request_counter)
.await?;
// Note:
check_community_deleted_or_removed,
community::{announce::GetCommunity, send_activity_in_community},
generate_activity_id,
- verify_activity,
verify_is_public,
verify_mod_action,
verify_person_in_community,
activity_lists::AnnouncableActivities,
objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost},
protocol::activities::{create_or_update::post::CreateOrUpdatePost, CreateOrUpdateType},
+ ActorType,
};
-use activitystreams_kinds::public;
-use lemmy_api_common::utils::blocking;
-use lemmy_apub_lib::{
+use activitypub_federation::{
+ core::object_id::ObjectId,
data::Data,
- object_id::ObjectId,
- traits::{ActivityHandler, ActorType, ApubObject},
- verify::{verify_domains_match, verify_urls_match},
+ traits::{ActivityHandler, ApubObject},
+ utils::{verify_domains_match, verify_urls_match},
};
+use activitystreams_kinds::public;
+use lemmy_api_common::utils::blocking;
use lemmy_db_schema::{
source::{
community::Community,
},
traits::{Crud, Likeable},
};
-use lemmy_utils::LemmyError;
+use lemmy_utils::error::LemmyError;
use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperationCrud};
+use url::Url;
impl CreateOrUpdatePost {
pub(crate) async fn new(
#[async_trait::async_trait(?Send)]
impl ActivityHandler for CreateOrUpdatePost {
type DataType = LemmyContext;
+ type Error = LemmyError;
+
+ fn id(&self) -> &Url {
+ &self.id
+ }
+
+ fn actor(&self) -> &Url {
+ self.actor.inner()
+ }
#[tracing::instrument(skip_all)]
async fn verify(
request_counter: &mut i32,
) -> Result<(), LemmyError> {
verify_is_public(&self.to, &self.cc)?;
- verify_activity(&self.id, self.actor.inner(), &context.settings())?;
let community = self.get_community(context, request_counter).await?;
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
check_community_deleted_or_removed(&community)?;
use crate::{
- activities::{generate_activity_id, send_lemmy_activity, verify_activity, verify_person},
+ activities::{generate_activity_id, send_lemmy_activity, verify_person},
objects::{person::ApubPerson, private_message::ApubPrivateMessage},
protocol::activities::{
create_or_update::private_message::CreateOrUpdatePrivateMessage,
CreateOrUpdateType,
},
+ ActorType,
};
-use lemmy_api_common::utils::blocking;
-use lemmy_apub_lib::{
+use activitypub_federation::{
+ core::object_id::ObjectId,
data::Data,
- object_id::ObjectId,
- traits::{ActivityHandler, ActorType, ApubObject},
- verify::verify_domains_match,
+ traits::{ActivityHandler, ApubObject},
+ utils::verify_domains_match,
};
+use lemmy_api_common::utils::blocking;
use lemmy_db_schema::{source::person::Person, traits::Crud};
-use lemmy_utils::LemmyError;
+use lemmy_utils::error::LemmyError;
use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud};
+use url::Url;
impl CreateOrUpdatePrivateMessage {
#[tracing::instrument(skip_all)]
let create_or_update = CreateOrUpdatePrivateMessage {
id: id.clone(),
actor: ObjectId::new(actor.actor_id()),
- to: [ObjectId::new(recipient.actor_id())],
+ to: ObjectId::new(recipient.actor_id()),
object: private_message.into_apub(context).await?,
kind,
unparsed: Default::default(),
#[async_trait::async_trait(?Send)]
impl ActivityHandler for CreateOrUpdatePrivateMessage {
type DataType = LemmyContext;
+ type Error = LemmyError;
+
+ fn id(&self) -> &Url {
+ &self.id
+ }
+
+ fn actor(&self) -> &Url {
+ self.actor.inner()
+ }
#[tracing::instrument(skip_all)]
async fn verify(
context: &Data<LemmyContext>,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
- verify_activity(&self.id, self.actor.inner(), &context.settings())?;
verify_person(&self.actor, context, request_counter).await?;
verify_domains_match(self.actor.inner(), self.object.id.inner())?;
+ verify_domains_match(self.to.inner(), self.object.to.inner())?;
ApubPrivateMessage::verify(&self.object, self.actor.inner(), context, request_counter).await?;
Ok(())
}
community::announce::GetCommunity,
deletion::{receive_delete_action, verify_delete_activity, DeletableObjects},
generate_activity_id,
- verify_activity,
},
+ local_instance,
objects::{community::ApubCommunity, person::ApubPerson},
protocol::{
activities::deletion::delete::Delete,
IdOrNestedObject,
},
};
+use activitypub_federation::{core::object_id::ObjectId, data::Data, traits::ActivityHandler};
use activitystreams_kinds::activity::DeleteType;
use anyhow::anyhow;
use lemmy_api_common::utils::blocking;
-use lemmy_apub_lib::{data::Data, object_id::ObjectId, traits::ActivityHandler};
use lemmy_db_schema::{
source::{
comment::Comment,
},
traits::Crud,
};
-use lemmy_utils::LemmyError;
+use lemmy_utils::error::LemmyError;
use lemmy_websocket::{
send::{send_comment_ws_message_simple, send_community_ws_message, send_post_ws_message},
LemmyContext,
#[async_trait::async_trait(?Send)]
impl ActivityHandler for Delete {
type DataType = LemmyContext;
+ type Error = LemmyError;
+
+ fn id(&self) -> &Url {
+ &self.id
+ }
+
+ fn actor(&self) -> &Url {
+ self.actor.inner()
+ }
#[tracing::instrument(skip_all)]
async fn verify(
context: &Data<LemmyContext>,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
- verify_activity(&self.id, self.actor.inner(), &context.settings())?;
verify_delete_activity(self, self.summary.is_some(), context, request_counter).await?;
Ok(())
}
receive_remove_action(
&self
.actor
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, local_instance(context), request_counter)
.await?,
self.object.id(),
reason,
use crate::{
activities::{generate_activity_id, send_lemmy_activity, verify_is_public, verify_person},
+ local_instance,
objects::person::ApubPerson,
protocol::activities::deletion::delete_user::DeleteUser,
};
-use activitystreams_kinds::{activity::DeleteType, public};
-use lemmy_api_common::utils::{blocking, delete_user_account};
-use lemmy_apub_lib::{
+use activitypub_federation::{
+ core::object_id::ObjectId,
data::Data,
- object_id::ObjectId,
traits::ActivityHandler,
- verify::verify_urls_match,
+ utils::verify_urls_match,
};
+use activitystreams_kinds::{activity::DeleteType, public};
+use lemmy_api_common::utils::{blocking, delete_user_account};
use lemmy_db_schema::source::site::Site;
-use lemmy_utils::LemmyError;
+use lemmy_utils::error::LemmyError;
use lemmy_websocket::LemmyContext;
+use url::Url;
/// This can be separate from Delete activity because it doesn't need to be handled in shared inbox
/// (cause instance actor doesn't have shared inbox).
#[async_trait::async_trait(?Send)]
impl ActivityHandler for DeleteUser {
type DataType = LemmyContext;
+ type Error = LemmyError;
+
+ fn id(&self) -> &Url {
+ &self.id
+ }
+
+ fn actor(&self) -> &Url {
+ self.actor.inner()
+ }
async fn verify(
&self,
) -> Result<(), LemmyError> {
let actor = self
.actor
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, local_instance(context), request_counter)
.await?;
delete_user_account(actor.id, context.pool()).await?;
Ok(())
verify_person_in_community,
},
activity_lists::AnnouncableActivities,
+ local_instance,
objects::{
comment::ApubComment,
community::ApubCommunity,
private_message::ApubPrivateMessage,
},
protocol::activities::deletion::{delete::Delete, undo_delete::UndoDelete},
+ ActorType,
+};
+use activitypub_federation::{
+ core::object_id::ObjectId,
+ traits::ApubObject,
+ utils::verify_domains_match,
};
use activitystreams_kinds::public;
use lemmy_api_common::utils::blocking;
-use lemmy_apub_lib::{
- object_id::ObjectId,
- traits::{ActorType, ApubObject},
- verify::verify_domains_match,
-};
use lemmy_db_schema::{
source::{
comment::Comment,
},
traits::Crud,
};
-use lemmy_utils::LemmyError;
+use lemmy_utils::error::LemmyError;
use lemmy_websocket::{
send::{
send_comment_ws_message_simple,
DeletableObjects::Community(community) => {
if community.local {
let mod_: Person = actor
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, local_instance(context), request_counter)
.await?
.deref()
.clone();
community::announce::GetCommunity,
deletion::{receive_delete_action, verify_delete_activity, DeletableObjects},
generate_activity_id,
- verify_activity,
},
+ local_instance,
objects::{community::ApubCommunity, person::ApubPerson},
protocol::activities::deletion::{delete::Delete, undo_delete::UndoDelete},
};
+use activitypub_federation::{core::object_id::ObjectId, data::Data, traits::ActivityHandler};
use activitystreams_kinds::activity::UndoType;
use lemmy_api_common::utils::blocking;
-use lemmy_apub_lib::{data::Data, object_id::ObjectId, traits::ActivityHandler};
use lemmy_db_schema::{
source::{
comment::Comment,
},
traits::Crud,
};
-use lemmy_utils::LemmyError;
+use lemmy_utils::error::LemmyError;
use lemmy_websocket::{
send::{send_comment_ws_message_simple, send_community_ws_message, send_post_ws_message},
LemmyContext,
#[async_trait::async_trait(?Send)]
impl ActivityHandler for UndoDelete {
type DataType = LemmyContext;
+ type Error = LemmyError;
+
+ fn id(&self) -> &Url {
+ &self.id
+ }
+
+ fn actor(&self) -> &Url {
+ self.actor.inner()
+ }
#[tracing::instrument(skip_all)]
async fn verify(
context: &Data<LemmyContext>,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
- verify_activity(&self.id, self.actor.inner(), &context.settings())?;
self.object.verify(context, request_counter).await?;
verify_delete_activity(
&self.object,
UndoDelete::receive_undo_remove_action(
&self
.actor
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, local_instance(context), request_counter)
.await?,
self.object.object.id(),
context,
use crate::{
- activities::{generate_activity_id, send_lemmy_activity, verify_activity},
+ activities::{generate_activity_id, send_lemmy_activity},
+ local_instance,
protocol::activities::following::{accept::AcceptFollowCommunity, follow::FollowCommunity},
+ ActorType,
};
-use activitystreams_kinds::activity::AcceptType;
-use lemmy_api_common::utils::blocking;
-use lemmy_apub_lib::{
+use activitypub_federation::{
+ core::object_id::ObjectId,
data::Data,
- object_id::ObjectId,
- traits::{ActivityHandler, ActorType},
- verify::verify_urls_match,
+ traits::ActivityHandler,
+ utils::verify_urls_match,
};
+use activitystreams_kinds::activity::AcceptType;
+use lemmy_api_common::utils::blocking;
use lemmy_db_schema::{source::community::CommunityFollower, traits::Followable};
-use lemmy_utils::LemmyError;
+use lemmy_utils::error::LemmyError;
use lemmy_websocket::LemmyContext;
+use url::Url;
impl AcceptFollowCommunity {
#[tracing::instrument(skip_all)]
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
- let community = follow.object.dereference_local(context).await?;
+ let community = follow
+ .object
+ .dereference_local::<LemmyError>(context)
+ .await?;
let person = follow
.actor
.clone()
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, local_instance(context), request_counter)
.await?;
let accept = AcceptFollowCommunity {
actor: ObjectId::new(community.actor_id()),
#[async_trait::async_trait(?Send)]
impl ActivityHandler for AcceptFollowCommunity {
type DataType = LemmyContext;
+ type Error = LemmyError;
+
+ fn id(&self) -> &Url {
+ &self.id
+ }
+
+ fn actor(&self) -> &Url {
+ self.actor.inner()
+ }
#[tracing::instrument(skip_all)]
async fn verify(
context: &Data<LemmyContext>,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
- verify_activity(&self.id, self.actor.inner(), &context.settings())?;
verify_urls_match(self.actor.inner(), self.object.object.inner())?;
self.object.verify(context, request_counter).await?;
Ok(())
) -> Result<(), LemmyError> {
let person = self
.actor
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, local_instance(context), request_counter)
.await?;
let community = self
.object
.actor
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, local_instance(context), request_counter)
.await?;
// This will throw an error if no follow was requested
blocking(context.pool(), move |conn| {
activities::{
generate_activity_id,
send_lemmy_activity,
- verify_activity,
verify_person,
verify_person_in_community,
},
+ local_instance,
objects::{community::ApubCommunity, person::ApubPerson},
protocol::activities::following::{accept::AcceptFollowCommunity, follow::FollowCommunity},
+ ActorType,
};
+use activitypub_federation::{core::object_id::ObjectId, data::Data, traits::ActivityHandler};
use activitystreams_kinds::activity::FollowType;
use lemmy_api_common::utils::blocking;
-use lemmy_apub_lib::{
- data::Data,
- object_id::ObjectId,
- traits::{ActivityHandler, ActorType},
-};
use lemmy_db_schema::{
source::community::{CommunityFollower, CommunityFollowerForm},
traits::Followable,
};
-use lemmy_utils::LemmyError;
+use lemmy_utils::error::LemmyError;
use lemmy_websocket::LemmyContext;
+use url::Url;
impl FollowCommunity {
pub(in crate::activities::following) fn new(
#[async_trait::async_trait(?Send)]
impl ActivityHandler for FollowCommunity {
type DataType = LemmyContext;
+ type Error = LemmyError;
+
+ fn id(&self) -> &Url {
+ &self.id
+ }
+
+ fn actor(&self) -> &Url {
+ self.actor.inner()
+ }
#[tracing::instrument(skip_all)]
async fn verify(
context: &Data<LemmyContext>,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
- verify_activity(&self.id, self.actor.inner(), &context.settings())?;
verify_person(&self.actor, context, request_counter).await?;
let community = self
.object
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, local_instance(context), request_counter)
.await?;
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
Ok(())
) -> Result<(), LemmyError> {
let person = self
.actor
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, local_instance(context), request_counter)
.await?;
let community = self
.object
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, local_instance(context), request_counter)
.await?;
let community_follower_form = CommunityFollowerForm {
community_id: community.id,
use crate::{
- activities::{generate_activity_id, send_lemmy_activity, verify_activity, verify_person},
+ activities::{generate_activity_id, send_lemmy_activity, verify_person},
+ local_instance,
objects::{community::ApubCommunity, person::ApubPerson},
protocol::activities::following::{follow::FollowCommunity, undo_follow::UndoFollowCommunity},
+ ActorType,
};
-use activitystreams_kinds::activity::UndoType;
-use lemmy_api_common::utils::blocking;
-use lemmy_apub_lib::{
+use activitypub_federation::{
+ core::object_id::ObjectId,
data::Data,
- object_id::ObjectId,
- traits::{ActivityHandler, ActorType},
- verify::verify_urls_match,
+ traits::ActivityHandler,
+ utils::verify_urls_match,
};
+use activitystreams_kinds::activity::UndoType;
+use lemmy_api_common::utils::blocking;
use lemmy_db_schema::{
source::community::{CommunityFollower, CommunityFollowerForm},
traits::Followable,
};
-use lemmy_utils::LemmyError;
+use lemmy_utils::error::LemmyError;
use lemmy_websocket::LemmyContext;
+use url::Url;
impl UndoFollowCommunity {
#[tracing::instrument(skip_all)]
#[async_trait::async_trait(?Send)]
impl ActivityHandler for UndoFollowCommunity {
type DataType = LemmyContext;
+ type Error = LemmyError;
+
+ fn id(&self) -> &Url {
+ &self.id
+ }
+
+ fn actor(&self) -> &Url {
+ self.actor.inner()
+ }
#[tracing::instrument(skip_all)]
async fn verify(
context: &Data<LemmyContext>,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
- verify_activity(&self.id, self.actor.inner(), &context.settings())?;
verify_urls_match(self.actor.inner(), self.object.actor.inner())?;
verify_person(&self.actor, context, request_counter).await?;
self.object.verify(context, request_counter).await?;
) -> Result<(), LemmyError> {
let person = self
.actor
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, local_instance(context), request_counter)
.await?;
let community = self
.object
.object
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, local_instance(context), request_counter)
.await?;
let community_follower_form = CommunityFollowerForm {
use crate::{
- check_is_apub_id_valid,
- context::WithContext,
generate_moderators_url,
insert_activity,
+ local_instance,
objects::{community::ApubCommunity, person::ApubPerson},
+ ActorType,
+ CONTEXT,
+};
+use activitypub_federation::{
+ core::{activity_queue::SendActivity, object_id::ObjectId},
+ deser::context::WithContext,
};
use activitystreams_kinds::public;
use anyhow::anyhow;
use lemmy_api_common::utils::blocking;
-use lemmy_apub_lib::{
- activity_queue::send_activity,
- object_id::ObjectId,
- traits::ActorType,
- verify::verify_domains_match,
-};
use lemmy_db_schema::source::community::Community;
use lemmy_db_views_actor::structs::{CommunityPersonBanView, CommunityView};
-use lemmy_utils::{settings::structs::Settings, LemmyError};
+use lemmy_utils::error::LemmyError;
use lemmy_websocket::LemmyContext;
use serde::Serialize;
+use std::ops::Deref;
use tracing::info;
use url::{ParseError, Url};
use uuid::Uuid;
request_counter: &mut i32,
) -> Result<(), LemmyError> {
let person = person_id
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, local_instance(context), request_counter)
.await?;
if person.banned {
let err = anyhow!("Person {} is banned", person_id);
request_counter: &mut i32,
) -> Result<(), LemmyError> {
let person = person_id
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, local_instance(context), request_counter)
.await?;
if person.banned {
return Err(LemmyError::from_message("Person is banned from site"));
Ok(())
}
-fn verify_activity(id: &Url, actor: &Url, settings: &Settings) -> Result<(), LemmyError> {
- check_is_apub_id_valid(actor, false, settings)?;
- verify_domains_match(id, actor)?;
- Ok(())
-}
-
/// Verify that the actor is a community mod. This check is only run if the community is local,
/// because in case of remote communities, admins can also perform mod actions. As admin status
/// is not federated, we cant verify their actions remotely.
) -> Result<(), LemmyError> {
if community.local {
let actor = mod_id
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, local_instance(context), request_counter)
.await?;
// Note: this will also return true for admins in addition to mods, but as we dont know about
if !context.settings().federation.enabled || inboxes.is_empty() {
return Ok(());
}
- let activity = WithContext::new(activity);
+ let activity = WithContext::new(activity, CONTEXT.deref().clone());
info!("Sending activity {}", activity_id.to_string());
// Don't send anything to ourselves
// TODO: this should be a debug assert
let hostname = context.settings().get_hostname_without_port()?;
- let inboxes: Vec<&Url> = inboxes
- .iter()
+ let inboxes: Vec<Url> = inboxes
+ .into_iter()
.filter(|i| i.domain().expect("valid inbox url") != hostname)
.collect();
let object_value = serde_json::to_value(&activity)?;
insert_activity(activity_id, object_value, true, sensitive, context.pool()).await?;
- send_activity(
- activity_id,
- actor,
+ SendActivity {
+ activity_id: activity_id.clone(),
+ actor_public_key: actor.get_public_key(),
+ actor_private_key: actor.private_key().expect("actor has private key"),
inboxes,
- serialised_activity,
- context.client(),
- context.activity_queue(),
- )
- .await
+ activity: serialised_activity,
+ }
+ .send(local_instance(context))
+ .await?;
+
+ Ok(())
}
},
traits::Likeable,
};
-use lemmy_utils::LemmyError;
+use lemmy_utils::error::LemmyError;
use lemmy_websocket::{
send::{send_comment_ws_message_simple, send_post_ws_message},
LemmyContext,
activities::{
community::{announce::GetCommunity, send_activity_in_community},
generate_activity_id,
- verify_activity,
verify_is_public,
verify_person_in_community,
voting::{undo_vote_comment, undo_vote_post},
},
activity_lists::AnnouncableActivities,
+ local_instance,
objects::{community::ApubCommunity, person::ApubPerson},
protocol::activities::voting::{
undo_vote::UndoVote,
vote::{Vote, VoteType},
},
+ ActorType,
PostOrComment,
};
-use activitystreams_kinds::{activity::UndoType, public};
-use lemmy_api_common::utils::blocking;
-use lemmy_apub_lib::{
+use activitypub_federation::{
+ core::object_id::ObjectId,
data::Data,
- object_id::ObjectId,
- traits::{ActivityHandler, ActorType},
- verify::verify_urls_match,
+ traits::ActivityHandler,
+ utils::verify_urls_match,
};
+use activitystreams_kinds::{activity::UndoType, public};
+use lemmy_api_common::utils::blocking;
use lemmy_db_schema::{newtypes::CommunityId, source::community::Community, traits::Crud};
-use lemmy_utils::LemmyError;
+use lemmy_utils::error::LemmyError;
use lemmy_websocket::LemmyContext;
+use url::Url;
impl UndoVote {
/// UndoVote has as:Public value in cc field, unlike other activities. This indicates to other
#[async_trait::async_trait(?Send)]
impl ActivityHandler for UndoVote {
type DataType = LemmyContext;
+ type Error = LemmyError;
+
+ fn id(&self) -> &Url {
+ &self.id
+ }
+
+ fn actor(&self) -> &Url {
+ self.actor.inner()
+ }
#[tracing::instrument(skip_all)]
async fn verify(
request_counter: &mut i32,
) -> Result<(), LemmyError> {
verify_is_public(&self.to, &self.cc)?;
- verify_activity(&self.id, self.actor.inner(), &context.settings())?;
let community = self.get_community(context, request_counter).await?;
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
verify_urls_match(self.actor.inner(), self.object.actor.inner())?;
) -> Result<(), LemmyError> {
let actor = self
.actor
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, local_instance(context), request_counter)
.await?;
let object = self
.object
.object
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, local_instance(context), request_counter)
.await?;
match object {
PostOrComment::Post(p) => undo_vote_post(actor, &p, context).await,
activities::{
community::{announce::GetCommunity, send_activity_in_community},
generate_activity_id,
- verify_activity,
verify_is_public,
verify_person_in_community,
voting::{vote_comment, vote_post},
},
activity_lists::AnnouncableActivities,
+ local_instance,
objects::{community::ApubCommunity, person::ApubPerson},
protocol::activities::voting::vote::{Vote, VoteType},
+ ActorType,
PostOrComment,
};
+use activitypub_federation::{core::object_id::ObjectId, data::Data, traits::ActivityHandler};
use activitystreams_kinds::public;
use anyhow::anyhow;
use lemmy_api_common::utils::blocking;
-use lemmy_apub_lib::{
- data::Data,
- object_id::ObjectId,
- traits::{ActivityHandler, ActorType},
-};
use lemmy_db_schema::{
newtypes::CommunityId,
source::{community::Community, post::Post, site::Site},
traits::Crud,
};
-use lemmy_utils::LemmyError;
+use lemmy_utils::error::LemmyError;
use lemmy_websocket::LemmyContext;
+use url::Url;
/// Vote has as:Public value in cc field, unlike other activities. This indicates to other software
/// (like GNU social, or presumably Mastodon), that the like actor should not be disclosed.
#[async_trait::async_trait(?Send)]
impl ActivityHandler for Vote {
type DataType = LemmyContext;
+ type Error = LemmyError;
+
+ fn id(&self) -> &Url {
+ &self.id
+ }
+
+ fn actor(&self) -> &Url {
+ self.actor.inner()
+ }
#[tracing::instrument(skip_all)]
async fn verify(
request_counter: &mut i32,
) -> Result<(), LemmyError> {
verify_is_public(&self.to, &self.cc)?;
- verify_activity(&self.id, self.actor.inner(), &context.settings())?;
let community = self.get_community(context, request_counter).await?;
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
let site = blocking(context.pool(), Site::read_local_site).await??;
) -> Result<(), LemmyError> {
let actor = self
.actor
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, local_instance(context), request_counter)
.await?;
let object = self
.object
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, local_instance(context), request_counter)
.await?;
match object {
PostOrComment::Post(p) => vote_post(&self.kind, actor, &p, context).await,
) -> Result<ApubCommunity, LemmyError> {
let object = self
.object
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, local_instance(context), request_counter)
.await?;
let cid = match object {
PostOrComment::Post(p) => p.community_id,
use crate::{
- activities::community::announce::GetCommunity,
+ activities::{community::announce::GetCommunity, verify_person_in_community},
objects::community::ApubCommunity,
protocol::{
activities::{
Id,
},
};
-use lemmy_apub_lib::traits::ActivityHandler;
-use lemmy_utils::LemmyError;
+use activitypub_federation::{
+ core::object_id::ObjectId,
+ data::Data,
+ deser::context::WithContext,
+ traits::{activity_handler, ActivityHandler},
+};
+use lemmy_utils::error::LemmyError;
use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize};
use url::Url;
-#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler)]
+#[derive(Debug, Deserialize, Serialize)]
#[serde(untagged)]
-#[activity_handler(LemmyContext)]
+#[activity_handler(LemmyContext, LemmyError)]
pub enum SharedInboxActivities {
- GroupInboxActivities(Box<GroupInboxActivities>),
+ GroupInboxActivities(Box<WithContext<GroupInboxActivities>>),
// Note, pm activities need to be at the end, otherwise comments will end up here. We can probably
// avoid this problem by replacing createpm.object with our own struct, instead of NoteExt.
- PersonInboxActivities(Box<PersonInboxActivities>),
+ PersonInboxActivities(Box<WithContext<PersonInboxActivities>>),
}
-#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler)]
+#[derive(Debug, Deserialize, Serialize)]
#[serde(untagged)]
-#[activity_handler(LemmyContext)]
pub enum GroupInboxActivities {
FollowCommunity(FollowCommunity),
UndoFollowCommunity(UndoFollowCommunity),
Report(Report),
}
-#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler)]
+#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(untagged)]
-#[activity_handler(LemmyContext)]
+#[activity_handler(LemmyContext, LemmyError)]
pub enum PersonInboxActivities {
AcceptFollowCommunity(AcceptFollowCommunity),
/// Some activities can also be sent from user to user, eg a comment with mentions
AnnounceActivity(AnnounceActivity),
}
-#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler)]
+#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(untagged)]
-#[activity_handler(LemmyContext)]
+#[activity_handler(LemmyContext, LemmyError)]
pub enum AnnouncableActivities {
CreateOrUpdateComment(CreateOrUpdateComment),
CreateOrUpdatePost(Box<CreateOrUpdatePost>),
Page(Page),
}
-#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler)]
+#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(untagged)]
-#[activity_handler(LemmyContext)]
+#[activity_handler(LemmyContext, LemmyError)]
#[allow(clippy::enum_variant_names)]
pub enum SiteInboxActivities {
BlockUser(BlockUser),
}
impl Id for AnnouncableActivities {
+ fn object_id(&self) -> &Url {
+ ActivityHandler::id(self)
+ }
+}
+
+// Need to implement this manually to announce matching activities
+#[async_trait::async_trait(?Send)]
+impl ActivityHandler for GroupInboxActivities {
+ type DataType = LemmyContext;
+ type Error = LemmyError;
+
fn id(&self) -> &Url {
- use AnnouncableActivities::*;
match self {
- CreateOrUpdateComment(c) => &c.id,
- CreateOrUpdatePost(c) => &c.id,
- Vote(v) => &v.id,
- UndoVote(u) => &u.id,
- Delete(d) => &d.id,
- UndoDelete(u) => &u.id,
- UpdateCommunity(u) => &u.id,
- BlockUser(b) => &b.id,
- UndoBlockUser(u) => &u.id,
- AddMod(a) => &a.id,
- RemoveMod(r) => &r.id,
- Page(p) => p.id.inner(),
+ GroupInboxActivities::FollowCommunity(a) => a.id(),
+ GroupInboxActivities::UndoFollowCommunity(a) => a.id(),
+ GroupInboxActivities::AnnouncableActivities(a) => a.object_id(),
+ GroupInboxActivities::Report(a) => a.id(),
+ }
+ }
+
+ fn actor(&self) -> &Url {
+ match self {
+ GroupInboxActivities::FollowCommunity(a) => a.actor(),
+ GroupInboxActivities::UndoFollowCommunity(a) => a.actor(),
+ GroupInboxActivities::AnnouncableActivities(a) => a.actor(),
+ GroupInboxActivities::Report(a) => a.actor(),
+ }
+ }
+
+ async fn verify(
+ &self,
+ data: &Data<Self::DataType>,
+ request_counter: &mut i32,
+ ) -> Result<(), LemmyError> {
+ match self {
+ GroupInboxActivities::FollowCommunity(a) => a.verify(data, request_counter).await,
+ GroupInboxActivities::UndoFollowCommunity(a) => a.verify(data, request_counter).await,
+ GroupInboxActivities::AnnouncableActivities(a) => a.verify(data, request_counter).await,
+ GroupInboxActivities::Report(a) => a.verify(data, request_counter).await,
+ }
+ }
+
+ async fn receive(
+ self,
+ data: &Data<Self::DataType>,
+ request_counter: &mut i32,
+ ) -> Result<(), LemmyError> {
+ match self {
+ GroupInboxActivities::FollowCommunity(a) => a.receive(data, request_counter).await,
+ GroupInboxActivities::UndoFollowCommunity(a) => a.receive(data, request_counter).await,
+ GroupInboxActivities::AnnouncableActivities(activity) => {
+ activity.clone().receive(data, request_counter).await?;
+
+ // Ignore failures in get_community(). those happen because Delete/PrivateMessage is not in a
+ // community, but looks identical to Delete/Post or Delete/Comment which are in a community.
+ let community = activity.get_community(data, &mut 0).await;
+ if let Ok(community) = community {
+ if community.local {
+ let actor_id = ObjectId::new(activity.actor().clone());
+ verify_person_in_community(&actor_id, &community, data, &mut 0).await?;
+ AnnounceActivity::send(*activity, &community, data).await?;
+ }
+ }
+ Ok(())
+ }
+ GroupInboxActivities::Report(a) => a.receive(data, request_counter).await,
}
}
}
use crate::{
collections::CommunityContext,
generate_moderators_url,
+ local_instance,
objects::person::ApubPerson,
protocol::collections::group_moderators::GroupModerators,
};
+use activitypub_federation::{
+ core::object_id::ObjectId,
+ traits::ApubObject,
+ utils::verify_domains_match,
+};
use activitystreams_kinds::collection::OrderedCollectionType;
use chrono::NaiveDateTime;
use lemmy_api_common::utils::blocking;
-use lemmy_apub_lib::{object_id::ObjectId, traits::ApubObject, verify::verify_domains_match};
use lemmy_db_schema::{
source::community::{CommunityModerator, CommunityModeratorForm},
traits::Joinable,
};
use lemmy_db_views_actor::structs::CommunityModeratorView;
-use lemmy_utils::LemmyError;
+use lemmy_utils::error::LemmyError;
use url::Url;
#[derive(Clone, Debug)]
#[async_trait::async_trait(?Send)]
impl ApubObject for ApubCommunityModerators {
type DataType = CommunityContext;
- type TombstoneType = ();
type ApubType = GroupModerators;
+ type Error = LemmyError;
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
None
})
}
- fn to_tombstone(&self) -> Result<Self::TombstoneType, LemmyError> {
- unimplemented!()
- }
-
#[tracing::instrument(skip_all)]
async fn verify(
group_moderators: &GroupModerators,
for mod_id in apub.ordered_items {
let mod_id = ObjectId::new(mod_id);
let mod_user: ApubPerson = mod_id
- .dereference(&data.1, data.1.client(), request_counter)
+ .dereference::<LemmyError>(&data.1, local_instance(&data.1), request_counter)
.await?;
if !current_moderators
collections::group_outbox::GroupOutbox,
},
};
+use activitypub_federation::{
+ data::Data,
+ traits::{ActivityHandler, ApubObject},
+ utils::verify_domains_match,
+};
use activitystreams_kinds::collection::OrderedCollectionType;
use chrono::NaiveDateTime;
use futures::future::join_all;
use lemmy_api_common::utils::blocking;
-use lemmy_apub_lib::{
- data::Data,
- traits::{ActivityHandler, ApubObject},
- verify::verify_domains_match,
-};
use lemmy_db_schema::source::post::Post;
-use lemmy_utils::LemmyError;
+use lemmy_utils::error::LemmyError;
use url::Url;
#[derive(Clone, Debug)]
#[async_trait::async_trait(?Send)]
impl ApubObject for ApubCommunityOutbox {
type DataType = CommunityContext;
- type TombstoneType = ();
type ApubType = GroupOutbox;
+ type Error = LemmyError;
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
None
})
}
- fn to_tombstone(&self) -> Result<Self::TombstoneType, LemmyError> {
- // no tombstone for this, there is only a tombstone for the community
- unimplemented!()
- }
-
#[tracing::instrument(skip_all)]
async fn verify(
group_outbox: &GroupOutbox,
+++ /dev/null
-use once_cell::sync::Lazy;
-use serde::{Deserialize, Serialize};
-
-static CONTEXT: Lazy<Vec<serde_json::Value>> = Lazy::new(|| {
- serde_json::from_str(include_str!("../assets/lemmy/context.json")).expect("parse context")
-});
-
-#[derive(Serialize, Deserialize, Debug)]
-pub(crate) struct WithContext<T> {
- #[serde(rename = "@context")]
- #[serde(deserialize_with = "crate::deserialize_one_or_many")]
- context: Vec<serde_json::Value>,
- #[serde(flatten)]
- inner: T,
-}
-
-impl<T> WithContext<T> {
- pub(crate) fn new(inner: T) -> WithContext<T> {
- WithContext {
- context: (*CONTEXT).clone(),
- inner,
- }
- }
- pub(crate) fn inner(self) -> T {
- self.inner
- }
-}
-use crate::fetcher::webfinger::webfinger_resolve_actor;
+use crate::{fetcher::webfinger::webfinger_resolve_actor, ActorType};
+use activitypub_federation::traits::ApubObject;
use itertools::Itertools;
use lemmy_api_common::utils::blocking;
-use lemmy_apub_lib::traits::{ActorType, ApubObject};
use lemmy_db_schema::traits::ApubActor;
-use lemmy_utils::{settings::structs::Settings, LemmyError};
+use lemmy_utils::{error::LemmyError, settings::structs::Settings};
use lemmy_websocket::LemmyContext;
pub mod post_or_comment;
context: &LemmyContext,
) -> Result<DbActor, LemmyError>
where
- Actor:
- ApubObject<DataType = LemmyContext> + ApubObject<DbType = DbActor> + ActorType + Send + 'static,
+ Actor: ApubObject<DataType = LemmyContext, Error = LemmyError>
+ + ApubObject<DbType = DbActor>
+ + ActorType
+ + Send
+ + 'static,
for<'de2> <Actor as ApubObject>::ApubType: serde::Deserialize<'de2>,
DbActor: ApubActor + Send + 'static,
{
objects::{comment::ApubComment, post::ApubPost},
protocol::objects::{note::Note, page::Page},
};
+use activitypub_federation::traits::ApubObject;
use chrono::NaiveDateTime;
-use lemmy_apub_lib::traits::ApubObject;
-use lemmy_utils::LemmyError;
+use lemmy_utils::error::LemmyError;
use lemmy_websocket::LemmyContext;
use serde::Deserialize;
use url::Url;
type DataType = LemmyContext;
type ApubType = PageOrNote;
type DbType = ();
- type TombstoneType = ();
+ type Error = LemmyError;
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
None
unimplemented!()
}
- fn to_tombstone(&self) -> Result<Self::TombstoneType, LemmyError> {
- unimplemented!()
- }
-
#[tracing::instrument(skip_all)]
async fn verify(
apub: &Self::ApubType,
use crate::{
fetcher::webfinger::webfinger_resolve_actor,
+ local_instance,
objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson, post::ApubPost},
protocol::objects::{group::Group, note::Note, page::Page, person::Person},
};
+use activitypub_federation::{core::object_id::ObjectId, traits::ApubObject};
use chrono::NaiveDateTime;
-use lemmy_apub_lib::{object_id::ObjectId, traits::ApubObject};
-use lemmy_utils::LemmyError;
+use lemmy_utils::error::LemmyError;
use lemmy_websocket::LemmyContext;
use serde::Deserialize;
use url::Url;
context: &LemmyContext,
) -> Result<SearchableObjects, LemmyError> {
let request_counter = &mut 0;
+ let instance = local_instance(context);
match Url::parse(query) {
Ok(url) => {
ObjectId::new(url)
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, instance, request_counter)
.await
}
Err(_) => {
webfinger_resolve_actor::<ApubPerson>(identifier, context, request_counter).await?;
Ok(SearchableObjects::Person(
ObjectId::new(id)
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, instance, request_counter)
.await?,
))
}
webfinger_resolve_actor::<ApubCommunity>(identifier, context, request_counter).await?;
Ok(SearchableObjects::Community(
ObjectId::new(id)
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, instance, request_counter)
.await?,
))
}
type DataType = LemmyContext;
type ApubType = SearchableApubTypes;
type DbType = ();
- type TombstoneType = ();
+ type Error = LemmyError;
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
match self {
unimplemented!()
}
- fn to_tombstone(&self) -> Result<Self::TombstoneType, LemmyError> {
- unimplemented!()
- }
-
#[tracing::instrument(skip_all)]
async fn verify(
apub: &Self::ApubType,
objects::{community::ApubCommunity, person::ApubPerson},
protocol::objects::{group::Group, person::Person},
};
+use activitypub_federation::{core::inbox::ActorPublicKey, traits::ApubObject};
use chrono::NaiveDateTime;
-use lemmy_apub_lib::traits::{ActorType, ApubObject};
-use lemmy_utils::LemmyError;
+use lemmy_utils::error::LemmyError;
use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize};
use url::Url;
type DataType = LemmyContext;
type ApubType = PersonOrGroup;
type DbType = ();
- type TombstoneType = ();
+ type Error = LemmyError;
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
Some(match self {
unimplemented!()
}
- fn to_tombstone(&self) -> Result<Self::TombstoneType, LemmyError> {
- unimplemented!()
- }
-
#[tracing::instrument(skip_all)]
async fn verify(
apub: &Self::ApubType,
}
}
-impl ActorType for UserOrCommunity {
- fn actor_id(&self) -> Url {
- match self {
- UserOrCommunity::User(p) => p.actor_id(),
- UserOrCommunity::Community(p) => p.actor_id(),
- }
- }
-
- fn public_key(&self) -> String {
+impl ActorPublicKey for UserOrCommunity {
+ fn public_key(&self) -> &str {
match self {
UserOrCommunity::User(p) => p.public_key(),
UserOrCommunity::Community(p) => p.public_key(),
}
}
-
- fn private_key(&self) -> Option<String> {
- todo!()
- }
-
- fn inbox_url(&self) -> Url {
- todo!()
- }
-
- fn shared_inbox_url(&self) -> Option<Url> {
- todo!()
- }
}
+use crate::{local_instance, ActorType};
+use activitypub_federation::{core::object_id::ObjectId, traits::ApubObject};
use anyhow::anyhow;
use itertools::Itertools;
-use lemmy_apub_lib::{
- object_id::ObjectId,
- traits::{ActorType, ApubObject},
-};
use lemmy_db_schema::newtypes::DbUrl;
-use lemmy_utils::{request::retry, LemmyError};
+use lemmy_utils::error::LemmyError;
use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize};
use tracing::debug;
request_counter: &mut i32,
) -> Result<DbUrl, LemmyError>
where
- Kind: ApubObject<DataType = LemmyContext> + ActorType + Send + 'static,
+ Kind: ApubObject<DataType = LemmyContext, Error = LemmyError> + ActorType + Send + 'static,
for<'de2> <Kind as ApubObject>::ApubType: serde::Deserialize<'de2>,
{
let protocol = context.settings().get_protocol_string();
return Err(LemmyError::from_message("Request retry limit reached"));
}
- let response = retry(|| context.client().get(&fetch_url).send()).await?;
+ let response = context.client().get(&fetch_url).send().await?;
let res: WebfingerResponse = response.json().await.map_err(LemmyError::from)?;
.collect();
for l in links {
let object = ObjectId::<Kind>::new(l)
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, local_instance(context), request_counter)
.await;
if object.is_ok() {
return object.map(|o| o.actor_id().into());
http::{create_apub_response, create_apub_tombstone_response},
objects::comment::ApubComment,
};
+use activitypub_federation::traits::ApubObject;
use actix_web::{web, web::Path, HttpResponse};
use diesel::result::Error::NotFound;
use lemmy_api_common::utils::blocking;
-use lemmy_apub_lib::traits::ApubObject;
use lemmy_db_schema::{newtypes::CommentId, source::comment::Comment, traits::Crud};
-use lemmy_utils::LemmyError;
+use lemmy_utils::error::LemmyError;
use lemmy_websocket::LemmyContext;
use serde::Deserialize;
if !comment.deleted {
Ok(create_apub_response(&comment.into_apub(&**context).await?))
} else {
- Ok(create_apub_tombstone_response(&comment.to_tombstone()?))
+ Ok(create_apub_tombstone_response(comment.ap_id.clone()))
}
}
use crate::{
- activities::{community::announce::GetCommunity, verify_person_in_community},
activity_lists::GroupInboxActivities,
collections::{
community_moderators::ApubCommunityModerators,
community_outbox::ApubCommunityOutbox,
CommunityContext,
},
- context::WithContext,
generate_outbox_url,
- http::{
- create_apub_response,
- create_apub_tombstone_response,
- payload_to_string,
- receive_activity,
- ActivityCommonFields,
- },
- objects::community::ApubCommunity,
- protocol::{
- activities::community::announce::AnnounceActivity,
- collections::group_followers::GroupFollowers,
- },
+ http::{create_apub_response, create_apub_tombstone_response, receive_lemmy_activity},
+ local_instance,
+ objects::{community::ApubCommunity, person::ApubPerson},
+ protocol::collections::group_followers::GroupFollowers,
+};
+use activitypub_federation::{
+ core::object_id::ObjectId,
+ deser::context::WithContext,
+ traits::ApubObject,
};
-use actix_web::{web, web::Payload, HttpRequest, HttpResponse};
+use actix_web::{web, HttpRequest, HttpResponse};
use lemmy_api_common::utils::blocking;
-use lemmy_apub_lib::{object_id::ObjectId, traits::ApubObject};
use lemmy_db_schema::{source::community::Community, traits::ApubActor};
-use lemmy_utils::LemmyError;
+use lemmy_utils::error::LemmyError;
use lemmy_websocket::LemmyContext;
use serde::Deserialize;
-use tracing::info;
#[derive(Deserialize)]
pub(crate) struct CommunityQuery {
Ok(create_apub_response(&apub))
} else {
- Ok(create_apub_tombstone_response(&community.to_tombstone()?))
+ Ok(create_apub_tombstone_response(community.actor_id.clone()))
}
}
#[tracing::instrument(skip_all)]
pub async fn community_inbox(
request: HttpRequest,
- payload: Payload,
- _path: web::Path<String>,
+ payload: String,
context: web::Data<LemmyContext>,
) -> Result<HttpResponse, LemmyError> {
- let unparsed = payload_to_string(payload).await?;
- info!("Received community inbox activity {}", unparsed);
- let activity_data: ActivityCommonFields = serde_json::from_str(&unparsed)?;
- let activity = serde_json::from_str::<WithContext<GroupInboxActivities>>(&unparsed)?;
-
- receive_group_inbox(activity.inner(), activity_data, request, &context).await?;
-
- Ok(HttpResponse::Ok().finish())
-}
-
-pub(in crate::http) async fn receive_group_inbox(
- activity: GroupInboxActivities,
- activity_data: ActivityCommonFields,
- request: HttpRequest,
- context: &LemmyContext,
-) -> Result<HttpResponse, LemmyError> {
- let actor_id = ObjectId::new(activity_data.actor.clone());
- let res = receive_activity(request, activity.clone(), activity_data, context).await?;
-
- if let GroupInboxActivities::AnnouncableActivities(announcable) = activity {
- // Ignore failures in get_community(). those happen because Delete/PrivateMessage is not in a
- // community, but looks identical to Delete/Post or Delete/Comment which are in a community.
- let community = announcable.get_community(context, &mut 0).await;
- if let Ok(community) = community {
- if community.local {
- verify_person_in_community(&actor_id, &community, context, &mut 0).await?;
- AnnounceActivity::send(*announcable, &community, context).await?;
- }
- }
- }
-
- Ok(res)
+ receive_lemmy_activity::<WithContext<GroupInboxActivities>, ApubPerson>(request, payload, context)
+ .await
}
/// Returns an empty followers collection, only populating the size (for privacy).
let id = ObjectId::new(generate_outbox_url(&community.actor_id)?);
let outbox_data = CommunityContext(community.into(), context.get_ref().clone());
let outbox: ApubCommunityOutbox = id
- .dereference(&outbox_data, context.client(), &mut 0)
+ .dereference::<LemmyError>(&outbox_data, local_instance(&context), &mut 0)
.await?;
Ok(create_apub_response(&outbox.into_apub(&outbox_data).await?))
}
let id = ObjectId::new(generate_outbox_url(&community.actor_id)?);
let outbox_data = CommunityContext(community, context.get_ref().clone());
let moderators: ApubCommunityModerators = id
- .dereference(&outbox_data, context.client(), &mut 0)
+ .dereference::<LemmyError>(&outbox_data, local_instance(&context), &mut 0)
.await?;
Ok(create_apub_response(
&moderators.into_apub(&outbox_data).await?,
use crate::{
activity_lists::SharedInboxActivities,
- check_is_apub_id_valid,
- context::WithContext,
fetcher::user_or_community::UserOrCommunity,
- http::{community::receive_group_inbox, person::receive_person_inbox},
insert_activity,
+ local_instance,
+ protocol::objects::tombstone::Tombstone,
+ CONTEXT,
};
-use actix_web::{
- web,
- web::{Bytes, BytesMut, Payload},
- HttpRequest,
- HttpResponse,
-};
-use anyhow::{anyhow, Context};
-use futures::StreamExt;
-use http::StatusCode;
-use lemmy_api_common::utils::blocking;
-use lemmy_apub_lib::{
+use activitypub_federation::{
+ core::inbox::{receive_activity, ActorPublicKey},
data::Data,
- object_id::ObjectId,
- signatures::verify_signature,
- traits::{ActivityHandler, ActorType},
+ deser::context::WithContext,
+ traits::{ActivityHandler, ApubObject},
APUB_JSON_CONTENT_TYPE,
};
+use actix_web::{web, HttpRequest, HttpResponse};
+use http::StatusCode;
+use lemmy_api_common::utils::blocking;
use lemmy_db_schema::source::activity::Activity;
-use lemmy_utils::{location_info, LemmyError};
+use lemmy_utils::error::LemmyError;
use lemmy_websocket::LemmyContext;
-use serde::{Deserialize, Serialize};
-use std::{fmt::Debug, io::Read};
-use tracing::{debug, info};
+use once_cell::sync::OnceCell;
+use serde::{de::DeserializeOwned, Deserialize, Serialize};
+use serde_json::Value;
+use std::ops::Deref;
+use tracing::{debug, log::info};
use url::Url;
mod comment;
#[tracing::instrument(skip_all)]
pub async fn shared_inbox(
request: HttpRequest,
- payload: Payload,
+ payload: String,
context: web::Data<LemmyContext>,
) -> Result<HttpResponse, LemmyError> {
- let unparsed = payload_to_string(payload).await?;
- info!("Received shared inbox activity {}", unparsed);
- let activity_data: ActivityCommonFields = serde_json::from_str(&unparsed)?;
- let activity = serde_json::from_str::<WithContext<SharedInboxActivities>>(&unparsed)?;
- match activity.inner() {
- SharedInboxActivities::GroupInboxActivities(g) => {
- receive_group_inbox(*g, activity_data, request, &context).await
- }
- SharedInboxActivities::PersonInboxActivities(p) => {
- receive_person_inbox(*p, activity_data, request, &context).await
- }
- }
+ receive_lemmy_activity::<SharedInboxActivities, UserOrCommunity>(request, payload, context).await
}
-async fn payload_to_string(mut payload: Payload) -> Result<String, LemmyError> {
- let mut bytes = BytesMut::new();
- while let Some(item) = payload.next().await {
- bytes.extend_from_slice(&item?);
- }
- let mut unparsed = String::new();
- Bytes::from(bytes).as_ref().read_to_string(&mut unparsed)?;
- Ok(unparsed)
-}
-
-#[derive(Clone, Debug, Deserialize, Serialize)]
-#[serde(rename_all = "camelCase")]
-pub(crate) struct ActivityCommonFields {
- pub(crate) id: Url,
- pub(crate) actor: Url,
-}
-
-// TODO: move most of this code to library
-#[tracing::instrument(skip_all)]
-async fn receive_activity<'a, T>(
+pub async fn receive_lemmy_activity<Activity, Actor>(
request: HttpRequest,
- activity: T,
- activity_data: ActivityCommonFields,
- context: &LemmyContext,
+ payload: String,
+ context: web::Data<LemmyContext>,
) -> Result<HttpResponse, LemmyError>
where
- T: ActivityHandler<DataType = LemmyContext>
- + Clone
- + Deserialize<'a>
- + Serialize
- + std::fmt::Debug
+ Activity: ActivityHandler<DataType = LemmyContext, Error = LemmyError>
+ + DeserializeOwned
+ Send
+ 'static,
+ Actor: ApubObject<DataType = LemmyContext, Error = LemmyError> + ActorPublicKey + Send + 'static,
+ for<'de2> <Actor as ApubObject>::ApubType: serde::Deserialize<'de2>,
{
- check_is_apub_id_valid(&activity_data.actor, false, &context.settings())?;
- let request_counter = &mut 0;
- let actor = ObjectId::<UserOrCommunity>::new(activity_data.actor)
- .dereference(context, context.client(), request_counter)
- .await?;
- verify_signature(&request, &actor.public_key())?;
-
- info!("Verifying activity {}", activity_data.id.to_string());
- activity
- .verify(&Data::new(context.clone()), request_counter)
- .await?;
- assert_activity_not_local(&activity_data.id, &context.settings().hostname)?;
-
- // Log the activity, so we avoid receiving and parsing it twice. Note that this could still happen
- // if we receive the same activity twice in very quick succession.
- let object_value = serde_json::to_value(&activity)?;
- let insert =
- insert_activity(&activity_data.id, object_value, false, true, context.pool()).await?;
+ let activity_value: Value = serde_json::from_str(&payload)?;
+ let activity: Activity = serde_json::from_value(activity_value.clone())?;
+ // Log the activity, so we avoid receiving and parsing it twice.
+ let insert = insert_activity(activity.id(), activity_value, false, true, context.pool()).await?;
if !insert {
- debug!(
- "Received duplicate activity {}",
- activity_data.id.to_string()
- );
+ debug!("Received duplicate activity {}", activity.id().to_string());
return Ok(HttpResponse::BadRequest().finish());
}
-
- info!("Receiving activity {}", activity_data.id.to_string());
- activity
- .receive(&Data::new(context.clone()), request_counter)
- .await?;
- Ok(HttpResponse::Ok().finish())
+ info!("Received activity {}", payload);
+
+ static DATA: OnceCell<Data<LemmyContext>> = OnceCell::new();
+ let data = DATA.get_or_init(|| Data::new(context.get_ref().clone()));
+ receive_activity::<Activity, Actor, LemmyContext, LemmyError>(
+ request,
+ activity,
+ local_instance(&context),
+ data,
+ )
+ .await
}
/// Convert the data to json and turn it into an HTTP Response with the correct ActivityPub
{
HttpResponse::Ok()
.content_type(APUB_JSON_CONTENT_TYPE)
- .json(WithContext::new(data))
+ .json(WithContext::new(data, CONTEXT.deref().clone()))
}
fn create_json_apub_response(data: serde_json::Value) -> HttpResponse {
.json(data)
}
-fn create_apub_tombstone_response<T>(data: &T) -> HttpResponse
-where
- T: Serialize,
-{
+fn create_apub_tombstone_response<T: Into<Url>>(id: T) -> HttpResponse {
+ let tombstone = Tombstone::new(id.into());
HttpResponse::Gone()
.content_type(APUB_JSON_CONTENT_TYPE)
.status(StatusCode::GONE)
- .json(WithContext::new(data))
+ .json(WithContext::new(tombstone, CONTEXT.deref().clone()))
}
#[derive(Deserialize)]
Ok(create_json_apub_response(activity.data))
}
}
-
-fn assert_activity_not_local(id: &Url, hostname: &str) -> Result<(), LemmyError> {
- let activity_domain = id.domain().context(location_info!())?;
-
- if activity_domain == hostname {
- let err = anyhow!(
- "Error: received activity which was sent by local instance: {:?}",
- id
- );
- return Err(LemmyError::from_error_message(
- err,
- "received_local_activity",
- ));
- }
- Ok(())
-}
use crate::{
activity_lists::PersonInboxActivities,
- context::WithContext,
+ fetcher::user_or_community::UserOrCommunity,
generate_outbox_url,
- http::{
- create_apub_response,
- create_apub_tombstone_response,
- payload_to_string,
- receive_activity,
- ActivityCommonFields,
- },
+ http::{create_apub_response, create_apub_tombstone_response, receive_lemmy_activity},
objects::person::ApubPerson,
protocol::collections::empty_outbox::EmptyOutbox,
};
-use actix_web::{web, web::Payload, HttpRequest, HttpResponse};
+use activitypub_federation::{deser::context::WithContext, traits::ApubObject};
+use actix_web::{web, HttpRequest, HttpResponse};
use lemmy_api_common::utils::blocking;
-use lemmy_apub_lib::traits::ApubObject;
use lemmy_db_schema::{source::person::Person, traits::ApubActor};
-use lemmy_utils::LemmyError;
+use lemmy_utils::error::LemmyError;
use lemmy_websocket::LemmyContext;
use serde::Deserialize;
-use tracing::info;
#[derive(Deserialize)]
pub struct PersonQuery {
Ok(create_apub_response(&apub))
} else {
- Ok(create_apub_tombstone_response(&person.to_tombstone()?))
+ Ok(create_apub_tombstone_response(person.actor_id.clone()))
}
}
#[tracing::instrument(skip_all)]
pub async fn person_inbox(
request: HttpRequest,
- payload: Payload,
- _path: web::Path<String>,
+ payload: String,
context: web::Data<LemmyContext>,
) -> Result<HttpResponse, LemmyError> {
- let unparsed = payload_to_string(payload).await?;
- info!("Received person inbox activity {}", unparsed);
- let activity_data: ActivityCommonFields = serde_json::from_str(&unparsed)?;
- let activity = serde_json::from_str::<WithContext<PersonInboxActivities>>(&unparsed)?;
- receive_person_inbox(activity.inner(), activity_data, request, &context).await
-}
-
-pub(in crate::http) async fn receive_person_inbox(
- activity: PersonInboxActivities,
- activity_data: ActivityCommonFields,
- request: HttpRequest,
- context: &LemmyContext,
-) -> Result<HttpResponse, LemmyError> {
- receive_activity(request, activity, activity_data, context).await
+ receive_lemmy_activity::<WithContext<PersonInboxActivities>, UserOrCommunity>(
+ request, payload, context,
+ )
+ .await
}
#[tracing::instrument(skip_all)]
http::{create_apub_response, create_apub_tombstone_response},
objects::post::ApubPost,
};
+use activitypub_federation::traits::ApubObject;
use actix_web::{web, HttpResponse};
use diesel::result::Error::NotFound;
use lemmy_api_common::utils::blocking;
-use lemmy_apub_lib::traits::ApubObject;
use lemmy_db_schema::{newtypes::PostId, source::post::Post, traits::Crud};
-use lemmy_utils::LemmyError;
+use lemmy_utils::error::LemmyError;
use lemmy_websocket::LemmyContext;
use serde::Deserialize;
if !post.deleted {
Ok(create_apub_response(&post.into_apub(&context).await?))
} else {
- Ok(create_apub_tombstone_response(&post.to_tombstone()?))
+ Ok(create_apub_tombstone_response(post.ap_id.clone()))
}
}
use crate::{
activity_lists::SiteInboxActivities,
- context::WithContext,
- http::{create_apub_response, payload_to_string, receive_activity, ActivityCommonFields},
- objects::instance::ApubSite,
+ http::{create_apub_response, receive_lemmy_activity},
+ objects::{instance::ApubSite, person::ApubPerson},
protocol::collections::empty_outbox::EmptyOutbox,
};
-use actix_web::{web, web::Payload, HttpRequest, HttpResponse};
+use activitypub_federation::{deser::context::WithContext, traits::ApubObject};
+use actix_web::{web, HttpRequest, HttpResponse};
use lemmy_api_common::utils::blocking;
-use lemmy_apub_lib::traits::ApubObject;
use lemmy_db_schema::source::site::Site;
-use lemmy_utils::{settings::structs::Settings, LemmyError};
+use lemmy_utils::{error::LemmyError, settings::structs::Settings};
use lemmy_websocket::LemmyContext;
-use tracing::info;
use url::Url;
pub(crate) async fn get_apub_site_http(
#[tracing::instrument(skip_all)]
pub async fn get_apub_site_inbox(
request: HttpRequest,
- payload: Payload,
+ payload: String,
context: web::Data<LemmyContext>,
) -> Result<HttpResponse, LemmyError> {
- let unparsed = payload_to_string(payload).await?;
- info!("Received site inbox activity {}", unparsed);
- let activity_data: ActivityCommonFields = serde_json::from_str(&unparsed)?;
- let activity = serde_json::from_str::<WithContext<SiteInboxActivities>>(&unparsed)?;
- receive_activity(request, activity.inner(), activity_data, &context).await
+ receive_lemmy_activity::<WithContext<SiteInboxActivities>, ApubPerson>(request, payload, context)
+ .await
}
use crate::fetcher::post_or_comment::PostOrComment;
-use anyhow::{anyhow, Context};
+use activitypub_federation::{
+ core::{inbox::ActorPublicKey, signatures::PublicKey},
+ InstanceSettingsBuilder,
+ LocalInstance,
+};
+use anyhow::Context;
use lemmy_api_common::utils::blocking;
use lemmy_db_schema::{newtypes::DbUrl, source::activity::Activity, utils::DbPool};
-use lemmy_utils::{location_info, settings::structs::Settings, LemmyError};
-use serde::{Deserialize, Deserializer};
-use std::net::IpAddr;
+use lemmy_utils::{error::LemmyError, location_info, settings::structs::Settings};
+use lemmy_websocket::LemmyContext;
+use once_cell::sync::{Lazy, OnceCell};
+use std::env;
use url::{ParseError, Url};
pub mod activities;
pub(crate) mod activity_lists;
pub(crate) mod collections;
-mod context;
pub mod fetcher;
pub mod http;
pub(crate) mod mentions;
pub mod objects;
pub mod protocol;
+static CONTEXT: Lazy<Vec<serde_json::Value>> = Lazy::new(|| {
+ serde_json::from_str(include_str!("../assets/lemmy/context.json")).expect("parse context")
+});
+
+// TODO: store this in context? but its only used in this crate, no need to expose it elsewhere
+fn local_instance(context: &LemmyContext) -> &'static LocalInstance {
+ static LOCAL_INSTANCE: OnceCell<LocalInstance> = OnceCell::new();
+ LOCAL_INSTANCE.get_or_init(|| {
+ let settings = InstanceSettingsBuilder::default()
+ .http_fetch_retry_limit(context.settings().http_fetch_retry_limit)
+ .worker_count(context.settings().federation.worker_count)
+ .testing_send_sync(env::var("APUB_TESTING_SEND_SYNC").is_ok())
+ .verify_url_function(|url| check_apub_id_valid(url, &Settings::get()))
+ .build()
+ .expect("configure federation");
+ LocalInstance::new(
+ context.settings().hostname,
+ context.client().clone(),
+ settings,
+ )
+ })
+}
+
/// Checks if the ID is allowed for sending or receiving.
///
/// In particular, it checks for:
/// `use_strict_allowlist` should be true only when parsing a remote community, or when parsing a
/// post/comment in a local community.
#[tracing::instrument(skip(settings))]
-pub(crate) fn check_is_apub_id_valid(
- apub_id: &Url,
- use_strict_allowlist: bool,
- settings: &Settings,
-) -> Result<(), LemmyError> {
- let domain = apub_id.domain().context(location_info!())?.to_string();
- let local_instance = settings.get_hostname_without_port()?;
-
- if !settings.federation.enabled {
- return if domain == local_instance {
- Ok(())
- } else {
- let err = anyhow!(
- "Trying to connect with {}, but federation is disabled",
- domain
- );
- Err(LemmyError::from_error_message(err, "federation_disabled"))
- };
+fn check_apub_id_valid(apub_id: &Url, settings: &Settings) -> Result<(), &'static str> {
+ let domain = apub_id.domain().expect("apud id has domain").to_string();
+ let local_instance = settings
+ .get_hostname_without_port()
+ .expect("local hostname is valid");
+ if domain == local_instance {
+ return Ok(());
}
- let host = apub_id.host_str().context(location_info!())?;
- let host_as_ip = host.parse::<IpAddr>();
- if host == "localhost" || host_as_ip.is_ok() {
- let err = anyhow!("invalid hostname {}: {}", host, apub_id);
- return Err(LemmyError::from_error_message(err, "invalid_hostname"));
+ if !settings.federation.enabled {
+ return Err("Federation disabled");
}
if apub_id.scheme() != settings.get_protocol_string() {
- let err = anyhow!("invalid apub id scheme {}: {}", apub_id.scheme(), apub_id);
- return Err(LemmyError::from_error_message(err, "invalid_scheme"));
+ return Err("Invalid protocol scheme");
}
- // TODO: might be good to put the part above in one method, and below in another
- // (which only gets called in apub::objects)
- // -> no that doesnt make sense, we still need the code below for blocklist and strict allowlist
if let Some(blocked) = settings.to_owned().federation.blocked_instances {
if blocked.contains(&domain) {
- let err = anyhow!("{} is in federation blocklist", domain);
- return Err(LemmyError::from_error_message(err, "federation_blocked"));
+ return Err("Domain is blocked");
}
}
+ if let Some(allowed) = settings.to_owned().federation.allowed_instances {
+ if !allowed.contains(&domain) {
+ return Err("Domain is not in allowlist");
+ }
+ }
+
+ Ok(())
+}
+
+#[tracing::instrument(skip(settings))]
+pub(crate) fn check_apub_id_valid_with_strictness(
+ apub_id: &Url,
+ is_strict: bool,
+ settings: &Settings,
+) -> Result<(), LemmyError> {
+ check_apub_id_valid(apub_id, settings).map_err(LemmyError::from_message)?;
+ let domain = apub_id.domain().expect("apud id has domain").to_string();
+ let local_instance = settings
+ .get_hostname_without_port()
+ .expect("local hostname is valid");
+ if domain == local_instance {
+ return Ok(());
+ }
+
if let Some(mut allowed) = settings.to_owned().federation.allowed_instances {
// Only check allowlist if this is a community, or strict allowlist is enabled.
let strict_allowlist = settings.to_owned().federation.strict_allowlist;
- if use_strict_allowlist || strict_allowlist {
+ if is_strict || strict_allowlist {
// need to allow this explicitly because apub receive might contain objects from our local
// instance.
allowed.push(local_instance);
if !allowed.contains(&domain) {
- let err = anyhow!("{} not in federation allowlist", domain);
- return Err(LemmyError::from_error_message(
- err,
- "federation_not_allowed",
+ return Err(LemmyError::from_message(
+ "Federation forbidden by strict allowlist",
));
}
}
}
-
Ok(())
}
-pub(crate) fn deserialize_one_or_many<'de, T, D>(deserializer: D) -> Result<Vec<T>, D::Error>
-where
- T: Deserialize<'de>,
- D: Deserializer<'de>,
-{
- #[derive(Deserialize)]
- #[serde(untagged)]
- enum OneOrMany<T> {
- One(T),
- Many(Vec<T>),
- }
-
- let result: OneOrMany<T> = Deserialize::deserialize(deserializer)?;
- Ok(match result {
- OneOrMany::Many(list) => list,
- OneOrMany::One(value) => vec![value],
- })
-}
-
-pub(crate) fn deserialize_one<'de, T, D>(deserializer: D) -> Result<[T; 1], D::Error>
-where
- T: Deserialize<'de>,
- D: Deserializer<'de>,
-{
- #[derive(Deserialize)]
- #[serde(untagged)]
- enum MaybeArray<T> {
- Simple(T),
- Array([T; 1]),
- }
-
- let result: MaybeArray<T> = Deserialize::deserialize(deserializer)?;
- Ok(match result {
- MaybeArray::Simple(value) => [value],
- MaybeArray::Array(value) => value,
- })
-}
-
-pub(crate) fn deserialize_skip_error<'de, T, D>(deserializer: D) -> Result<T, D::Error>
-where
- T: Deserialize<'de> + Default,
- D: Deserializer<'de>,
-{
- let result = Deserialize::deserialize(deserializer);
- Ok(match result {
- Ok(o) => o,
- Err(_) => Default::default(),
- })
-}
-
pub enum EndpointType {
Community,
Person,
.await??,
)
}
+
+/// Common methods provided by ActivityPub actors (community and person). Not all methods are
+/// implemented by all actors.
+pub trait ActorType: ActorPublicKey {
+ fn actor_id(&self) -> Url;
+
+ fn private_key(&self) -> Option<String>;
+
+ fn inbox_url(&self) -> Url;
+
+ fn shared_inbox_url(&self) -> Option<Url>;
+
+ fn shared_inbox_or_inbox_url(&self) -> Url {
+ self.shared_inbox_url().unwrap_or_else(|| self.inbox_url())
+ }
+
+ fn get_public_key(&self) -> PublicKey {
+ PublicKey::new_main_key(self.actor_id(), self.public_key().to_string())
+ }
+}
use crate::{
fetcher::webfinger::webfinger_resolve_actor,
objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson},
+ ActorType,
};
+use activitypub_federation::core::object_id::ObjectId;
use activitystreams_kinds::link::MentionType;
use lemmy_api_common::utils::blocking;
-use lemmy_apub_lib::{object_id::ObjectId, traits::ActorType};
use lemmy_db_schema::{
source::{comment::Comment, person::Person, post::Post},
traits::Crud,
utils::DbPool,
};
use lemmy_utils::{
+ error::LemmyError,
utils::{scrape_text_for_mentions, MentionData},
- LemmyError,
};
use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize};
use crate::{
activities::{verify_is_public, verify_person_in_community},
- check_is_apub_id_valid,
+ check_apub_id_valid_with_strictness,
+ local_instance,
mentions::collect_non_local_mentions,
objects::{read_from_string_or_source, verify_is_remote_object},
- protocol::{
- objects::{note::Note, tombstone::Tombstone},
- Source,
- },
+ protocol::{objects::note::Note, Source},
PostOrComment,
};
+use activitypub_federation::{
+ core::object_id::ObjectId,
+ deser::values::MediaTypeMarkdownOrHtml,
+ traits::ApubObject,
+ utils::verify_domains_match,
+};
use activitystreams_kinds::{object::NoteType, public};
use chrono::NaiveDateTime;
use lemmy_api_common::utils::blocking;
-use lemmy_apub_lib::{
- object_id::ObjectId,
- traits::ApubObject,
- values::MediaTypeMarkdownOrHtml,
- verify::verify_domains_match,
-};
use lemmy_db_schema::{
source::{
comment::{Comment, CommentForm},
traits::Crud,
};
use lemmy_utils::{
+ error::LemmyError,
utils::{convert_datetime, markdown_to_html, remove_slurs},
- LemmyError,
};
use lemmy_websocket::LemmyContext;
use std::ops::Deref;
type DataType = LemmyContext;
type ApubType = Note;
type DbType = Comment;
- type TombstoneType = Tombstone;
+ type Error = LemmyError;
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
None
Ok(note)
}
- fn to_tombstone(&self) -> Result<Tombstone, LemmyError> {
- Ok(Tombstone::new(self.ap_id.clone().into()))
- }
-
#[tracing::instrument(skip_all)]
async fn verify(
note: &Note,
Community::read(conn, community_id)
})
.await??;
- check_is_apub_id_valid(note.id.inner(), community.local, &context.settings())?;
+ check_apub_id_valid_with_strictness(note.id.inner(), community.local, &context.settings())?;
verify_is_remote_object(note.id.inner())?;
verify_person_in_community(
¬e.attributed_to,
) -> Result<ApubComment, LemmyError> {
let creator = note
.attributed_to
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, local_instance(context), request_counter)
.await?;
let (post, parent_comment_id) = note.get_parents(context, request_counter).await?;
use crate::{
- check_is_apub_id_valid,
+ check_apub_id_valid_with_strictness,
collections::{community_moderators::ApubCommunityModerators, CommunityContext},
generate_moderators_url,
generate_outbox_url,
+ local_instance,
objects::instance::fetch_instance_actor_for_object,
protocol::{
- objects::{group::Group, tombstone::Tombstone, Endpoints},
+ objects::{group::Group, Endpoints},
ImageObject,
Source,
},
+ ActorType,
+};
+use activitypub_federation::{
+ core::{inbox::ActorPublicKey, object_id::ObjectId},
+ traits::ApubObject,
};
use activitystreams_kinds::actor::GroupType;
use chrono::NaiveDateTime;
use itertools::Itertools;
use lemmy_api_common::utils::blocking;
-use lemmy_apub_lib::{
- object_id::ObjectId,
- traits::{ActorType, ApubObject},
-};
use lemmy_db_schema::{source::community::Community, traits::ApubActor};
use lemmy_db_views_actor::structs::CommunityFollowerView;
use lemmy_utils::{
+ error::LemmyError,
utils::{convert_datetime, markdown_to_html},
- LemmyError,
};
use lemmy_websocket::LemmyContext;
use std::ops::Deref;
type DataType = LemmyContext;
type ApubType = Group;
type DbType = Community;
- type TombstoneType = Tombstone;
+ type Error = LemmyError;
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
Some(self.last_refreshed_at)
endpoints: self.shared_inbox_url.clone().map(|s| Endpoints {
shared_inbox: s.into(),
}),
- public_key: self.get_public_key()?,
+ public_key: self.get_public_key(),
published: Some(convert_datetime(self.published)),
updated: self.updated.map(convert_datetime),
posting_restricted_to_mods: Some(self.posting_restricted_to_mods),
Ok(group)
}
- fn to_tombstone(&self) -> Result<Tombstone, LemmyError> {
- Ok(Tombstone::new(self.actor_id()))
- }
-
#[tracing::instrument(skip_all)]
async fn verify(
group: &Group,
group
.outbox
- .dereference(&outbox_data, context.client(), request_counter)
+ .dereference::<LemmyError>(&outbox_data, local_instance(context), request_counter)
.await
.map_err(|e| debug!("{}", e))
.ok();
if let Some(moderators) = &group.moderators {
moderators
- .dereference(&outbox_data, context.client(), request_counter)
+ .dereference::<LemmyError>(&outbox_data, local_instance(context), request_counter)
.await
.map_err(|e| debug!("{}", e))
.ok();
fn actor_id(&self) -> Url {
self.actor_id.to_owned().into()
}
- fn public_key(&self) -> String {
- self.public_key.to_owned()
- }
fn private_key(&self) -> Option<String> {
self.private_key.to_owned()
}
}
}
+impl ActorPublicKey for ApubCommunity {
+ fn public_key(&self) -> &str {
+ &self.public_key
+ }
+}
+
impl ApubCommunity {
/// For a given community, returns the inboxes of all followers.
#[tracing::instrument(skip_all)]
.unique()
.filter(|inbox: &Url| inbox.host_str() != Some(&context.settings().hostname))
// Don't send to blocked instances
- .filter(|inbox| check_is_apub_id_valid(inbox, false, &context.settings()).is_ok())
+ .filter(|inbox| {
+ check_apub_id_valid_with_strictness(inbox, false, &context.settings()).is_ok()
+ })
.collect();
Ok(inboxes)
use crate::{
- check_is_apub_id_valid,
+ check_apub_id_valid_with_strictness,
+ local_instance,
objects::read_from_string_or_source_opt,
protocol::{
objects::instance::{Instance, InstanceType},
ImageObject,
Source,
},
+ ActorType,
+};
+use activitypub_federation::{
+ core::{inbox::ActorPublicKey, object_id::ObjectId},
+ deser::values::MediaTypeHtml,
+ traits::ApubObject,
+ utils::verify_domains_match,
};
use chrono::NaiveDateTime;
use lemmy_api_common::utils::blocking;
-use lemmy_apub_lib::{
- object_id::ObjectId,
- traits::{ActorType, ApubObject},
- values::MediaTypeHtml,
- verify::verify_domains_match,
-};
use lemmy_db_schema::{
source::site::{Site, SiteForm},
utils::naive_now,
};
use lemmy_utils::{
+ error::LemmyError,
utils::{check_slurs, check_slurs_opt, convert_datetime, markdown_to_html},
- LemmyError,
};
use lemmy_websocket::LemmyContext;
use std::ops::Deref;
type DataType = LemmyContext;
type ApubType = Instance;
type DbType = Site;
- type TombstoneType = ();
+ type Error = LemmyError;
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
Some(self.last_refreshed_at)
image: self.banner.clone().map(ImageObject::new),
inbox: self.inbox_url.clone().into(),
outbox: Url::parse(&format!("{}/site_outbox", self.actor_id))?,
- public_key: self.get_public_key()?,
+ public_key: self.get_public_key(),
published: convert_datetime(self.published),
updated: self.updated.map(convert_datetime),
};
Ok(instance)
}
- fn to_tombstone(&self) -> Result<Self::TombstoneType, LemmyError> {
- unimplemented!()
- }
-
#[tracing::instrument(skip_all)]
async fn verify(
apub: &Self::ApubType,
data: &Self::DataType,
_request_counter: &mut i32,
) -> Result<(), LemmyError> {
- check_is_apub_id_valid(apub.id.inner(), true, &data.settings())?;
+ check_apub_id_valid_with_strictness(apub.id.inner(), true, &data.settings())?;
verify_domains_match(expected_domain, apub.id.inner())?;
let slur_regex = &data.settings().slur_regex();
fn actor_id(&self) -> Url {
self.actor_id.to_owned().into()
}
- fn public_key(&self) -> String {
- self.public_key.to_owned()
- }
fn private_key(&self) -> Option<String> {
self.private_key.to_owned()
}
}
}
+impl ActorPublicKey for ApubSite {
+ fn public_key(&self) -> &str {
+ &self.public_key
+ }
+}
+
/// Instance actor is at the root path, so we simply need to clear the path and other unnecessary
/// parts of the url.
pub fn instance_actor_id_from_url(mut url: Url) -> Url {
// try to fetch the instance actor (to make things like instance rules available)
let instance_id = instance_actor_id_from_url(object_id);
let site = ObjectId::<ApubSite>::new(instance_id.clone())
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, local_instance(context), request_counter)
.await;
if let Err(e) = site {
debug!("Failed to dereference site for {}: {}", instance_id, e);
use crate::protocol::Source;
+use activitypub_federation::deser::values::MediaTypeMarkdownOrHtml;
use anyhow::anyhow;
use html2md::parse_html;
-use lemmy_apub_lib::values::MediaTypeMarkdownOrHtml;
-use lemmy_utils::{settings::structs::Settings, LemmyError};
+use lemmy_utils::{error::LemmyError, settings::structs::Settings};
use url::Url;
pub mod comment;
PgConnection,
};
use lemmy_api_common::request::build_user_agent;
- use lemmy_apub_lib::activity_queue::create_activity_queue;
use lemmy_db_schema::{
source::secret::Secret,
utils::{establish_unpooled_connection, get_database_url_from_env},
};
use lemmy_utils::{
+ error::LemmyError,
rate_limit::{rate_limiter::RateLimiter, RateLimit},
settings::structs::Settings,
- LemmyError,
};
use lemmy_websocket::{chat_server::ChatServer, LemmyContext};
use parking_lot::Mutex;
use std::sync::Arc;
// TODO: would be nice if we didnt have to use a full context for tests.
- // or at least write a helper function so this code is shared with main.rs
pub(crate) fn init_context() -> LemmyContext {
- let client = reqwest::Client::new().into();
- // activity queue isnt used in tests, so worker count makes no difference
- let queue_manager = create_activity_queue(client, 4);
- let activity_queue = queue_manager.queue_handle().clone();
// call this to run migrations
establish_unpooled_connection();
let settings = Settings::init().unwrap();
|_, _, _, _| Box::pin(x()),
|_, _, _, _| Box::pin(x()),
client.clone(),
- activity_queue.clone(),
settings.clone(),
secret.clone(),
)
.start();
- LemmyContext::create(pool, chat_server, client, activity_queue, settings, secret)
+ LemmyContext::create(pool, chat_server, client, settings, secret)
}
}
use crate::{
- check_is_apub_id_valid,
+ check_apub_id_valid_with_strictness,
generate_outbox_url,
objects::{instance::fetch_instance_actor_for_object, read_from_string_or_source_opt},
protocol::{
ImageObject,
Source,
},
+ ActorType,
+};
+use activitypub_federation::{
+ core::{inbox::ActorPublicKey, object_id::ObjectId},
+ traits::ApubObject,
+ utils::verify_domains_match,
};
use chrono::NaiveDateTime;
use lemmy_api_common::utils::blocking;
-use lemmy_apub_lib::{
- object_id::ObjectId,
- traits::{ActorType, ApubObject},
- verify::verify_domains_match,
-};
use lemmy_db_schema::{
source::person::{Person as DbPerson, PersonForm},
traits::ApubActor,
utils::naive_now,
};
use lemmy_utils::{
+ error::LemmyError,
utils::{check_slurs, check_slurs_opt, convert_datetime, markdown_to_html},
- LemmyError,
};
use lemmy_websocket::LemmyContext;
use std::ops::Deref;
type DataType = LemmyContext;
type ApubType = Person;
type DbType = DbPerson;
- type TombstoneType = ();
+ type Error = LemmyError;
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
Some(self.last_refreshed_at)
endpoints: self.shared_inbox_url.clone().map(|s| Endpoints {
shared_inbox: s.into(),
}),
- public_key: self.get_public_key()?,
+ public_key: self.get_public_key(),
updated: self.updated.map(convert_datetime),
inbox: self.inbox_url.clone().into(),
};
Ok(person)
}
- fn to_tombstone(&self) -> Result<(), LemmyError> {
- unimplemented!()
- }
-
#[tracing::instrument(skip_all)]
async fn verify(
person: &Person,
_request_counter: &mut i32,
) -> Result<(), LemmyError> {
verify_domains_match(person.id.inner(), expected_domain)?;
- check_is_apub_id_valid(person.id.inner(), false, &context.settings())?;
+ check_apub_id_valid_with_strictness(person.id.inner(), false, &context.settings())?;
let slur_regex = &context.settings().slur_regex();
check_slurs(&person.preferred_username, slur_regex)?;
self.actor_id.to_owned().into()
}
- fn public_key(&self) -> String {
- self.public_key.to_owned()
- }
-
fn private_key(&self) -> Option<String> {
self.private_key.to_owned()
}
}
}
+impl ActorPublicKey for ApubPerson {
+ fn public_key(&self) -> &str {
+ &self.public_key
+ }
+}
+
#[cfg(test)]
pub(crate) mod tests {
use super::*;
use crate::{
activities::{verify_is_public, verify_person_in_community},
- check_is_apub_id_valid,
+ check_apub_id_valid_with_strictness,
+ local_instance,
objects::{read_from_string_or_source_opt, verify_is_remote_object},
protocol::{
- objects::{
- page::{Attachment, AttributedTo, Page, PageType},
- tombstone::Tombstone,
- },
+ objects::page::{Attachment, AttributedTo, Page, PageType},
ImageObject,
Source,
},
};
+use activitypub_federation::{
+ core::object_id::ObjectId,
+ deser::values::MediaTypeMarkdownOrHtml,
+ traits::ApubObject,
+ utils::verify_domains_match,
+};
use activitystreams_kinds::public;
use chrono::NaiveDateTime;
use lemmy_api_common::{request::fetch_site_data, utils::blocking};
-use lemmy_apub_lib::{
- object_id::ObjectId,
- traits::ApubObject,
- values::MediaTypeMarkdownOrHtml,
- verify::verify_domains_match,
-};
use lemmy_db_schema::{
- self,
source::{
community::Community,
moderator::{ModLockPost, ModLockPostForm, ModStickyPost, ModStickyPostForm},
post::{Post, PostForm},
},
traits::Crud,
+ {self},
};
use lemmy_utils::{
+ error::LemmyError,
utils::{check_slurs, convert_datetime, markdown_to_html, remove_slurs},
- LemmyError,
};
use lemmy_websocket::LemmyContext;
use std::ops::Deref;
type DataType = LemmyContext;
type ApubType = Page;
type DbType = Post;
- type TombstoneType = Tombstone;
+ type Error = LemmyError;
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
None
Ok(page)
}
- fn to_tombstone(&self) -> Result<Tombstone, LemmyError> {
- Ok(Tombstone::new(self.ap_id.clone().into()))
- }
-
#[tracing::instrument(skip_all)]
async fn verify(
page: &Page,
};
let community = page.extract_community(context, request_counter).await?;
- check_is_apub_id_valid(page.id.inner(), community.local, &context.settings())?;
+ check_apub_id_valid_with_strictness(page.id.inner(), community.local, &context.settings())?;
verify_person_in_community(&page.creator()?, &community, context, request_counter).await?;
check_slurs(&page.name, &context.settings().slur_regex())?;
verify_domains_match(page.creator()?.inner(), page.id.inner())?;
) -> Result<ApubPost, LemmyError> {
let creator = page
.creator()?
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, local_instance(context), request_counter)
.await?;
let community = page.extract_community(context, request_counter).await?;
// read existing, local post if any (for generating mod log)
let old_post = ObjectId::<ApubPost>::new(page.id.clone())
- .dereference_local(context)
+ .dereference_local::<LemmyError>(context)
.await;
let post = blocking(context.pool(), move |conn| Post::upsert(conn, &form)).await??;
use crate::{
+ check_apub_id_valid_with_strictness,
+ local_instance,
objects::read_from_string_or_source,
protocol::{
objects::chat_message::{ChatMessage, ChatMessageType},
Source,
},
};
-use chrono::NaiveDateTime;
-use lemmy_api_common::utils::blocking;
-use lemmy_apub_lib::{
- object_id::ObjectId,
+use activitypub_federation::{
+ core::object_id::ObjectId,
+ deser::values::MediaTypeHtml,
traits::ApubObject,
- values::MediaTypeHtml,
- verify::verify_domains_match,
+ utils::verify_domains_match,
};
+use chrono::NaiveDateTime;
+use lemmy_api_common::utils::blocking;
use lemmy_db_schema::{
source::{
person::Person,
traits::Crud,
};
use lemmy_utils::{
+ error::LemmyError,
+ settings::structs::Settings,
utils::{convert_datetime, markdown_to_html},
- LemmyError,
};
use lemmy_websocket::LemmyContext;
use std::ops::Deref;
type DataType = LemmyContext;
type ApubType = ChatMessage;
type DbType = PrivateMessage;
- type TombstoneType = ();
+ type Error = LemmyError;
fn last_refreshed_at(&self) -> Option<NaiveDateTime> {
None
r#type: ChatMessageType::ChatMessage,
id: ObjectId::new(self.ap_id.clone()),
attributed_to: ObjectId::new(creator.actor_id),
- to: [ObjectId::new(recipient.actor_id)],
+ to: ObjectId::new(recipient.actor_id),
content: markdown_to_html(&self.content),
media_type: Some(MediaTypeHtml::Html),
source: Some(Source::new(self.content.clone())),
Ok(note)
}
- fn to_tombstone(&self) -> Result<(), LemmyError> {
- unimplemented!()
- }
-
#[tracing::instrument(skip_all)]
async fn verify(
note: &ChatMessage,
) -> Result<(), LemmyError> {
verify_domains_match(note.id.inner(), expected_domain)?;
verify_domains_match(note.attributed_to.inner(), note.id.inner())?;
+ check_apub_id_valid_with_strictness(note.id.inner(), false, &Settings::get())?;
let person = note
.attributed_to
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, local_instance(context), request_counter)
.await?;
if person.banned {
return Err(LemmyError::from_message("Person is banned from site"));
) -> Result<ApubPrivateMessage, LemmyError> {
let creator = note
.attributed_to
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, local_instance(context), request_counter)
.await?;
- let recipient = note.to[0]
- .dereference(context, context.client(), request_counter)
+ let recipient = note
+ .to
+ .dereference::<LemmyError>(context, local_instance(context), request_counter)
.await?;
let form = PrivateMessageForm {
use crate::{activities::block::SiteOrCommunity, objects::person::ApubPerson, protocol::Unparsed};
+use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
use activitystreams_kinds::activity::BlockType;
use chrono::{DateTime, FixedOffset};
-use lemmy_apub_lib::object_id::ObjectId;
use serde::{Deserialize, Serialize};
use url::Url;
#[serde(rename_all = "camelCase")]
pub struct BlockUser {
pub(crate) actor: ObjectId<ApubPerson>,
- #[serde(deserialize_with = "crate::deserialize_one_or_many")]
+ #[serde(deserialize_with = "deserialize_one_or_many")]
pub(crate) to: Vec<Url>,
pub(crate) object: ObjectId<ApubPerson>,
- #[serde(deserialize_with = "crate::deserialize_one_or_many")]
+ #[serde(deserialize_with = "deserialize_one_or_many")]
pub(crate) cc: Vec<Url>,
pub(crate) target: ObjectId<SiteOrCommunity>,
#[serde(rename = "type")]
objects::person::ApubPerson,
protocol::{activities::block::block_user::BlockUser, Unparsed},
};
+use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
use activitystreams_kinds::activity::UndoType;
-use lemmy_apub_lib::object_id::ObjectId;
use serde::{Deserialize, Serialize};
use url::Url;
#[serde(rename_all = "camelCase")]
pub struct UndoBlockUser {
pub(crate) actor: ObjectId<ApubPerson>,
- #[serde(deserialize_with = "crate::deserialize_one_or_many")]
+ #[serde(deserialize_with = "deserialize_one_or_many")]
pub(crate) to: Vec<Url>,
pub(crate) object: BlockUser,
- #[serde(deserialize_with = "crate::deserialize_one_or_many")]
+ #[serde(deserialize_with = "deserialize_one_or_many")]
pub(crate) cc: Vec<Url>,
#[serde(rename = "type")]
pub(crate) kind: UndoType,
use crate::{objects::person::ApubPerson, protocol::Unparsed};
+use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
use activitystreams_kinds::activity::AddType;
-use lemmy_apub_lib::object_id::ObjectId;
use serde::{Deserialize, Serialize};
use url::Url;
#[serde(rename_all = "camelCase")]
pub struct AddMod {
pub(crate) actor: ObjectId<ApubPerson>,
- #[serde(deserialize_with = "crate::deserialize_one_or_many")]
+ #[serde(deserialize_with = "deserialize_one_or_many")]
pub(crate) to: Vec<Url>,
pub(crate) object: ObjectId<ApubPerson>,
pub(crate) target: Url,
- #[serde(deserialize_with = "crate::deserialize_one_or_many")]
+ #[serde(deserialize_with = "deserialize_one_or_many")]
pub(crate) cc: Vec<Url>,
#[serde(rename = "type")]
pub(crate) kind: AddType,
objects::community::ApubCommunity,
protocol::{IdOrNestedObject, Unparsed},
};
+use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
use activitystreams_kinds::activity::AnnounceType;
-use lemmy_apub_lib::object_id::ObjectId;
use serde::{Deserialize, Serialize};
use url::Url;
#[serde(rename_all = "camelCase")]
pub struct AnnounceActivity {
pub(crate) actor: ObjectId<ApubCommunity>,
- #[serde(deserialize_with = "crate::deserialize_one_or_many")]
+ #[serde(deserialize_with = "deserialize_one_or_many")]
pub(crate) to: Vec<Url>,
pub(crate) object: IdOrNestedObject<AnnouncableActivities>,
- #[serde(deserialize_with = "crate::deserialize_one_or_many")]
+ #[serde(deserialize_with = "deserialize_one_or_many")]
pub(crate) cc: Vec<Url>,
#[serde(rename = "type")]
pub(crate) kind: AnnounceType,
use crate::{objects::person::ApubPerson, protocol::Unparsed};
+use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
use activitystreams_kinds::activity::RemoveType;
-use lemmy_apub_lib::object_id::ObjectId;
use serde::{Deserialize, Serialize};
use url::Url;
#[serde(rename_all = "camelCase")]
pub struct RemoveMod {
pub(crate) actor: ObjectId<ApubPerson>,
- #[serde(deserialize_with = "crate::deserialize_one_or_many")]
+ #[serde(deserialize_with = "deserialize_one_or_many")]
pub(crate) to: Vec<Url>,
pub(crate) object: ObjectId<ApubPerson>,
- #[serde(deserialize_with = "crate::deserialize_one_or_many")]
+ #[serde(deserialize_with = "deserialize_one_or_many")]
pub(crate) cc: Vec<Url>,
#[serde(rename = "type")]
pub(crate) kind: RemoveType,
objects::{community::ApubCommunity, person::ApubPerson},
protocol::Unparsed,
};
+use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one};
use activitystreams_kinds::activity::FlagType;
-use lemmy_apub_lib::object_id::ObjectId;
use serde::{Deserialize, Serialize};
use url::Url;
#[serde(rename_all = "camelCase")]
pub struct Report {
pub(crate) actor: ObjectId<ApubPerson>,
- #[serde(deserialize_with = "crate::deserialize_one")]
- pub(crate) to: [ObjectId<ApubCommunity>; 1],
+ #[serde(deserialize_with = "deserialize_one")]
+ pub(crate) to: ObjectId<ApubCommunity>,
pub(crate) object: ObjectId<PostOrComment>,
pub(crate) summary: String,
#[serde(rename = "type")]
objects::person::ApubPerson,
protocol::{objects::group::Group, Unparsed},
};
+use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
use activitystreams_kinds::activity::UpdateType;
-use lemmy_apub_lib::object_id::ObjectId;
use serde::{Deserialize, Serialize};
use url::Url;
#[serde(rename_all = "camelCase")]
pub struct UpdateCommunity {
pub(crate) actor: ObjectId<ApubPerson>,
- #[serde(deserialize_with = "crate::deserialize_one_or_many")]
+ #[serde(deserialize_with = "deserialize_one_or_many")]
pub(crate) to: Vec<Url>,
// TODO: would be nice to use a separate struct here, which only contains the fields updated here
pub(crate) object: Box<Group>,
- #[serde(deserialize_with = "crate::deserialize_one_or_many")]
+ #[serde(deserialize_with = "deserialize_one_or_many")]
pub(crate) cc: Vec<Url>,
#[serde(rename = "type")]
pub(crate) kind: UpdateType,
objects::person::ApubPerson,
protocol::{activities::CreateOrUpdateType, objects::note::Note, Unparsed},
};
-use lemmy_apub_lib::object_id::ObjectId;
+use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
use serde::{Deserialize, Serialize};
use url::Url;
#[serde(rename_all = "camelCase")]
pub struct CreateOrUpdateComment {
pub(crate) actor: ObjectId<ApubPerson>,
- #[serde(deserialize_with = "crate::deserialize_one_or_many")]
+ #[serde(deserialize_with = "deserialize_one_or_many")]
pub(crate) to: Vec<Url>,
pub(crate) object: Note,
- #[serde(deserialize_with = "crate::deserialize_one_or_many")]
+ #[serde(deserialize_with = "deserialize_one_or_many")]
pub(crate) cc: Vec<Url>,
#[serde(default)]
pub(crate) tag: Vec<MentionOrValue>,
objects::person::ApubPerson,
protocol::{activities::CreateOrUpdateType, objects::page::Page, Unparsed},
};
-use lemmy_apub_lib::object_id::ObjectId;
+use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
use serde::{Deserialize, Serialize};
use url::Url;
#[serde(rename_all = "camelCase")]
pub struct CreateOrUpdatePost {
pub(crate) actor: ObjectId<ApubPerson>,
- #[serde(deserialize_with = "crate::deserialize_one_or_many")]
+ #[serde(deserialize_with = "deserialize_one_or_many")]
pub(crate) to: Vec<Url>,
pub(crate) object: Page,
- #[serde(deserialize_with = "crate::deserialize_one_or_many")]
+ #[serde(deserialize_with = "deserialize_one_or_many")]
pub(crate) cc: Vec<Url>,
#[serde(rename = "type")]
pub(crate) kind: CreateOrUpdateType,
objects::person::ApubPerson,
protocol::{activities::CreateOrUpdateType, objects::chat_message::ChatMessage, Unparsed},
};
-use lemmy_apub_lib::object_id::ObjectId;
+use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one};
use serde::{Deserialize, Serialize};
use url::Url;
pub struct CreateOrUpdatePrivateMessage {
pub(crate) id: Url,
pub(crate) actor: ObjectId<ApubPerson>,
- #[serde(deserialize_with = "crate::deserialize_one")]
- pub(crate) to: [ObjectId<ApubPerson>; 1],
+ #[serde(deserialize_with = "deserialize_one")]
+ pub(crate) to: ObjectId<ApubPerson>,
pub(crate) object: ChatMessage,
#[serde(rename = "type")]
pub(crate) kind: CreateOrUpdateType,
objects::person::ApubPerson,
protocol::{objects::tombstone::Tombstone, IdOrNestedObject, Unparsed},
};
+use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
use activitystreams_kinds::activity::DeleteType;
-use lemmy_apub_lib::object_id::ObjectId;
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;
use url::Url;
#[serde(rename_all = "camelCase")]
pub struct Delete {
pub(crate) actor: ObjectId<ApubPerson>,
- #[serde(deserialize_with = "crate::deserialize_one_or_many")]
+ #[serde(deserialize_with = "deserialize_one_or_many")]
pub(crate) to: Vec<Url>,
pub(crate) object: IdOrNestedObject<Tombstone>,
#[serde(rename = "type")]
pub(crate) kind: DeleteType,
pub(crate) id: Url,
- #[serde(deserialize_with = "crate::deserialize_one_or_many")]
+ #[serde(deserialize_with = "deserialize_one_or_many")]
#[serde(default)]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub(crate) cc: Vec<Url>,
use crate::objects::person::ApubPerson;
+use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
use activitystreams_kinds::activity::DeleteType;
-use lemmy_apub_lib::object_id::ObjectId;
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;
use url::Url;
#[serde(rename_all = "camelCase")]
pub struct DeleteUser {
pub(crate) actor: ObjectId<ApubPerson>,
- #[serde(deserialize_with = "crate::deserialize_one_or_many")]
+ #[serde(deserialize_with = "deserialize_one_or_many")]
pub(crate) to: Vec<Url>,
pub(crate) object: ObjectId<ApubPerson>,
#[serde(rename = "type")]
pub(crate) kind: DeleteType,
pub(crate) id: Url,
- #[serde(deserialize_with = "crate::deserialize_one_or_many", default)]
+ #[serde(deserialize_with = "deserialize_one_or_many", default)]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub(crate) cc: Vec<Url>,
}
objects::person::ApubPerson,
protocol::{activities::deletion::delete::Delete, Unparsed},
};
+use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
use activitystreams_kinds::activity::UndoType;
-use lemmy_apub_lib::object_id::ObjectId;
use serde::{Deserialize, Serialize};
use url::Url;
#[serde(rename_all = "camelCase")]
pub struct UndoDelete {
pub(crate) actor: ObjectId<ApubPerson>,
- #[serde(deserialize_with = "crate::deserialize_one_or_many")]
+ #[serde(deserialize_with = "deserialize_one_or_many")]
pub(crate) to: Vec<Url>,
pub(crate) object: Delete,
#[serde(rename = "type")]
pub(crate) kind: UndoType,
pub(crate) id: Url,
- #[serde(deserialize_with = "crate::deserialize_one_or_many", default)]
+ #[serde(deserialize_with = "deserialize_one_or_many", default)]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub(crate) cc: Vec<Url>,
#[serde(flatten)]
objects::community::ApubCommunity,
protocol::{activities::following::follow::FollowCommunity, Unparsed},
};
+use activitypub_federation::core::object_id::ObjectId;
use activitystreams_kinds::activity::AcceptType;
-use lemmy_apub_lib::object_id::ObjectId;
use serde::{Deserialize, Serialize};
use url::Url;
objects::{community::ApubCommunity, person::ApubPerson},
protocol::Unparsed,
};
+use activitypub_federation::core::object_id::ObjectId;
use activitystreams_kinds::activity::FollowType;
-use lemmy_apub_lib::object_id::ObjectId;
use serde::{Deserialize, Serialize};
use url::Url;
objects::person::ApubPerson,
protocol::{activities::following::follow::FollowCommunity, Unparsed},
};
+use activitypub_federation::core::object_id::ObjectId;
use activitystreams_kinds::activity::UndoType;
-use lemmy_apub_lib::object_id::ObjectId;
use serde::{Deserialize, Serialize};
use url::Url;
objects::person::ApubPerson,
protocol::{activities::voting::vote::Vote, Unparsed},
};
+use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
use activitystreams_kinds::activity::UndoType;
-use lemmy_apub_lib::object_id::ObjectId;
use serde::{Deserialize, Serialize};
use url::Url;
#[serde(rename_all = "camelCase")]
pub struct UndoVote {
pub(crate) actor: ObjectId<ApubPerson>,
- #[serde(deserialize_with = "crate::deserialize_one_or_many")]
+ #[serde(deserialize_with = "deserialize_one_or_many")]
pub(crate) to: Vec<Url>,
pub(crate) object: Vote,
- #[serde(deserialize_with = "crate::deserialize_one_or_many")]
+ #[serde(deserialize_with = "deserialize_one_or_many")]
pub(crate) cc: Vec<Url>,
#[serde(rename = "type")]
pub(crate) kind: UndoType,
objects::person::ApubPerson,
protocol::Unparsed,
};
-use lemmy_apub_lib::object_id::ObjectId;
-use lemmy_utils::LemmyError;
+use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
+use lemmy_utils::error::LemmyError;
use serde::{Deserialize, Serialize};
use std::convert::TryFrom;
use strum_macros::Display;
#[serde(rename_all = "camelCase")]
pub struct Vote {
pub(crate) actor: ObjectId<ApubPerson>,
- #[serde(deserialize_with = "crate::deserialize_one_or_many")]
+ #[serde(deserialize_with = "deserialize_one_or_many")]
pub(crate) to: Vec<Url>,
pub(crate) object: ObjectId<PostOrComment>,
- #[serde(deserialize_with = "crate::deserialize_one_or_many")]
+ #[serde(deserialize_with = "deserialize_one_or_many")]
pub(crate) cc: Vec<Url>,
#[serde(rename = "type")]
pub(crate) kind: VoteType,
use activitystreams_kinds::collection::OrderedCollectionType;
-use lemmy_utils::LemmyError;
+use lemmy_utils::error::LemmyError;
use serde::{Deserialize, Serialize};
use url::Url;
use lemmy_api_common::utils::blocking;
use lemmy_db_schema::source::community::Community;
use lemmy_db_views_actor::structs::CommunityFollowerView;
-use lemmy_utils::LemmyError;
+use lemmy_utils::error::LemmyError;
use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize};
use url::Url;
use crate::objects::person::ApubPerson;
+use activitypub_federation::core::object_id::ObjectId;
use activitystreams_kinds::collection::OrderedCollectionType;
-use lemmy_apub_lib::object_id::ObjectId;
use serde::{Deserialize, Serialize};
use url::Url;
+use crate::local_instance;
+use activitypub_federation::{deser::values::MediaTypeMarkdown, utils::fetch_object_http};
use activitystreams_kinds::object::ImageType;
-use lemmy_apub_lib::{utils::fetch_object_http, values::MediaTypeMarkdown};
use lemmy_db_schema::newtypes::DbUrl;
-use lemmy_utils::LemmyError;
+use lemmy_utils::error::LemmyError;
use lemmy_websocket::LemmyContext;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use std::collections::HashMap;
pub struct Unparsed(HashMap<String, serde_json::Value>);
pub(crate) trait Id {
- fn id(&self) -> &Url;
+ fn object_id(&self) -> &Url;
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub(crate) fn id(&self) -> &Url {
match self {
IdOrNestedObject::Id(i) => i,
- IdOrNestedObject::NestedObject(n) => n.id(),
+ IdOrNestedObject::NestedObject(n) => n.object_id(),
}
}
pub(crate) async fn object(
request_counter: &mut i32,
) -> Result<Kind, LemmyError> {
match self {
- IdOrNestedObject::Id(i) => fetch_object_http(&i, context.client(), request_counter).await,
+ IdOrNestedObject::Id(i) => {
+ Ok(fetch_object_http(&i, local_instance(context), request_counter).await?)
+ }
IdOrNestedObject::NestedObject(o) => Ok(o),
}
}
#[cfg(test)]
pub(crate) mod tests {
- use crate::context::WithContext;
+ use activitypub_federation::deser::context::WithContext;
use assert_json_diff::assert_json_include;
- use lemmy_utils::LemmyError;
+ use lemmy_utils::error::LemmyError;
use serde::{de::DeserializeOwned, Serialize};
use std::{collections::HashMap, fs::File, io::BufReader};
objects::{person::ApubPerson, private_message::ApubPrivateMessage},
protocol::Source,
};
+use activitypub_federation::{
+ core::object_id::ObjectId,
+ deser::{
+ helpers::{deserialize_one, deserialize_skip_error},
+ values::MediaTypeHtml,
+ },
+};
use chrono::{DateTime, FixedOffset};
-use lemmy_apub_lib::{object_id::ObjectId, values::MediaTypeHtml};
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;
pub(crate) r#type: ChatMessageType,
pub(crate) id: ObjectId<ApubPrivateMessage>,
pub(crate) attributed_to: ObjectId<ApubPerson>,
- #[serde(deserialize_with = "crate::deserialize_one")]
- pub(crate) to: [ObjectId<ApubPerson>; 1],
+ #[serde(deserialize_with = "deserialize_one")]
+ pub(crate) to: ObjectId<ApubPerson>,
pub(crate) content: String,
pub(crate) media_type: Option<MediaTypeHtml>,
- #[serde(deserialize_with = "crate::deserialize_skip_error", default)]
+ #[serde(deserialize_with = "deserialize_skip_error", default)]
pub(crate) source: Option<Source>,
pub(crate) published: Option<DateTime<FixedOffset>>,
pub(crate) updated: Option<DateTime<FixedOffset>>,
use crate::{
- check_is_apub_id_valid,
+ check_apub_id_valid_with_strictness,
collections::{
community_moderators::ApubCommunityModerators,
community_outbox::ApubCommunityOutbox,
objects::{community::ApubCommunity, read_from_string_or_source_opt},
protocol::{objects::Endpoints, ImageObject, Source},
};
+use activitypub_federation::{
+ core::{object_id::ObjectId, signatures::PublicKey},
+ deser::helpers::deserialize_skip_error,
+ utils::verify_domains_match,
+};
use activitystreams_kinds::actor::GroupType;
use chrono::{DateTime, FixedOffset};
-use lemmy_apub_lib::{object_id::ObjectId, signatures::PublicKey, verify::verify_domains_match};
use lemmy_db_schema::{source::community::CommunityForm, utils::naive_now};
use lemmy_utils::{
+ error::LemmyError,
utils::{check_slurs, check_slurs_opt},
- LemmyError,
};
use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize};
/// title
pub(crate) name: Option<String>,
pub(crate) summary: Option<String>,
- #[serde(deserialize_with = "crate::deserialize_skip_error", default)]
+ #[serde(deserialize_with = "deserialize_skip_error", default)]
pub(crate) source: Option<Source>,
pub(crate) icon: Option<ImageObject>,
/// banner
expected_domain: &Url,
context: &LemmyContext,
) -> Result<(), LemmyError> {
- check_is_apub_id_valid(self.id.inner(), true, &context.settings())?;
+ check_apub_id_valid_with_strictness(self.id.inner(), true, &context.settings())?;
verify_domains_match(expected_domain, self.id.inner())?;
let slur_regex = &context.settings().slur_regex();
objects::instance::ApubSite,
protocol::{ImageObject, Source},
};
+use activitypub_federation::{
+ core::{object_id::ObjectId, signatures::PublicKey},
+ deser::{helpers::deserialize_skip_error, values::MediaTypeHtml},
+};
use chrono::{DateTime, FixedOffset};
-use lemmy_apub_lib::{object_id::ObjectId, signatures::PublicKey, values::MediaTypeHtml};
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;
use url::Url;
// sidebar
pub(crate) content: Option<String>,
- #[serde(deserialize_with = "crate::deserialize_skip_error", default)]
+ #[serde(deserialize_with = "deserialize_skip_error", default)]
pub(crate) source: Option<Source>,
// short instance description
pub(crate) summary: Option<String>,
use crate::{
fetcher::post_or_comment::PostOrComment,
+ local_instance,
mentions::MentionOrValue,
objects::{comment::ApubComment, person::ApubPerson, post::ApubPost},
protocol::Source,
};
+use activitypub_federation::{
+ core::object_id::ObjectId,
+ deser::{
+ helpers::{deserialize_one_or_many, deserialize_skip_error},
+ values::MediaTypeMarkdownOrHtml,
+ },
+};
use activitystreams_kinds::object::NoteType;
use chrono::{DateTime, FixedOffset};
use lemmy_api_common::utils::blocking;
-use lemmy_apub_lib::{object_id::ObjectId, values::MediaTypeMarkdownOrHtml};
use lemmy_db_schema::{newtypes::CommentId, source::post::Post, traits::Crud};
-use lemmy_utils::LemmyError;
+use lemmy_utils::error::LemmyError;
use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;
pub(crate) r#type: NoteType,
pub(crate) id: ObjectId<ApubComment>,
pub(crate) attributed_to: ObjectId<ApubPerson>,
- #[serde(deserialize_with = "crate::deserialize_one_or_many")]
+ #[serde(deserialize_with = "deserialize_one_or_many")]
pub(crate) to: Vec<Url>,
- #[serde(deserialize_with = "crate::deserialize_one_or_many", default)]
+ #[serde(deserialize_with = "deserialize_one_or_many", default)]
pub(crate) cc: Vec<Url>,
pub(crate) content: String,
pub(crate) in_reply_to: ObjectId<PostOrComment>,
pub(crate) media_type: Option<MediaTypeMarkdownOrHtml>,
- #[serde(deserialize_with = "crate::deserialize_skip_error", default)]
+ #[serde(deserialize_with = "deserialize_skip_error", default)]
pub(crate) source: Option<Source>,
pub(crate) published: Option<DateTime<FixedOffset>>,
pub(crate) updated: Option<DateTime<FixedOffset>>,
let parent = Box::pin(
self
.in_reply_to
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, local_instance(context), request_counter)
.await?,
);
match parent.deref() {
use crate::{
fetcher::user_or_community::{PersonOrGroupType, UserOrCommunity},
+ local_instance,
objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost},
protocol::{ImageObject, Source},
};
-use activitystreams_kinds::link::LinkType;
-use chrono::{DateTime, FixedOffset};
-use itertools::Itertools;
-use lemmy_apub_lib::{
+use activitypub_federation::{
+ core::object_id::ObjectId,
data::Data,
- object_id::ObjectId,
+ deser::{
+ helpers::{deserialize_one_or_many, deserialize_skip_error},
+ values::MediaTypeMarkdownOrHtml,
+ },
traits::{ActivityHandler, ApubObject},
- values::MediaTypeMarkdownOrHtml,
};
+use activitystreams_kinds::link::LinkType;
+use chrono::{DateTime, FixedOffset};
+use itertools::Itertools;
use lemmy_db_schema::newtypes::DbUrl;
-use lemmy_utils::LemmyError;
+use lemmy_utils::error::LemmyError;
use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;
pub(crate) kind: PageType,
pub(crate) id: ObjectId<ApubPost>,
pub(crate) attributed_to: AttributedTo,
- #[serde(deserialize_with = "crate::deserialize_one_or_many")]
+ #[serde(deserialize_with = "deserialize_one_or_many")]
pub(crate) to: Vec<Url>,
pub(crate) name: String,
- #[serde(deserialize_with = "crate::deserialize_one_or_many", default)]
+ #[serde(deserialize_with = "deserialize_one_or_many", default)]
pub(crate) cc: Vec<Url>,
pub(crate) content: Option<String>,
pub(crate) media_type: Option<MediaTypeMarkdownOrHtml>,
- #[serde(deserialize_with = "crate::deserialize_skip_error", default)]
+ #[serde(deserialize_with = "deserialize_skip_error", default)]
pub(crate) source: Option<Source>,
/// deprecated, use attachment field
- #[serde(deserialize_with = "crate::deserialize_skip_error", default)]
+ #[serde(deserialize_with = "deserialize_skip_error", default)]
pub(crate) url: Option<Url>,
/// most software uses array type for attachment field, so we do the same. nevertheless, we only
/// use the first item
/// Both stickied and locked need to be false on a newly created post (verified in [[CreatePost]].
pub(crate) async fn is_mod_action(&self, context: &LemmyContext) -> Result<bool, LemmyError> {
let old_post = ObjectId::<ApubPost>::new(self.id.clone())
- .dereference_local(context)
+ .dereference_local::<LemmyError>(context)
.await;
let stickied_changed = Page::is_stickied_changed(&old_post, &self.stickied);
if let Some(cid) = iter.next() {
let cid = ObjectId::new(cid.clone());
if let Ok(c) = cid
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, local_instance(context), request_counter)
.await
{
break Ok(c);
.find(|a| a.kind == PersonOrGroupType::Group)
.map(|a| ObjectId::<ApubCommunity>::new(a.id.clone().into_inner()))
.ok_or_else(|| LemmyError::from_message("page does not specify group"))?
- .dereference(context, context.client(), request_counter)
+ .dereference::<LemmyError>(context, local_instance(context), request_counter)
.await
}
}
#[async_trait::async_trait(?Send)]
impl ActivityHandler for Page {
type DataType = LemmyContext;
+ type Error = LemmyError;
+ fn id(&self) -> &Url {
+ unimplemented!()
+ }
+ fn actor(&self) -> &Url {
+ unimplemented!()
+ }
async fn verify(
&self,
data: &Data<Self::DataType>,
objects::person::ApubPerson,
protocol::{objects::Endpoints, ImageObject, Source},
};
+use activitypub_federation::{
+ core::{object_id::ObjectId, signatures::PublicKey},
+ deser::helpers::deserialize_skip_error,
+};
use chrono::{DateTime, FixedOffset};
-use lemmy_apub_lib::{object_id::ObjectId, signatures::PublicKey};
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;
use url::Url;
/// displayname
pub(crate) name: Option<String>,
pub(crate) summary: Option<String>,
- #[serde(deserialize_with = "crate::deserialize_skip_error", default)]
+ #[serde(deserialize_with = "deserialize_skip_error", default)]
pub(crate) source: Option<Source>,
/// user avatar
pub(crate) icon: Option<ImageObject>,
}
impl Id for Tombstone {
- fn id(&self) -> &Url {
+ fn object_id(&self) -> &Url {
&self.id
}
}
+++ /dev/null
-use crate::{signatures::sign_and_send, traits::ActorType};
-use anyhow::{anyhow, Context, Error};
-use background_jobs::{
- memory_storage::Storage,
- ActixJob,
- Backoff,
- Manager,
- MaxRetries,
- QueueHandle,
- WorkerConfig,
-};
-use lemmy_utils::{location_info, LemmyError};
-use reqwest_middleware::ClientWithMiddleware;
-use serde::{Deserialize, Serialize};
-use std::{env, fmt::Debug, future::Future, pin::Pin};
-use tracing::{info, warn};
-use url::Url;
-
-pub async fn send_activity(
- activity_id: &Url,
- actor: &dyn ActorType,
- inboxes: Vec<&Url>,
- activity: String,
- client: &ClientWithMiddleware,
- activity_queue: &QueueHandle,
-) -> Result<(), LemmyError> {
- for i in inboxes {
- let message = SendActivityTask {
- activity_id: activity_id.clone(),
- inbox: i.to_owned(),
- actor_id: actor.actor_id(),
- activity: activity.clone(),
- private_key: actor.private_key().context(location_info!())?,
- };
- if env::var("APUB_TESTING_SEND_SYNC").is_ok() {
- let res = do_send(message, client).await;
- // Don't fail on error, as we intentionally do some invalid actions in tests, to verify that
- // they are rejected on the receiving side. These errors shouldn't bubble up to make the API
- // call fail. This matches the behaviour in production.
- if let Err(e) = res {
- warn!("{}", e);
- }
- } else {
- activity_queue.queue::<SendActivityTask>(message).await?;
- let stats = activity_queue.get_stats().await?;
- info!(
- "Activity queue stats: pending: {}, running: {}, dead (this hour): {}, complete (this hour): {}",
- stats.pending,
- stats.running,
- stats.dead.this_hour(),
- stats.complete.this_hour()
- );
- }
- }
-
- Ok(())
-}
-
-#[derive(Clone, Debug, Deserialize, Serialize)]
-struct SendActivityTask {
- activity_id: Url,
- inbox: Url,
- actor_id: Url,
- activity: String,
- private_key: String,
-}
-
-/// Signs the activity with the sending actor's key, and delivers to the given inbox. Also retries
-/// if the delivery failed.
-impl ActixJob for SendActivityTask {
- type State = MyState;
- type Future = Pin<Box<dyn Future<Output = Result<(), Error>>>>;
- const NAME: &'static str = "SendActivityTask";
-
- /// With these params, retries are made at the following intervals:
- /// 3s
- /// 9s
- /// 27s
- /// 1m 21s
- /// 4m 3s
- /// 12m 9s
- /// 36m 27s
- /// 1h 49m 21s
- /// 5h 28m 3s
- /// 16h 24m 9s
- const MAX_RETRIES: MaxRetries = MaxRetries::Count(10);
- const BACKOFF: Backoff = Backoff::Exponential(3);
-
- fn run(self, state: Self::State) -> Self::Future {
- Box::pin(async move { do_send(self, &state.client).await })
- }
-}
-
-async fn do_send(task: SendActivityTask, client: &ClientWithMiddleware) -> Result<(), Error> {
- info!("Sending {} to {}", task.activity_id, task.inbox);
- let result = sign_and_send(
- client,
- &task.inbox,
- task.activity.clone(),
- &task.actor_id,
- task.private_key.to_owned(),
- )
- .await;
-
- let r: Result<(), Error> = match result {
- Ok(o) => {
- if !o.status().is_success() {
- let status = o.status();
- let text = o.text().await?;
-
- Err(anyhow!(
- "Send {} to {} failed with status {}: {}",
- task.activity_id,
- task.inbox,
- status,
- text,
- ))
- } else {
- Ok(())
- }
- }
- Err(e) => Err(anyhow!(
- "Failed to send activity {} to {}: {}",
- &task.activity_id,
- task.inbox,
- e
- )),
- };
- r
-}
-
-pub fn create_activity_queue(client: ClientWithMiddleware, worker_count: u64) -> Manager {
- // Configure and start our workers
- WorkerConfig::new_managed(Storage::new(), move |_| MyState {
- client: client.clone(),
- })
- .register::<SendActivityTask>()
- .set_worker_count("default", worker_count)
- .start()
-}
-
-#[derive(Clone)]
-struct MyState {
- pub client: ClientWithMiddleware,
-}
+++ /dev/null
-use std::{ops::Deref, sync::Arc};
-
-#[derive(Debug)]
-pub struct Data<T: ?Sized>(Arc<T>);
-
-impl<T> Data<T> {
- /// Create new `Data` instance.
- pub fn new(state: T) -> Data<T> {
- Data(Arc::new(state))
- }
-
- /// Get reference to inner app data.
- pub fn get_ref(&self) -> &T {
- self.0.as_ref()
- }
-
- /// Convert to the internal Arc<T>
- pub fn into_inner(self) -> Arc<T> {
- self.0
- }
-}
-
-impl<T: ?Sized> Deref for Data<T> {
- type Target = Arc<T>;
-
- fn deref(&self) -> &Arc<T> {
- &self.0
- }
-}
-
-impl<T: ?Sized> Clone for Data<T> {
- fn clone(&self) -> Data<T> {
- Data(self.0.clone())
- }
-}
+++ /dev/null
-pub mod activity_queue;
-pub mod data;
-pub mod object_id;
-pub mod signatures;
-pub mod traits;
-pub mod utils;
-pub mod values;
-pub mod verify;
-
-pub static APUB_JSON_CONTENT_TYPE: &str = "application/activity+json";
+++ /dev/null
-use crate::{traits::ApubObject, utils::fetch_object_http};
-use anyhow::anyhow;
-use chrono::{Duration as ChronoDuration, NaiveDateTime, Utc};
-use diesel::NotFound;
-use lemmy_utils::{settings::structs::Settings, LemmyError};
-use reqwest_middleware::ClientWithMiddleware;
-use serde::{Deserialize, Serialize};
-use std::{
- fmt::{Debug, Display, Formatter},
- marker::PhantomData,
-};
-use url::Url;
-
-/// We store Url on the heap because it is quite large (88 bytes).
-#[derive(Clone, PartialEq, Serialize, Deserialize, Debug)]
-#[serde(transparent)]
-pub struct ObjectId<Kind>(Box<Url>, #[serde(skip)] PhantomData<Kind>)
-where
- Kind: ApubObject + Send + 'static,
- for<'de2> <Kind as ApubObject>::ApubType: serde::Deserialize<'de2>;
-
-impl<Kind> ObjectId<Kind>
-where
- Kind: ApubObject + Send + 'static,
- for<'de2> <Kind as ApubObject>::ApubType: serde::Deserialize<'de2>,
-{
- pub fn new<T>(url: T) -> Self
- where
- T: Into<Url>,
- {
- ObjectId(Box::new(url.into()), PhantomData::<Kind>)
- }
-
- pub fn inner(&self) -> &Url {
- &self.0
- }
-
- pub fn into_inner(self) -> Url {
- *self.0
- }
-
- /// Fetches an activitypub object, either from local database (if possible), or over http.
- pub async fn dereference(
- &self,
- data: &<Kind as ApubObject>::DataType,
- client: &ClientWithMiddleware,
- request_counter: &mut i32,
- ) -> Result<Kind, LemmyError> {
- let db_object = self.dereference_from_db(data).await?;
-
- // if its a local object, only fetch it from the database and not over http
- if self.0.domain() == Some(&Settings::get().get_hostname_without_port()?) {
- return match db_object {
- None => Err(NotFound {}.into()),
- Some(o) => Ok(o),
- };
- }
-
- // object found in database
- if let Some(object) = db_object {
- // object is old and should be refetched
- if let Some(last_refreshed_at) = object.last_refreshed_at() {
- if should_refetch_object(last_refreshed_at) {
- return self
- .dereference_from_http(data, client, request_counter, Some(object))
- .await;
- }
- }
- Ok(object)
- }
- // object not found, need to fetch over http
- else {
- self
- .dereference_from_http(data, client, request_counter, None)
- .await
- }
- }
-
- /// Fetch an object from the local db. Instead of falling back to http, this throws an error if
- /// the object is not found in the database.
- pub async fn dereference_local(
- &self,
- data: &<Kind as ApubObject>::DataType,
- ) -> Result<Kind, LemmyError> {
- let object = self.dereference_from_db(data).await?;
- object.ok_or_else(|| anyhow!("object not found in database {}", self).into())
- }
-
- /// returning none means the object was not found in local db
- async fn dereference_from_db(
- &self,
- data: &<Kind as ApubObject>::DataType,
- ) -> Result<Option<Kind>, LemmyError> {
- let id = self.0.clone();
- ApubObject::read_from_apub_id(*id, data).await
- }
-
- async fn dereference_from_http(
- &self,
- data: &<Kind as ApubObject>::DataType,
- client: &ClientWithMiddleware,
- request_counter: &mut i32,
- db_object: Option<Kind>,
- ) -> Result<Kind, LemmyError> {
- let res = fetch_object_http(&self.0, client, request_counter).await;
-
- if let Err(e) = &res {
- // TODO: very ugly
- if e.message == Some("410".to_string()) {
- if let Some(db_object) = db_object {
- db_object.delete(data).await?;
- }
- return Err(anyhow!("Fetched remote object {} which was deleted", self).into());
- }
- }
-
- let res2 = res?;
-
- Kind::verify(&res2, self.inner(), data, request_counter).await?;
- Kind::from_apub(res2, data, request_counter).await
- }
-}
-
-static ACTOR_REFETCH_INTERVAL_SECONDS: i64 = 24 * 60 * 60;
-static ACTOR_REFETCH_INTERVAL_SECONDS_DEBUG: i64 = 20;
-
-/// Determines when a remote actor should be refetched from its instance. In release builds, this is
-/// `ACTOR_REFETCH_INTERVAL_SECONDS` after the last refetch, in debug builds
-/// `ACTOR_REFETCH_INTERVAL_SECONDS_DEBUG`.
-///
-/// TODO it won't pick up new avatars, summaries etc until a day after.
-/// Actors need an "update" activity pushed to other servers to fix this.
-fn should_refetch_object(last_refreshed: NaiveDateTime) -> bool {
- let update_interval = if cfg!(debug_assertions) {
- // avoid infinite loop when fetching community outbox
- ChronoDuration::seconds(ACTOR_REFETCH_INTERVAL_SECONDS_DEBUG)
- } else {
- ChronoDuration::seconds(ACTOR_REFETCH_INTERVAL_SECONDS)
- };
- let refresh_limit = Utc::now().naive_utc() - update_interval;
- last_refreshed.lt(&refresh_limit)
-}
-
-impl<Kind> Display for ObjectId<Kind>
-where
- Kind: ApubObject + Send + 'static,
- for<'de2> <Kind as ApubObject>::ApubType: serde::Deserialize<'de2>,
-{
- #[allow(clippy::to_string_in_display)]
- fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
- // Use to_string here because Url.display is not useful for us
- write!(f, "{}", self.0)
- }
-}
-
-impl<Kind> From<ObjectId<Kind>> for Url
-where
- Kind: ApubObject + Send + 'static,
- for<'de2> <Kind as ApubObject>::ApubType: serde::Deserialize<'de2>,
-{
- fn from(id: ObjectId<Kind>) -> Self {
- *id.0
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use crate::object_id::should_refetch_object;
-
- #[test]
- fn test_should_refetch_object() {
- let one_second_ago = Utc::now().naive_utc() - ChronoDuration::seconds(1);
- assert!(!should_refetch_object(one_second_ago));
-
- let two_days_ago = Utc::now().naive_utc() - ChronoDuration::days(2);
- assert!(should_refetch_object(two_days_ago));
- }
-}
+++ /dev/null
-use crate::APUB_JSON_CONTENT_TYPE;
-use actix_web::HttpRequest;
-use anyhow::anyhow;
-use http::{header::HeaderName, HeaderMap, HeaderValue};
-use http_signature_normalization_actix::Config as ConfigActix;
-use http_signature_normalization_reqwest::prelude::{Config, SignExt};
-use lemmy_utils::{LemmyError, REQWEST_TIMEOUT};
-use once_cell::sync::Lazy;
-use openssl::{
- hash::MessageDigest,
- pkey::PKey,
- sign::{Signer, Verifier},
-};
-use reqwest::Response;
-use reqwest_middleware::ClientWithMiddleware;
-use serde::{Deserialize, Serialize};
-use sha2::{Digest, Sha256};
-use std::str::FromStr;
-use tracing::debug;
-use url::Url;
-
-static CONFIG2: Lazy<ConfigActix> = Lazy::new(ConfigActix::new);
-static HTTP_SIG_CONFIG: Lazy<Config> = Lazy::new(Config::new);
-
-/// Creates an HTTP post request to `inbox_url`, with the given `client` and `headers`, and
-/// `activity` as request body. The request is signed with `private_key` and then sent.
-pub async fn sign_and_send(
- client: &ClientWithMiddleware,
- inbox_url: &Url,
- activity: String,
- actor_id: &Url,
- private_key: String,
-) -> Result<Response, LemmyError> {
- let signing_key_id = format!("{}#main-key", actor_id);
-
- let mut headers = HeaderMap::new();
- let mut host = inbox_url.domain().expect("read inbox domain").to_string();
- if let Some(port) = inbox_url.port() {
- host = format!("{}:{}", host, port);
- }
- headers.insert(
- HeaderName::from_str("Content-Type")?,
- HeaderValue::from_str(APUB_JSON_CONTENT_TYPE)?,
- );
- headers.insert(HeaderName::from_str("Host")?, HeaderValue::from_str(&host)?);
-
- let request = client
- .post(&inbox_url.to_string())
- // signature is only valid for 10 seconds, so no reason to wait any longer
- .timeout(REQWEST_TIMEOUT)
- .headers(headers)
- .signature_with_digest(
- HTTP_SIG_CONFIG.clone(),
- signing_key_id,
- Sha256::new(),
- activity,
- move |signing_string| {
- let private_key = PKey::private_key_from_pem(private_key.as_bytes())?;
- let mut signer = Signer::new(MessageDigest::sha256(), &private_key)?;
- signer.update(signing_string.as_bytes())?;
-
- Ok(base64::encode(signer.sign_to_vec()?)) as Result<_, LemmyError>
- },
- )
- .await?;
-
- let response = client.execute(request).await?;
-
- Ok(response)
-}
-
-/// Verifies the HTTP signature on an incoming inbox request.
-pub fn verify_signature(request: &HttpRequest, public_key: &str) -> Result<(), LemmyError> {
- let verified = CONFIG2
- .begin_verify(
- request.method(),
- request.uri().path_and_query(),
- request.headers().clone(),
- )?
- .verify(|signature, signing_string| -> Result<bool, LemmyError> {
- debug!(
- "Verifying with key {}, message {}",
- &public_key, &signing_string
- );
- let public_key = PKey::public_key_from_pem(public_key.as_bytes())?;
- let mut verifier = Verifier::new(MessageDigest::sha256(), &public_key)?;
- verifier.update(signing_string.as_bytes())?;
- Ok(verifier.verify(&base64::decode(signature)?)?)
- })?;
-
- if verified {
- debug!("verified signature for {}", &request.uri());
- Ok(())
- } else {
- Err(anyhow!("Invalid signature on request: {}", &request.uri()).into())
- }
-}
-
-#[derive(Clone, Debug, Deserialize, Serialize)]
-#[serde(rename_all = "camelCase")]
-pub struct PublicKey {
- pub(crate) id: String,
- pub(crate) owner: Box<Url>,
- pub public_key_pem: String,
-}
+++ /dev/null
-use crate::{data::Data, signatures::PublicKey};
-use chrono::NaiveDateTime;
-pub use lemmy_apub_lib_derive::*;
-use lemmy_utils::LemmyError;
-use url::Url;
-
-#[async_trait::async_trait(?Send)]
-pub trait ActivityHandler {
- type DataType;
- async fn verify(
- &self,
- data: &Data<Self::DataType>,
- request_counter: &mut i32,
- ) -> Result<(), LemmyError>;
-
- async fn receive(
- self,
- data: &Data<Self::DataType>,
- request_counter: &mut i32,
- ) -> Result<(), LemmyError>;
-}
-
-#[async_trait::async_trait(?Send)]
-pub trait ApubObject {
- type DataType;
- type ApubType;
- type DbType;
- type TombstoneType;
-
- /// If this object should be refetched after a certain interval, it should return the last refresh
- /// time here. This is mainly used to update remote actors.
- fn last_refreshed_at(&self) -> Option<NaiveDateTime>;
- /// Try to read the object with given ID from local database. Returns Ok(None) if it doesn't exist.
- async fn read_from_apub_id(
- object_id: Url,
- data: &Self::DataType,
- ) -> Result<Option<Self>, LemmyError>
- where
- Self: Sized;
- /// Marks the object as deleted in local db. Called when a tombstone is received.
- async fn delete(self, data: &Self::DataType) -> Result<(), LemmyError>;
-
- /// Trait for converting an object or actor into the respective ActivityPub type.
- async fn into_apub(self, data: &Self::DataType) -> Result<Self::ApubType, LemmyError>;
- fn to_tombstone(&self) -> Result<Self::TombstoneType, LemmyError>;
-
- async fn verify(
- apub: &Self::ApubType,
- expected_domain: &Url,
- data: &Self::DataType,
- request_counter: &mut i32,
- ) -> Result<(), LemmyError>;
-
- /// Converts an object from ActivityPub type to Lemmy internal type.
- ///
- /// * `apub` The object to read from
- /// * `context` LemmyContext which holds DB pool, HTTP client etc
- /// * `expected_domain` Domain where the object was received from. None in case of mod action.
- /// * `mod_action_allowed` True if the object can be a mod activity, ignore `expected_domain` in this case
- async fn from_apub(
- apub: Self::ApubType,
- data: &Self::DataType,
- request_counter: &mut i32,
- ) -> Result<Self, LemmyError>
- where
- Self: Sized;
-}
-
-/// Common methods provided by ActivityPub actors (community and person). Not all methods are
-/// implemented by all actors.
-pub trait ActorType {
- fn actor_id(&self) -> Url;
-
- fn public_key(&self) -> String;
- fn private_key(&self) -> Option<String>;
-
- fn inbox_url(&self) -> Url;
-
- fn shared_inbox_url(&self) -> Option<Url>;
-
- fn shared_inbox_or_inbox_url(&self) -> Url {
- self.shared_inbox_url().unwrap_or_else(|| self.inbox_url())
- }
-
- fn get_public_key(&self) -> Result<PublicKey, LemmyError> {
- Ok(PublicKey {
- id: format!("{}#main-key", self.actor_id()),
- owner: Box::new(self.actor_id()),
- public_key_pem: self.public_key(),
- })
- }
-}
+++ /dev/null
-use crate::APUB_JSON_CONTENT_TYPE;
-use anyhow::anyhow;
-use http::StatusCode;
-use lemmy_utils::{request::retry, settings::structs::Settings, LemmyError, REQWEST_TIMEOUT};
-use reqwest_middleware::ClientWithMiddleware;
-use serde::de::DeserializeOwned;
-use tracing::log::info;
-use url::Url;
-
-pub async fn fetch_object_http<Kind: DeserializeOwned>(
- url: &Url,
- client: &ClientWithMiddleware,
- request_counter: &mut i32,
-) -> Result<Kind, LemmyError> {
- // dont fetch local objects this way
- debug_assert!(url.domain() != Some(&Settings::get().hostname));
- info!("Fetching remote object {}", url.to_string());
-
- *request_counter += 1;
- if *request_counter > Settings::get().http_fetch_retry_limit {
- return Err(LemmyError::from(anyhow!("Request retry limit reached")));
- }
-
- let res = retry(|| {
- client
- .get(url.as_str())
- .header("Accept", APUB_JSON_CONTENT_TYPE)
- .timeout(REQWEST_TIMEOUT)
- .send()
- })
- .await?;
-
- if res.status() == StatusCode::GONE {
- return Err(LemmyError::from_message("410"));
- }
-
- Ok(res.json().await?)
-}
+++ /dev/null
-//! The enums here serve to limit a json string value to a single, hardcoded value which can be
-//! verified at compilation time. When using it as the type of a struct field, the struct can only
-//! be constructed or deserialized if the field has the exact same value.
-//!
-//! If we used String as the field type, any value would be accepted, and we would have to check
-//! manually at runtime that it contains the expected value.
-//!
-//! The enums in [`activitystreams::activity::kind`] work in the same way, and can be used to
-//! distinguish different activity types.
-//!
-//! In the example below, `MyObject` can only be constructed or
-//! deserialized if `media_type` is `text/markdown`, but not if it is `text/html`.
-//!
-//! ```
-//! use lemmy_apub_lib::values::MediaTypeMarkdown;
-//! use serde_json::from_str;
-//! use serde::{Deserialize, Serialize};
-//!
-//! #[derive(Deserialize, Serialize)]
-//! struct MyObject {
-//! content: String,
-//! media_type: MediaTypeMarkdown,
-//! }
-//!
-//! let markdown_json = r#"{"content": "**test**", "media_type": "text/markdown"}"#;
-//! let from_markdown = from_str::<MyObject>(markdown_json);
-//! assert!(from_markdown.is_ok());
-//!
-//! let markdown_html = r#"{"content": "<b>test</b>", "media_type": "text/html"}"#;
-//! let from_html = from_str::<MyObject>(markdown_html);
-//! assert!(from_html.is_err());
-//! ```
-
-use serde::{Deserialize, Serialize};
-
-/// Media type for markdown text.
-///
-/// <https://www.iana.org/assignments/media-types/media-types.xhtml>
-#[derive(Clone, Debug, Deserialize, Serialize)]
-pub enum MediaTypeMarkdown {
- #[serde(rename = "text/markdown")]
- Markdown,
-}
-
-/// Media type for HTML text.
-///
-/// <https://www.iana.org/assignments/media-types/media-types.xhtml>
-#[derive(Clone, Debug, Deserialize, Serialize)]
-pub enum MediaTypeHtml {
- #[serde(rename = "text/html")]
- Html,
-}
-/// Media type which allows both markdown and HTML.
-#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
-pub enum MediaTypeMarkdownOrHtml {
- #[serde(rename = "text/markdown")]
- Markdown,
- #[serde(rename = "text/html")]
- Html,
-}
+++ /dev/null
-use lemmy_utils::LemmyError;
-use url::Url;
-
-#[derive(Debug)]
-struct DomainError;
-
-impl std::fmt::Display for DomainError {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- write!(f, "Domain mismatch")
- }
-}
-
-impl std::error::Error for DomainError {}
-
-pub fn verify_domains_match(a: &Url, b: &Url) -> Result<(), LemmyError> {
- if a.domain() != b.domain() {
- return Err(DomainError.into());
- }
- Ok(())
-}
-
-pub fn verify_urls_match(a: &Url, b: &Url) -> Result<(), LemmyError> {
- if a != b {
- return Err(DomainError.into());
- }
- Ok(())
-}
+++ /dev/null
-use proc_macro2::{TokenStream, TokenTree};
-use quote::quote;
-use syn::{parse_macro_input, Attribute, Data, DeriveInput, Fields::Unnamed, Ident, Variant};
-
-/// Generates implementation ActivityHandler for an enum, which looks like the following (handling
-/// all enum variants).
-///
-/// Based on this code:
-/// ```ignore
-/// #[derive(serde::Deserialize, serde::Serialize, ActivityHandler)]
-/// #[serde(untagged)]
-/// pub enum PersonInboxActivities {
-/// CreateNote(CreateNote),
-/// UpdateNote(UpdateNote),
-/// ```
-/// It will generate this:
-/// ```ignore
-/// impl ActivityHandler for PersonInboxActivities {
-///
-/// async fn verify(
-/// &self,
-/// context: &LemmyContext,
-/// request_counter: &mut i32,
-/// ) -> Result<(), LemmyError> {
-/// match self {
-/// PersonInboxActivities::CreateNote(a) => a.verify(context, request_counter).await,
-/// PersonInboxActivities::UpdateNote(a) => a.verify(context, request_counter).await,
-/// }
-/// }
-///
-/// async fn receive(
-/// &self,
-/// context: &LemmyContext,
-/// request_counter: &mut i32,
-/// ) -> Result<(), LemmyError> {
-/// match self {
-/// PersonInboxActivities::CreateNote(a) => a.receive(context, request_counter).await,
-/// PersonInboxActivities::UpdateNote(a) => a.receive(context, request_counter).await,
-/// }
-/// }
-/// fn common(&self) -> &ActivityCommonFields {
-/// match self {
-/// PersonInboxActivities::CreateNote(a) => a.common(),
-/// PersonInboxActivities::UpdateNote(a) => a.common(),
-/// }
-/// }
-///
-/// ```
-#[proc_macro_derive(ActivityHandler, attributes(activity_handler))]
-pub fn derive_activity_handler(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
- let input = parse_macro_input!(input as DeriveInput);
- let attrs: Vec<&Attribute> = input
- .attrs
- .iter()
- .filter(|attr| attr.path.is_ident("activity_handler"))
- .collect();
- let attrs: &Vec<TokenStream> = &attrs
- .first()
- .expect("Could not decode first attribute from token stream")
- .tokens
- .clone()
- .into_iter()
- .map(|t| {
- if let TokenTree::Group(g) = t {
- g.stream()
- } else {
- panic!()
- }
- })
- .collect();
- let attrs = attrs.first();
-
- let enum_name = input.ident;
-
- let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
-
- let enum_variants = if let Data::Enum(d) = input.data {
- d.variants
- } else {
- unimplemented!()
- };
-
- let body_verify = quote! {a.verify(context, request_counter).await};
- let impl_verify = enum_variants
- .iter()
- .map(|v| generate_match_arm(&enum_name, v, &body_verify));
- let body_receive = quote! {a.receive(context, request_counter).await};
- let impl_receive = enum_variants
- .iter()
- .map(|v| generate_match_arm(&enum_name, v, &body_receive));
-
- let expanded = quote! {
- #[async_trait::async_trait(?Send)]
- impl #impl_generics lemmy_apub_lib::traits::ActivityHandler for #enum_name #ty_generics #where_clause {
- type DataType = #attrs;
- async fn verify(
- &self,
- context: &lemmy_apub_lib::data::Data<Self::DataType>,
- request_counter: &mut i32,
- ) -> Result<(), lemmy_utils::LemmyError> {
- match self {
- #(#impl_verify)*
- }
- }
- async fn receive(
- self,
- context: &lemmy_apub_lib::data::Data<Self::DataType>,
- request_counter: &mut i32,
- ) -> Result<(), lemmy_utils::LemmyError> {
- match self {
- #(#impl_receive)*
- }
- }
- }
- };
- expanded.into()
-}
-
-fn generate_match_arm(enum_name: &Ident, variant: &Variant, body: &TokenStream) -> TokenStream {
- let id = &variant.ident;
- match &variant.fields {
- Unnamed(_) => {
- quote! {
- #enum_name::#id(a) => #body,
- }
- }
- _ => unimplemented!(),
- }
-}
[features]
full = ["diesel", "diesel-derive-newtype", "diesel_migrations", "bcrypt", "lemmy_utils",
- "lemmy_apub_lib", "sha2", "regex", "once_cell", "serde_json"]
+ "activitypub_federation", "sha2", "regex", "once_cell", "serde_json"]
[dependencies]
chrono = { version = "0.4.19", features = ["serde"], default-features = false }
strum = "0.24.0"
strum_macros = "0.24.0"
serde_json = { version = "1.0.79", features = ["preserve_order"], optional = true }
-lemmy_apub_lib = { version = "=0.16.5", path = "../apub_lib", optional = true }
+activitypub_federation = { version = "0.1.0", optional = true }
lemmy_utils = { version = "=0.16.5", path = "../utils", optional = true }
bcrypt = { version = "0.12.1", optional = true }
diesel = { version = "1.4.8", features = ["postgres","chrono","r2d2","serde_json"], optional = true }
utils::naive_now,
};
use diesel::{dsl::*, result::Error, *};
-use lemmy_utils::LemmyError;
+use lemmy_utils::error::LemmyError;
use url::Url;
impl Crud for PrivateMessage {
use crate::newtypes::DbUrl;
+use activitypub_federation::{core::object_id::ObjectId, traits::ApubObject};
use chrono::NaiveDateTime;
use diesel::{
backend::Backend,
Connection,
PgConnection,
};
-use lemmy_apub_lib::{object_id::ObjectId, traits::ApubObject};
-use lemmy_utils::LemmyError;
+use lemmy_utils::error::LemmyError;
use once_cell::sync::Lazy;
use regex::Regex;
use std::{env, env::VarError, io::Write};
person_mention_view::PersonMentionQueryBuilder,
structs::PersonMentionView,
};
-use lemmy_utils::{claims::Claims, utils::markdown_to_html, LemmyError};
+use lemmy_utils::{claims::Claims, error::LemmyError, utils::markdown_to_html};
use lemmy_websocket::LemmyContext;
use once_cell::sync::Lazy;
use rss::{
};
use anyhow::anyhow;
use futures::stream::{Stream, StreamExt};
-use lemmy_utils::{claims::Claims, rate_limit::RateLimit, LemmyError, REQWEST_TIMEOUT};
+use lemmy_utils::{claims::Claims, error::LemmyError, rate_limit::RateLimit};
use lemmy_websocket::LemmyContext;
use reqwest::Body;
use reqwest_middleware::{ClientWithMiddleware, RequestBuilder};
// remove accept-encoding header so that pictrs doesnt compress the response
const INVALID_HEADERS: &[HeaderName] = &[ACCEPT_ENCODING, HOST];
- let client_request = client
- .request(request.method().clone(), url)
- .timeout(REQWEST_TIMEOUT);
+ let client_request = client.request(request.method().clone(), url);
request
.headers()
use anyhow::anyhow;
use lemmy_api_common::utils::blocking;
use lemmy_db_views::structs::SiteView;
-use lemmy_utils::{version, LemmyError};
+use lemmy_utils::{error::LemmyError, version};
use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize};
use url::Url;
source::{community::Community, person::Person},
traits::ApubActor,
};
-use lemmy_utils::{location_info, settings::structs::Settings, LemmyError};
+use lemmy_utils::{error::LemmyError, location_info, settings::structs::Settings};
use lemmy_websocket::LemmyContext;
use serde::Deserialize;
use url::Url;
-use crate::LemmyError;
+use crate::error::LemmyError;
use chrono::Utc;
use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, TokenData, Validation};
use serde::{Deserialize, Serialize};
-use crate::{settings::structs::Settings, LemmyError};
+use crate::{error::LemmyError, settings::structs::Settings};
use html2text;
use lettre::{
message::{Mailbox, MultiPart},
--- /dev/null
+use std::{
+ fmt,
+ fmt::{Debug, Display},
+};
+use tracing_error::SpanTrace;
+
+#[derive(serde::Serialize)]
+struct ApiError {
+ error: String,
+}
+
+pub struct LemmyError {
+ pub message: Option<String>,
+ pub inner: anyhow::Error,
+ pub context: SpanTrace,
+}
+
+impl LemmyError {
+ /// Create LemmyError from a message, including stack trace
+ pub fn from_message(message: &str) -> Self {
+ let inner = anyhow::anyhow!("{}", message);
+ LemmyError {
+ message: Some(message.into()),
+ inner,
+ context: SpanTrace::capture(),
+ }
+ }
+
+ /// Create a LemmyError from error and message, including stack trace
+ pub fn from_error_message<E>(error: E, message: &str) -> Self
+ where
+ E: Into<anyhow::Error>,
+ {
+ LemmyError {
+ message: Some(message.into()),
+ inner: error.into(),
+ context: SpanTrace::capture(),
+ }
+ }
+
+ /// Add message to existing LemmyError (or overwrite existing error)
+ pub fn with_message(self, message: &str) -> Self {
+ LemmyError {
+ message: Some(message.into()),
+ ..self
+ }
+ }
+
+ pub fn to_json(&self) -> Result<String, Self> {
+ let api_error = match &self.message {
+ Some(error) => ApiError {
+ error: error.into(),
+ },
+ None => ApiError {
+ error: "Unknown".into(),
+ },
+ };
+
+ Ok(serde_json::to_string(&api_error)?)
+ }
+}
+
+impl<T> From<T> for LemmyError
+where
+ T: Into<anyhow::Error>,
+{
+ fn from(t: T) -> Self {
+ LemmyError {
+ message: None,
+ inner: t.into(),
+ context: SpanTrace::capture(),
+ }
+ }
+}
+
+impl Debug for LemmyError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("LemmyError")
+ .field("message", &self.message)
+ .field("inner", &self.inner)
+ .field("context", &"SpanTrace")
+ .finish()
+ }
+}
+
+impl Display for LemmyError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ if let Some(message) = &self.message {
+ write!(f, "{}: ", message)?;
+ }
+ writeln!(f, "{}", self.inner)?;
+ fmt::Display::fmt(&self.context, f)
+ }
+}
+
+impl actix_web::error::ResponseError for LemmyError {
+ fn status_code(&self) -> http::StatusCode {
+ match self.inner.downcast_ref::<diesel::result::Error>() {
+ Some(diesel::result::Error::NotFound) => http::StatusCode::NOT_FOUND,
+ _ => http::StatusCode::BAD_REQUEST,
+ }
+ }
+
+ fn error_response(&self) -> actix_web::HttpResponse {
+ if let Some(message) = &self.message {
+ actix_web::HttpResponse::build(self.status_code()).json(ApiError {
+ error: message.into(),
+ })
+ } else {
+ actix_web::HttpResponse::build(self.status_code())
+ .content_type("text/plain")
+ .body(self.inner.to_string())
+ }
+ }
+}
pub mod settings;
pub mod claims;
+pub mod error;
pub mod request;
#[cfg(test)]
mod test;
pub mod utils;
pub mod version;
-use actix_web::HttpResponse;
-use http::StatusCode;
-use std::{fmt, fmt::Display, time::Duration};
-use tracing_error::SpanTrace;
+use std::{fmt, time::Duration};
pub type ConnectionId = usize;
)
};
}
-
-#[derive(serde::Serialize)]
-struct ApiError {
- error: String,
-}
-
-pub struct LemmyError {
- pub message: Option<String>,
- pub inner: anyhow::Error,
- pub context: SpanTrace,
-}
-
-impl LemmyError {
- /// Create LemmyError from a message, including stack trace
- pub fn from_message(message: &str) -> Self {
- let inner = anyhow::anyhow!("{}", message);
- LemmyError {
- message: Some(message.into()),
- inner,
- context: SpanTrace::capture(),
- }
- }
-
- /// Create a LemmyError from error and message, including stack trace
- pub fn from_error_message<E>(error: E, message: &str) -> Self
- where
- E: Into<anyhow::Error>,
- {
- LemmyError {
- message: Some(message.into()),
- inner: error.into(),
- context: SpanTrace::capture(),
- }
- }
-
- /// Add message to existing LemmyError (or overwrite existing error)
- pub fn with_message(self, message: &str) -> Self {
- LemmyError {
- message: Some(message.into()),
- ..self
- }
- }
-
- pub fn to_json(&self) -> Result<String, Self> {
- let api_error = match &self.message {
- Some(error) => ApiError {
- error: error.into(),
- },
- None => ApiError {
- error: "Unknown".into(),
- },
- };
-
- Ok(serde_json::to_string(&api_error)?)
- }
-}
-
-impl<T> From<T> for LemmyError
-where
- T: Into<anyhow::Error>,
-{
- fn from(t: T) -> Self {
- LemmyError {
- message: None,
- inner: t.into(),
- context: SpanTrace::capture(),
- }
- }
-}
-
-impl std::fmt::Debug for LemmyError {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("LemmyError")
- .field("message", &self.message)
- .field("inner", &self.inner)
- .field("context", &"SpanTrace")
- .finish()
- }
-}
-
-impl Display for LemmyError {
- fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
- if let Some(message) = &self.message {
- write!(f, "{}: ", message)?;
- }
- writeln!(f, "{}", self.inner)?;
- self.context.fmt(f)
- }
-}
-
-impl actix_web::error::ResponseError for LemmyError {
- fn status_code(&self) -> StatusCode {
- match self.inner.downcast_ref::<diesel::result::Error>() {
- Some(diesel::result::Error::NotFound) => StatusCode::NOT_FOUND,
- _ => StatusCode::BAD_REQUEST,
- }
- }
-
- fn error_response(&self) -> HttpResponse {
- if let Some(message) = &self.message {
- HttpResponse::build(self.status_code()).json(ApiError {
- error: message.into(),
- })
- } else {
- HttpResponse::build(self.status_code())
- .content_type("text/plain")
- .body(self.inner.to_string())
- }
- }
-}
-use crate::{location_info, settings::structs::Settings, LemmyError};
+use crate::{error::LemmyError, location_info, settings::structs::Settings};
use anyhow::{anyhow, Context};
use deser_hjson::from_str;
use once_cell::sync::Lazy;
-use crate::{IpAddr, LemmyError};
+use crate::{error::LemmyError, IpAddr};
use actix_web::dev::ConnectionInfo;
use chrono::{DateTime, FixedOffset, NaiveDateTime};
use itertools::Itertools;
};
use actix::prelude::*;
use anyhow::Context as acontext;
-use background_jobs::QueueHandle;
use diesel::{
r2d2::{ConnectionManager, Pool},
PgConnection,
source::secret::Secret,
};
use lemmy_utils::{
+ error::LemmyError,
location_info,
rate_limit::RateLimit,
settings::structs::Settings,
ConnectionId,
IpAddr,
- LemmyError,
};
use rand::rngs::ThreadRng;
use reqwest_middleware::ClientWithMiddleware;
/// An HTTP Client
client: ClientWithMiddleware,
-
- activity_queue: QueueHandle,
}
pub struct SessionInfo {
message_handler: MessageHandlerType,
message_handler_crud: MessageHandlerCrudType,
client: ClientWithMiddleware,
- activity_queue: QueueHandle,
settings: Settings,
secret: Secret,
) -> ChatServer {
message_handler,
message_handler_crud,
client,
- activity_queue,
settings,
secret,
}
pool: self.pool.clone(),
chat_server: ctx.address(),
client: self.client.to_owned(),
- activity_queue: self.activity_queue.to_owned(),
settings: self.settings.to_owned(),
secret: self.secret.to_owned(),
};
use crate::chat_server::ChatServer;
use actix::Addr;
-use background_jobs::QueueHandle;
use lemmy_db_schema::{source::secret::Secret, utils::DbPool};
-use lemmy_utils::{settings::structs::Settings, LemmyError};
+use lemmy_utils::{error::LemmyError, settings::structs::Settings};
use reqwest_middleware::ClientWithMiddleware;
use serde::Serialize;
pool: DbPool,
chat_server: Addr<ChatServer>,
client: ClientWithMiddleware,
- activity_queue: QueueHandle,
settings: Settings,
secret: Secret,
}
pool: DbPool,
chat_server: Addr<ChatServer>,
client: ClientWithMiddleware,
- activity_queue: QueueHandle,
settings: Settings,
secret: Secret,
) -> LemmyContext {
pool,
chat_server,
client,
- activity_queue,
settings,
secret,
}
pub fn client(&self) -> &ClientWithMiddleware {
&self.client
}
- pub fn activity_queue(&self) -> &QueueHandle {
- &self.activity_queue
- }
pub fn settings(&self) -> Settings {
// TODO hacky solution to be able to hotload the settings.
Settings::get()
pool: self.pool.clone(),
chat_server: self.chat_server.clone(),
client: self.client.clone(),
- activity_queue: self.activity_queue.clone(),
settings: self.settings.clone(),
secret: self.secret.clone(),
}
};
use lemmy_db_views::structs::{CommentView, LocalUserView, PostView, PrivateMessageView};
use lemmy_db_views_actor::structs::CommunityView;
-use lemmy_utils::{utils::MentionData, ConnectionId, LemmyError};
+use lemmy_utils::{error::LemmyError, utils::MentionData, ConnectionId};
#[tracing::instrument(skip_all)]
pub async fn send_post_ws_message<OP: ToString + Send + OperationType + 'static>(
// This is for db migrations that require code
+use activitypub_federation::core::signatures::generate_actor_keypair;
use diesel::{
sql_types::{Nullable, Text},
*,
traits::Crud,
utils::naive_now,
};
-use lemmy_utils::{apub::generate_actor_keypair, LemmyError};
+use lemmy_utils::error::LemmyError;
use tracing::info;
use url::Url;
#[cfg(feature = "console")]
pub mod telemetry;
-use lemmy_utils::LemmyError;
+use lemmy_utils::error::LemmyError;
use tracing::subscriber::set_global_default;
use tracing_error::ErrorLayer;
use tracing_log::LogTracer;
utils::{blocking, check_private_instance_and_federation_enabled},
};
use lemmy_api_crud::match_websocket_operation_crud;
-use lemmy_apub_lib::activity_queue::create_activity_queue;
use lemmy_db_schema::{source::secret::Secret, utils::get_database_url_from_env};
use lemmy_routes::{feeds, images, nodeinfo, webfinger};
use lemmy_server::{
scheduled_tasks,
};
use lemmy_utils::{
+ error::LemmyError,
rate_limit::{rate_limiter::RateLimiter, RateLimit},
settings::structs::Settings,
- LemmyError,
- REQWEST_TIMEOUT,
};
use lemmy_websocket::{chat_server::ChatServer, LemmyContext};
use parking_lot::Mutex;
use reqwest::Client;
use reqwest_middleware::ClientBuilder;
+use reqwest_retry::{policies::ExponentialBackoff, RetryTransientMiddleware};
use reqwest_tracing::TracingMiddleware;
-use std::{env, sync::Arc, thread};
+use std::{env, sync::Arc, thread, time::Duration};
use tracing_actix_web::TracingLogger;
embed_migrations!();
+/// Max timeout for http requests
+pub const REQWEST_TIMEOUT: Duration = Duration::from_secs(10);
+
#[actix_web::main]
async fn main() -> Result<(), LemmyError> {
let args: Vec<String> = env::args().collect();
.timeout(REQWEST_TIMEOUT)
.build()?;
- let client = ClientBuilder::new(client).with(TracingMiddleware).build();
-
- let queue_manager = create_activity_queue(client.clone(), settings.federation.worker_count);
+ let retry_policy = ExponentialBackoff {
+ max_n_retries: 3,
+ max_retry_interval: REQWEST_TIMEOUT,
+ min_retry_interval: Duration::from_millis(100),
+ backoff_exponent: 2,
+ };
- let activity_queue = queue_manager.queue_handle().clone();
+ let client = ClientBuilder::new(client)
+ .with(TracingMiddleware)
+ .with(RetryTransientMiddleware::new_with_policy(retry_policy))
+ .build();
check_private_instance_and_federation_enabled(&pool, &settings).await?;
|c, i, o, d| Box::pin(match_websocket_operation(c, i, o, d)),
|c, i, o, d| Box::pin(match_websocket_operation_crud(c, i, o, d)),
client.clone(),
- activity_queue.clone(),
settings.clone(),
secret.clone(),
)
pool.clone(),
chat_server.to_owned(),
client.clone(),
- activity_queue.to_owned(),
settings.to_owned(),
secret.to_owned(),
);
.run()
.await?;
- drop(queue_manager);
-
Ok(())
}
// Import week days and WeekDay
use diesel::{sql_query, PgConnection, RunQueryDsl};
use lemmy_db_schema::{source::activity::Activity, utils::DbPool};
-use lemmy_utils::LemmyError;
+use lemmy_utils::error::LemmyError;
use std::{thread, time::Duration};
use tracing::info;
use console_subscriber::ConsoleLayer;
-use lemmy_utils::LemmyError;
+use lemmy_utils::error::LemmyError;
use opentelemetry::{
sdk::{propagation::TraceContextPropagator, Resource},
KeyValue,