mirror of
https://github.com/LemmyNet/lemmy.git
synced 2025-02-04 00:01:43 +00:00
Federate "resolve report" action (#5367)
* Federete resolve report action (fixes #4744) * working * clippy and fixes * fix test * verify mod action * add workaround for test --------- Co-authored-by: Dessalines <dessalines@users.noreply.github.com>
This commit is contained in:
parent
a248365834
commit
5fcf166a89
21 changed files with 554 additions and 117 deletions
|
@ -84,13 +84,11 @@ LEMMY_CONFIG_LOCATION=./docker/federation/lemmy_gamma.hjson \
|
||||||
target/lemmy_server >$LOG_DIR/lemmy_gamma.out 2>&1 &
|
target/lemmy_server >$LOG_DIR/lemmy_gamma.out 2>&1 &
|
||||||
|
|
||||||
echo "start delta"
|
echo "start delta"
|
||||||
# An instance with only an allowlist for beta
|
|
||||||
LEMMY_CONFIG_LOCATION=./docker/federation/lemmy_delta.hjson \
|
LEMMY_CONFIG_LOCATION=./docker/federation/lemmy_delta.hjson \
|
||||||
LEMMY_DATABASE_URL="${LEMMY_DATABASE_URL}/lemmy_delta" \
|
LEMMY_DATABASE_URL="${LEMMY_DATABASE_URL}/lemmy_delta" \
|
||||||
target/lemmy_server >$LOG_DIR/lemmy_delta.out 2>&1 &
|
target/lemmy_server >$LOG_DIR/lemmy_delta.out 2>&1 &
|
||||||
|
|
||||||
echo "start epsilon"
|
echo "start epsilon"
|
||||||
# An instance who has a blocklist, with lemmy-alpha blocked
|
|
||||||
LEMMY_CONFIG_LOCATION=./docker/federation/lemmy_epsilon.hjson \
|
LEMMY_CONFIG_LOCATION=./docker/federation/lemmy_epsilon.hjson \
|
||||||
LEMMY_DATABASE_URL="${LEMMY_DATABASE_URL}/lemmy_epsilon" \
|
LEMMY_DATABASE_URL="${LEMMY_DATABASE_URL}/lemmy_epsilon" \
|
||||||
target/lemmy_server >$LOG_DIR/lemmy_epsilon.out 2>&1 &
|
target/lemmy_server >$LOG_DIR/lemmy_epsilon.out 2>&1 &
|
||||||
|
|
|
@ -39,16 +39,20 @@ import {
|
||||||
listReports,
|
listReports,
|
||||||
getMyUser,
|
getMyUser,
|
||||||
listInbox,
|
listInbox,
|
||||||
|
allowInstance,
|
||||||
} from "./shared";
|
} from "./shared";
|
||||||
import { PostView } from "lemmy-js-client/dist/types/PostView";
|
import { PostView } from "lemmy-js-client/dist/types/PostView";
|
||||||
import { AdminBlockInstanceParams } from "lemmy-js-client/dist/types/AdminBlockInstanceParams";
|
import { AdminBlockInstanceParams } from "lemmy-js-client/dist/types/AdminBlockInstanceParams";
|
||||||
import {
|
import {
|
||||||
|
AddModToCommunity,
|
||||||
EditSite,
|
EditSite,
|
||||||
|
LemmyHttp,
|
||||||
PersonPostMentionView,
|
PersonPostMentionView,
|
||||||
PostReport,
|
PostReport,
|
||||||
PostReportView,
|
PostReportView,
|
||||||
ReportCombinedView,
|
ReportCombinedView,
|
||||||
ResolveObject,
|
ResolveObject,
|
||||||
|
ResolvePostReport,
|
||||||
} from "lemmy-js-client";
|
} from "lemmy-js-client";
|
||||||
|
|
||||||
let betaCommunity: CommunityView | undefined;
|
let betaCommunity: CommunityView | undefined;
|
||||||
|
@ -57,6 +61,11 @@ beforeAll(async () => {
|
||||||
await setupLogins();
|
await setupLogins();
|
||||||
betaCommunity = (await resolveBetaCommunity(alpha)).community;
|
betaCommunity = (await resolveBetaCommunity(alpha)).community;
|
||||||
expect(betaCommunity).toBeDefined();
|
expect(betaCommunity).toBeDefined();
|
||||||
|
|
||||||
|
// Hack: Force outgoing federation queue for beta to be created on epsilon,
|
||||||
|
// otherwise report test fails
|
||||||
|
let person = await resolvePerson(epsilon, "@lemmy_beta@lemmy-beta:8551");
|
||||||
|
expect(person.person).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(unfollows);
|
afterAll(unfollows);
|
||||||
|
@ -679,16 +688,26 @@ test("Report a post", async () => {
|
||||||
// Create post from alpha
|
// Create post from alpha
|
||||||
let alphaCommunity = (await resolveBetaCommunity(alpha)).community!;
|
let alphaCommunity = (await resolveBetaCommunity(alpha)).community!;
|
||||||
await followBeta(alpha);
|
await followBeta(alpha);
|
||||||
let postRes = await createPost(alpha, alphaCommunity.community.id);
|
let alphaPost = await createPost(alpha, alphaCommunity.community.id);
|
||||||
expect(postRes.post_view.post).toBeDefined();
|
expect(alphaPost.post_view.post).toBeDefined();
|
||||||
|
|
||||||
let alphaPost = (await resolvePost(alpha, postRes.post_view.post)).post;
|
// add remote mod on epsilon
|
||||||
if (!alphaPost) {
|
await followBeta(epsilon);
|
||||||
throw "Missing alpha post";
|
|
||||||
}
|
let betaCommunity = (await resolveBetaCommunity(beta)).community!;
|
||||||
|
let epsilonUser = (
|
||||||
|
await resolvePerson(beta, "@lemmy_epsilon@lemmy-epsilon:8581")
|
||||||
|
).person!;
|
||||||
|
let mod_params: AddModToCommunity = {
|
||||||
|
community_id: betaCommunity.community.id,
|
||||||
|
person_id: epsilonUser.person.id,
|
||||||
|
added: true,
|
||||||
|
};
|
||||||
|
let res = await beta.addModToCommunity(mod_params);
|
||||||
|
expect(res.moderators.length).toBe(2);
|
||||||
|
|
||||||
// Send report from gamma
|
// Send report from gamma
|
||||||
let gammaPost = (await resolvePost(gamma, alphaPost.post)).post!;
|
let gammaPost = (await resolvePost(gamma, alphaPost.post_view.post)).post!;
|
||||||
let gammaReport = (
|
let gammaReport = (
|
||||||
await reportPost(gamma, gammaPost.post.id, randomString(10))
|
await reportPost(gamma, gammaPost.post.id, randomString(10))
|
||||||
).post_report_view.post_report;
|
).post_report_view.post_report;
|
||||||
|
@ -732,6 +751,45 @@ test("Report a post", async () => {
|
||||||
//expect(alphaReport.original_post_url).toBe(gammaReport.original_post_url);
|
//expect(alphaReport.original_post_url).toBe(gammaReport.original_post_url);
|
||||||
expect(alphaReport.original_post_body).toBe(gammaReport.original_post_body);
|
expect(alphaReport.original_post_body).toBe(gammaReport.original_post_body);
|
||||||
expect(alphaReport.reason).toBe(gammaReport.reason);
|
expect(alphaReport.reason).toBe(gammaReport.reason);
|
||||||
|
|
||||||
|
// Report was federated to remote mod instance
|
||||||
|
let epsilonReport = (
|
||||||
|
(await waitUntil(
|
||||||
|
() =>
|
||||||
|
listReports(epsilon).then(p =>
|
||||||
|
p.reports.find(r => {
|
||||||
|
return checkPostReportName(r, gammaReport);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
res => !!res,
|
||||||
|
))! as PostReportView
|
||||||
|
).post_report;
|
||||||
|
expect(epsilonReport).toBeDefined();
|
||||||
|
expect(epsilonReport.resolved).toBe(false);
|
||||||
|
expect(epsilonReport.original_post_name).toBe(gammaReport.original_post_name);
|
||||||
|
|
||||||
|
// Resolve report as remote mod
|
||||||
|
let resolve_params: ResolvePostReport = {
|
||||||
|
report_id: epsilonReport.id,
|
||||||
|
resolved: true,
|
||||||
|
};
|
||||||
|
let resolve = await epsilon.resolvePostReport(resolve_params);
|
||||||
|
expect(resolve.post_report_view.post_report.resolved).toBeTruthy();
|
||||||
|
|
||||||
|
// Report should be marked resolved on community instance
|
||||||
|
let resolvedReport = (
|
||||||
|
(await waitUntil(
|
||||||
|
() =>
|
||||||
|
listReports(beta).then(p =>
|
||||||
|
p.reports.find(r => {
|
||||||
|
return checkPostReportName(r, gammaReport) && r.resolver != null;
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
res => !!res,
|
||||||
|
))! as PostReportView
|
||||||
|
).post_report;
|
||||||
|
expect(resolvedReport).toBeDefined();
|
||||||
|
expect(resolvedReport.resolved).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Fetch post via redirect", async () => {
|
test("Fetch post via redirect", async () => {
|
||||||
|
@ -852,7 +910,6 @@ test("Rewrite markdown links", async () => {
|
||||||
"https://example.com/",
|
"https://example.com/",
|
||||||
`[link](${postRes1.post_view.post.ap_id})`,
|
`[link](${postRes1.post_view.post.ap_id})`,
|
||||||
);
|
);
|
||||||
console.log(postRes2.post_view.post.body);
|
|
||||||
expect(postRes2.post_view.post).toBeDefined();
|
expect(postRes2.post_view.post).toBeDefined();
|
||||||
|
|
||||||
// fetch both posts from another instance
|
// fetch both posts from another instance
|
||||||
|
|
|
@ -199,7 +199,7 @@ export async function setupLogins() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function allowInstance(api: LemmyHttp, instance: string) {
|
export async function allowInstance(api: LemmyHttp, instance: string) {
|
||||||
const params: AdminAllowInstanceParams = {
|
const params: AdminAllowInstanceParams = {
|
||||||
instance,
|
instance,
|
||||||
allow: true,
|
allow: true,
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
use actix_web::web::{Data, Json};
|
use activitypub_federation::config::Data;
|
||||||
|
use actix_web::web::Json;
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
reports::comment::{CommentReportResponse, ResolveCommentReport},
|
reports::comment::{CommentReportResponse, ResolveCommentReport},
|
||||||
|
send_activity::{ActivityChannel, SendActivityData},
|
||||||
utils::check_community_mod_action,
|
utils::check_community_mod_action,
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{source::comment_report::CommentReport, traits::Reportable};
|
use lemmy_db_schema::{source::comment_report::CommentReport, traits::Reportable};
|
||||||
|
@ -41,6 +43,16 @@ pub async fn resolve_comment_report(
|
||||||
let comment_report_view =
|
let comment_report_view =
|
||||||
CommentReportView::read(&mut context.pool(), report_id, person_id).await?;
|
CommentReportView::read(&mut context.pool(), report_id, person_id).await?;
|
||||||
|
|
||||||
|
ActivityChannel::submit_activity(
|
||||||
|
SendActivityData::SendResolveReport {
|
||||||
|
object_id: comment_report_view.comment.ap_id.inner().clone(),
|
||||||
|
actor: local_user_view.person,
|
||||||
|
report_creator: report.creator,
|
||||||
|
community: comment_report_view.community.clone(),
|
||||||
|
},
|
||||||
|
&context,
|
||||||
|
)?;
|
||||||
|
|
||||||
Ok(Json(CommentReportResponse {
|
Ok(Json(CommentReportResponse {
|
||||||
comment_report_view,
|
comment_report_view,
|
||||||
}))
|
}))
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
use actix_web::web::{Data, Json};
|
use activitypub_federation::config::Data;
|
||||||
|
use actix_web::web::Json;
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
reports::post::{PostReportResponse, ResolvePostReport},
|
reports::post::{PostReportResponse, ResolvePostReport},
|
||||||
|
send_activity::{ActivityChannel, SendActivityData},
|
||||||
utils::check_community_mod_action,
|
utils::check_community_mod_action,
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{source::post_report::PostReport, traits::Reportable};
|
use lemmy_db_schema::{source::post_report::PostReport, traits::Reportable};
|
||||||
|
@ -32,6 +34,7 @@ pub async fn resolve_post_report(
|
||||||
.await
|
.await
|
||||||
.with_lemmy_type(LemmyErrorType::CouldntResolveReport)?;
|
.with_lemmy_type(LemmyErrorType::CouldntResolveReport)?;
|
||||||
} else {
|
} else {
|
||||||
|
// TODO: not federated
|
||||||
PostReport::unresolve(&mut context.pool(), report_id, person_id)
|
PostReport::unresolve(&mut context.pool(), report_id, person_id)
|
||||||
.await
|
.await
|
||||||
.with_lemmy_type(LemmyErrorType::CouldntResolveReport)?;
|
.with_lemmy_type(LemmyErrorType::CouldntResolveReport)?;
|
||||||
|
@ -39,5 +42,15 @@ pub async fn resolve_post_report(
|
||||||
|
|
||||||
let post_report_view = PostReportView::read(&mut context.pool(), report_id, person_id).await?;
|
let post_report_view = PostReportView::read(&mut context.pool(), report_id, person_id).await?;
|
||||||
|
|
||||||
|
ActivityChannel::submit_activity(
|
||||||
|
SendActivityData::SendResolveReport {
|
||||||
|
object_id: post_report_view.post.ap_id.inner().clone(),
|
||||||
|
actor: local_user_view.person,
|
||||||
|
report_creator: report.creator,
|
||||||
|
community: post_report_view.community.clone(),
|
||||||
|
},
|
||||||
|
&context,
|
||||||
|
)?;
|
||||||
|
|
||||||
Ok(Json(PostReportResponse { post_report_view }))
|
Ok(Json(PostReportResponse { post_report_view }))
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,6 +99,12 @@ pub enum SendActivityData {
|
||||||
community: Community,
|
community: Community,
|
||||||
reason: String,
|
reason: String,
|
||||||
},
|
},
|
||||||
|
SendResolveReport {
|
||||||
|
object_id: Url,
|
||||||
|
actor: Person,
|
||||||
|
report_creator: Person,
|
||||||
|
community: Community,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: instead of static, move this into LemmyContext. make sure that stopping the process with
|
// TODO: instead of static, move this into LemmyContext. make sure that stopping the process with
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"actor": "http://ds9.lemmy.ml/u/lemmy_user",
|
||||||
|
"to": ["http://enterprise.lemmy.ml/c/main"],
|
||||||
|
"type": "Resolve",
|
||||||
|
"id": "http://ds9.lemmy.ml/activities/flag/4323412-5e45-4a95-a15f-e0dc86361ba4",
|
||||||
|
"object": {
|
||||||
|
"actor": "http://ds9.lemmy.ml/u/lemmy_alpha",
|
||||||
|
"to": ["http://enterprise.lemmy.ml/c/main"],
|
||||||
|
"object": "http://enterprise.lemmy.ml/post/7",
|
||||||
|
"summary": "report this post",
|
||||||
|
"type": "Flag",
|
||||||
|
"id": "http://ds9.lemmy.ml/activities/flag/98b0933f-5e45-4a95-a15f-e0dc86361ba4"
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,15 +1,22 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::send_lemmy_activity,
|
activities::send_lemmy_activity,
|
||||||
activity_lists::AnnouncableActivities,
|
activity_lists::AnnouncableActivities,
|
||||||
objects::{community::ApubCommunity, person::ApubPerson},
|
fetcher::post_or_comment::PostOrComment,
|
||||||
|
objects::{community::ApubCommunity, instance::ApubSite, person::ApubPerson},
|
||||||
protocol::activities::community::announce::AnnounceActivity,
|
protocol::activities::community::announce::AnnounceActivity,
|
||||||
};
|
};
|
||||||
use activitypub_federation::{config::Data, traits::Actor};
|
use activitypub_federation::{config::Data, fetch::object_id::ObjectId, traits::Actor};
|
||||||
use lemmy_api_common::context::LemmyContext;
|
use lemmy_api_common::context::LemmyContext;
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::{activity::ActivitySendTargets, person::PersonFollower},
|
source::{
|
||||||
|
activity::ActivitySendTargets,
|
||||||
|
person::{Person, PersonFollower},
|
||||||
|
site::Site,
|
||||||
|
},
|
||||||
|
traits::Crud,
|
||||||
CommunityVisibility,
|
CommunityVisibility,
|
||||||
};
|
};
|
||||||
|
use lemmy_db_views::structs::CommunityModeratorView;
|
||||||
use lemmy_utils::error::LemmyResult;
|
use lemmy_utils::error::LemmyResult;
|
||||||
|
|
||||||
pub mod announce;
|
pub mod announce;
|
||||||
|
@ -17,6 +24,7 @@ pub mod collection_add;
|
||||||
pub mod collection_remove;
|
pub mod collection_remove;
|
||||||
pub mod lock_page;
|
pub mod lock_page;
|
||||||
pub mod report;
|
pub mod report;
|
||||||
|
pub mod resolve_report;
|
||||||
pub mod update;
|
pub mod update;
|
||||||
|
|
||||||
/// This function sends all activities which are happening in a community to the right inboxes.
|
/// This function sends all activities which are happening in a community to the right inboxes.
|
||||||
|
@ -70,3 +78,37 @@ pub(crate) async fn send_activity_in_community(
|
||||||
send_lemmy_activity(context, activity.clone(), actor, inboxes, false).await?;
|
send_lemmy_activity(context, activity.clone(), actor, inboxes, false).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn report_inboxes(
|
||||||
|
object_id: ObjectId<PostOrComment>,
|
||||||
|
community: &ApubCommunity,
|
||||||
|
context: &Data<LemmyContext>,
|
||||||
|
) -> LemmyResult<ActivitySendTargets> {
|
||||||
|
// send report to the community where object was posted
|
||||||
|
let mut inboxes = ActivitySendTargets::to_inbox(community.shared_inbox_or_inbox());
|
||||||
|
|
||||||
|
if community.local {
|
||||||
|
// send to all moderators
|
||||||
|
let moderators =
|
||||||
|
CommunityModeratorView::for_community(&mut context.pool(), community.id).await?;
|
||||||
|
for m in moderators {
|
||||||
|
inboxes.add_inbox(m.moderator.inbox_url.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
// also send report to user's home instance if possible
|
||||||
|
let object_creator_id = match object_id.dereference_local(context).await? {
|
||||||
|
PostOrComment::Post(p) => p.creator_id,
|
||||||
|
PostOrComment::Comment(c) => c.creator_id,
|
||||||
|
};
|
||||||
|
let object_creator = Person::read(&mut context.pool(), object_creator_id).await?;
|
||||||
|
let object_creator_site: Option<ApubSite> =
|
||||||
|
Site::read_from_instance_id(&mut context.pool(), object_creator.instance_id)
|
||||||
|
.await
|
||||||
|
.ok()
|
||||||
|
.map(Into::into);
|
||||||
|
if let Some(inbox) = object_creator_site.map(|s| s.shared_inbox_or_inbox()) {
|
||||||
|
inboxes.add_inbox(inbox);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(inboxes)
|
||||||
|
}
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
|
use super::report_inboxes;
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::{generate_activity_id, send_lemmy_activity, verify_person_in_community},
|
activities::{generate_activity_id, send_lemmy_activity, verify_person_in_community},
|
||||||
|
activity_lists::AnnouncableActivities,
|
||||||
insert_received_activity,
|
insert_received_activity,
|
||||||
objects::{community::ApubCommunity, instance::ApubSite, person::ApubPerson},
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
protocol::{
|
protocol::{
|
||||||
activities::community::report::{Report, ReportObject},
|
activities::community::{
|
||||||
|
announce::AnnounceActivity,
|
||||||
|
report::{Report, ReportObject},
|
||||||
|
},
|
||||||
InCommunity,
|
InCommunity,
|
||||||
},
|
},
|
||||||
PostOrComment,
|
PostOrComment,
|
||||||
|
@ -20,71 +25,49 @@ use lemmy_api_common::{
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::{
|
source::{
|
||||||
activity::ActivitySendTargets,
|
|
||||||
comment_report::{CommentReport, CommentReportForm},
|
comment_report::{CommentReport, CommentReportForm},
|
||||||
community::Community,
|
|
||||||
person::Person,
|
|
||||||
post_report::{PostReport, PostReportForm},
|
post_report::{PostReport, PostReportForm},
|
||||||
site::Site,
|
|
||||||
},
|
},
|
||||||
traits::{Crud, Reportable},
|
traits::Reportable,
|
||||||
};
|
};
|
||||||
use lemmy_db_views::structs::CommunityModeratorView;
|
|
||||||
use lemmy_utils::error::{LemmyError, LemmyResult};
|
use lemmy_utils::error::{LemmyError, LemmyResult};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
impl Report {
|
impl Report {
|
||||||
#[tracing::instrument(skip_all)]
|
pub(crate) fn new(
|
||||||
pub(crate) async fn send(
|
object_id: &ObjectId<PostOrComment>,
|
||||||
object_id: ObjectId<PostOrComment>,
|
actor: &ApubPerson,
|
||||||
actor: Person,
|
community: &ApubCommunity,
|
||||||
community: Community,
|
reason: Option<String>,
|
||||||
reason: String,
|
context: &Data<LemmyContext>,
|
||||||
context: Data<LemmyContext>,
|
) -> LemmyResult<Self> {
|
||||||
) -> LemmyResult<()> {
|
|
||||||
let actor: ApubPerson = actor.into();
|
|
||||||
let community: ApubCommunity = community.into();
|
|
||||||
let kind = FlagType::Flag;
|
let kind = FlagType::Flag;
|
||||||
let id = generate_activity_id(
|
let id = generate_activity_id(
|
||||||
kind.clone(),
|
kind.clone(),
|
||||||
&context.settings().get_protocol_and_hostname(),
|
&context.settings().get_protocol_and_hostname(),
|
||||||
)?;
|
)?;
|
||||||
let report = Report {
|
Ok(Report {
|
||||||
actor: actor.id().into(),
|
actor: actor.id().into(),
|
||||||
to: [community.id().into()],
|
to: [community.id().into()],
|
||||||
object: ReportObject::Lemmy(object_id.clone()),
|
object: ReportObject::Lemmy(object_id.clone()),
|
||||||
summary: Some(reason),
|
summary: reason,
|
||||||
content: None,
|
content: None,
|
||||||
kind,
|
kind,
|
||||||
id: id.clone(),
|
id: id.clone(),
|
||||||
};
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// send report to the community where object was posted
|
pub(crate) async fn send(
|
||||||
let mut inboxes = ActivitySendTargets::to_inbox(community.shared_inbox_or_inbox());
|
object_id: ObjectId<PostOrComment>,
|
||||||
|
actor: &ApubPerson,
|
||||||
|
community: &ApubCommunity,
|
||||||
|
reason: String,
|
||||||
|
context: Data<LemmyContext>,
|
||||||
|
) -> LemmyResult<()> {
|
||||||
|
let report = Self::new(&object_id, actor, community, Some(reason), &context)?;
|
||||||
|
let inboxes = report_inboxes(object_id, community, &context).await?;
|
||||||
|
|
||||||
// send to all moderators
|
send_lemmy_activity(&context, report, actor, inboxes, false).await
|
||||||
let moderators =
|
|
||||||
CommunityModeratorView::for_community(&mut context.pool(), community.id).await?;
|
|
||||||
for m in moderators {
|
|
||||||
inboxes.add_inbox(m.moderator.actor_id.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// also send report to user's home instance if possible
|
|
||||||
let object_creator_id = match object_id.dereference_local(&context).await? {
|
|
||||||
PostOrComment::Post(p) => p.creator_id,
|
|
||||||
PostOrComment::Comment(c) => c.creator_id,
|
|
||||||
};
|
|
||||||
let object_creator = Person::read(&mut context.pool(), object_creator_id).await?;
|
|
||||||
let object_creator_site: Option<ApubSite> =
|
|
||||||
Site::read_from_instance_id(&mut context.pool(), object_creator.instance_id)
|
|
||||||
.await
|
|
||||||
.ok()
|
|
||||||
.map(Into::into);
|
|
||||||
if let Some(inbox) = object_creator_site.map(|s| s.shared_inbox_or_inbox()) {
|
|
||||||
inboxes.add_inbox(inbox);
|
|
||||||
}
|
|
||||||
|
|
||||||
send_lemmy_activity(&context, report, &actor, inboxes, false).await
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,6 +120,17 @@ impl ActivityHandler for Report {
|
||||||
CommentReport::report(&mut context.pool(), &report_form).await?;
|
CommentReport::report(&mut context.pool(), &report_form).await?;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let community = self.community(context).await?;
|
||||||
|
if community.local {
|
||||||
|
// forward to remote mods
|
||||||
|
let object_id = self.object.object_id(context).await?;
|
||||||
|
let announce = AnnouncableActivities::Report(self);
|
||||||
|
let announce = AnnounceActivity::new(announce.try_into()?, &community, context)?;
|
||||||
|
let inboxes = report_inboxes(object_id, &community, context).await?;
|
||||||
|
send_lemmy_activity(context, announce, &community, inboxes.clone(), false).await?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
110
crates/apub/src/activities/community/resolve_report.rs
Normal file
110
crates/apub/src/activities/community/resolve_report.rs
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
use super::report_inboxes;
|
||||||
|
use crate::{
|
||||||
|
activities::{
|
||||||
|
generate_activity_id,
|
||||||
|
send_lemmy_activity,
|
||||||
|
verify_mod_action,
|
||||||
|
verify_person_in_community,
|
||||||
|
},
|
||||||
|
activity_lists::AnnouncableActivities,
|
||||||
|
insert_received_activity,
|
||||||
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
|
protocol::{
|
||||||
|
activities::community::{
|
||||||
|
announce::AnnounceActivity,
|
||||||
|
report::Report,
|
||||||
|
resolve_report::{ResolveReport, ResolveType},
|
||||||
|
},
|
||||||
|
InCommunity,
|
||||||
|
},
|
||||||
|
PostOrComment,
|
||||||
|
};
|
||||||
|
use activitypub_federation::{
|
||||||
|
config::Data,
|
||||||
|
fetch::object_id::ObjectId,
|
||||||
|
protocol::verification::verify_urls_match,
|
||||||
|
traits::{ActivityHandler, Actor},
|
||||||
|
};
|
||||||
|
use lemmy_api_common::context::LemmyContext;
|
||||||
|
use lemmy_db_schema::{
|
||||||
|
source::{comment_report::CommentReport, post_report::PostReport},
|
||||||
|
traits::Reportable,
|
||||||
|
};
|
||||||
|
use lemmy_utils::error::{LemmyError, LemmyResult};
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
|
impl ResolveReport {
|
||||||
|
pub(crate) async fn send(
|
||||||
|
object_id: ObjectId<PostOrComment>,
|
||||||
|
actor: &ApubPerson,
|
||||||
|
report_creator: &ApubPerson,
|
||||||
|
community: &ApubCommunity,
|
||||||
|
context: Data<LemmyContext>,
|
||||||
|
) -> LemmyResult<()> {
|
||||||
|
let kind = ResolveType::Resolve;
|
||||||
|
let id = generate_activity_id(
|
||||||
|
kind.clone(),
|
||||||
|
&context.settings().get_protocol_and_hostname(),
|
||||||
|
)?;
|
||||||
|
let object = Report::new(&object_id, report_creator, community, None, &context)?;
|
||||||
|
let resolve = ResolveReport {
|
||||||
|
actor: actor.id().into(),
|
||||||
|
to: [community.id().into()],
|
||||||
|
object,
|
||||||
|
kind,
|
||||||
|
id: id.clone(),
|
||||||
|
};
|
||||||
|
let inboxes = report_inboxes(object_id, community, &context).await?;
|
||||||
|
|
||||||
|
send_lemmy_activity(&context, resolve, actor, inboxes, false).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl ActivityHandler for ResolveReport {
|
||||||
|
type DataType = LemmyContext;
|
||||||
|
type Error = LemmyError;
|
||||||
|
|
||||||
|
fn id(&self) -> &Url {
|
||||||
|
&self.id
|
||||||
|
}
|
||||||
|
|
||||||
|
fn actor(&self) -> &Url {
|
||||||
|
self.actor.inner()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn verify(&self, context: &Data<Self::DataType>) -> LemmyResult<()> {
|
||||||
|
self.object.verify(context).await?;
|
||||||
|
let community = self.community(context).await?;
|
||||||
|
verify_person_in_community(&self.actor, &community, context).await?;
|
||||||
|
verify_urls_match(self.to[0].inner(), self.object.to[0].inner())?;
|
||||||
|
verify_mod_action(&self.actor, &community, context).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn receive(self, context: &Data<Self::DataType>) -> LemmyResult<()> {
|
||||||
|
insert_received_activity(&self.id, context).await?;
|
||||||
|
let reporter = self.object.actor.dereference(context).await?;
|
||||||
|
let actor = self.actor.dereference(context).await?;
|
||||||
|
match self.object.object.dereference(context).await? {
|
||||||
|
PostOrComment::Post(post) => {
|
||||||
|
PostReport::resolve_apub(&mut context.pool(), post.id, reporter.id, actor.id).await?;
|
||||||
|
}
|
||||||
|
PostOrComment::Comment(comment) => {
|
||||||
|
CommentReport::resolve_apub(&mut context.pool(), comment.id, reporter.id, actor.id).await?;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let community = self.community(context).await?;
|
||||||
|
if community.local {
|
||||||
|
// forward to remote mods
|
||||||
|
let object_id = self.object.object.object_id(context).await?;
|
||||||
|
let announce = AnnouncableActivities::ResolveReport(self);
|
||||||
|
let announce = AnnounceActivity::new(announce.try_into()?, &community, context)?;
|
||||||
|
let inboxes = report_inboxes(object_id, &community, context).await?;
|
||||||
|
send_lemmy_activity(context, announce, &community, inboxes.clone(), false).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,7 +18,7 @@ use crate::{
|
||||||
},
|
},
|
||||||
objects::{community::ApubCommunity, person::ApubPerson},
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
protocol::activities::{
|
protocol::activities::{
|
||||||
community::report::Report,
|
community::{report::Report, resolve_report::ResolveReport},
|
||||||
create_or_update::{note::CreateOrUpdateNote, page::CreateOrUpdatePage},
|
create_or_update::{note::CreateOrUpdateNote, page::CreateOrUpdatePage},
|
||||||
CreateOrUpdateType,
|
CreateOrUpdateType,
|
||||||
},
|
},
|
||||||
|
@ -378,7 +378,31 @@ pub async fn match_outgoing_activities(
|
||||||
actor,
|
actor,
|
||||||
community,
|
community,
|
||||||
reason,
|
reason,
|
||||||
} => Report::send(ObjectId::from(object_id), actor, community, reason, context).await,
|
} => {
|
||||||
|
Report::send(
|
||||||
|
ObjectId::from(object_id),
|
||||||
|
&actor.into(),
|
||||||
|
&community.into(),
|
||||||
|
reason,
|
||||||
|
context,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
SendResolveReport {
|
||||||
|
object_id,
|
||||||
|
actor,
|
||||||
|
report_creator,
|
||||||
|
community,
|
||||||
|
} => {
|
||||||
|
ResolveReport::send(
|
||||||
|
ObjectId::from(object_id),
|
||||||
|
&actor.into(),
|
||||||
|
&report_creator.into(),
|
||||||
|
&community.into(),
|
||||||
|
context,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
AcceptFollower(community_id, person_id) => {
|
AcceptFollower(community_id, person_id) => {
|
||||||
send_accept_or_reject_follow(community_id, person_id, true, &context).await
|
send_accept_or_reject_follow(community_id, person_id, true, &context).await
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ use crate::{
|
||||||
collection_remove::CollectionRemove,
|
collection_remove::CollectionRemove,
|
||||||
lock_page::{LockPage, UndoLockPage},
|
lock_page::{LockPage, UndoLockPage},
|
||||||
report::Report,
|
report::Report,
|
||||||
|
resolve_report::ResolveReport,
|
||||||
update::UpdateCommunity,
|
update::UpdateCommunity,
|
||||||
},
|
},
|
||||||
create_or_update::{note_wrapper::CreateOrUpdateNoteWrapper, page::CreateOrUpdatePage},
|
create_or_update::{note_wrapper::CreateOrUpdateNoteWrapper, page::CreateOrUpdatePage},
|
||||||
|
@ -45,6 +46,7 @@ pub enum SharedInboxActivities {
|
||||||
RejectFollow(RejectFollow),
|
RejectFollow(RejectFollow),
|
||||||
UndoFollow(UndoFollow),
|
UndoFollow(UndoFollow),
|
||||||
Report(Report),
|
Report(Report),
|
||||||
|
ResolveReport(ResolveReport),
|
||||||
AnnounceActivity(AnnounceActivity),
|
AnnounceActivity(AnnounceActivity),
|
||||||
/// This is a catch-all and needs to be last
|
/// This is a catch-all and needs to be last
|
||||||
RawAnnouncableActivities(RawAnnouncableActivities),
|
RawAnnouncableActivities(RawAnnouncableActivities),
|
||||||
|
@ -67,6 +69,8 @@ pub enum AnnouncableActivities {
|
||||||
CollectionRemove(CollectionRemove),
|
CollectionRemove(CollectionRemove),
|
||||||
LockPost(LockPage),
|
LockPost(LockPage),
|
||||||
UndoLockPost(UndoLockPage),
|
UndoLockPost(UndoLockPage),
|
||||||
|
Report(Report),
|
||||||
|
ResolveReport(ResolveReport),
|
||||||
// For compatibility with Pleroma/Mastodon (send only)
|
// For compatibility with Pleroma/Mastodon (send only)
|
||||||
Page(Page),
|
Page(Page),
|
||||||
}
|
}
|
||||||
|
@ -89,6 +93,8 @@ impl InCommunity for AnnouncableActivities {
|
||||||
CollectionRemove(a) => a.community(context).await,
|
CollectionRemove(a) => a.community(context).await,
|
||||||
LockPost(a) => a.community(context).await,
|
LockPost(a) => a.community(context).await,
|
||||||
UndoLockPost(a) => a.community(context).await,
|
UndoLockPost(a) => a.community(context).await,
|
||||||
|
Report(a) => a.community(context).await,
|
||||||
|
ResolveReport(a) => a.community(context).await,
|
||||||
Page(_) => Err(LemmyErrorType::NotFound.into()),
|
Page(_) => Err(LemmyErrorType::NotFound.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ use lemmy_utils::{
|
||||||
use moka::future::Cache;
|
use moka::future::Cache;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use std::sync::{Arc, LazyLock};
|
use std::sync::{Arc, LazyLock};
|
||||||
|
use tracing::debug;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
pub mod activities;
|
pub mod activities;
|
||||||
|
@ -213,6 +214,7 @@ pub(crate) async fn check_apub_id_valid_with_strictness(
|
||||||
/// This ensures that the same activity doesn't get received and processed more than once, which
|
/// This ensures that the same activity doesn't get received and processed more than once, which
|
||||||
/// would be a waste of resources.
|
/// would be a waste of resources.
|
||||||
async fn insert_received_activity(ap_id: &Url, data: &Data<LemmyContext>) -> LemmyResult<()> {
|
async fn insert_received_activity(ap_id: &Url, data: &Data<LemmyContext>) -> LemmyResult<()> {
|
||||||
|
debug!("Received activity {}", ap_id.to_string());
|
||||||
ReceivedActivity::create(&mut data.pool(), &ap_id.clone().into()).await?;
|
ReceivedActivity::create(&mut data.pool(), &ap_id.clone().into()).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,10 +3,12 @@ pub mod collection_add;
|
||||||
pub mod collection_remove;
|
pub mod collection_remove;
|
||||||
pub mod lock_page;
|
pub mod lock_page;
|
||||||
pub mod report;
|
pub mod report;
|
||||||
|
pub mod resolve_report;
|
||||||
pub mod update;
|
pub mod update;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use super::resolve_report::ResolveReport;
|
||||||
use crate::protocol::{
|
use crate::protocol::{
|
||||||
activities::community::{
|
activities::community::{
|
||||||
announce::AnnounceActivity,
|
announce::AnnounceActivity,
|
||||||
|
@ -44,6 +46,10 @@ mod tests {
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
test_parse_lemmy_item::<Report>("assets/lemmy/activities/community/report_page.json")?;
|
test_parse_lemmy_item::<Report>("assets/lemmy/activities/community/report_page.json")?;
|
||||||
|
test_parse_lemmy_item::<ResolveReport>(
|
||||||
|
"assets/lemmy/activities/community/resolve_report_page.json",
|
||||||
|
)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,14 +49,17 @@ pub(crate) enum ReportObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ReportObject {
|
impl ReportObject {
|
||||||
pub async fn dereference(self, context: &Data<LemmyContext>) -> LemmyResult<PostOrComment> {
|
pub(crate) async fn dereference(
|
||||||
|
&self,
|
||||||
|
context: &Data<LemmyContext>,
|
||||||
|
) -> LemmyResult<PostOrComment> {
|
||||||
match self {
|
match self {
|
||||||
ReportObject::Lemmy(l) => l.dereference(context).await,
|
ReportObject::Lemmy(l) => l.dereference(context).await,
|
||||||
ReportObject::Mastodon(objects) => {
|
ReportObject::Mastodon(objects) => {
|
||||||
for o in objects {
|
for o in objects {
|
||||||
// Find the first reported item which can be dereferenced as post or comment (Lemmy can
|
// Find the first reported item which can be dereferenced as post or comment (Lemmy can
|
||||||
// only handle one item per report).
|
// only handle one item per report).
|
||||||
let deref = ObjectId::from(o).dereference(context).await;
|
let deref = ObjectId::from(o.clone()).dereference(context).await;
|
||||||
if deref.is_ok() {
|
if deref.is_ok() {
|
||||||
return deref;
|
return deref;
|
||||||
}
|
}
|
||||||
|
@ -65,6 +68,27 @@ impl ReportObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn object_id(
|
||||||
|
&self,
|
||||||
|
context: &Data<LemmyContext>,
|
||||||
|
) -> LemmyResult<ObjectId<PostOrComment>> {
|
||||||
|
match self {
|
||||||
|
ReportObject::Lemmy(l) => Ok(l.clone()),
|
||||||
|
ReportObject::Mastodon(objects) => {
|
||||||
|
for o in objects {
|
||||||
|
// Same logic as above, but return the ID and not the object itself.
|
||||||
|
let deref = ObjectId::<PostOrComment>::from(o.clone())
|
||||||
|
.dereference(context)
|
||||||
|
.await;
|
||||||
|
if deref.is_ok() {
|
||||||
|
return Ok(o.clone().into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(LemmyErrorType::NotFound.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
use super::report::Report;
|
||||||
|
use crate::{
|
||||||
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
|
protocol::InCommunity,
|
||||||
|
};
|
||||||
|
use activitypub_federation::{
|
||||||
|
config::Data,
|
||||||
|
fetch::object_id::ObjectId,
|
||||||
|
protocol::helpers::deserialize_one,
|
||||||
|
};
|
||||||
|
use lemmy_api_common::context::LemmyContext;
|
||||||
|
use lemmy_utils::error::LemmyResult;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use strum::Display;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, Display)]
|
||||||
|
pub enum ResolveType {
|
||||||
|
Resolve,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct ResolveReport {
|
||||||
|
pub(crate) actor: ObjectId<ApubPerson>,
|
||||||
|
#[serde(deserialize_with = "deserialize_one")]
|
||||||
|
pub(crate) to: [ObjectId<ApubCommunity>; 1],
|
||||||
|
pub(crate) object: Report,
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub(crate) kind: ResolveType,
|
||||||
|
pub(crate) id: Url,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl InCommunity for ResolveReport {
|
||||||
|
async fn community(&self, context: &Data<LemmyContext>) -> LemmyResult<ApubCommunity> {
|
||||||
|
self.object.community(context).await
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
newtypes::{CommentId, CommentReportId, PersonId},
|
newtypes::{CommentId, CommentReportId, PersonId},
|
||||||
schema::comment_report::{
|
schema::comment_report,
|
||||||
comment_id,
|
|
||||||
dsl::{comment_report, resolved, resolver_id, updated},
|
|
||||||
},
|
|
||||||
source::comment_report::{CommentReport, CommentReportForm},
|
source::comment_report::{CommentReport, CommentReportForm},
|
||||||
traits::Reportable,
|
traits::Reportable,
|
||||||
utils::{get_conn, DbPool},
|
utils::{get_conn, DbPool},
|
||||||
|
@ -12,10 +9,12 @@ use chrono::Utc;
|
||||||
use diesel::{
|
use diesel::{
|
||||||
dsl::{insert_into, update},
|
dsl::{insert_into, update},
|
||||||
result::Error,
|
result::Error,
|
||||||
|
BoolExpressionMethods,
|
||||||
ExpressionMethods,
|
ExpressionMethods,
|
||||||
QueryDsl,
|
QueryDsl,
|
||||||
};
|
};
|
||||||
use diesel_async::RunQueryDsl;
|
use diesel_async::RunQueryDsl;
|
||||||
|
use lemmy_utils::error::LemmyResult;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Reportable for CommentReport {
|
impl Reportable for CommentReport {
|
||||||
|
@ -31,7 +30,7 @@ impl Reportable for CommentReport {
|
||||||
comment_report_form: &CommentReportForm,
|
comment_report_form: &CommentReportForm,
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
insert_into(comment_report)
|
insert_into(comment_report::table)
|
||||||
.values(comment_report_form)
|
.values(comment_report_form)
|
||||||
.get_result::<Self>(conn)
|
.get_result::<Self>(conn)
|
||||||
.await
|
.await
|
||||||
|
@ -48,27 +47,52 @@ impl Reportable for CommentReport {
|
||||||
by_resolver_id: PersonId,
|
by_resolver_id: PersonId,
|
||||||
) -> Result<usize, Error> {
|
) -> Result<usize, Error> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
update(comment_report.find(report_id_))
|
update(comment_report::table.find(report_id_))
|
||||||
.set((
|
.set((
|
||||||
resolved.eq(true),
|
comment_report::resolved.eq(true),
|
||||||
resolver_id.eq(by_resolver_id),
|
comment_report::resolver_id.eq(by_resolver_id),
|
||||||
updated.eq(Utc::now()),
|
comment_report::updated.eq(Utc::now()),
|
||||||
))
|
))
|
||||||
.execute(conn)
|
.execute(conn)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn resolve_apub(
|
||||||
|
pool: &mut DbPool<'_>,
|
||||||
|
object_id: Self::ObjectIdType,
|
||||||
|
report_creator_id: PersonId,
|
||||||
|
resolver_id: PersonId,
|
||||||
|
) -> LemmyResult<usize> {
|
||||||
|
let conn = &mut get_conn(pool).await?;
|
||||||
|
Ok(
|
||||||
|
update(
|
||||||
|
comment_report::table.filter(
|
||||||
|
comment_report::comment_id
|
||||||
|
.eq(object_id)
|
||||||
|
.and(comment_report::creator_id.eq(report_creator_id)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.set((
|
||||||
|
comment_report::resolved.eq(true),
|
||||||
|
comment_report::resolver_id.eq(resolver_id),
|
||||||
|
comment_report::updated.eq(Utc::now()),
|
||||||
|
))
|
||||||
|
.execute(conn)
|
||||||
|
.await?,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
async fn resolve_all_for_object(
|
async fn resolve_all_for_object(
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
comment_id_: CommentId,
|
comment_id_: CommentId,
|
||||||
by_resolver_id: PersonId,
|
by_resolver_id: PersonId,
|
||||||
) -> Result<usize, Error> {
|
) -> Result<usize, Error> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
update(comment_report.filter(comment_id.eq(comment_id_)))
|
update(comment_report::table.filter(comment_report::comment_id.eq(comment_id_)))
|
||||||
.set((
|
.set((
|
||||||
resolved.eq(true),
|
comment_report::resolved.eq(true),
|
||||||
resolver_id.eq(by_resolver_id),
|
comment_report::resolver_id.eq(by_resolver_id),
|
||||||
updated.eq(Utc::now()),
|
comment_report::updated.eq(Utc::now()),
|
||||||
))
|
))
|
||||||
.execute(conn)
|
.execute(conn)
|
||||||
.await
|
.await
|
||||||
|
@ -85,11 +109,11 @@ impl Reportable for CommentReport {
|
||||||
by_resolver_id: PersonId,
|
by_resolver_id: PersonId,
|
||||||
) -> Result<usize, Error> {
|
) -> Result<usize, Error> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
update(comment_report.find(report_id_))
|
update(comment_report::table.find(report_id_))
|
||||||
.set((
|
.set((
|
||||||
resolved.eq(false),
|
comment_report::resolved.eq(false),
|
||||||
resolver_id.eq(by_resolver_id),
|
comment_report::resolver_id.eq(by_resolver_id),
|
||||||
updated.eq(Utc::now()),
|
comment_report::updated.eq(Utc::now()),
|
||||||
))
|
))
|
||||||
.execute(conn)
|
.execute(conn)
|
||||||
.await
|
.await
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
newtypes::{CommunityId, CommunityReportId, PersonId},
|
newtypes::{CommunityId, CommunityReportId, PersonId},
|
||||||
schema::community_report::{
|
schema::community_report,
|
||||||
community_id,
|
|
||||||
dsl::{community_report, resolved, resolver_id, updated},
|
|
||||||
},
|
|
||||||
source::community_report::{CommunityReport, CommunityReportForm},
|
source::community_report::{CommunityReport, CommunityReportForm},
|
||||||
traits::Reportable,
|
traits::Reportable,
|
||||||
utils::{get_conn, DbPool},
|
utils::{get_conn, DbPool},
|
||||||
|
@ -12,10 +9,12 @@ use chrono::Utc;
|
||||||
use diesel::{
|
use diesel::{
|
||||||
dsl::{insert_into, update},
|
dsl::{insert_into, update},
|
||||||
result::Error,
|
result::Error,
|
||||||
|
BoolExpressionMethods,
|
||||||
ExpressionMethods,
|
ExpressionMethods,
|
||||||
QueryDsl,
|
QueryDsl,
|
||||||
};
|
};
|
||||||
use diesel_async::RunQueryDsl;
|
use diesel_async::RunQueryDsl;
|
||||||
|
use lemmy_utils::error::LemmyResult;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Reportable for CommunityReport {
|
impl Reportable for CommunityReport {
|
||||||
|
@ -31,7 +30,7 @@ impl Reportable for CommunityReport {
|
||||||
community_report_form: &CommunityReportForm,
|
community_report_form: &CommunityReportForm,
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
insert_into(community_report)
|
insert_into(community_report::table)
|
||||||
.values(community_report_form)
|
.values(community_report_form)
|
||||||
.get_result::<Self>(conn)
|
.get_result::<Self>(conn)
|
||||||
.await
|
.await
|
||||||
|
@ -48,27 +47,52 @@ impl Reportable for CommunityReport {
|
||||||
by_resolver_id: PersonId,
|
by_resolver_id: PersonId,
|
||||||
) -> Result<usize, Error> {
|
) -> Result<usize, Error> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
update(community_report.find(report_id_))
|
update(community_report::table.find(report_id_))
|
||||||
.set((
|
.set((
|
||||||
resolved.eq(true),
|
community_report::resolved.eq(true),
|
||||||
resolver_id.eq(by_resolver_id),
|
community_report::resolver_id.eq(by_resolver_id),
|
||||||
updated.eq(Utc::now()),
|
community_report::updated.eq(Utc::now()),
|
||||||
))
|
))
|
||||||
.execute(conn)
|
.execute(conn)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn resolve_apub(
|
||||||
|
pool: &mut DbPool<'_>,
|
||||||
|
object_id: Self::ObjectIdType,
|
||||||
|
report_creator_id: PersonId,
|
||||||
|
resolver_id: PersonId,
|
||||||
|
) -> LemmyResult<usize> {
|
||||||
|
let conn = &mut get_conn(pool).await?;
|
||||||
|
Ok(
|
||||||
|
update(
|
||||||
|
community_report::table.filter(
|
||||||
|
community_report::community_id
|
||||||
|
.eq(object_id)
|
||||||
|
.and(community_report::creator_id.eq(report_creator_id)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.set((
|
||||||
|
community_report::resolved.eq(true),
|
||||||
|
community_report::resolver_id.eq(resolver_id),
|
||||||
|
community_report::updated.eq(Utc::now()),
|
||||||
|
))
|
||||||
|
.execute(conn)
|
||||||
|
.await?,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
async fn resolve_all_for_object(
|
async fn resolve_all_for_object(
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
community_id_: CommunityId,
|
community_id_: CommunityId,
|
||||||
by_resolver_id: PersonId,
|
by_resolver_id: PersonId,
|
||||||
) -> Result<usize, Error> {
|
) -> Result<usize, Error> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
update(community_report.filter(community_id.eq(community_id_)))
|
update(community_report::table.filter(community_report::community_id.eq(community_id_)))
|
||||||
.set((
|
.set((
|
||||||
resolved.eq(true),
|
community_report::resolved.eq(true),
|
||||||
resolver_id.eq(by_resolver_id),
|
community_report::resolver_id.eq(by_resolver_id),
|
||||||
updated.eq(Utc::now()),
|
community_report::updated.eq(Utc::now()),
|
||||||
))
|
))
|
||||||
.execute(conn)
|
.execute(conn)
|
||||||
.await
|
.await
|
||||||
|
@ -85,11 +109,11 @@ impl Reportable for CommunityReport {
|
||||||
by_resolver_id: PersonId,
|
by_resolver_id: PersonId,
|
||||||
) -> Result<usize, Error> {
|
) -> Result<usize, Error> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
update(community_report.find(report_id_))
|
update(community_report::table.find(report_id_))
|
||||||
.set((
|
.set((
|
||||||
resolved.eq(false),
|
community_report::resolved.eq(false),
|
||||||
resolver_id.eq(by_resolver_id),
|
community_report::resolver_id.eq(by_resolver_id),
|
||||||
updated.eq(Utc::now()),
|
community_report::updated.eq(Utc::now()),
|
||||||
))
|
))
|
||||||
.execute(conn)
|
.execute(conn)
|
||||||
.await
|
.await
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
|
diesel::BoolExpressionMethods,
|
||||||
newtypes::{PersonId, PostId, PostReportId},
|
newtypes::{PersonId, PostId, PostReportId},
|
||||||
schema::post_report::{
|
schema::post_report,
|
||||||
dsl::{post_report, resolved, resolver_id, updated},
|
|
||||||
post_id,
|
|
||||||
},
|
|
||||||
source::post_report::{PostReport, PostReportForm},
|
source::post_report::{PostReport, PostReportForm},
|
||||||
traits::Reportable,
|
traits::Reportable,
|
||||||
utils::{get_conn, DbPool},
|
utils::{get_conn, DbPool},
|
||||||
|
@ -16,6 +14,7 @@ use diesel::{
|
||||||
QueryDsl,
|
QueryDsl,
|
||||||
};
|
};
|
||||||
use diesel_async::RunQueryDsl;
|
use diesel_async::RunQueryDsl;
|
||||||
|
use lemmy_utils::error::LemmyResult;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Reportable for PostReport {
|
impl Reportable for PostReport {
|
||||||
|
@ -25,7 +24,7 @@ impl Reportable for PostReport {
|
||||||
|
|
||||||
async fn report(pool: &mut DbPool<'_>, post_report_form: &PostReportForm) -> Result<Self, Error> {
|
async fn report(pool: &mut DbPool<'_>, post_report_form: &PostReportForm) -> Result<Self, Error> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
insert_into(post_report)
|
insert_into(post_report::table)
|
||||||
.values(post_report_form)
|
.values(post_report_form)
|
||||||
.get_result::<Self>(conn)
|
.get_result::<Self>(conn)
|
||||||
.await
|
.await
|
||||||
|
@ -37,27 +36,52 @@ impl Reportable for PostReport {
|
||||||
by_resolver_id: PersonId,
|
by_resolver_id: PersonId,
|
||||||
) -> Result<usize, Error> {
|
) -> Result<usize, Error> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
update(post_report.find(report_id))
|
update(post_report::table.find(report_id))
|
||||||
.set((
|
.set((
|
||||||
resolved.eq(true),
|
post_report::resolved.eq(true),
|
||||||
resolver_id.eq(by_resolver_id),
|
post_report::resolver_id.eq(by_resolver_id),
|
||||||
updated.eq(Utc::now()),
|
post_report::updated.eq(Utc::now()),
|
||||||
))
|
))
|
||||||
.execute(conn)
|
.execute(conn)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn resolve_apub(
|
||||||
|
pool: &mut DbPool<'_>,
|
||||||
|
object_id: Self::ObjectIdType,
|
||||||
|
report_creator_id: PersonId,
|
||||||
|
resolver_id: PersonId,
|
||||||
|
) -> LemmyResult<usize> {
|
||||||
|
let conn = &mut get_conn(pool).await?;
|
||||||
|
Ok(
|
||||||
|
update(
|
||||||
|
post_report::table.filter(
|
||||||
|
post_report::post_id
|
||||||
|
.eq(object_id)
|
||||||
|
.and(post_report::creator_id.eq(report_creator_id)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.set((
|
||||||
|
post_report::resolved.eq(true),
|
||||||
|
post_report::resolver_id.eq(resolver_id),
|
||||||
|
post_report::updated.eq(Utc::now()),
|
||||||
|
))
|
||||||
|
.execute(conn)
|
||||||
|
.await?,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
async fn resolve_all_for_object(
|
async fn resolve_all_for_object(
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
post_id_: PostId,
|
post_id_: PostId,
|
||||||
by_resolver_id: PersonId,
|
by_resolver_id: PersonId,
|
||||||
) -> Result<usize, Error> {
|
) -> Result<usize, Error> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
update(post_report.filter(post_id.eq(post_id_)))
|
update(post_report::table.filter(post_report::post_id.eq(post_id_)))
|
||||||
.set((
|
.set((
|
||||||
resolved.eq(true),
|
post_report::resolved.eq(true),
|
||||||
resolver_id.eq(by_resolver_id),
|
post_report::resolver_id.eq(by_resolver_id),
|
||||||
updated.eq(Utc::now()),
|
post_report::updated.eq(Utc::now()),
|
||||||
))
|
))
|
||||||
.execute(conn)
|
.execute(conn)
|
||||||
.await
|
.await
|
||||||
|
@ -69,11 +93,11 @@ impl Reportable for PostReport {
|
||||||
by_resolver_id: PersonId,
|
by_resolver_id: PersonId,
|
||||||
) -> Result<usize, Error> {
|
) -> Result<usize, Error> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
update(post_report.find(report_id))
|
update(post_report::table.find(report_id))
|
||||||
.set((
|
.set((
|
||||||
resolved.eq(false),
|
post_report::resolved.eq(false),
|
||||||
resolver_id.eq(by_resolver_id),
|
post_report::resolver_id.eq(by_resolver_id),
|
||||||
updated.eq(Utc::now()),
|
post_report::updated.eq(Utc::now()),
|
||||||
))
|
))
|
||||||
.execute(conn)
|
.execute(conn)
|
||||||
.await
|
.await
|
||||||
|
|
|
@ -13,6 +13,7 @@ use diesel::{
|
||||||
QueryDsl,
|
QueryDsl,
|
||||||
};
|
};
|
||||||
use diesel_async::RunQueryDsl;
|
use diesel_async::RunQueryDsl;
|
||||||
|
use lemmy_utils::error::{FederationError, LemmyResult};
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Reportable for PrivateMessageReport {
|
impl Reportable for PrivateMessageReport {
|
||||||
|
@ -46,6 +47,14 @@ impl Reportable for PrivateMessageReport {
|
||||||
.execute(conn)
|
.execute(conn)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
async fn resolve_apub(
|
||||||
|
_pool: &mut DbPool<'_>,
|
||||||
|
_object_id: Self::ObjectIdType,
|
||||||
|
_report_creator_id: PersonId,
|
||||||
|
_resolver_id: PersonId,
|
||||||
|
) -> LemmyResult<usize> {
|
||||||
|
Err(FederationError::Unreachable.into())
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: this is unused because private message doesn't have remove handler
|
// TODO: this is unused because private message doesn't have remove handler
|
||||||
async fn resolve_all_for_object(
|
async fn resolve_all_for_object(
|
||||||
|
|
|
@ -15,6 +15,7 @@ use diesel_async::{
|
||||||
AsyncPgConnection,
|
AsyncPgConnection,
|
||||||
RunQueryDsl,
|
RunQueryDsl,
|
||||||
};
|
};
|
||||||
|
use lemmy_utils::error::LemmyResult;
|
||||||
|
|
||||||
/// Returned by `diesel::delete`
|
/// Returned by `diesel::delete`
|
||||||
pub type Delete<T> = DeleteStatement<<T as HasTable>::Table, <T as IntoUpdateTarget>::WhereClause>;
|
pub type Delete<T> = DeleteStatement<<T as HasTable>::Table, <T as IntoUpdateTarget>::WhereClause>;
|
||||||
|
@ -154,6 +155,14 @@ pub trait Reportable {
|
||||||
report_id: Self::IdType,
|
report_id: Self::IdType,
|
||||||
resolver_id: PersonId,
|
resolver_id: PersonId,
|
||||||
) -> Result<usize, Error>
|
) -> Result<usize, Error>
|
||||||
|
where
|
||||||
|
Self: Sized;
|
||||||
|
async fn resolve_apub(
|
||||||
|
pool: &mut DbPool<'_>,
|
||||||
|
object_id: Self::ObjectIdType,
|
||||||
|
report_creator_id: PersonId,
|
||||||
|
resolver_id: PersonId,
|
||||||
|
) -> LemmyResult<usize>
|
||||||
where
|
where
|
||||||
Self: Sized;
|
Self: Sized;
|
||||||
async fn resolve_all_for_object(
|
async fn resolve_all_for_object(
|
||||||
|
|
Loading…
Reference in a new issue