diff --git a/.woodpecker.yml b/.woodpecker.yml index 0122a0a680..fe549aa8a2 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -95,6 +95,15 @@ steps: when: - event: pull_request + cargo_clippy: + image: *rust_image + environment: + CARGO_HOME: .cargo_home + commands: + - rustup component add clippy + - cargo clippy --workspace --tests --all-targets -- -D warnings + when: *slow_check_paths + # `DROP OWNED` doesn't work for default user create_database_user: image: postgres:16-alpine @@ -107,6 +116,68 @@ steps: - psql -c "CREATE USER lemmy WITH PASSWORD 'password' SUPERUSER;" when: *slow_check_paths + cargo_test: + image: *rust_image + environment: + LEMMY_DATABASE_URL: postgres://lemmy:password@database:5432/lemmy + RUST_BACKTRACE: "1" + CARGO_HOME: .cargo_home + LEMMY_TEST_FAST_FEDERATION: "1" + LEMMY_CONFIG_LOCATION: ../../config/config.hjson + commands: + # Install pg_dump for the schema setup test (must match server version) + - apt update && apt install -y lsb-release + - sh -c 'echo "deb https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list' + - wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - + - apt update && apt install -y postgresql-client-16 + # Run tests + - cargo test --workspace --no-fail-fast + when: *slow_check_paths + + check_ts_bindings: + image: *rust_image + environment: + CARGO_HOME: .cargo_home + commands: + - ./scripts/ts_bindings_check.sh + when: + - event: pull_request + + # make sure api builds with default features (used by other crates relying on lemmy api) + check_api_common_default_features: + image: *rust_image + environment: + CARGO_HOME: .cargo_home + commands: + - cargo check --package lemmy_api_common + when: *slow_check_paths + + lemmy_api_common_doesnt_depend_on_diesel: + image: *rust_image + environment: + CARGO_HOME: .cargo_home + commands: + - "! cargo tree -p lemmy_api_common --no-default-features -i diesel" + when: *slow_check_paths + + lemmy_api_common_works_with_wasm: + image: *rust_image + environment: + CARGO_HOME: .cargo_home + commands: + - "rustup target add wasm32-unknown-unknown" + - "cargo check --target wasm32-unknown-unknown -p lemmy_api_common" + when: *slow_check_paths + + check_defaults_hjson_updated: + image: *rust_image + environment: + CARGO_HOME: .cargo_home + commands: + - ./scripts/update_config_defaults.sh config/defaults_current.hjson + - diff config/defaults.hjson config/defaults_current.hjson + when: *slow_check_paths + cargo_build: image: *rust_image environment: @@ -116,6 +187,32 @@ steps: - mv target/debug/lemmy_server target/lemmy_server when: *slow_check_paths + check_diesel_schema: + image: *rust_image + environment: + LEMMY_DATABASE_URL: postgres://lemmy:password@database:5432/lemmy + DATABASE_URL: postgres://lemmy:password@database:5432/lemmy + RUST_BACKTRACE: "1" + CARGO_HOME: .cargo_home + commands: + - cp crates/db_schema/src/schema.rs tmp.schema + - target/lemmy_server migration --all run + - <<: *install_diesel_cli + - diesel print-schema + - diff tmp.schema crates/db_schema/src/schema.rs + when: *slow_check_paths + + check_db_perf_tool: + image: *rust_image + environment: + LEMMY_DATABASE_URL: postgres://lemmy:password@database:5432/lemmy + RUST_BACKTRACE: "1" + CARGO_HOME: .cargo_home + commands: + # same as scripts/db_perf.sh but without creating a new database server + - cargo run --package lemmy_db_perf -- --posts 10 --read-post-pages 1 + when: *slow_check_paths + run_federation_tests: image: node:22-bookworm-slim environment: @@ -127,7 +224,7 @@ steps: - bash api_tests/prepare-drone-federation-test.sh - cd api_tests/ - pnpm i - - pnpm api-test-image + - pnpm api-test when: *slow_check_paths federation_tests_server_output: diff --git a/api_tests/run-federation-test.sh b/api_tests/run-federation-test.sh index f9eab50395..969a95b3e7 100755 --- a/api_tests/run-federation-test.sh +++ b/api_tests/run-federation-test.sh @@ -11,7 +11,7 @@ killall -s1 lemmy_server || true popd pnpm i -pnpm api-test-image || true +pnpm api-test || true killall -s1 lemmy_server || true killall -s1 pict-rs || true diff --git a/api_tests/src/image.spec.ts b/api_tests/src/image.spec.ts index 961eed50cb..d25ab7f4c3 100644 --- a/api_tests/src/image.spec.ts +++ b/api_tests/src/image.spec.ts @@ -104,7 +104,7 @@ test("Upload image and delete it", async () => { expect(deletedListAllMediaRes.images.length).toBe(previousThumbnails - 1); }); -test.only("Purge user, uploaded image removed", async () => { +test("Purge user, uploaded image removed", async () => { let user = await registerUser(alphaImage, alphaUrl); // upload test image @@ -128,7 +128,6 @@ test.only("Purge user, uploaded image removed", async () => { }; const delete_ = await alphaImage.purgePerson(purgeForm); expect(delete_.success).toBe(true); - console.log(upload.image_url + " should be purged"); // ensure that image is deleted const response2 = await fetch(upload.image_url ?? ""); diff --git a/api_tests/src/shared.ts b/api_tests/src/shared.ts index a2cf48d847..4cad739f4c 100644 --- a/api_tests/src/shared.ts +++ b/api_tests/src/shared.ts @@ -5,7 +5,7 @@ import { CommunityId, CommunityVisibility, CreatePrivateMessageReport, - DeleteImageParams, + DeleteImage, EditCommunity, GetCommunityPendingFollowsCountResponse, GetReplies, @@ -714,6 +714,8 @@ export async function saveUserSettingsBio( export async function saveUserSettingsFederated( api: LemmyHttp, ): Promise { + let avatar = sampleImage; + let banner = sampleImage; let bio = "a changed bio"; let form: SaveUserSettings = { show_nsfw: false, @@ -721,6 +723,8 @@ export async function saveUserSettingsFederated( default_post_sort_type: "Hot", default_listing_type: "All", interface_language: "", + avatar, + banner, display_name: "user321", show_avatars: false, send_notifications_to_email: false, @@ -932,19 +936,17 @@ export async function deleteAllImages(api: LemmyHttp) { const imagesRes = await api.listAllMedia({ limit: imageFetchLimit, }); - const forms = imagesRes.images.map(image => { - const form: DeleteImageParams = { - token: image.local_image.pictrs_delete_token, - filename: image.local_image.pictrs_alias, - }; - return form; - }); - for (const form of forms) { - console.log( - "delete image: token=" + form.token + ", name=" + form.filename, - ); - await api.deleteImage(form); - } + Promise.all( + imagesRes.images + .map(image => { + const form: DeleteImage = { + token: image.local_image.pictrs_delete_token, + filename: image.local_image.pictrs_alias, + }; + return form; + }) + .map(form => api.deleteImage(form)), + ); } export async function unfollows() { diff --git a/config/defaults.hjson b/config/defaults.hjson index 56565b213e..b5d3b1004d 100644 --- a/config/defaults.hjson +++ b/config/defaults.hjson @@ -20,11 +20,6 @@ url: "http://localhost:8080/" # Set a custom pictrs API key. ( Required for deleting images ) api_key: "string" - # Backwards compatibility with 0.18.1. False is equivalent to `image_mode: None`, true is - # equivalent to `image_mode: StoreLinkPreviews`. - # - # To be removed in 0.20 - cache_external_link_previews: true # Specifies how to handle remote images, so that users don't have to connect directly to remote # servers. image_mode: @@ -38,7 +33,7 @@ # ensures that they can be reliably retrieved and can be resized using pict-rs APIs. However # it also increases storage usage. # - # This is the default behaviour, and also matches Lemmy 0.18. + # This behaviour matches Lemmy 0.18. "StoreLinkPreviews" # or diff --git a/crates/api_common/src/request.rs b/crates/api_common/src/request.rs index 8982b85c8d..385bdfbaf0 100644 --- a/crates/api_common/src/request.rs +++ b/crates/api_common/src/request.rs @@ -385,7 +385,7 @@ pub async fn delete_image_from_pictrs( async fn generate_pictrs_thumbnail(image_url: &Url, context: &LemmyContext) -> LemmyResult { let pictrs_config = context.settings().pictrs()?; - match pictrs_config.image_mode() { + match pictrs_config.image_mode { PictrsImageMode::None => return Ok(image_url.clone()), PictrsImageMode::ProxyAllImages => { return Ok(proxy_image_link(image_url.clone(), context).await?.into()) diff --git a/crates/api_common/src/utils.rs b/crates/api_common/src/utils.rs index c474037e5d..91acde0a90 100644 --- a/crates/api_common/src/utils.rs +++ b/crates/api_common/src/utils.rs @@ -1060,7 +1060,7 @@ pub async fn process_markdown( markdown_check_for_blocked_urls(&text, url_blocklist)?; - if context.settings().pictrs()?.image_mode() == PictrsImageMode::ProxyAllImages { + if context.settings().pictrs()?.image_mode == PictrsImageMode::ProxyAllImages { let (text, links) = markdown_rewrite_image_links(text); RemoteImage::create(&mut context.pool(), links.clone()).await?; @@ -1128,7 +1128,7 @@ async fn proxy_image_link_internal( /// Rewrite a link to go through `/api/v4/image_proxy` endpoint. This is only for remote urls and /// if image_proxy setting is enabled. pub async fn proxy_image_link(link: Url, context: &LemmyContext) -> LemmyResult { - proxy_image_link_internal(link, context.settings().pictrs()?.image_mode(), context).await + proxy_image_link_internal(link, context.settings().pictrs()?.image_mode, context).await } pub async fn proxy_image_link_opt_apub( diff --git a/crates/apub/assets/lemmy/activities/block/block_user.json b/crates/apub/assets/lemmy/activities/block/block_user.json index f6d6170c32..1fd0d534e3 100644 --- a/crates/apub/assets/lemmy/activities/block/block_user.json +++ b/crates/apub/assets/lemmy/activities/block/block_user.json @@ -3,7 +3,6 @@ "to": ["https://www.w3.org/ns/activitystreams#Public"], "object": "http://ds9.lemmy.ml/u/lemmy_alpha", "cc": ["http://enterprise.lemmy.ml/c/main"], - "audience": "http://enterprise.lemmy.ml/u/main", "target": "http://enterprise.lemmy.ml/c/main", "type": "Block", "removeData": true, diff --git a/crates/apub/assets/lemmy/activities/block/undo_block_user.json b/crates/apub/assets/lemmy/activities/block/undo_block_user.json index 922b5e7779..a403a82b7d 100644 --- a/crates/apub/assets/lemmy/activities/block/undo_block_user.json +++ b/crates/apub/assets/lemmy/activities/block/undo_block_user.json @@ -6,7 +6,6 @@ "to": ["https://www.w3.org/ns/activitystreams#Public"], "object": "http://ds9.lemmy.ml/u/lemmy_alpha", "cc": ["http://enterprise.lemmy.ml/c/main"], - "audience": "http://enterprise.lemmy.ml/u/main", "target": "http://enterprise.lemmy.ml/c/main", "type": "Block", "removeData": true, @@ -15,7 +14,6 @@ "id": "http://enterprise.lemmy.ml/activities/block/726f43ab-bd0e-4ab3-89c8-627e976f553c" }, "cc": ["http://enterprise.lemmy.ml/c/main"], - "audience": "http://enterprise.lemmy.ml/u/main", "type": "Undo", "id": "http://enterprise.lemmy.ml/activities/undo/06a20ffb-3e32-42fb-8f4c-674b36d7c557" } diff --git a/crates/apub/assets/lemmy/activities/community/add_featured_post.json b/crates/apub/assets/lemmy/activities/community/add_featured_post.json index 20fe6fd354..f2f0765765 100644 --- a/crates/apub/assets/lemmy/activities/community/add_featured_post.json +++ b/crates/apub/assets/lemmy/activities/community/add_featured_post.json @@ -5,6 +5,5 @@ "type": "Add", "actor": "https://ds9.lemmy.ml/u/lemmy_alpha", "object": "https://ds9.lemmy.ml/post/2", - "target": "https://ds9.lemmy.ml/c/main/featured", - "audience": "https://ds9.lemmy.ml/c/main" + "target": "https://ds9.lemmy.ml/c/main/featured" } diff --git a/crates/apub/assets/lemmy/activities/community/add_mod.json b/crates/apub/assets/lemmy/activities/community/add_mod.json index f440cab8f5..84f1bacbbc 100644 --- a/crates/apub/assets/lemmy/activities/community/add_mod.json +++ b/crates/apub/assets/lemmy/activities/community/add_mod.json @@ -4,7 +4,6 @@ "object": "http://ds9.lemmy.ml/u/lemmy_alpha", "target": "http://enterprise.lemmy.ml/c/main/moderators", "cc": ["http://enterprise.lemmy.ml/c/main"], - "audience": "http://enterprise.lemmy.ml/u/main", "type": "Add", "id": "http://enterprise.lemmy.ml/activities/add/ec069147-77c3-447f-88c8-0ef1df10403f" } diff --git a/crates/apub/assets/lemmy/activities/community/lock_page.json b/crates/apub/assets/lemmy/activities/community/lock_page.json index b98db74025..cbf41ca62a 100644 --- a/crates/apub/assets/lemmy/activities/community/lock_page.json +++ b/crates/apub/assets/lemmy/activities/community/lock_page.json @@ -4,6 +4,5 @@ "to": ["https://www.w3.org/ns/activitystreams#Public"], "object": "http://lemmy-alpha:8541/post/2", "cc": ["http://lemmy-alpha:8541/c/main"], - "type": "Lock", - "audience": "http://lemmy-alpha:8541/c/main" + "type": "Lock" } diff --git a/crates/apub/assets/lemmy/activities/community/remove_featured_post.json b/crates/apub/assets/lemmy/activities/community/remove_featured_post.json index 4cabb95d2a..b9b84e2d13 100644 --- a/crates/apub/assets/lemmy/activities/community/remove_featured_post.json +++ b/crates/apub/assets/lemmy/activities/community/remove_featured_post.json @@ -5,6 +5,5 @@ "type": "Remove", "actor": "https://ds9.lemmy.ml/u/lemmy_alpha", "object": "https://ds9.lemmy.ml/post/2", - "target": "https://ds9.lemmy.ml/c/main/featured", - "audience": "https://ds9.lemmy.ml/c/main" + "target": "https://ds9.lemmy.ml/c/main/featured" } diff --git a/crates/apub/assets/lemmy/activities/community/remove_mod.json b/crates/apub/assets/lemmy/activities/community/remove_mod.json index 17f38078c6..1deee80580 100644 --- a/crates/apub/assets/lemmy/activities/community/remove_mod.json +++ b/crates/apub/assets/lemmy/activities/community/remove_mod.json @@ -5,6 +5,5 @@ "cc": ["http://enterprise.lemmy.ml/c/main"], "type": "Remove", "target": "http://enterprise.lemmy.ml/c/main/moderators", - "audience": "http://enterprise.lemmy.ml/u/main", "id": "http://enterprise.lemmy.ml/activities/remove/aab114f8-cfbd-4935-a5b7-e1a64603650d" } diff --git a/crates/apub/assets/lemmy/activities/community/report_page.json b/crates/apub/assets/lemmy/activities/community/report_page.json index e35a623df1..718b142e1d 100644 --- a/crates/apub/assets/lemmy/activities/community/report_page.json +++ b/crates/apub/assets/lemmy/activities/community/report_page.json @@ -1,7 +1,6 @@ { "actor": "http://ds9.lemmy.ml/u/lemmy_alpha", "to": ["http://enterprise.lemmy.ml/c/main"], - "audience": "http://enterprise.lemmy.ml/u/main", "object": "http://enterprise.lemmy.ml/post/7", "summary": "report this post", "type": "Flag", diff --git a/crates/apub/assets/lemmy/activities/community/undo_lock_page.json b/crates/apub/assets/lemmy/activities/community/undo_lock_page.json index 58c8cdbb3d..f50ef4e2a2 100644 --- a/crates/apub/assets/lemmy/activities/community/undo_lock_page.json +++ b/crates/apub/assets/lemmy/activities/community/undo_lock_page.json @@ -8,10 +8,8 @@ "object": "http://lemmy-alpha:8541/post/2", "cc": ["http://lemmy-alpha:8541/c/main"], "type": "Lock", - "id": "http://lemmy-alpha:8541/activities/lock/08b6fd3e-9ef3-4358-a987-8bb641f3e2c3", - "audience": "http://lemmy-alpha:8541/c/main" + "id": "http://lemmy-alpha:8541/activities/lock/08b6fd3e-9ef3-4358-a987-8bb641f3e2c3" }, "cc": ["http://lemmy-alpha:8541/c/main"], - "type": "Undo", - "audience": "http://lemmy-alpha:8541/c/main" + "type": "Undo" } diff --git a/crates/apub/assets/lemmy/activities/community/update_community.json b/crates/apub/assets/lemmy/activities/community/update_community.json index 604e04d45c..824cb055c1 100644 --- a/crates/apub/assets/lemmy/activities/community/update_community.json +++ b/crates/apub/assets/lemmy/activities/community/update_community.json @@ -39,7 +39,6 @@ "updated": "2021-11-01T12:23:50.151874Z" }, "cc": ["http://enterprise.lemmy.ml/c/main"], - "audience": "http://enterprise.lemmy.ml/u/main", "type": "Update", "id": "http://ds9.lemmy.ml/activities/update/d3717cf5-096d-473f-9530-5d52f9d51f5f" } diff --git a/crates/apub/assets/lemmy/activities/create_or_update/create_comment.json b/crates/apub/assets/lemmy/activities/create_or_update/create_comment.json index a12eeb51cb..de419d42ba 100644 --- a/crates/apub/assets/lemmy/activities/create_or_update/create_comment.json +++ b/crates/apub/assets/lemmy/activities/create_or_update/create_comment.json @@ -10,7 +10,6 @@ "http://enterprise.lemmy.ml/c/main", "http://ds9.lemmy.ml/u/lemmy_alpha" ], - "audience": "http://ds9.lemmy.ml/u/lemmy_alpha", "content": "hello", "mediaType": "text/html", "source": { @@ -24,7 +23,6 @@ "http://enterprise.lemmy.ml/c/main", "http://ds9.lemmy.ml/u/lemmy_alpha" ], - "audience": "http://ds9.lemmy.ml/u/lemmy_alpha", "tag": [ { "href": "http://ds9.lemmy.ml/u/lemmy_alpha", diff --git a/crates/apub/assets/lemmy/activities/create_or_update/create_page.json b/crates/apub/assets/lemmy/activities/create_or_update/create_page.json index 114c5b557b..1db8ccf967 100644 --- a/crates/apub/assets/lemmy/activities/create_or_update/create_page.json +++ b/crates/apub/assets/lemmy/activities/create_or_update/create_page.json @@ -9,7 +9,6 @@ "http://enterprise.lemmy.ml/c/main", "https://www.w3.org/ns/activitystreams#Public" ], - "audience": "https://enterprise.lemmy.ml/c/main", "name": "test post", "content": "

test body

\n", "mediaType": "text/html", @@ -31,7 +30,6 @@ "published": "2021-10-29T15:10:51.557399Z" }, "cc": ["http://enterprise.lemmy.ml/c/main"], - "audience": "https://enterprise.lemmy.ml/c/main", "type": "Create", "id": "http://ds9.lemmy.ml/activities/create/eee6a57a-622f-464d-b560-73ae1fcd3ddf" } diff --git a/crates/apub/assets/lemmy/activities/create_or_update/update_page.json b/crates/apub/assets/lemmy/activities/create_or_update/update_page.json index e9569e6f7b..fba3aa2bd7 100644 --- a/crates/apub/assets/lemmy/activities/create_or_update/update_page.json +++ b/crates/apub/assets/lemmy/activities/create_or_update/update_page.json @@ -9,7 +9,6 @@ "http://enterprise.lemmy.ml/c/main", "https://www.w3.org/ns/activitystreams#Public" ], - "audience": "https://enterprise.lemmy.ml/c/main", "name": "test post 1", "content": "

test body

\n", "mediaType": "text/html", @@ -28,7 +27,6 @@ "updated": "2021-10-29T15:11:35.976374Z" }, "cc": ["http://enterprise.lemmy.ml/c/main"], - "audience": "https://enterprise.lemmy.ml/c/main", "type": "Update", "id": "http://ds9.lemmy.ml/activities/update/ab360117-e165-4de4-b7fc-906b62c98631" } diff --git a/crates/apub/assets/lemmy/activities/deletion/delete_page.json b/crates/apub/assets/lemmy/activities/deletion/delete_page.json index d0fcdea5b5..73bcb18fad 100644 --- a/crates/apub/assets/lemmy/activities/deletion/delete_page.json +++ b/crates/apub/assets/lemmy/activities/deletion/delete_page.json @@ -3,7 +3,6 @@ "to": ["https://www.w3.org/ns/activitystreams#Public"], "object": "http://ds9.lemmy.ml/post/1", "cc": ["http://enterprise.lemmy.ml/c/main"], - "audience": "http://enterprise.lemmy.ml/u/main", "type": "Delete", "id": "http://ds9.lemmy.ml/activities/delete/f2abee48-c7bb-41d5-9e27-8775ff32db12" } diff --git a/crates/apub/assets/lemmy/activities/deletion/remove_note.json b/crates/apub/assets/lemmy/activities/deletion/remove_note.json index b0d6e5e8d1..78a3413040 100644 --- a/crates/apub/assets/lemmy/activities/deletion/remove_note.json +++ b/crates/apub/assets/lemmy/activities/deletion/remove_note.json @@ -3,7 +3,6 @@ "to": ["https://www.w3.org/ns/activitystreams#Public"], "object": "http://ds9.lemmy.ml/comment/1", "cc": ["http://enterprise.lemmy.ml/c/main"], - "audience": "http://enterprise.lemmy.ml/u/main", "type": "Delete", "summary": "bad comment", "id": "http://enterprise.lemmy.ml/activities/delete/42ca1a79-f99e-4518-a2ca-ba2df221eb5e" diff --git a/crates/apub/assets/lemmy/activities/deletion/undo_delete_page.json b/crates/apub/assets/lemmy/activities/deletion/undo_delete_page.json index dc5307d7cc..1d50739597 100644 --- a/crates/apub/assets/lemmy/activities/deletion/undo_delete_page.json +++ b/crates/apub/assets/lemmy/activities/deletion/undo_delete_page.json @@ -6,12 +6,10 @@ "to": ["https://www.w3.org/ns/activitystreams#Public"], "object": "http://ds9.lemmy.ml/post/1", "cc": ["http://enterprise.lemmy.ml/c/main"], - "audience": "http://enterprise.lemmy.ml/u/main", "type": "Delete", "id": "http://ds9.lemmy.ml/activities/delete/b13cca96-7737-41e1-9769-8fbf972b3509" }, "cc": ["http://enterprise.lemmy.ml/c/main"], - "audience": "http://enterprise.lemmy.ml/u/main", "type": "Undo", "id": "http://ds9.lemmy.ml/activities/undo/5e939cfb-b8a1-4de8-950f-9d684e9162b9" } diff --git a/crates/apub/assets/lemmy/activities/deletion/undo_remove_note.json b/crates/apub/assets/lemmy/activities/deletion/undo_remove_note.json index 4c335acca3..d8182b0334 100644 --- a/crates/apub/assets/lemmy/activities/deletion/undo_remove_note.json +++ b/crates/apub/assets/lemmy/activities/deletion/undo_remove_note.json @@ -6,13 +6,11 @@ "to": ["https://www.w3.org/ns/activitystreams#Public"], "object": "http://ds9.lemmy.ml/comment/1", "cc": ["http://enterprise.lemmy.ml/c/main"], - "audience": "http://enterprise.lemmy.ml/u/main", "type": "Delete", "summary": "bad comment", "id": "http://enterprise.lemmy.ml/activities/delete/2598435c-87a3-49cd-81f3-a44b03b7af9d" }, "cc": ["http://enterprise.lemmy.ml/c/main"], - "audience": "http://enterprise.lemmy.ml/u/main", "type": "Undo", "id": "http://enterprise.lemmy.ml/activities/undo/a850cf21-3866-4b3a-b80b-56aa00997fee" } diff --git a/crates/apub/assets/lemmy/activities/voting/dislike_page.json b/crates/apub/assets/lemmy/activities/voting/dislike_page.json index bfa12972b2..220e1c3d4e 100644 --- a/crates/apub/assets/lemmy/activities/voting/dislike_page.json +++ b/crates/apub/assets/lemmy/activities/voting/dislike_page.json @@ -1,7 +1,6 @@ { "actor": "http://enterprise.lemmy.ml/u/lemmy_beta", "object": "http://ds9.lemmy.ml/post/1", - "audience": "https://enterprise.lemmy.ml/c/tenforward", "type": "Dislike", "id": "http://enterprise.lemmy.ml/activities/dislike/64d40d40-a829-43a5-8247-1fb595b3ca1c" } diff --git a/crates/apub/assets/lemmy/activities/voting/like_note.json b/crates/apub/assets/lemmy/activities/voting/like_note.json index ffc6fccc96..a53cae113c 100644 --- a/crates/apub/assets/lemmy/activities/voting/like_note.json +++ b/crates/apub/assets/lemmy/activities/voting/like_note.json @@ -1,7 +1,6 @@ { "actor": "http://ds9.lemmy.ml/u/lemmy_alpha", "object": "http://ds9.lemmy.ml/comment/1", - "audience": "https://enterprise.lemmy.ml/c/tenforward", "type": "Like", "id": "http://ds9.lemmy.ml/activities/like/fd61d070-7382-46a9-b2b7-6bb253732877" } diff --git a/crates/apub/assets/lemmy/activities/voting/undo_dislike_page.json b/crates/apub/assets/lemmy/activities/voting/undo_dislike_page.json index d5e1982b1f..9bc1e448a1 100644 --- a/crates/apub/assets/lemmy/activities/voting/undo_dislike_page.json +++ b/crates/apub/assets/lemmy/activities/voting/undo_dislike_page.json @@ -3,11 +3,9 @@ "object": { "actor": "http://enterprise.lemmy.ml/u/lemmy_beta", "object": "http://ds9.lemmy.ml/post/1", - "audience": "https://enterprise.lemmy.ml/c/tenforward", "type": "Like", "id": "http://enterprise.lemmy.ml/activities/like/2227ab2c-79e2-4fca-a1d2-1d67dacf2457" }, - "audience": "https://enterprise.lemmy.ml/c/tenforward", "type": "Undo", "id": "http://enterprise.lemmy.ml/activities/undo/6cc6fb71-39fe-49ea-9506-f0423b101e98" } diff --git a/crates/apub/assets/lemmy/activities/voting/undo_like_note.json b/crates/apub/assets/lemmy/activities/voting/undo_like_note.json index 3619636a8d..f807623ed1 100644 --- a/crates/apub/assets/lemmy/activities/voting/undo_like_note.json +++ b/crates/apub/assets/lemmy/activities/voting/undo_like_note.json @@ -3,11 +3,9 @@ "object": { "actor": "http://ds9.lemmy.ml/u/lemmy_alpha", "object": "http://ds9.lemmy.ml/comment/1", - "audience": "https://enterprise.lemmy.ml/c/tenforward", "type": "Like", "id": "http://ds9.lemmy.ml/activities/like/efcf7ae2-dfcc-4ff4-9ce4-6adf251ff004" }, - "audience": "https://enterprise.lemmy.ml/c/tenforward", "type": "Undo", "id": "http://ds9.lemmy.ml/activities/undo/3518565c-24a7-4d9e-8e0a-f7a2f45ac618" } diff --git a/crates/apub/assets/lemmy/collections/group_featured_posts.json b/crates/apub/assets/lemmy/collections/group_featured_posts.json index 70b4d7d3ab..bc81b0dbd4 100644 --- a/crates/apub/assets/lemmy/collections/group_featured_posts.json +++ b/crates/apub/assets/lemmy/collections/group_featured_posts.json @@ -20,8 +20,7 @@ "language": { "identifier": "de", "name": "Deutsch" - }, - "audience": "https://ds9.lemmy.ml/c/main" + } }, { "type": "Page", @@ -40,8 +39,7 @@ "language": { "identifier": "de", "name": "Deutsch" - }, - "audience": "https://ds9.lemmy.ml/c/main" + } } ] } diff --git a/crates/apub/assets/lemmy/objects/comment.json b/crates/apub/assets/lemmy/objects/comment.json index 8366ebd6d8..d8282ab5c1 100644 --- a/crates/apub/assets/lemmy/objects/comment.json +++ b/crates/apub/assets/lemmy/objects/comment.json @@ -2,12 +2,11 @@ "id": "https://enterprise.lemmy.ml/comment/38741", "type": "Note", "attributedTo": "https://enterprise.lemmy.ml/u/picard", - "to": ["https://www.w3.org/ns/activitystreams#Public"], - "cc": [ + "to": [ "https://enterprise.lemmy.ml/c/tenforward", - "https://enterprise.lemmy.ml/u/picard" + "https://www.w3.org/ns/activitystreams#Public" ], - "audience": "https://enterprise.lemmy.ml/c/tenforward", + "cc": ["https://enterprise.lemmy.ml/u/picard"], "inReplyTo": "https://enterprise.lemmy.ml/post/55143", "content": "

first comment!

\n", "mediaType": "text/html", diff --git a/crates/apub/assets/lemmy/objects/page.json b/crates/apub/assets/lemmy/objects/page.json index 20af5dfd26..7c926c5d17 100644 --- a/crates/apub/assets/lemmy/objects/page.json +++ b/crates/apub/assets/lemmy/objects/page.json @@ -6,7 +6,6 @@ "https://enterprise.lemmy.ml/c/tenforward", "https://www.w3.org/ns/activitystreams#Public" ], - "audience": "https://enterprise.lemmy.ml/c/tenforward", "name": "Post title", "content": "

This is a post in the /c/tenforward community

\n", "mediaType": "text/html", diff --git a/crates/apub/src/activities/block/block_user.rs b/crates/apub/src/activities/block/block_user.rs index 14b9f9adce..258de713f5 100644 --- a/crates/apub/src/activities/block/block_user.rs +++ b/crates/apub/src/activities/block/block_user.rs @@ -1,4 +1,4 @@ -use super::to_and_audience; +use super::to; use crate::{ activities::{ block::{generate_cc, SiteOrCommunity}, @@ -54,7 +54,7 @@ impl BlockUser { expires: Option>, context: &Data, ) -> LemmyResult { - let (to, audience) = to_and_audience(target)?; + let to = to(target)?; Ok(BlockUser { actor: mod_.id().into(), to, @@ -68,7 +68,6 @@ impl BlockUser { BlockType::Block, &context.settings().get_protocol_and_hostname(), )?, - audience, end_time: expires, }) } diff --git a/crates/apub/src/activities/block/mod.rs b/crates/apub/src/activities/block/mod.rs index 550d981837..6638fa0523 100644 --- a/crates/apub/src/activities/block/mod.rs +++ b/crates/apub/src/activities/block/mod.rs @@ -208,12 +208,10 @@ pub(crate) async fn send_ban_from_community( } } -fn to_and_audience( - target: &SiteOrCommunity, -) -> LemmyResult<(Vec, Option>)> { +fn to(target: &SiteOrCommunity) -> LemmyResult> { Ok(if let SiteOrCommunity::Community(c) = target { - (vec![generate_to(c)?], Some(c.id().into())) + generate_to(c)? } else { - (vec![public()], None) + vec![public()] }) } diff --git a/crates/apub/src/activities/block/undo_block_user.rs b/crates/apub/src/activities/block/undo_block_user.rs index 55715fd303..d4d9a7a6e2 100644 --- a/crates/apub/src/activities/block/undo_block_user.rs +++ b/crates/apub/src/activities/block/undo_block_user.rs @@ -1,4 +1,4 @@ -use super::to_and_audience; +use super::to; use crate::{ activities::{ block::{generate_cc, SiteOrCommunity}, @@ -46,7 +46,7 @@ impl UndoBlockUser { context: &Data, ) -> LemmyResult<()> { let block = BlockUser::new(target, user, mod_, None, reason, None, context).await?; - let (to, audience) = to_and_audience(target)?; + let to = to(target)?; let id = generate_activity_id( UndoType::Undo, @@ -59,7 +59,6 @@ impl UndoBlockUser { cc: generate_cc(target, &mut context.pool()).await?, kind: UndoType::Undo, id: id.clone(), - audience, restore_data: Some(restore_data), }; diff --git a/crates/apub/src/activities/community/announce.rs b/crates/apub/src/activities/community/announce.rs index 9d714e304e..e63ea3b4e5 100644 --- a/crates/apub/src/activities/community/announce.rs +++ b/crates/apub/src/activities/community/announce.rs @@ -94,7 +94,7 @@ impl AnnounceActivity { generate_announce_activity_id(inner_kind, &context.settings().get_protocol_and_hostname())?; Ok(AnnounceActivity { actor: community.id().into(), - to: vec![generate_to(community)?], + to: generate_to(community)?, object: IdOrNestedObject::NestedObject(object), cc: community .followers_url diff --git a/crates/apub/src/activities/community/collection_add.rs b/crates/apub/src/activities/community/collection_add.rs index 1014229c86..c84909ac1f 100644 --- a/crates/apub/src/activities/community/collection_add.rs +++ b/crates/apub/src/activities/community/collection_add.rs @@ -54,13 +54,12 @@ impl CollectionAdd { )?; let add = CollectionAdd { actor: actor.id().into(), - to: vec![generate_to(community)?], + to: generate_to(community)?, object: added_mod.id(), target: generate_moderators_url(&community.actor_id)?.into(), cc: vec![community.id()], kind: AddType::Add, id: id.clone(), - audience: Some(community.id().into()), }; let activity = AnnouncableActivities::CollectionAdd(add); @@ -80,13 +79,12 @@ impl CollectionAdd { )?; let add = CollectionAdd { actor: actor.id().into(), - to: vec![generate_to(community)?], + to: generate_to(community)?, object: featured_post.ap_id.clone().into(), target: generate_featured_url(&community.actor_id)?.into(), cc: vec![community.id()], kind: AddType::Add, id: id.clone(), - audience: Some(community.id().into()), }; let activity = AnnouncableActivities::CollectionAdd(add); send_activity_in_community( diff --git a/crates/apub/src/activities/community/collection_remove.rs b/crates/apub/src/activities/community/collection_remove.rs index c942867039..3ffcca8537 100644 --- a/crates/apub/src/activities/community/collection_remove.rs +++ b/crates/apub/src/activities/community/collection_remove.rs @@ -49,13 +49,12 @@ impl CollectionRemove { )?; let remove = CollectionRemove { actor: actor.id().into(), - to: vec![generate_to(community)?], + to: generate_to(community)?, object: removed_mod.id(), target: generate_moderators_url(&community.actor_id)?.into(), id: id.clone(), cc: vec![community.id()], kind: RemoveType::Remove, - audience: Some(community.id().into()), }; let activity = AnnouncableActivities::CollectionRemove(remove); @@ -75,13 +74,12 @@ impl CollectionRemove { )?; let remove = CollectionRemove { actor: actor.id().into(), - to: vec![generate_to(community)?], + to: generate_to(community)?, object: featured_post.ap_id.clone().into(), target: generate_featured_url(&community.actor_id)?.into(), cc: vec![community.id()], kind: RemoveType::Remove, id: id.clone(), - audience: Some(community.id().into()), }; let activity = AnnouncableActivities::CollectionRemove(remove); send_activity_in_community( diff --git a/crates/apub/src/activities/community/lock_page.rs b/crates/apub/src/activities/community/lock_page.rs index af6a5796f0..412711c6bd 100644 --- a/crates/apub/src/activities/community/lock_page.rs +++ b/crates/apub/src/activities/community/lock_page.rs @@ -138,12 +138,11 @@ pub(crate) async fn send_lock_post( let community_id = community.actor_id.inner().clone(); let lock = LockPage { actor: actor.actor_id.clone().into(), - to: vec![generate_to(&community)?], + to: generate_to(&community)?, object: ObjectId::from(post.ap_id), cc: vec![community_id.clone()], kind: LockType::Lock, id, - audience: Some(community_id.into()), }; let activity = if locked { AnnouncableActivities::LockPost(lock) @@ -154,11 +153,10 @@ pub(crate) async fn send_lock_post( )?; let undo = UndoLockPage { actor: lock.actor.clone(), - to: vec![generate_to(&community)?], + to: generate_to(&community)?, cc: lock.cc.clone(), kind: UndoType::Undo, id, - audience: lock.audience.clone(), object: lock, }; AnnouncableActivities::UndoLockPost(undo) diff --git a/crates/apub/src/activities/community/report.rs b/crates/apub/src/activities/community/report.rs index c33064459f..804822d6ea 100644 --- a/crates/apub/src/activities/community/report.rs +++ b/crates/apub/src/activities/community/report.rs @@ -56,7 +56,6 @@ impl Report { content: None, kind, id: id.clone(), - audience: Some(community.id().into()), }; // send report to the community where object was posted diff --git a/crates/apub/src/activities/community/update.rs b/crates/apub/src/activities/community/update.rs index b6bc50ca0b..280038d5fb 100644 --- a/crates/apub/src/activities/community/update.rs +++ b/crates/apub/src/activities/community/update.rs @@ -43,12 +43,11 @@ pub(crate) async fn send_update_community( )?; let update = UpdateCommunity { actor: actor.id().into(), - to: vec![generate_to(&community)?], + to: generate_to(&community)?, object: Box::new(community.clone().into_json(&context).await?), cc: vec![community.id()], kind: UpdateType::Update, id: id.clone(), - audience: Some(community.id().into()), }; let activity = AnnouncableActivities::UpdateCommunity(update); diff --git a/crates/apub/src/activities/create_or_update/comment.rs b/crates/apub/src/activities/create_or_update/comment.rs index 72dae48b71..f8d2a6d714 100644 --- a/crates/apub/src/activities/create_or_update/comment.rs +++ b/crates/apub/src/activities/create_or_update/comment.rs @@ -71,13 +71,12 @@ impl CreateOrUpdateNote { let create_or_update = CreateOrUpdateNote { actor: person.id().into(), - to: vec![generate_to(&community)?], + to: generate_to(&community)?, cc: note.cc.clone(), tag: note.tag.clone(), object: note, kind, id: id.clone(), - audience: Some(community.id().into()), }; let tagged_users: Vec> = create_or_update diff --git a/crates/apub/src/activities/create_or_update/post.rs b/crates/apub/src/activities/create_or_update/post.rs index 832b2da6d4..80a95f4bec 100644 --- a/crates/apub/src/activities/create_or_update/post.rs +++ b/crates/apub/src/activities/create_or_update/post.rs @@ -49,12 +49,11 @@ impl CreateOrUpdatePage { )?; Ok(CreateOrUpdatePage { actor: actor.id().into(), - to: vec![generate_to(community)?], + to: generate_to(community)?, object: post.into_json(context).await?, cc: vec![community.id()], kind, id: id.clone(), - audience: Some(community.id().into()), }) } diff --git a/crates/apub/src/activities/deletion/delete.rs b/crates/apub/src/activities/deletion/delete.rs index 4ad24d9662..48e24bbebd 100644 --- a/crates/apub/src/activities/deletion/delete.rs +++ b/crates/apub/src/activities/deletion/delete.rs @@ -102,7 +102,6 @@ impl Delete { kind: DeleteType::Delete, summary, id, - audience: community.map(|c| c.actor_id.clone().into()), remove_data: None, }) } diff --git a/crates/apub/src/activities/deletion/mod.rs b/crates/apub/src/activities/deletion/mod.rs index 941ac4237d..3b50cfd1aa 100644 --- a/crates/apub/src/activities/deletion/mod.rs +++ b/crates/apub/src/activities/deletion/mod.rs @@ -60,7 +60,7 @@ pub(crate) async fn send_apub_delete_in_community( ) -> LemmyResult<()> { let actor = ApubPerson::from(actor); let is_mod_action = reason.is_some(); - let to = vec![generate_to(&community)?]; + let to = generate_to(&community)?; let activity = if deleted { let delete = Delete::new(&actor, object, to, Some(&community), reason, context)?; AnnouncableActivities::Delete(delete) diff --git a/crates/apub/src/activities/deletion/undo_delete.rs b/crates/apub/src/activities/deletion/undo_delete.rs index b30b22fd42..0329369099 100644 --- a/crates/apub/src/activities/deletion/undo_delete.rs +++ b/crates/apub/src/activities/deletion/undo_delete.rs @@ -87,7 +87,6 @@ impl UndoDelete { cc: cc.into_iter().collect(), kind: UndoType::Undo, id, - audience: community.map(|c| c.actor_id.clone().into()), }) } diff --git a/crates/apub/src/activities/mod.rs b/crates/apub/src/activities/mod.rs index ffb6a662e7..feef9cbd03 100644 --- a/crates/apub/src/activities/mod.rs +++ b/crates/apub/src/activities/mod.rs @@ -136,23 +136,15 @@ pub(crate) fn verify_visibility(to: &[Url], cc: &[Url], community: &Community) - } /// Marks object as public only if the community is public -pub(crate) fn generate_to(community: &Community) -> LemmyResult { +pub(crate) fn generate_to(community: &Community) -> LemmyResult> { + let actor_id = community.actor_id.clone().into(); if community.visibility == CommunityVisibility::Public { - Ok(public()) + Ok(vec![actor_id, public()]) } else { - Ok(Url::parse(&format!("{}/followers", community.actor_id))?) - } -} - -pub(crate) fn verify_community_matches(a: &ObjectId, b: T) -> LemmyResult<()> -where - T: Into>, -{ - let b: ObjectId = b.into(); - if a != &b { - Err(FederationError::InvalidCommunity)? - } else { - Ok(()) + Ok(vec![ + actor_id.clone(), + Url::parse(&format!("{}/followers", actor_id))?, + ]) } } diff --git a/crates/apub/src/activities/voting/mod.rs b/crates/apub/src/activities/voting/mod.rs index 5cda291ebe..4ce0f695bc 100644 --- a/crates/apub/src/activities/voting/mod.rs +++ b/crates/apub/src/activities/voting/mod.rs @@ -40,13 +40,13 @@ pub(crate) async fn send_like_activity( let empty = ActivitySendTargets::empty(); // score of 1 means upvote, -1 downvote, 0 undo a previous vote if score != 0 { - let vote = Vote::new(object_id, &actor, &community, score.try_into()?, &context)?; + let vote = Vote::new(object_id, &actor, score.try_into()?, &context)?; let activity = AnnouncableActivities::Vote(vote); send_activity_in_community(activity, &actor, &community, empty, false, &context).await } else { // Lemmy API doesn't distinguish between Undo/Like and Undo/Dislike, so we hardcode it here. - let vote = Vote::new(object_id, &actor, &community, VoteType::Like, &context)?; - let undo_vote = UndoVote::new(vote, &actor, &community, &context)?; + let vote = Vote::new(object_id, &actor, VoteType::Like, &context)?; + let undo_vote = UndoVote::new(vote, &actor, &context)?; let activity = AnnouncableActivities::UndoVote(undo_vote); send_activity_in_community(activity, &actor, &community, empty, false, &context).await } diff --git a/crates/apub/src/activities/voting/undo_vote.rs b/crates/apub/src/activities/voting/undo_vote.rs index 61875d442f..f6a6039b08 100644 --- a/crates/apub/src/activities/voting/undo_vote.rs +++ b/crates/apub/src/activities/voting/undo_vote.rs @@ -5,7 +5,7 @@ use crate::{ voting::{undo_vote_comment, undo_vote_post}, }, insert_received_activity, - objects::{community::ApubCommunity, person::ApubPerson}, + objects::person::ApubPerson, protocol::{ activities::voting::{undo_vote::UndoVote, vote::Vote}, InCommunity, @@ -26,7 +26,6 @@ impl UndoVote { pub(in crate::activities::voting) fn new( vote: Vote, actor: &ApubPerson, - community: &ApubCommunity, context: &Data, ) -> LemmyResult { Ok(UndoVote { @@ -37,7 +36,6 @@ impl UndoVote { UndoType::Undo, &context.settings().get_protocol_and_hostname(), )?, - audience: Some(community.id().into()), }) } } diff --git a/crates/apub/src/activities/voting/vote.rs b/crates/apub/src/activities/voting/vote.rs index 1cdc819529..bb0b32bd94 100644 --- a/crates/apub/src/activities/voting/vote.rs +++ b/crates/apub/src/activities/voting/vote.rs @@ -5,7 +5,7 @@ use crate::{ voting::{undo_vote_comment, undo_vote_post, vote_comment, vote_post}, }, insert_received_activity, - objects::{community::ApubCommunity, person::ApubPerson}, + objects::person::ApubPerson, protocol::{ activities::voting::vote::{Vote, VoteType}, InCommunity, @@ -26,7 +26,6 @@ impl Vote { pub(in crate::activities::voting) fn new( object_id: ObjectId, actor: &ApubPerson, - community: &ApubCommunity, kind: VoteType, context: &Data, ) -> LemmyResult { @@ -35,7 +34,6 @@ impl Vote { object: object_id, kind: kind.clone(), id: generate_activity_id(kind, &context.settings().get_protocol_and_hostname())?, - audience: Some(community.id().into()), }) } } diff --git a/crates/apub/src/http/mod.rs b/crates/apub/src/http/mod.rs index fc2fbf0d3f..52e0363769 100644 --- a/crates/apub/src/http/mod.rs +++ b/crates/apub/src/http/mod.rs @@ -110,7 +110,7 @@ pub(crate) async fn get_activity( .into(); let activity = SentActivity::read_from_apub_id(&mut context.pool(), &activity_id) .await - .with_lemmy_type(FederationError::CouldntFindActivity.into())?; + .with_lemmy_type(LemmyErrorType::NotFound)?; let sensitive = activity.sensitive; if sensitive { diff --git a/crates/apub/src/mentions.rs b/crates/apub/src/mentions.rs index cb46be52a0..0f04818a39 100644 --- a/crates/apub/src/mentions.rs +++ b/crates/apub/src/mentions.rs @@ -1,7 +1,7 @@ -use crate::objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson}; +use crate::objects::{comment::ApubComment, person::ApubPerson}; use activitypub_federation::{ config::Data, - fetch::{object_id::ObjectId, webfinger::webfinger_resolve_actor}, + fetch::webfinger::webfinger_resolve_actor, kinds::link::MentionType, traits::Actor, }; @@ -42,14 +42,13 @@ pub struct MentionsAndAddresses { /// This takes a comment, and builds a list of to_addresses, inboxes, /// and mention tags, so they know where to be sent to. /// Addresses are the persons / addresses that go in the cc field. -#[tracing::instrument(skip(comment, community_id, context))] +#[tracing::instrument(skip(comment, context))] pub async fn collect_non_local_mentions( comment: &ApubComment, - community_id: ObjectId, context: &Data, ) -> LemmyResult { let parent_creator = get_comment_parent_creator(&mut context.pool(), comment).await?; - let mut addressed_ccs: Vec = vec![community_id.into(), parent_creator.id()]; + let mut addressed_ccs: Vec = vec![parent_creator.id()]; // Add the mention tag let parent_creator_tag = Mention { diff --git a/crates/apub/src/objects/comment.rs b/crates/apub/src/objects/comment.rs index ed9a9e1a2a..fd168e370f 100644 --- a/crates/apub/src/objects/comment.rs +++ b/crates/apub/src/objects/comment.rs @@ -105,13 +105,13 @@ impl Object for ApubComment { post.ap_id.into() }; let language = Some(LanguageTag::new_single(self.language_id, &mut context.pool()).await?); - let maa = collect_non_local_mentions(&self, community.actor_id.clone().into(), context).await?; + let maa = collect_non_local_mentions(&self, context).await?; let note = Note { r#type: NoteType::Note, id: self.ap_id.clone().into(), attributed_to: creator.actor_id.into(), - to: vec![generate_to(&community)?], + to: generate_to(&community)?, cc: maa.ccs, content: markdown_to_html(&self.content), media_type: Some(MediaTypeMarkdownOrHtml::Html), @@ -122,7 +122,6 @@ impl Object for ApubComment { tag: maa.tags, distinguished: Some(self.distinguished), language, - audience: Some(community.actor_id.into()), attachment: vec![], }; diff --git a/crates/apub/src/objects/post.rs b/crates/apub/src/objects/post.rs index 0dd9201c21..73e940a3c6 100644 --- a/crates/apub/src/objects/post.rs +++ b/crates/apub/src/objects/post.rs @@ -133,7 +133,7 @@ impl Object for ApubPost { kind: PageType::Page, id: self.ap_id.clone().into(), attributed_to: AttributedTo::Lemmy(creator.actor_id.into()), - to: vec![generate_to(&community)?], + to: generate_to(&community)?, cc: vec![], name: Some(self.name.clone()), content: self.body.as_ref().map(|b| markdown_to_html(b)), @@ -145,7 +145,6 @@ impl Object for ApubPost { language, published: Some(self.published), updated: self.updated, - audience: Some(community.actor_id.into()), in_reply_to: None, tag: vec![hashtag], }; diff --git a/crates/apub/src/protocol/activities/block/block_user.rs b/crates/apub/src/protocol/activities/block/block_user.rs index 96135d6454..97f54a2a30 100644 --- a/crates/apub/src/protocol/activities/block/block_user.rs +++ b/crates/apub/src/protocol/activities/block/block_user.rs @@ -1,5 +1,5 @@ use crate::{ - activities::{block::SiteOrCommunity, verify_community_matches}, + activities::block::SiteOrCommunity, objects::{community::ApubCommunity, person::ApubPerson}, protocol::InCommunity, }; @@ -31,7 +31,6 @@ pub struct BlockUser { #[serde(rename = "type")] pub(crate) kind: BlockType, pub(crate) id: Url, - pub(crate) audience: Option>, /// Quick and dirty solution. /// TODO: send a separate Delete activity instead @@ -49,9 +48,6 @@ impl InCommunity for BlockUser { SiteOrCommunity::Community(c) => c, SiteOrCommunity::Site(_) => return Err(anyhow!("activity is not in community").into()), }; - if let Some(audience) = &self.audience { - verify_community_matches(audience, community.actor_id.clone())?; - } Ok(community) } } diff --git a/crates/apub/src/protocol/activities/block/undo_block_user.rs b/crates/apub/src/protocol/activities/block/undo_block_user.rs index e038fa2dcb..1dad9b03ce 100644 --- a/crates/apub/src/protocol/activities/block/undo_block_user.rs +++ b/crates/apub/src/protocol/activities/block/undo_block_user.rs @@ -1,5 +1,4 @@ use crate::{ - activities::verify_community_matches, objects::{community::ApubCommunity, person::ApubPerson}, protocol::{activities::block::block_user::BlockUser, InCommunity}, }; @@ -28,7 +27,6 @@ pub struct UndoBlockUser { #[serde(rename = "type")] pub(crate) kind: UndoType, pub(crate) id: Url, - pub(crate) audience: Option>, /// Quick and dirty solution. /// TODO: send a separate Delete activity instead @@ -39,9 +37,6 @@ pub struct UndoBlockUser { impl InCommunity for UndoBlockUser { async fn community(&self, context: &Data) -> LemmyResult { let community = self.object.community(context).await?; - if let Some(audience) = &self.audience { - verify_community_matches(audience, community.actor_id.clone())?; - } Ok(community) } } diff --git a/crates/apub/src/protocol/activities/community/collection_add.rs b/crates/apub/src/protocol/activities/community/collection_add.rs index 777ad8b621..d7c5e5143a 100644 --- a/crates/apub/src/protocol/activities/community/collection_add.rs +++ b/crates/apub/src/protocol/activities/community/collection_add.rs @@ -1,5 +1,4 @@ use crate::{ - activities::verify_community_matches, objects::{community::ApubCommunity, person::ApubPerson}, protocol::InCommunity, }; @@ -28,7 +27,6 @@ pub struct CollectionAdd { #[serde(rename = "type")] pub(crate) kind: AddType, pub(crate) id: Url, - pub(crate) audience: Option>, } #[async_trait::async_trait] @@ -36,9 +34,6 @@ impl InCommunity for CollectionAdd { async fn community(&self, context: &Data) -> LemmyResult { let (community, _) = Community::get_by_collection_url(&mut context.pool(), &self.clone().target.into()).await?; - if let Some(audience) = &self.audience { - verify_community_matches(audience, community.actor_id.clone())?; - } Ok(community.into()) } } diff --git a/crates/apub/src/protocol/activities/community/collection_remove.rs b/crates/apub/src/protocol/activities/community/collection_remove.rs index afc0c24a01..85b314b4b9 100644 --- a/crates/apub/src/protocol/activities/community/collection_remove.rs +++ b/crates/apub/src/protocol/activities/community/collection_remove.rs @@ -1,5 +1,4 @@ use crate::{ - activities::verify_community_matches, objects::{community::ApubCommunity, person::ApubPerson}, protocol::InCommunity, }; @@ -28,7 +27,6 @@ pub struct CollectionRemove { pub(crate) kind: RemoveType, pub(crate) target: Url, pub(crate) id: Url, - pub(crate) audience: Option>, } #[async_trait::async_trait] @@ -36,9 +34,6 @@ impl InCommunity for CollectionRemove { async fn community(&self, context: &Data) -> LemmyResult { let (community, _) = Community::get_by_collection_url(&mut context.pool(), &self.clone().target.into()).await?; - if let Some(audience) = &self.audience { - verify_community_matches(audience, community.actor_id.clone())?; - } Ok(community.into()) } } diff --git a/crates/apub/src/protocol/activities/community/lock_page.rs b/crates/apub/src/protocol/activities/community/lock_page.rs index 5c8ecfca90..a97466edca 100644 --- a/crates/apub/src/protocol/activities/community/lock_page.rs +++ b/crates/apub/src/protocol/activities/community/lock_page.rs @@ -1,5 +1,4 @@ use crate::{ - activities::verify_community_matches, objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost}, protocol::InCommunity, }; @@ -33,7 +32,6 @@ pub struct LockPage { #[serde(rename = "type")] pub(crate) kind: LockType, pub(crate) id: Url, - pub(crate) audience: Option>, } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -48,7 +46,6 @@ pub struct UndoLockPage { #[serde(rename = "type")] pub(crate) kind: UndoType, pub(crate) id: Url, - pub(crate) audience: Option>, } #[async_trait::async_trait] @@ -56,9 +53,6 @@ impl InCommunity for LockPage { async fn community(&self, context: &Data) -> LemmyResult { let post = self.object.dereference(context).await?; let community = Community::read(&mut context.pool(), post.community_id).await?; - if let Some(audience) = &self.audience { - verify_community_matches(audience, community.actor_id.clone())?; - } Ok(community.into()) } } @@ -67,9 +61,6 @@ impl InCommunity for LockPage { impl InCommunity for UndoLockPage { async fn community(&self, context: &Data) -> LemmyResult { let community = self.object.community(context).await?; - if let Some(audience) = &self.audience { - verify_community_matches(audience, community.actor_id.clone())?; - } Ok(community) } } diff --git a/crates/apub/src/protocol/activities/community/report.rs b/crates/apub/src/protocol/activities/community/report.rs index dd0f72f43d..71ecf4c5ce 100644 --- a/crates/apub/src/protocol/activities/community/report.rs +++ b/crates/apub/src/protocol/activities/community/report.rs @@ -1,5 +1,4 @@ use crate::{ - activities::verify_community_matches, fetcher::post_or_comment::PostOrComment, objects::{community::ApubCommunity, person::ApubPerson}, protocol::InCommunity, @@ -29,7 +28,6 @@ pub struct Report { #[serde(rename = "type")] pub(crate) kind: FlagType, pub(crate) id: Url, - pub(crate) audience: Option>, } impl Report { @@ -73,9 +71,6 @@ impl ReportObject { impl InCommunity for Report { async fn community(&self, context: &Data) -> LemmyResult { let community = self.to[0].dereference(context).await?; - if let Some(audience) = &self.audience { - verify_community_matches(audience, community.actor_id.clone())?; - } Ok(community) } } diff --git a/crates/apub/src/protocol/activities/community/update.rs b/crates/apub/src/protocol/activities/community/update.rs index 268f050737..080dd27e60 100644 --- a/crates/apub/src/protocol/activities/community/update.rs +++ b/crates/apub/src/protocol/activities/community/update.rs @@ -1,5 +1,4 @@ use crate::{ - activities::verify_community_matches, objects::{community::ApubCommunity, person::ApubPerson}, protocol::{objects::group::Group, InCommunity}, }; @@ -29,16 +28,12 @@ pub struct UpdateCommunity { #[serde(rename = "type")] pub(crate) kind: UpdateType, pub(crate) id: Url, - pub(crate) audience: Option>, } #[async_trait::async_trait] impl InCommunity for UpdateCommunity { async fn community(&self, context: &Data) -> LemmyResult { let community: ApubCommunity = self.object.id.clone().dereference(context).await?; - if let Some(audience) = &self.audience { - verify_community_matches(audience, community.actor_id.clone())?; - } Ok(community) } } diff --git a/crates/apub/src/protocol/activities/create_or_update/note.rs b/crates/apub/src/protocol/activities/create_or_update/note.rs index ff07281744..37f5953375 100644 --- a/crates/apub/src/protocol/activities/create_or_update/note.rs +++ b/crates/apub/src/protocol/activities/create_or_update/note.rs @@ -1,5 +1,4 @@ use crate::{ - activities::verify_community_matches, mentions::MentionOrValue, objects::{community::ApubCommunity, person::ApubPerson}, protocol::{activities::CreateOrUpdateType, objects::note::Note, InCommunity}, @@ -29,7 +28,6 @@ pub struct CreateOrUpdateNote { #[serde(rename = "type")] pub(crate) kind: CreateOrUpdateType, pub(crate) id: Url, - pub(crate) audience: Option>, } #[async_trait::async_trait] @@ -37,9 +35,6 @@ impl InCommunity for CreateOrUpdateNote { async fn community(&self, context: &Data) -> LemmyResult { let post = self.object.get_parents(context).await?.0; let community = Community::read(&mut context.pool(), post.community_id).await?; - if let Some(audience) = &self.audience { - verify_community_matches(audience, community.actor_id.clone())?; - } Ok(community.into()) } } diff --git a/crates/apub/src/protocol/activities/create_or_update/page.rs b/crates/apub/src/protocol/activities/create_or_update/page.rs index 21052a9ef9..3fd397022e 100644 --- a/crates/apub/src/protocol/activities/create_or_update/page.rs +++ b/crates/apub/src/protocol/activities/create_or_update/page.rs @@ -1,5 +1,4 @@ use crate::{ - activities::verify_community_matches, objects::{community::ApubCommunity, person::ApubPerson}, protocol::{activities::CreateOrUpdateType, objects::page::Page, InCommunity}, }; @@ -25,16 +24,12 @@ pub struct CreateOrUpdatePage { #[serde(rename = "type")] pub(crate) kind: CreateOrUpdateType, pub(crate) id: Url, - pub(crate) audience: Option>, } #[async_trait::async_trait] impl InCommunity for CreateOrUpdatePage { async fn community(&self, context: &Data) -> LemmyResult { let community = self.object.community(context).await?; - if let Some(audience) = &self.audience { - verify_community_matches(audience, community.actor_id.clone())?; - } Ok(community) } } diff --git a/crates/apub/src/protocol/activities/deletion/delete.rs b/crates/apub/src/protocol/activities/deletion/delete.rs index 3a29da069a..878f6fb6c2 100644 --- a/crates/apub/src/protocol/activities/deletion/delete.rs +++ b/crates/apub/src/protocol/activities/deletion/delete.rs @@ -1,5 +1,5 @@ use crate::{ - activities::{deletion::DeletableObjects, verify_community_matches}, + activities::deletion::DeletableObjects, objects::{community::ApubCommunity, person::ApubPerson}, protocol::{objects::tombstone::Tombstone, IdOrNestedObject, InCommunity}, }; @@ -31,7 +31,6 @@ pub struct Delete { #[serde(rename = "type")] pub(crate) kind: DeleteType, pub(crate) id: Url, - pub(crate) audience: Option>, #[serde(deserialize_with = "deserialize_one_or_many")] #[serde(default)] @@ -61,9 +60,6 @@ impl InCommunity for Delete { } }; let community = Community::read(&mut context.pool(), community_id).await?; - if let Some(audience) = &self.audience { - verify_community_matches(audience, community.actor_id.clone())?; - } Ok(community.into()) } } diff --git a/crates/apub/src/protocol/activities/deletion/undo_delete.rs b/crates/apub/src/protocol/activities/deletion/undo_delete.rs index 508b901134..a6bf0589bd 100644 --- a/crates/apub/src/protocol/activities/deletion/undo_delete.rs +++ b/crates/apub/src/protocol/activities/deletion/undo_delete.rs @@ -1,5 +1,4 @@ use crate::{ - activities::verify_community_matches, objects::{community::ApubCommunity, person::ApubPerson}, protocol::{activities::deletion::delete::Delete, InCommunity}, }; @@ -26,7 +25,6 @@ pub struct UndoDelete { #[serde(rename = "type")] pub(crate) kind: UndoType, pub(crate) id: Url, - pub(crate) audience: Option>, #[serde(deserialize_with = "deserialize_one_or_many", default)] #[serde(skip_serializing_if = "Vec::is_empty")] @@ -37,9 +35,6 @@ pub struct UndoDelete { impl InCommunity for UndoDelete { async fn community(&self, context: &Data) -> LemmyResult { let community = self.object.community(context).await?; - if let Some(audience) = &self.audience { - verify_community_matches(audience, community.actor_id.clone())?; - } Ok(community) } } diff --git a/crates/apub/src/protocol/activities/voting/undo_vote.rs b/crates/apub/src/protocol/activities/voting/undo_vote.rs index e9ccbd593e..a9f0d05410 100644 --- a/crates/apub/src/protocol/activities/voting/undo_vote.rs +++ b/crates/apub/src/protocol/activities/voting/undo_vote.rs @@ -1,5 +1,4 @@ use crate::{ - activities::verify_community_matches, objects::{community::ApubCommunity, person::ApubPerson}, protocol::{activities::voting::vote::Vote, InCommunity}, }; @@ -17,16 +16,12 @@ pub struct UndoVote { #[serde(rename = "type")] pub(crate) kind: UndoType, pub(crate) id: Url, - pub(crate) audience: Option>, } #[async_trait::async_trait] impl InCommunity for UndoVote { async fn community(&self, context: &Data) -> LemmyResult { let community = self.object.community(context).await?; - if let Some(audience) = &self.audience { - verify_community_matches(audience, community.actor_id.clone())?; - } Ok(community) } } diff --git a/crates/apub/src/protocol/activities/voting/vote.rs b/crates/apub/src/protocol/activities/voting/vote.rs index 883fc85fb0..b6ffe485a0 100644 --- a/crates/apub/src/protocol/activities/voting/vote.rs +++ b/crates/apub/src/protocol/activities/voting/vote.rs @@ -1,5 +1,4 @@ use crate::{ - activities::verify_community_matches, fetcher::post_or_comment::PostOrComment, objects::{community::ApubCommunity, person::ApubPerson}, protocol::InCommunity, @@ -19,7 +18,6 @@ pub struct Vote { #[serde(rename = "type")] pub(crate) kind: VoteType, pub(crate) id: Url, - pub(crate) audience: Option>, } #[derive(Clone, Debug, Display, Deserialize, Serialize, PartialEq, Eq)] @@ -58,9 +56,6 @@ impl InCommunity for Vote { .await? .community(context) .await?; - if let Some(audience) = &self.audience { - verify_community_matches(audience, community.actor_id.clone())?; - } Ok(community) } } diff --git a/crates/apub/src/protocol/mod.rs b/crates/apub/src/protocol/mod.rs index a4774ac1d6..90f1f51ea9 100644 --- a/crates/apub/src/protocol/mod.rs +++ b/crates/apub/src/protocol/mod.rs @@ -82,8 +82,6 @@ impl IdOrNestedObject { #[async_trait::async_trait] pub trait InCommunity { - // TODO: after we use audience field and remove backwards compat, it should be possible to change - // this to simply `fn community(&self) -> LemmyResult>` async fn community(&self, context: &Data) -> LemmyResult; } diff --git a/crates/apub/src/protocol/objects/note.rs b/crates/apub/src/protocol/objects/note.rs index fc38b9b5ed..d4c87333ff 100644 --- a/crates/apub/src/protocol/objects/note.rs +++ b/crates/apub/src/protocol/objects/note.rs @@ -1,5 +1,4 @@ use crate::{ - activities::verify_community_matches, fetcher::post_or_comment::PostOrComment, mentions::MentionOrValue, objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson, post::ApubPost}, @@ -56,7 +55,6 @@ pub struct Note { // lemmy extension pub(crate) distinguished: Option, pub(crate) language: Option, - pub(crate) audience: Option>, #[serde(default)] pub(crate) attachment: Vec, } @@ -94,9 +92,6 @@ impl InCommunity for Note { async fn community(&self, context: &Data) -> LemmyResult { let (post, _) = self.get_parents(context).await?; let community = Community::read(&mut context.pool(), post.community_id).await?; - if let Some(audience) = &self.audience { - verify_community_matches(audience, community.actor_id.clone())?; - } Ok(community.into()) } } diff --git a/crates/apub/src/protocol/objects/page.rs b/crates/apub/src/protocol/objects/page.rs index 3ce720bc0b..ae5925e573 100644 --- a/crates/apub/src/protocol/objects/page.rs +++ b/crates/apub/src/protocol/objects/page.rs @@ -1,5 +1,4 @@ use crate::{ - activities::verify_community_matches, fetcher::user_or_community::{PersonOrGroupType, UserOrCommunity}, objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost}, protocol::{objects::LanguageTag, ImageObject, InCommunity, Source}, @@ -64,7 +63,6 @@ pub struct Page { pub(crate) published: Option>, pub(crate) updated: Option>, pub(crate) language: Option, - pub(crate) audience: Option>, #[serde(deserialize_with = "deserialize_skip_error", default)] pub(crate) tag: Vec, } @@ -231,10 +229,6 @@ impl ActivityHandler for Page { #[async_trait::async_trait] impl InCommunity for Page { async fn community(&self, context: &Data) -> LemmyResult { - if let Some(audience) = &self.audience { - return audience.dereference(context).await; - } - let community = match &self.attributed_to { AttributedTo::Lemmy(_) => { let mut iter = self.to.iter().merge(self.cc.iter()); @@ -259,9 +253,6 @@ impl InCommunity for Page { } }; - if let Some(audience) = &self.audience { - verify_community_matches(audience, community.actor_id.clone())?; - } Ok(community) } } diff --git a/crates/db_schema/replaceable_schema/triggers.sql b/crates/db_schema/replaceable_schema/triggers.sql index 77d368287c..2f78214129 100644 --- a/crates/db_schema/replaceable_schema/triggers.sql +++ b/crates/db_schema/replaceable_schema/triggers.sql @@ -422,6 +422,25 @@ END; $$); +CALL r.create_triggers ('community_report', $$ +BEGIN + UPDATE + community_aggregates AS a + SET + report_count = a.report_count + diff.report_count, unresolved_report_count = a.unresolved_report_count + diff.unresolved_report_count + FROM ( + SELECT + (community_report).community_id, coalesce(sum(count_diff), 0) AS report_count, coalesce(sum(count_diff) FILTER (WHERE NOT (community_report).resolved), 0) AS unresolved_report_count + FROM select_old_and_new_rows AS old_and_new_rows GROUP BY (community_report).community_id) AS diff +WHERE (diff.report_count, diff.unresolved_report_count) != (0, 0) + AND a.community_id = diff.community_id; + +RETURN NULL; + +END; + +$$); + -- These triggers create and update rows in each aggregates table to match its associated table's rows. -- Deleting rows and updating IDs are already handled by `CASCADE` in foreign key constraints. CREATE FUNCTION r.comment_aggregates_from_comment () @@ -685,6 +704,8 @@ CALL r.create_report_combined_trigger ('comment_report'); CALL r.create_report_combined_trigger ('private_message_report'); +CALL r.create_report_combined_trigger ('community_report'); + -- person_content (comment, post) CREATE PROCEDURE r.create_person_content_combined_trigger (table_name text) LANGUAGE plpgsql diff --git a/crates/db_schema/src/aggregates/structs.rs b/crates/db_schema/src/aggregates/structs.rs index 7a97666aab..a254b0a638 100644 --- a/crates/db_schema/src/aggregates/structs.rs +++ b/crates/db_schema/src/aggregates/structs.rs @@ -73,6 +73,8 @@ pub struct CommunityAggregates { #[serde(skip)] pub hot_rank: f64, pub subscribers_local: i64, + pub report_count: i16, + pub unresolved_report_count: i16, } #[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone, Default)] diff --git a/crates/db_schema/src/impls/community_report.rs b/crates/db_schema/src/impls/community_report.rs new file mode 100644 index 0000000000..85c3cc5c0d --- /dev/null +++ b/crates/db_schema/src/impls/community_report.rs @@ -0,0 +1,97 @@ +use crate::{ + newtypes::{CommunityId, CommunityReportId, PersonId}, + schema::community_report::{ + community_id, + dsl::{community_report, resolved, resolver_id, updated}, + }, + source::community_report::{CommunityReport, CommunityReportForm}, + traits::Reportable, + utils::{get_conn, DbPool}, +}; +use chrono::Utc; +use diesel::{ + dsl::{insert_into, update}, + result::Error, + ExpressionMethods, + QueryDsl, +}; +use diesel_async::RunQueryDsl; + +#[async_trait] +impl Reportable for CommunityReport { + type Form = CommunityReportForm; + type IdType = CommunityReportId; + type ObjectIdType = CommunityId; + /// creates a community report and returns it + /// + /// * `conn` - the postgres connection + /// * `community_report_form` - the filled CommunityReportForm to insert + async fn report( + pool: &mut DbPool<'_>, + community_report_form: &CommunityReportForm, + ) -> Result { + let conn = &mut get_conn(pool).await?; + insert_into(community_report) + .values(community_report_form) + .get_result::(conn) + .await + } + + /// resolve a community report + /// + /// * `conn` - the postgres connection + /// * `report_id` - the id of the report to resolve + /// * `by_resolver_id` - the id of the user resolving the report + async fn resolve( + pool: &mut DbPool<'_>, + report_id_: Self::IdType, + by_resolver_id: PersonId, + ) -> Result { + let conn = &mut get_conn(pool).await?; + update(community_report.find(report_id_)) + .set(( + resolved.eq(true), + resolver_id.eq(by_resolver_id), + updated.eq(Utc::now()), + )) + .execute(conn) + .await + } + + async fn resolve_all_for_object( + pool: &mut DbPool<'_>, + community_id_: CommunityId, + by_resolver_id: PersonId, + ) -> Result { + let conn = &mut get_conn(pool).await?; + update(community_report.filter(community_id.eq(community_id_))) + .set(( + resolved.eq(true), + resolver_id.eq(by_resolver_id), + updated.eq(Utc::now()), + )) + .execute(conn) + .await + } + + /// unresolve a community report + /// + /// * `conn` - the postgres connection + /// * `report_id` - the id of the report to unresolve + /// * `by_resolver_id` - the id of the user unresolving the report + async fn unresolve( + pool: &mut DbPool<'_>, + report_id_: Self::IdType, + by_resolver_id: PersonId, + ) -> Result { + let conn = &mut get_conn(pool).await?; + update(community_report.find(report_id_)) + .set(( + resolved.eq(false), + resolver_id.eq(by_resolver_id), + updated.eq(Utc::now()), + )) + .execute(conn) + .await + } +} diff --git a/crates/db_schema/src/impls/mod.rs b/crates/db_schema/src/impls/mod.rs index 2d7a16c2c6..37e16c2549 100644 --- a/crates/db_schema/src/impls/mod.rs +++ b/crates/db_schema/src/impls/mod.rs @@ -6,6 +6,7 @@ pub mod comment_reply; pub mod comment_report; pub mod community; pub mod community_block; +pub mod community_report; pub mod custom_emoji; pub mod email_verification; pub mod federation_allowlist; diff --git a/crates/db_schema/src/newtypes.rs b/crates/db_schema/src/newtypes.rs index f4f53ee3db..7b5a005731 100644 --- a/crates/db_schema/src/newtypes.rs +++ b/crates/db_schema/src/newtypes.rs @@ -91,6 +91,12 @@ pub struct PersonMentionId(i32); /// The comment report id. pub struct CommentReportId(pub i32); +#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)] +#[cfg_attr(feature = "full", derive(DieselNewType, TS))] +#[cfg_attr(feature = "full", ts(export))] +/// The community report id. +pub struct CommunityReportId(pub i32); + #[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)] #[cfg_attr(feature = "full", derive(DieselNewType, TS))] #[cfg_attr(feature = "full", ts(export))] diff --git a/crates/db_schema/src/schema.rs b/crates/db_schema/src/schema.rs index 59ba7be69f..ff9a1948e5 100644 --- a/crates/db_schema/src/schema.rs +++ b/crates/db_schema/src/schema.rs @@ -253,6 +253,8 @@ diesel::table! { users_active_half_year -> Int8, hot_rank -> Float8, subscribers_local -> Int8, + report_count -> Int2, + unresolved_report_count -> Int2, } } @@ -263,6 +265,25 @@ diesel::table! { } } +diesel::table! { + community_report (id) { + id -> Int4, + creator_id -> Int4, + community_id -> Int4, + original_community_name -> Text, + original_community_title -> Text, + original_community_description -> Nullable, + original_community_sidebar -> Nullable, + original_community_icon -> Nullable, + original_community_banner -> Nullable, + reason -> Text, + resolved -> Bool, + resolver_id -> Nullable, + published -> Timestamptz, + updated -> Nullable, + } +} + diesel::table! { custom_emoji (id) { id -> Int4, @@ -922,6 +943,7 @@ diesel::table! { post_report_id -> Nullable, comment_report_id -> Nullable, private_message_report_id -> Nullable, + community_report_id -> Nullable, } } @@ -1040,6 +1062,7 @@ diesel::joinable!(community_actions -> community (community_id)); diesel::joinable!(community_aggregates -> community (community_id)); diesel::joinable!(community_language -> community (community_id)); diesel::joinable!(community_language -> language (language_id)); +diesel::joinable!(community_report -> community (community_id)); diesel::joinable!(custom_emoji_keyword -> custom_emoji (custom_emoji_id)); diesel::joinable!(email_verification -> local_user (local_user_id)); diesel::joinable!(federation_allowlist -> instance (instance_id)); @@ -1099,6 +1122,7 @@ diesel::joinable!(private_message_report -> private_message (private_message_id) diesel::joinable!(registration_application -> local_user (local_user_id)); diesel::joinable!(registration_application -> person (admin_id)); diesel::joinable!(report_combined -> comment_report (comment_report_id)); +diesel::joinable!(report_combined -> community_report (community_report_id)); diesel::joinable!(report_combined -> post_report (post_report_id)); diesel::joinable!(report_combined -> private_message_report (private_message_report_id)); diesel::joinable!(site -> instance (instance_id)); @@ -1124,6 +1148,7 @@ diesel::allow_tables_to_appear_in_same_query!( community_actions, community_aggregates, community_language, + community_report, custom_emoji, custom_emoji_keyword, email_verification, diff --git a/crates/db_schema/src/source/community_report.rs b/crates/db_schema/src/source/community_report.rs new file mode 100644 index 0000000000..57d863acc8 --- /dev/null +++ b/crates/db_schema/src/source/community_report.rs @@ -0,0 +1,60 @@ +use crate::newtypes::{CommunityId, CommunityReportId, DbUrl, PersonId}; +#[cfg(feature = "full")] +use crate::schema::community_report; +use chrono::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; +use serde_with::skip_serializing_none; +#[cfg(feature = "full")] +use ts_rs::TS; + +#[skip_serializing_none] +#[derive(PartialEq, Eq, Serialize, Deserialize, Debug, Clone)] +#[cfg_attr( + feature = "full", + derive(Queryable, Selectable, Associations, Identifiable, TS) +)] +#[cfg_attr( + feature = "full", + diesel(belongs_to(crate::source::community::Community)) +)] +#[cfg_attr(feature = "full", diesel(table_name = community_report))] +#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] +#[cfg_attr(feature = "full", ts(export))] +/// A comment report. +pub struct CommunityReport { + pub id: CommunityReportId, + pub creator_id: PersonId, + pub community_id: CommunityId, + pub original_community_name: String, + pub original_community_title: String, + #[cfg_attr(feature = "full", ts(optional))] + pub original_community_description: Option, + #[cfg_attr(feature = "full", ts(optional))] + pub original_community_sidebar: Option, + #[cfg_attr(feature = "full", ts(optional))] + pub original_community_icon: Option, + #[cfg_attr(feature = "full", ts(optional))] + pub original_community_banner: Option, + pub reason: String, + pub resolved: bool, + #[cfg_attr(feature = "full", ts(optional))] + pub resolver_id: Option, + pub published: DateTime, + #[cfg_attr(feature = "full", ts(optional))] + pub updated: Option>, +} + +#[derive(Clone)] +#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] +#[cfg_attr(feature = "full", diesel(table_name = community_report))] +pub struct CommunityReportForm { + pub creator_id: PersonId, + pub community_id: CommunityId, + pub original_community_name: String, + pub original_community_title: String, + pub original_community_description: Option, + pub original_community_sidebar: Option, + pub original_community_icon: Option, + pub original_community_banner: Option, + pub reason: String, +} diff --git a/crates/db_schema/src/source/mod.rs b/crates/db_schema/src/source/mod.rs index 2ac2692b4c..2f6f9172b6 100644 --- a/crates/db_schema/src/source/mod.rs +++ b/crates/db_schema/src/source/mod.rs @@ -11,6 +11,7 @@ pub mod comment_reply; pub mod comment_report; pub mod community; pub mod community_block; +pub mod community_report; pub mod custom_emoji; pub mod custom_emoji_keyword; pub mod email_verification; diff --git a/crates/db_views/src/report_combined_view.rs b/crates/db_views/src/report_combined_view.rs index ec32709a65..520a8c0578 100644 --- a/crates/db_views/src/report_combined_view.rs +++ b/crates/db_views/src/report_combined_view.rs @@ -1,6 +1,7 @@ use crate::{ structs::{ CommentReportView, + CommunityReportView, LocalUserView, PostReportView, PrivateMessageReportView, @@ -32,6 +33,8 @@ use lemmy_db_schema::{ comment_report, community, community_actions, + community_aggregates, + community_report, local_user, person, person_actions, @@ -67,6 +70,7 @@ impl ReportCombinedViewInternal { .left_join(post_report::table) .left_join(comment_report::table) .left_join(private_message_report::table) + .left_join(community_report::table) // Need to join to comment and post to get the community .left_join(comment::table.on(comment_report::comment_id.eq(comment::id))) // The post @@ -87,6 +91,7 @@ impl ReportCombinedViewInternal { post_report::resolved .or(comment_report::resolved) .or(private_message_report::resolved) + .or(community_report::resolved) .is_distinct_from(true), ) .into_boxed(); @@ -114,6 +119,7 @@ impl ReportCombinedPaginationCursor { ReportCombinedView::Comment(v) => ('C', v.comment_report.id.0), ReportCombinedView::Post(v) => ('P', v.post_report.id.0), ReportCombinedView::PrivateMessage(v) => ('M', v.private_message_report.id.0), + ReportCombinedView::Community(v) => ('Y', v.community_report.id.0), }; // hex encoding to prevent ossification ReportCombinedPaginationCursor(format!("{prefix}{id:x}")) @@ -130,6 +136,7 @@ impl ReportCombinedPaginationCursor { "C" => query.filter(report_combined::comment_report_id.eq(id)), "P" => query.filter(report_combined::post_report_id.eq(id)), "M" => query.filter(report_combined::private_message_report_id.eq(id)), + "Y" => query.filter(report_combined::community_report_id.eq(id)), _ => return Err(err_msg()), }; let token = query.first(&mut get_conn(pool).await?).await?; @@ -171,13 +178,15 @@ impl ReportCombinedQuery { .left_join(post_report::table) .left_join(comment_report::table) .left_join(private_message_report::table) + .left_join(community_report::table) // The report creator .inner_join( person::table.on( post_report::creator_id .eq(report_creator) .or(comment_report::creator_id.eq(report_creator)) - .or(private_message_report::creator_id.eq(report_creator)), + .or(private_message_report::creator_id.eq(report_creator)) + .or(community_report::creator_id.eq(report_creator)), ), ) // The comment @@ -196,7 +205,7 @@ impl ReportCombinedQuery { ), ) // The item creator (`item_creator` is the id of this person) - .inner_join( + .left_join( aliases::person1.on( post::creator_id .eq(item_creator) @@ -205,7 +214,13 @@ impl ReportCombinedQuery { ), ) // The community - .left_join(community::table.on(post::community_id.eq(community::id))) + .left_join( + community::table.on( + post::community_id + .eq(community::id) + .or(community_report::community_id.eq(community::id)), + ), + ) .left_join(actions_alias( creator_community_actions, item_creator, @@ -221,7 +236,7 @@ impl ReportCombinedQuery { .left_join(actions( community_actions::table, Some(my_person_id), - post::community_id, + community::id, )) .left_join(actions(post_actions::table, Some(my_person_id), post::id)) .left_join(actions( @@ -233,13 +248,18 @@ impl ReportCombinedQuery { .left_join( comment_aggregates::table.on(comment_report::comment_id.eq(comment_aggregates::comment_id)), ) + .left_join( + community_aggregates::table + .on(community_report::community_id.eq(community_aggregates::community_id)), + ) // The resolver .left_join( aliases::person2.on( private_message_report::resolver_id .eq(resolver) .or(post_report::resolver_id.eq(resolver)) - .or(comment_report::resolver_id.eq(resolver)), + .or(comment_report::resolver_id.eq(resolver)) + .or(community_report::resolver_id.eq(resolver)), ), ) .left_join(actions( @@ -270,9 +290,12 @@ impl ReportCombinedQuery { // Private-message-specific private_message_report::all_columns.nullable(), private_message::all_columns.nullable(), + // Community-specific + community_report::all_columns.nullable(), + community_aggregates::all_columns.nullable(), // Shared person::all_columns, - aliases::person1.fields(person::all_columns), + aliases::person1.fields(person::all_columns.nullable()), community::all_columns.nullable(), CommunityFollower::select_subscribed_type(), aliases::person2.fields(person::all_columns.nullable()), @@ -290,12 +313,20 @@ impl ReportCombinedQuery { .into_boxed(); if let Some(community_id) = self.community_id { - query = query.filter(community::id.eq(community_id)); + query = query.filter( + community::id + .eq(community_id) + .and(report_combined::community_report_id.is_null()), + ); } // If its not an admin, get only the ones you mod if !user.local_user.admin { - query = query.filter(community_actions::became_moderator.is_not_null()); + query = query.filter( + community_actions::became_moderator + .is_not_null() + .and(report_combined::community_report_id.is_null()), + ); } let mut query = PaginatedQueryBuilder::new(query); @@ -316,6 +347,7 @@ impl ReportCombinedQuery { post_report::resolved .or(comment_report::resolved) .or(private_message_report::resolved) + .or(community_report::resolved) .is_distinct_from(true), ) // TODO: when a `then_asc` method is added, use it here, make the id sort direction match, @@ -344,12 +376,20 @@ impl InternalToCombinedView for ReportCombinedViewInternal { // Use for a short alias let v = self.clone(); - if let (Some(post_report), Some(post), Some(community), Some(unread_comments), Some(counts)) = ( + if let ( + Some(post_report), + Some(post), + Some(community), + Some(unread_comments), + Some(counts), + Some(post_creator), + ) = ( v.post_report, v.post.clone(), v.community.clone(), v.post_unread_comments, v.post_counts, + v.item_creator.clone(), ) { Some(ReportCombinedView::Post(PostReportView { post_report, @@ -358,7 +398,7 @@ impl InternalToCombinedView for ReportCombinedViewInternal { unread_comments, counts, creator: v.report_creator, - post_creator: v.item_creator, + post_creator, creator_banned_from_community: v.item_creator_banned_from_community, creator_is_moderator: v.item_creator_is_moderator, creator_is_admin: v.item_creator_is_admin, @@ -370,12 +410,20 @@ impl InternalToCombinedView for ReportCombinedViewInternal { my_vote: v.my_post_vote, resolver: v.resolver, })) - } else if let (Some(comment_report), Some(comment), Some(counts), Some(post), Some(community)) = ( + } else if let ( + Some(comment_report), + Some(comment), + Some(counts), + Some(post), + Some(community), + Some(comment_creator), + ) = ( v.comment_report, v.comment, v.comment_counts, v.post, - v.community, + v.community.clone(), + v.item_creator.clone(), ) { Some(ReportCombinedView::Comment(CommentReportView { comment_report, @@ -384,7 +432,7 @@ impl InternalToCombinedView for ReportCombinedViewInternal { post, community, creator: v.report_creator, - comment_creator: v.item_creator, + comment_creator, creator_banned_from_community: v.item_creator_banned_from_community, creator_is_moderator: v.item_creator_is_moderator, creator_is_admin: v.item_creator_is_admin, @@ -394,18 +442,32 @@ impl InternalToCombinedView for ReportCombinedViewInternal { my_vote: v.my_comment_vote, resolver: v.resolver, })) - } else if let (Some(private_message_report), Some(private_message)) = - (v.private_message_report, v.private_message) + } else if let ( + Some(private_message_report), + Some(private_message), + Some(private_message_creator), + ) = (v.private_message_report, v.private_message, v.item_creator) { Some(ReportCombinedView::PrivateMessage( PrivateMessageReportView { private_message_report, private_message, creator: v.report_creator, - private_message_creator: v.item_creator, + private_message_creator, resolver: v.resolver, }, )) + } else if let (Some(community), Some(community_report), Some(counts)) = + (v.community, v.community_report, v.community_counts) + { + Some(ReportCombinedView::Community(CommunityReportView { + community_report, + community, + creator: v.report_creator, + counts, + subscribed: v.subscribed, + resolver: v.resolver, + })) } else { None } @@ -433,6 +495,7 @@ mod tests { comment::{Comment, CommentInsertForm}, comment_report::{CommentReport, CommentReportForm}, community::{Community, CommunityInsertForm, CommunityModerator, CommunityModeratorForm}, + community_report::{CommunityReport, CommunityReportForm}, instance::Instance, local_user::{LocalUser, LocalUserInsertForm}, local_user_vote_display_mode::LocalUserVoteDisplayMode, @@ -558,6 +621,20 @@ mod tests { let pool = &mut pool.into(); let data = init_data(pool).await?; + // Sara reports the community + let sara_report_community_form = CommunityReportForm { + creator_id: data.sara.id, + community_id: data.community.id, + original_community_name: data.community.name.clone(), + original_community_title: data.community.title.clone(), + original_community_banner: None, + original_community_description: None, + original_community_sidebar: None, + original_community_icon: None, + reason: "from sara".into(), + }; + CommunityReport::report(pool, &sara_report_community_form).await?; + // sara reports the post let sara_report_post_form = PostReportForm { creator_id: data.sara.id, @@ -599,9 +676,14 @@ mod tests { let reports = ReportCombinedQuery::default() .list(pool, &data.admin_view) .await?; - assert_eq!(3, reports.len()); + assert_eq!(4, reports.len()); // Make sure the report types are correct + if let ReportCombinedView::Community(v) = &reports[3] { + assert_eq!(data.community.id, v.community.id); + } else { + panic!("wrong type"); + } if let ReportCombinedView::Post(v) = &reports[2] { assert_eq!(data.post.id, v.post.id); assert_eq!(data.sara.id, v.creator.id); @@ -624,7 +706,7 @@ mod tests { let report_count_admin = ReportCombinedViewInternal::get_report_count(pool, &data.admin_view, None).await?; - assert_eq!(3, report_count_admin); + assert_eq!(4, report_count_admin); // Timmy should only see 2 reports, since they're not an admin, // but they do mod the community @@ -971,4 +1053,62 @@ mod tests { Ok(()) } + + #[tokio::test] + #[serial] + async fn test_community_reports() -> LemmyResult<()> { + let pool = &build_db_pool_for_tests(); + let pool = &mut pool.into(); + let data = init_data(pool).await?; + + // jessica reports community + let community_report_form = CommunityReportForm { + creator_id: data.jessica.id, + community_id: data.community.id, + original_community_name: data.community.name.clone(), + original_community_title: data.community.title.clone(), + original_community_banner: None, + original_community_description: None, + original_community_sidebar: None, + original_community_icon: None, + reason: "the ice cream incident".into(), + }; + let community_report = CommunityReport::report(pool, &community_report_form).await?; + + let reports = ReportCombinedQuery::default() + .list(pool, &data.admin_view) + .await?; + assert_length!(1, reports); + if let ReportCombinedView::Community(v) = &reports[0] { + assert!(!v.community_report.resolved); + assert_eq!(data.jessica.name, v.creator.name); + assert_eq!(community_report.reason, v.community_report.reason); + assert_eq!(data.community.name, v.community.name); + assert_eq!(data.community.title, v.community.title); + } else { + panic!("wrong type"); + } + + // admin resolves the report (after taking appropriate action) + CommunityReport::resolve(pool, community_report.id, data.admin_view.person.id).await?; + + let reports = ReportCombinedQuery::default() + .list(pool, &data.admin_view) + .await?; + assert_length!(1, reports); + if let ReportCombinedView::Community(v) = &reports[0] { + assert!(v.community_report.resolved); + assert!(v.resolver.is_some()); + assert_eq!( + Some(&data.admin_view.person.name), + v.resolver.as_ref().map(|r| &r.name) + ); + } else { + panic!("wrong type"); + } + + cleanup(data, pool).await?; + + Ok(()) + } } diff --git a/crates/db_views/src/structs.rs b/crates/db_views/src/structs.rs index 41322c1d53..434cf7dac3 100644 --- a/crates/db_views/src/structs.rs +++ b/crates/db_views/src/structs.rs @@ -3,11 +3,18 @@ use diesel::Queryable; #[cfg(feature = "full")] use diesel::{deserialize::FromSqlRow, expression::AsExpression, sql_types}; use lemmy_db_schema::{ - aggregates::structs::{CommentAggregates, PersonAggregates, PostAggregates, SiteAggregates}, + aggregates::structs::{ + CommentAggregates, + CommunityAggregates, + PersonAggregates, + PostAggregates, + SiteAggregates, + }, source::{ comment::Comment, comment_report::CommentReport, community::Community, + community_report::CommunityReport, custom_emoji::CustomEmoji, custom_emoji_keyword::CustomEmojiKeyword, images::{ImageDetails, LocalImage}, @@ -80,6 +87,22 @@ pub struct CommentView { pub my_vote: Option, } +#[skip_serializing_none] +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] +#[cfg_attr(feature = "full", derive(TS, Queryable))] +#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] +#[cfg_attr(feature = "full", ts(export))] +/// A community report view. +pub struct CommunityReportView { + pub community_report: CommunityReport, + pub community: Community, + pub creator: Person, + pub counts: CommunityAggregates, + pub subscribed: SubscribedType, + #[cfg_attr(feature = "full", ts(optional))] + pub resolver: Option, +} + #[derive(Debug, Serialize, Deserialize, Clone)] #[cfg_attr(feature = "full", derive(TS, Queryable))] #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] @@ -283,9 +306,12 @@ pub struct ReportCombinedViewInternal { // Private-message-specific pub private_message_report: Option, pub private_message: Option, + // Community-specific + pub community_report: Option, + pub community_counts: Option, // Shared pub report_creator: Person, - pub item_creator: Person, + pub item_creator: Option, pub community: Option, pub subscribed: SubscribedType, pub resolver: Option, @@ -304,6 +330,7 @@ pub enum ReportCombinedView { Post(PostReportView), Comment(CommentReportView), PrivateMessage(PrivateMessageReportView), + Community(CommunityReportView), } #[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] diff --git a/crates/federate/src/worker.rs b/crates/federate/src/worker.rs index 1d666cfa31..260103c4af 100644 --- a/crates/federate/src/worker.rs +++ b/crates/federate/src/worker.rs @@ -710,7 +710,6 @@ mod test { let data = json!({ "actor": "http://ds9.lemmy.ml/u/lemmy_alpha", "object": "http://ds9.lemmy.ml/comment/1", - "audience": "https://enterprise.lemmy.ml/c/tenforward", "type": "Like", "id": format!("http://ds9.lemmy.ml/activities/like/{}", uuid::Uuid::new_v4()), }); diff --git a/crates/utils/src/error.rs b/crates/utils/src/error.rs index b06c7d57ac..1b3b36c27a 100644 --- a/crates/utils/src/error.rs +++ b/crates/utils/src/error.rs @@ -165,8 +165,6 @@ pub enum LemmyErrorType { #[cfg_attr(feature = "full", ts(export))] #[non_exhaustive] pub enum FederationError { - // TODO: merge into a single NotFound error - CouldntFindActivity, InvalidCommunity, CannotCreatePostOrCommentInDeletedOrRemovedCommunity, CannotReceivePage, @@ -247,6 +245,9 @@ cfg_if! { if self.error_type == LemmyErrorType::IncorrectLogin { return actix_web::http::StatusCode::UNAUTHORIZED; } + if self.error_type == LemmyErrorType::NotFound { + return actix_web::http::StatusCode::NOT_FOUND; + } match self.inner.downcast_ref::() { Some(diesel::result::Error::NotFound) => actix_web::http::StatusCode::NOT_FOUND, _ => actix_web::http::StatusCode::BAD_REQUEST, diff --git a/crates/utils/src/settings/mod.rs b/crates/utils/src/settings/mod.rs index 6ef535ab95..ecd0a9b553 100644 --- a/crates/utils/src/settings/mod.rs +++ b/crates/utils/src/settings/mod.rs @@ -3,12 +3,12 @@ use anyhow::{anyhow, Context}; use deser_hjson::from_str; use regex::Regex; use std::{env, fs, io::Error, sync::LazyLock}; -use structs::{PictrsConfig, PictrsImageMode, Settings}; +use structs::{PictrsConfig, Settings}; use url::Url; pub mod structs; -const DEFAULT_CONFIG_FILE: &str = "config/config.hjson"; +static DEFAULT_CONFIG_FILE: &str = "config/config.hjson"; #[allow(clippy::expect_used)] pub static SETTINGS: LazyLock = LazyLock::new(|| { @@ -104,21 +104,6 @@ impl Settings { .ok_or_else(|| anyhow!("images_disabled").into()) } } - -impl PictrsConfig { - pub fn image_mode(&self) -> PictrsImageMode { - if let Some(cache_external_link_previews) = self.cache_external_link_previews { - if cache_external_link_previews { - PictrsImageMode::StoreLinkPreviews - } else { - PictrsImageMode::None - } - } else { - self.image_mode.clone() - } - } -} - #[allow(clippy::expect_used)] /// Necessary to avoid URL expect failures fn pictrs_placeholder_url() -> Url { diff --git a/crates/utils/src/settings/structs.rs b/crates/utils/src/settings/structs.rs index 1b0bc60f0c..4e5152acca 100644 --- a/crates/utils/src/settings/structs.rs +++ b/crates/utils/src/settings/structs.rs @@ -77,16 +77,10 @@ pub struct PictrsConfig { #[default(None)] pub api_key: Option, - /// Backwards compatibility with 0.18.1. False is equivalent to `image_mode: None`, true is - /// equivalent to `image_mode: StoreLinkPreviews`. - /// - /// To be removed in 0.20 - pub(super) cache_external_link_previews: Option, - /// Specifies how to handle remote images, so that users don't have to connect directly to remote /// servers. - #[default(PictrsImageMode::StoreLinkPreviews)] - pub(super) image_mode: PictrsImageMode, + #[default(PictrsImageMode::ProxyAllImages)] + pub image_mode: PictrsImageMode, /// Allows bypassing proxy for specific image hosts when using ProxyAllImages. /// @@ -130,8 +124,7 @@ pub enum PictrsImageMode { /// ensures that they can be reliably retrieved and can be resized using pict-rs APIs. However /// it also increases storage usage. /// - /// This is the default behaviour, and also matches Lemmy 0.18. - #[default] + /// This behaviour matches Lemmy 0.18. StoreLinkPreviews, /// If enabled, all images from remote domains are rewritten to pass through /// `/api/v4/image/proxy`, including embedded images in markdown. Images are stored temporarily @@ -140,6 +133,7 @@ pub enum PictrsImageMode { /// local server. /// /// Requires pict-rs 0.5 + #[default] ProxyAllImages, } diff --git a/docker/federation/lemmy_alpha.hjson b/docker/federation/lemmy_alpha.hjson index a3bf2bb212..178e9124e1 100644 --- a/docker/federation/lemmy_alpha.hjson +++ b/docker/federation/lemmy_alpha.hjson @@ -12,5 +12,6 @@ } pictrs: { api_key: "my-pictrs-key" + image_mode: StoreLinkPreviews } } diff --git a/docker/federation/lemmy_beta.hjson b/docker/federation/lemmy_beta.hjson index c026b2f71b..cac5827b84 100644 --- a/docker/federation/lemmy_beta.hjson +++ b/docker/federation/lemmy_beta.hjson @@ -12,5 +12,6 @@ } pictrs: { api_key: "my-pictrs-key" + image_mode: StoreLinkPreviews } } diff --git a/migrations/2024-12-27-220142_community_report/down.sql b/migrations/2024-12-27-220142_community_report/down.sql new file mode 100644 index 0000000000..1913ee3d22 --- /dev/null +++ b/migrations/2024-12-27-220142_community_report/down.sql @@ -0,0 +1,14 @@ +DELETE FROM report_combined +WHERE community_report_id IS NOT NULL; + +ALTER TABLE report_combined + DROP CONSTRAINT report_combined_check, + ADD CHECK (num_nonnulls (post_report_id, comment_report_id, private_message_report_id) = 1), + DROP COLUMN community_report_id; + +DROP TABLE community_report CASCADE; + +ALTER TABLE community_aggregates + DROP COLUMN report_count, + DROP COLUMN unresolved_report_count; + diff --git a/migrations/2024-12-27-220142_community_report/up.sql b/migrations/2024-12-27-220142_community_report/up.sql new file mode 100644 index 0000000000..fc39059920 --- /dev/null +++ b/migrations/2024-12-27-220142_community_report/up.sql @@ -0,0 +1,29 @@ +CREATE TABLE community_report ( + id serial PRIMARY KEY, + creator_id int REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE NOT NULL, + community_id int REFERENCES community ON UPDATE CASCADE ON DELETE CASCADE NOT NULL, + original_community_name text NOT NULL, + original_community_title text NOT NULL, + original_community_description text, + original_community_sidebar text, + original_community_icon text, + original_community_banner text, + reason text NOT NULL, + resolved bool NOT NULL DEFAULT FALSE, + resolver_id int REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE, + published timestamptz NOT NULL DEFAULT now(), + updated timestamptz NULL, + UNIQUE (community_id, creator_id) +); + +CREATE INDEX idx_community_report_published ON community_report (published DESC); + +ALTER TABLE report_combined + ADD COLUMN community_report_id int UNIQUE REFERENCES community_report ON UPDATE CASCADE ON DELETE CASCADE, + DROP CONSTRAINT report_combined_check, + ADD CHECK (num_nonnulls (post_report_id, comment_report_id, private_message_report_id, community_report_id) = 1); + +ALTER TABLE community_aggregates + ADD COLUMN report_count smallint NOT NULL DEFAULT 0, + ADD COLUMN unresolved_report_count smallint NOT NULL DEFAULT 0; + diff --git a/src/scheduled_tasks.rs b/src/scheduled_tasks.rs index b656ac36f5..2a129a7d1d 100644 --- a/src/scheduled_tasks.rs +++ b/src/scheduled_tasks.rs @@ -3,6 +3,7 @@ use chrono::{DateTime, TimeZone, Utc}; use clokwerk::{AsyncScheduler, TimeUnits as CTimeUnits}; use diesel::{ dsl::{exists, not, IntervalDsl}, + query_builder::AsQuery, sql_query, sql_types::{Integer, Timestamptz}, BoolExpressionMethods, @@ -37,7 +38,15 @@ use lemmy_db_schema::{ post::{Post, PostUpdateForm}, }, traits::Crud, - utils::{find_action, functions::coalesce, get_conn, now, DbPool, DELETED_REPLACEMENT_TEXT}, + utils::{ + find_action, + functions::coalesce, + get_conn, + now, + uplete, + DbPool, + DELETED_REPLACEMENT_TEXT, + }, }; use lemmy_routes::nodeinfo::{NodeInfo, NodeInfoWellKnown}; use lemmy_utils::error::{LemmyErrorType, LemmyResult}; @@ -389,11 +398,12 @@ async fn update_banned_when_expired(pool: &mut DbPool<'_>) -> LemmyResult<()> { .execute(&mut conn) .await?; - diesel::delete( - community_actions::table.filter(community_actions::ban_expires.lt(now().nullable())), - ) - .execute(&mut conn) - .await?; + uplete::new(community_actions::table.filter(community_actions::ban_expires.lt(now().nullable()))) + .set_null(community_actions::received_ban) + .set_null(community_actions::ban_expires) + .as_query() + .execute(&mut conn) + .await?; Ok(()) }