mirror of
https://github.com/LemmyNet/lemmy.git
synced 2025-02-02 23:31:41 +00:00
Merge branch 'main' into image-api-rework
This commit is contained in:
commit
4b940431b2
88 changed files with 640 additions and 281 deletions
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ?? "");
|
||||
|
|
|
@ -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<SuccessResponse> {
|
||||
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() {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -385,7 +385,7 @@ pub async fn delete_image_from_pictrs(
|
|||
async fn generate_pictrs_thumbnail(image_url: &Url, context: &LemmyContext) -> LemmyResult<Url> {
|
||||
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())
|
||||
|
|
|
@ -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<DbUrl> {
|
||||
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(
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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": "<p>test body</p>\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"
|
||||
}
|
||||
|
|
|
@ -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": "<p>test body</p>\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"
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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": "<p>first comment!</p>\n",
|
||||
"mediaType": "text/html",
|
||||
|
|
|
@ -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": "<p>This is a post in the /c/tenforward community</p>\n",
|
||||
"mediaType": "text/html",
|
||||
|
|
|
@ -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<DateTime<Utc>>,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> LemmyResult<BlockUser> {
|
||||
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,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -208,12 +208,10 @@ pub(crate) async fn send_ban_from_community(
|
|||
}
|
||||
}
|
||||
|
||||
fn to_and_audience(
|
||||
target: &SiteOrCommunity,
|
||||
) -> LemmyResult<(Vec<Url>, Option<ObjectId<ApubCommunity>>)> {
|
||||
fn to(target: &SiteOrCommunity) -> LemmyResult<Vec<Url>> {
|
||||
Ok(if let SiteOrCommunity::Community(c) = target {
|
||||
(vec![generate_to(c)?], Some(c.id().into()))
|
||||
generate_to(c)?
|
||||
} else {
|
||||
(vec![public()], None)
|
||||
vec![public()]
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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<LemmyContext>,
|
||||
) -> 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),
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<ObjectId<ApubPerson>> = create_or_update
|
||||
|
|
|
@ -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()),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -102,7 +102,6 @@ impl Delete {
|
|||
kind: DeleteType::Delete,
|
||||
summary,
|
||||
id,
|
||||
audience: community.map(|c| c.actor_id.clone().into()),
|
||||
remove_data: None,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -87,7 +87,6 @@ impl UndoDelete {
|
|||
cc: cc.into_iter().collect(),
|
||||
kind: UndoType::Undo,
|
||||
id,
|
||||
audience: community.map(|c| c.actor_id.clone().into()),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -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<Url> {
|
||||
pub(crate) fn generate_to(community: &Community) -> LemmyResult<Vec<Url>> {
|
||||
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<T>(a: &ObjectId<ApubCommunity>, b: T) -> LemmyResult<()>
|
||||
where
|
||||
T: Into<ObjectId<ApubCommunity>>,
|
||||
{
|
||||
let b: ObjectId<ApubCommunity> = b.into();
|
||||
if a != &b {
|
||||
Err(FederationError::InvalidCommunity)?
|
||||
} else {
|
||||
Ok(())
|
||||
Ok(vec![
|
||||
actor_id.clone(),
|
||||
Url::parse(&format!("{}/followers", actor_id))?,
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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<LemmyContext>,
|
||||
) -> LemmyResult<Self> {
|
||||
Ok(UndoVote {
|
||||
|
@ -37,7 +36,6 @@ impl UndoVote {
|
|||
UndoType::Undo,
|
||||
&context.settings().get_protocol_and_hostname(),
|
||||
)?,
|
||||
audience: Some(community.id().into()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<PostOrComment>,
|
||||
actor: &ApubPerson,
|
||||
community: &ApubCommunity,
|
||||
kind: VoteType,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> LemmyResult<Vote> {
|
||||
|
@ -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()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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<ApubCommunity>,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> LemmyResult<MentionsAndAddresses> {
|
||||
let parent_creator = get_comment_parent_creator(&mut context.pool(), comment).await?;
|
||||
let mut addressed_ccs: Vec<Url> = vec![community_id.into(), parent_creator.id()];
|
||||
let mut addressed_ccs: Vec<Url> = vec![parent_creator.id()];
|
||||
|
||||
// Add the mention tag
|
||||
let parent_creator_tag = Mention {
|
||||
|
|
|
@ -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![],
|
||||
};
|
||||
|
||||
|
|
|
@ -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],
|
||||
};
|
||||
|
|
|
@ -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<ObjectId<ApubCommunity>>,
|
||||
|
||||
/// 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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<ObjectId<ApubCommunity>>,
|
||||
|
||||
/// 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<LemmyContext>) -> LemmyResult<ApubCommunity> {
|
||||
let community = self.object.community(context).await?;
|
||||
if let Some(audience) = &self.audience {
|
||||
verify_community_matches(audience, community.actor_id.clone())?;
|
||||
}
|
||||
Ok(community)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<ObjectId<ApubCommunity>>,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
|
@ -36,9 +34,6 @@ impl InCommunity for CollectionAdd {
|
|||
async fn community(&self, context: &Data<LemmyContext>) -> LemmyResult<ApubCommunity> {
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<ObjectId<ApubCommunity>>,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
|
@ -36,9 +34,6 @@ impl InCommunity for CollectionRemove {
|
|||
async fn community(&self, context: &Data<LemmyContext>) -> LemmyResult<ApubCommunity> {
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<ObjectId<ApubCommunity>>,
|
||||
}
|
||||
|
||||
#[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<ObjectId<ApubCommunity>>,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
|
@ -56,9 +53,6 @@ impl InCommunity for LockPage {
|
|||
async fn community(&self, context: &Data<LemmyContext>) -> LemmyResult<ApubCommunity> {
|
||||
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<LemmyContext>) -> LemmyResult<ApubCommunity> {
|
||||
let community = self.object.community(context).await?;
|
||||
if let Some(audience) = &self.audience {
|
||||
verify_community_matches(audience, community.actor_id.clone())?;
|
||||
}
|
||||
Ok(community)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<ObjectId<ApubCommunity>>,
|
||||
}
|
||||
|
||||
impl Report {
|
||||
|
@ -73,9 +71,6 @@ impl ReportObject {
|
|||
impl InCommunity for Report {
|
||||
async fn community(&self, context: &Data<LemmyContext>) -> LemmyResult<ApubCommunity> {
|
||||
let community = self.to[0].dereference(context).await?;
|
||||
if let Some(audience) = &self.audience {
|
||||
verify_community_matches(audience, community.actor_id.clone())?;
|
||||
}
|
||||
Ok(community)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<ObjectId<ApubCommunity>>,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl InCommunity for UpdateCommunity {
|
||||
async fn community(&self, context: &Data<LemmyContext>) -> LemmyResult<ApubCommunity> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<ObjectId<ApubCommunity>>,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
|
@ -37,9 +35,6 @@ impl InCommunity for CreateOrUpdateNote {
|
|||
async fn community(&self, context: &Data<LemmyContext>) -> LemmyResult<ApubCommunity> {
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<ObjectId<ApubCommunity>>,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl InCommunity for CreateOrUpdatePage {
|
||||
async fn community(&self, context: &Data<LemmyContext>) -> LemmyResult<ApubCommunity> {
|
||||
let community = self.object.community(context).await?;
|
||||
if let Some(audience) = &self.audience {
|
||||
verify_community_matches(audience, community.actor_id.clone())?;
|
||||
}
|
||||
Ok(community)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<ObjectId<ApubCommunity>>,
|
||||
|
||||
#[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())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<ObjectId<ApubCommunity>>,
|
||||
|
||||
#[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<LemmyContext>) -> LemmyResult<ApubCommunity> {
|
||||
let community = self.object.community(context).await?;
|
||||
if let Some(audience) = &self.audience {
|
||||
verify_community_matches(audience, community.actor_id.clone())?;
|
||||
}
|
||||
Ok(community)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<ObjectId<ApubCommunity>>,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl InCommunity for UndoVote {
|
||||
async fn community(&self, context: &Data<LemmyContext>) -> LemmyResult<ApubCommunity> {
|
||||
let community = self.object.community(context).await?;
|
||||
if let Some(audience) = &self.audience {
|
||||
verify_community_matches(audience, community.actor_id.clone())?;
|
||||
}
|
||||
Ok(community)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<ObjectId<ApubCommunity>>,
|
||||
}
|
||||
|
||||
#[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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -82,8 +82,6 @@ impl<Kind: Id + DeserializeOwned + Send> IdOrNestedObject<Kind> {
|
|||
|
||||
#[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<ObjectId<ApubCommunity>>`
|
||||
async fn community(&self, context: &Data<LemmyContext>) -> LemmyResult<ApubCommunity>;
|
||||
}
|
||||
|
||||
|
|
|
@ -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<bool>,
|
||||
pub(crate) language: Option<LanguageTag>,
|
||||
pub(crate) audience: Option<ObjectId<ApubCommunity>>,
|
||||
#[serde(default)]
|
||||
pub(crate) attachment: Vec<Attachment>,
|
||||
}
|
||||
|
@ -94,9 +92,6 @@ impl InCommunity for Note {
|
|||
async fn community(&self, context: &Data<LemmyContext>) -> LemmyResult<ApubCommunity> {
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<DateTime<Utc>>,
|
||||
pub(crate) updated: Option<DateTime<Utc>>,
|
||||
pub(crate) language: Option<LanguageTag>,
|
||||
pub(crate) audience: Option<ObjectId<ApubCommunity>>,
|
||||
#[serde(deserialize_with = "deserialize_skip_error", default)]
|
||||
pub(crate) tag: Vec<Hashtag>,
|
||||
}
|
||||
|
@ -231,10 +229,6 @@ impl ActivityHandler for Page {
|
|||
#[async_trait::async_trait]
|
||||
impl InCommunity for Page {
|
||||
async fn community(&self, context: &Data<LemmyContext>) -> LemmyResult<ApubCommunity> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)]
|
||||
|
|
97
crates/db_schema/src/impls/community_report.rs
Normal file
97
crates/db_schema/src/impls/community_report.rs
Normal file
|
@ -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<Self, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
insert_into(community_report)
|
||||
.values(community_report_form)
|
||||
.get_result::<Self>(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<usize, Error> {
|
||||
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<usize, Error> {
|
||||
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<usize, Error> {
|
||||
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
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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))]
|
||||
|
|
|
@ -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<Text>,
|
||||
original_community_sidebar -> Nullable<Text>,
|
||||
original_community_icon -> Nullable<Text>,
|
||||
original_community_banner -> Nullable<Text>,
|
||||
reason -> Text,
|
||||
resolved -> Bool,
|
||||
resolver_id -> Nullable<Int4>,
|
||||
published -> Timestamptz,
|
||||
updated -> Nullable<Timestamptz>,
|
||||
}
|
||||
}
|
||||
|
||||
diesel::table! {
|
||||
custom_emoji (id) {
|
||||
id -> Int4,
|
||||
|
@ -922,6 +943,7 @@ diesel::table! {
|
|||
post_report_id -> Nullable<Int4>,
|
||||
comment_report_id -> Nullable<Int4>,
|
||||
private_message_report_id -> Nullable<Int4>,
|
||||
community_report_id -> Nullable<Int4>,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
|
60
crates/db_schema/src/source/community_report.rs
Normal file
60
crates/db_schema/src/source/community_report.rs
Normal file
|
@ -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<String>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub original_community_sidebar: Option<String>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub original_community_icon: Option<String>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub original_community_banner: Option<String>,
|
||||
pub reason: String,
|
||||
pub resolved: bool,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub resolver_id: Option<PersonId>,
|
||||
pub published: DateTime<Utc>,
|
||||
#[cfg_attr(feature = "full", ts(optional))]
|
||||
pub updated: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
||||
#[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<String>,
|
||||
pub original_community_sidebar: Option<String>,
|
||||
pub original_community_icon: Option<DbUrl>,
|
||||
pub original_community_banner: Option<DbUrl>,
|
||||
pub reason: String,
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<i16>,
|
||||
}
|
||||
|
||||
#[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<Person>,
|
||||
}
|
||||
|
||||
#[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<PrivateMessageReport>,
|
||||
pub private_message: Option<PrivateMessage>,
|
||||
// Community-specific
|
||||
pub community_report: Option<CommunityReport>,
|
||||
pub community_counts: Option<CommunityAggregates>,
|
||||
// Shared
|
||||
pub report_creator: Person,
|
||||
pub item_creator: Person,
|
||||
pub item_creator: Option<Person>,
|
||||
pub community: Option<Community>,
|
||||
pub subscribed: SubscribedType,
|
||||
pub resolver: Option<Person>,
|
||||
|
@ -304,6 +330,7 @@ pub enum ReportCombinedView {
|
|||
Post(PostReportView),
|
||||
Comment(CommentReportView),
|
||||
PrivateMessage(PrivateMessageReportView),
|
||||
Community(CommunityReportView),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
|
||||
|
|
|
@ -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()),
|
||||
});
|
||||
|
|
|
@ -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::<diesel::result::Error>() {
|
||||
Some(diesel::result::Error::NotFound) => actix_web::http::StatusCode::NOT_FOUND,
|
||||
_ => actix_web::http::StatusCode::BAD_REQUEST,
|
||||
|
|
|
@ -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<Settings> = 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 {
|
||||
|
|
|
@ -77,16 +77,10 @@ pub struct PictrsConfig {
|
|||
#[default(None)]
|
||||
pub api_key: Option<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
|
||||
pub(super) cache_external_link_previews: Option<bool>,
|
||||
|
||||
/// 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,
|
||||
}
|
||||
|
||||
|
|
|
@ -12,5 +12,6 @@
|
|||
}
|
||||
pictrs: {
|
||||
api_key: "my-pictrs-key"
|
||||
image_mode: StoreLinkPreviews
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,5 +12,6 @@
|
|||
}
|
||||
pictrs: {
|
||||
api_key: "my-pictrs-key"
|
||||
image_mode: StoreLinkPreviews
|
||||
}
|
||||
}
|
||||
|
|
14
migrations/2024-12-27-220142_community_report/down.sql
Normal file
14
migrations/2024-12-27-220142_community_report/down.sql
Normal file
|
@ -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;
|
||||
|
29
migrations/2024-12-27-220142_community_report/up.sql
Normal file
29
migrations/2024-12-27-220142_community_report/up.sql
Normal file
|
@ -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;
|
||||
|
|
@ -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(())
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue