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 &
|
||||
|
||||
echo "start delta"
|
||||
# An instance with only an allowlist for beta
|
||||
LEMMY_CONFIG_LOCATION=./docker/federation/lemmy_delta.hjson \
|
||||
LEMMY_DATABASE_URL="${LEMMY_DATABASE_URL}/lemmy_delta" \
|
||||
target/lemmy_server >$LOG_DIR/lemmy_delta.out 2>&1 &
|
||||
|
||||
echo "start epsilon"
|
||||
# An instance who has a blocklist, with lemmy-alpha blocked
|
||||
LEMMY_CONFIG_LOCATION=./docker/federation/lemmy_epsilon.hjson \
|
||||
LEMMY_DATABASE_URL="${LEMMY_DATABASE_URL}/lemmy_epsilon" \
|
||||
target/lemmy_server >$LOG_DIR/lemmy_epsilon.out 2>&1 &
|
||||
|
|
|
@ -39,16 +39,20 @@ import {
|
|||
listReports,
|
||||
getMyUser,
|
||||
listInbox,
|
||||
allowInstance,
|
||||
} from "./shared";
|
||||
import { PostView } from "lemmy-js-client/dist/types/PostView";
|
||||
import { AdminBlockInstanceParams } from "lemmy-js-client/dist/types/AdminBlockInstanceParams";
|
||||
import {
|
||||
AddModToCommunity,
|
||||
EditSite,
|
||||
LemmyHttp,
|
||||
PersonPostMentionView,
|
||||
PostReport,
|
||||
PostReportView,
|
||||
ReportCombinedView,
|
||||
ResolveObject,
|
||||
ResolvePostReport,
|
||||
} from "lemmy-js-client";
|
||||
|
||||
let betaCommunity: CommunityView | undefined;
|
||||
|
@ -57,6 +61,11 @@ beforeAll(async () => {
|
|||
await setupLogins();
|
||||
betaCommunity = (await resolveBetaCommunity(alpha)).community;
|
||||
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);
|
||||
|
@ -679,16 +688,26 @@ test("Report a post", async () => {
|
|||
// Create post from alpha
|
||||
let alphaCommunity = (await resolveBetaCommunity(alpha)).community!;
|
||||
await followBeta(alpha);
|
||||
let postRes = await createPost(alpha, alphaCommunity.community.id);
|
||||
expect(postRes.post_view.post).toBeDefined();
|
||||
let alphaPost = await createPost(alpha, alphaCommunity.community.id);
|
||||
expect(alphaPost.post_view.post).toBeDefined();
|
||||
|
||||
let alphaPost = (await resolvePost(alpha, postRes.post_view.post)).post;
|
||||
if (!alphaPost) {
|
||||
throw "Missing alpha post";
|
||||
}
|
||||
// add remote mod on epsilon
|
||||
await followBeta(epsilon);
|
||||
|
||||
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
|
||||
let gammaPost = (await resolvePost(gamma, alphaPost.post)).post!;
|
||||
let gammaPost = (await resolvePost(gamma, alphaPost.post_view.post)).post!;
|
||||
let gammaReport = (
|
||||
await reportPost(gamma, gammaPost.post.id, randomString(10))
|
||||
).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_body).toBe(gammaReport.original_post_body);
|
||||
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 () => {
|
||||
|
@ -852,7 +910,6 @@ test("Rewrite markdown links", async () => {
|
|||
"https://example.com/",
|
||||
`[link](${postRes1.post_view.post.ap_id})`,
|
||||
);
|
||||
console.log(postRes2.post_view.post.body);
|
||||
expect(postRes2.post_view.post).toBeDefined();
|
||||
|
||||
// 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 = {
|
||||
instance,
|
||||
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::{
|
||||
context::LemmyContext,
|
||||
reports::comment::{CommentReportResponse, ResolveCommentReport},
|
||||
send_activity::{ActivityChannel, SendActivityData},
|
||||
utils::check_community_mod_action,
|
||||
};
|
||||
use lemmy_db_schema::{source::comment_report::CommentReport, traits::Reportable};
|
||||
|
@ -41,6 +43,16 @@ pub async fn resolve_comment_report(
|
|||
let comment_report_view =
|
||||
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 {
|
||||
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::{
|
||||
context::LemmyContext,
|
||||
reports::post::{PostReportResponse, ResolvePostReport},
|
||||
send_activity::{ActivityChannel, SendActivityData},
|
||||
utils::check_community_mod_action,
|
||||
};
|
||||
use lemmy_db_schema::{source::post_report::PostReport, traits::Reportable};
|
||||
|
@ -32,6 +34,7 @@ pub async fn resolve_post_report(
|
|||
.await
|
||||
.with_lemmy_type(LemmyErrorType::CouldntResolveReport)?;
|
||||
} else {
|
||||
// TODO: not federated
|
||||
PostReport::unresolve(&mut context.pool(), report_id, person_id)
|
||||
.await
|
||||
.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?;
|
||||
|
||||
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 }))
|
||||
}
|
||||
|
|
|
@ -99,6 +99,12 @@ pub enum SendActivityData {
|
|||
community: Community,
|
||||
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
|
||||
|
|
|
@ -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::{
|
||||
activities::send_lemmy_activity,
|
||||
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,
|
||||
};
|
||||
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_db_schema::{
|
||||
source::{activity::ActivitySendTargets, person::PersonFollower},
|
||||
source::{
|
||||
activity::ActivitySendTargets,
|
||||
person::{Person, PersonFollower},
|
||||
site::Site,
|
||||
},
|
||||
traits::Crud,
|
||||
CommunityVisibility,
|
||||
};
|
||||
use lemmy_db_views::structs::CommunityModeratorView;
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
pub mod announce;
|
||||
|
@ -17,6 +24,7 @@ pub mod collection_add;
|
|||
pub mod collection_remove;
|
||||
pub mod lock_page;
|
||||
pub mod report;
|
||||
pub mod resolve_report;
|
||||
pub mod update;
|
||||
|
||||
/// 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?;
|
||||
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::{
|
||||
activities::{generate_activity_id, send_lemmy_activity, verify_person_in_community},
|
||||
activity_lists::AnnouncableActivities,
|
||||
insert_received_activity,
|
||||
objects::{community::ApubCommunity, instance::ApubSite, person::ApubPerson},
|
||||
objects::{community::ApubCommunity, person::ApubPerson},
|
||||
protocol::{
|
||||
activities::community::report::{Report, ReportObject},
|
||||
activities::community::{
|
||||
announce::AnnounceActivity,
|
||||
report::{Report, ReportObject},
|
||||
},
|
||||
InCommunity,
|
||||
},
|
||||
PostOrComment,
|
||||
|
@ -20,71 +25,49 @@ use lemmy_api_common::{
|
|||
};
|
||||
use lemmy_db_schema::{
|
||||
source::{
|
||||
activity::ActivitySendTargets,
|
||||
comment_report::{CommentReport, CommentReportForm},
|
||||
community::Community,
|
||||
person::Person,
|
||||
post_report::{PostReport, PostReportForm},
|
||||
site::Site,
|
||||
},
|
||||
traits::{Crud, Reportable},
|
||||
traits::Reportable,
|
||||
};
|
||||
use lemmy_db_views::structs::CommunityModeratorView;
|
||||
use lemmy_utils::error::{LemmyError, LemmyResult};
|
||||
use url::Url;
|
||||
|
||||
impl Report {
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub(crate) async fn send(
|
||||
object_id: ObjectId<PostOrComment>,
|
||||
actor: Person,
|
||||
community: Community,
|
||||
reason: String,
|
||||
context: Data<LemmyContext>,
|
||||
) -> LemmyResult<()> {
|
||||
let actor: ApubPerson = actor.into();
|
||||
let community: ApubCommunity = community.into();
|
||||
pub(crate) fn new(
|
||||
object_id: &ObjectId<PostOrComment>,
|
||||
actor: &ApubPerson,
|
||||
community: &ApubCommunity,
|
||||
reason: Option<String>,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> LemmyResult<Self> {
|
||||
let kind = FlagType::Flag;
|
||||
let id = generate_activity_id(
|
||||
kind.clone(),
|
||||
&context.settings().get_protocol_and_hostname(),
|
||||
)?;
|
||||
let report = Report {
|
||||
Ok(Report {
|
||||
actor: actor.id().into(),
|
||||
to: [community.id().into()],
|
||||
object: ReportObject::Lemmy(object_id.clone()),
|
||||
summary: Some(reason),
|
||||
summary: reason,
|
||||
content: None,
|
||||
kind,
|
||||
id: id.clone(),
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
// send report to the community where object was posted
|
||||
let mut inboxes = ActivitySendTargets::to_inbox(community.shared_inbox_or_inbox());
|
||||
pub(crate) async fn send(
|
||||
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
|
||||
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
|
||||
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?;
|
||||
}
|
||||
};
|
||||
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
|
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},
|
||||
protocol::activities::{
|
||||
community::report::Report,
|
||||
community::{report::Report, resolve_report::ResolveReport},
|
||||
create_or_update::{note::CreateOrUpdateNote, page::CreateOrUpdatePage},
|
||||
CreateOrUpdateType,
|
||||
},
|
||||
|
@ -378,7 +378,31 @@ pub async fn match_outgoing_activities(
|
|||
actor,
|
||||
community,
|
||||
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) => {
|
||||
send_accept_or_reject_follow(community_id, person_id, true, &context).await
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ use crate::{
|
|||
collection_remove::CollectionRemove,
|
||||
lock_page::{LockPage, UndoLockPage},
|
||||
report::Report,
|
||||
resolve_report::ResolveReport,
|
||||
update::UpdateCommunity,
|
||||
},
|
||||
create_or_update::{note_wrapper::CreateOrUpdateNoteWrapper, page::CreateOrUpdatePage},
|
||||
|
@ -45,6 +46,7 @@ pub enum SharedInboxActivities {
|
|||
RejectFollow(RejectFollow),
|
||||
UndoFollow(UndoFollow),
|
||||
Report(Report),
|
||||
ResolveReport(ResolveReport),
|
||||
AnnounceActivity(AnnounceActivity),
|
||||
/// This is a catch-all and needs to be last
|
||||
RawAnnouncableActivities(RawAnnouncableActivities),
|
||||
|
@ -67,6 +69,8 @@ pub enum AnnouncableActivities {
|
|||
CollectionRemove(CollectionRemove),
|
||||
LockPost(LockPage),
|
||||
UndoLockPost(UndoLockPage),
|
||||
Report(Report),
|
||||
ResolveReport(ResolveReport),
|
||||
// For compatibility with Pleroma/Mastodon (send only)
|
||||
Page(Page),
|
||||
}
|
||||
|
@ -89,6 +93,8 @@ impl InCommunity for AnnouncableActivities {
|
|||
CollectionRemove(a) => a.community(context).await,
|
||||
LockPost(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()),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ use lemmy_utils::{
|
|||
use moka::future::Cache;
|
||||
use serde_json::Value;
|
||||
use std::sync::{Arc, LazyLock};
|
||||
use tracing::debug;
|
||||
use url::Url;
|
||||
|
||||
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
|
||||
/// would be a waste of resources.
|
||||
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?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -3,10 +3,12 @@ pub mod collection_add;
|
|||
pub mod collection_remove;
|
||||
pub mod lock_page;
|
||||
pub mod report;
|
||||
pub mod resolve_report;
|
||||
pub mod update;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::resolve_report::ResolveReport;
|
||||
use crate::protocol::{
|
||||
activities::community::{
|
||||
announce::AnnounceActivity,
|
||||
|
@ -44,6 +46,10 @@ mod tests {
|
|||
)?;
|
||||
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,14 +49,17 @@ pub(crate) enum 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 {
|
||||
ReportObject::Lemmy(l) => l.dereference(context).await,
|
||||
ReportObject::Mastodon(objects) => {
|
||||
for o in objects {
|
||||
// Find the first reported item which can be dereferenced as post or comment (Lemmy can
|
||||
// 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() {
|
||||
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]
|
||||
|
|
|
@ -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::{
|
||||
newtypes::{CommentId, CommentReportId, PersonId},
|
||||
schema::comment_report::{
|
||||
comment_id,
|
||||
dsl::{comment_report, resolved, resolver_id, updated},
|
||||
},
|
||||
schema::comment_report,
|
||||
source::comment_report::{CommentReport, CommentReportForm},
|
||||
traits::Reportable,
|
||||
utils::{get_conn, DbPool},
|
||||
|
@ -12,10 +9,12 @@ use chrono::Utc;
|
|||
use diesel::{
|
||||
dsl::{insert_into, update},
|
||||
result::Error,
|
||||
BoolExpressionMethods,
|
||||
ExpressionMethods,
|
||||
QueryDsl,
|
||||
};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
#[async_trait]
|
||||
impl Reportable for CommentReport {
|
||||
|
@ -31,7 +30,7 @@ impl Reportable for CommentReport {
|
|||
comment_report_form: &CommentReportForm,
|
||||
) -> Result<Self, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
insert_into(comment_report)
|
||||
insert_into(comment_report::table)
|
||||
.values(comment_report_form)
|
||||
.get_result::<Self>(conn)
|
||||
.await
|
||||
|
@ -48,27 +47,52 @@ impl Reportable for CommentReport {
|
|||
by_resolver_id: PersonId,
|
||||
) -> Result<usize, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
update(comment_report.find(report_id_))
|
||||
update(comment_report::table.find(report_id_))
|
||||
.set((
|
||||
resolved.eq(true),
|
||||
resolver_id.eq(by_resolver_id),
|
||||
updated.eq(Utc::now()),
|
||||
comment_report::resolved.eq(true),
|
||||
comment_report::resolver_id.eq(by_resolver_id),
|
||||
comment_report::updated.eq(Utc::now()),
|
||||
))
|
||||
.execute(conn)
|
||||
.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(
|
||||
pool: &mut DbPool<'_>,
|
||||
comment_id_: CommentId,
|
||||
by_resolver_id: PersonId,
|
||||
) -> Result<usize, Error> {
|
||||
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((
|
||||
resolved.eq(true),
|
||||
resolver_id.eq(by_resolver_id),
|
||||
updated.eq(Utc::now()),
|
||||
comment_report::resolved.eq(true),
|
||||
comment_report::resolver_id.eq(by_resolver_id),
|
||||
comment_report::updated.eq(Utc::now()),
|
||||
))
|
||||
.execute(conn)
|
||||
.await
|
||||
|
@ -85,11 +109,11 @@ impl Reportable for CommentReport {
|
|||
by_resolver_id: PersonId,
|
||||
) -> Result<usize, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
update(comment_report.find(report_id_))
|
||||
update(comment_report::table.find(report_id_))
|
||||
.set((
|
||||
resolved.eq(false),
|
||||
resolver_id.eq(by_resolver_id),
|
||||
updated.eq(Utc::now()),
|
||||
comment_report::resolved.eq(false),
|
||||
comment_report::resolver_id.eq(by_resolver_id),
|
||||
comment_report::updated.eq(Utc::now()),
|
||||
))
|
||||
.execute(conn)
|
||||
.await
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
use crate::{
|
||||
newtypes::{CommunityId, CommunityReportId, PersonId},
|
||||
schema::community_report::{
|
||||
community_id,
|
||||
dsl::{community_report, resolved, resolver_id, updated},
|
||||
},
|
||||
schema::community_report,
|
||||
source::community_report::{CommunityReport, CommunityReportForm},
|
||||
traits::Reportable,
|
||||
utils::{get_conn, DbPool},
|
||||
|
@ -12,10 +9,12 @@ use chrono::Utc;
|
|||
use diesel::{
|
||||
dsl::{insert_into, update},
|
||||
result::Error,
|
||||
BoolExpressionMethods,
|
||||
ExpressionMethods,
|
||||
QueryDsl,
|
||||
};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
#[async_trait]
|
||||
impl Reportable for CommunityReport {
|
||||
|
@ -31,7 +30,7 @@ impl Reportable for CommunityReport {
|
|||
community_report_form: &CommunityReportForm,
|
||||
) -> Result<Self, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
insert_into(community_report)
|
||||
insert_into(community_report::table)
|
||||
.values(community_report_form)
|
||||
.get_result::<Self>(conn)
|
||||
.await
|
||||
|
@ -48,27 +47,52 @@ impl Reportable for CommunityReport {
|
|||
by_resolver_id: PersonId,
|
||||
) -> Result<usize, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
update(community_report.find(report_id_))
|
||||
update(community_report::table.find(report_id_))
|
||||
.set((
|
||||
resolved.eq(true),
|
||||
resolver_id.eq(by_resolver_id),
|
||||
updated.eq(Utc::now()),
|
||||
community_report::resolved.eq(true),
|
||||
community_report::resolver_id.eq(by_resolver_id),
|
||||
community_report::updated.eq(Utc::now()),
|
||||
))
|
||||
.execute(conn)
|
||||
.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(
|
||||
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_)))
|
||||
update(community_report::table.filter(community_report::community_id.eq(community_id_)))
|
||||
.set((
|
||||
resolved.eq(true),
|
||||
resolver_id.eq(by_resolver_id),
|
||||
updated.eq(Utc::now()),
|
||||
community_report::resolved.eq(true),
|
||||
community_report::resolver_id.eq(by_resolver_id),
|
||||
community_report::updated.eq(Utc::now()),
|
||||
))
|
||||
.execute(conn)
|
||||
.await
|
||||
|
@ -85,11 +109,11 @@ impl Reportable for CommunityReport {
|
|||
by_resolver_id: PersonId,
|
||||
) -> Result<usize, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
update(community_report.find(report_id_))
|
||||
update(community_report::table.find(report_id_))
|
||||
.set((
|
||||
resolved.eq(false),
|
||||
resolver_id.eq(by_resolver_id),
|
||||
updated.eq(Utc::now()),
|
||||
community_report::resolved.eq(false),
|
||||
community_report::resolver_id.eq(by_resolver_id),
|
||||
community_report::updated.eq(Utc::now()),
|
||||
))
|
||||
.execute(conn)
|
||||
.await
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
use crate::{
|
||||
diesel::BoolExpressionMethods,
|
||||
newtypes::{PersonId, PostId, PostReportId},
|
||||
schema::post_report::{
|
||||
dsl::{post_report, resolved, resolver_id, updated},
|
||||
post_id,
|
||||
},
|
||||
schema::post_report,
|
||||
source::post_report::{PostReport, PostReportForm},
|
||||
traits::Reportable,
|
||||
utils::{get_conn, DbPool},
|
||||
|
@ -16,6 +14,7 @@ use diesel::{
|
|||
QueryDsl,
|
||||
};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
#[async_trait]
|
||||
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> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
insert_into(post_report)
|
||||
insert_into(post_report::table)
|
||||
.values(post_report_form)
|
||||
.get_result::<Self>(conn)
|
||||
.await
|
||||
|
@ -37,27 +36,52 @@ impl Reportable for PostReport {
|
|||
by_resolver_id: PersonId,
|
||||
) -> Result<usize, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
update(post_report.find(report_id))
|
||||
update(post_report::table.find(report_id))
|
||||
.set((
|
||||
resolved.eq(true),
|
||||
resolver_id.eq(by_resolver_id),
|
||||
updated.eq(Utc::now()),
|
||||
post_report::resolved.eq(true),
|
||||
post_report::resolver_id.eq(by_resolver_id),
|
||||
post_report::updated.eq(Utc::now()),
|
||||
))
|
||||
.execute(conn)
|
||||
.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(
|
||||
pool: &mut DbPool<'_>,
|
||||
post_id_: PostId,
|
||||
by_resolver_id: PersonId,
|
||||
) -> Result<usize, Error> {
|
||||
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((
|
||||
resolved.eq(true),
|
||||
resolver_id.eq(by_resolver_id),
|
||||
updated.eq(Utc::now()),
|
||||
post_report::resolved.eq(true),
|
||||
post_report::resolver_id.eq(by_resolver_id),
|
||||
post_report::updated.eq(Utc::now()),
|
||||
))
|
||||
.execute(conn)
|
||||
.await
|
||||
|
@ -69,11 +93,11 @@ impl Reportable for PostReport {
|
|||
by_resolver_id: PersonId,
|
||||
) -> Result<usize, Error> {
|
||||
let conn = &mut get_conn(pool).await?;
|
||||
update(post_report.find(report_id))
|
||||
update(post_report::table.find(report_id))
|
||||
.set((
|
||||
resolved.eq(false),
|
||||
resolver_id.eq(by_resolver_id),
|
||||
updated.eq(Utc::now()),
|
||||
post_report::resolved.eq(false),
|
||||
post_report::resolver_id.eq(by_resolver_id),
|
||||
post_report::updated.eq(Utc::now()),
|
||||
))
|
||||
.execute(conn)
|
||||
.await
|
||||
|
|
|
@ -13,6 +13,7 @@ use diesel::{
|
|||
QueryDsl,
|
||||
};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use lemmy_utils::error::{FederationError, LemmyResult};
|
||||
|
||||
#[async_trait]
|
||||
impl Reportable for PrivateMessageReport {
|
||||
|
@ -46,6 +47,14 @@ impl Reportable for PrivateMessageReport {
|
|||
.execute(conn)
|
||||
.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
|
||||
async fn resolve_all_for_object(
|
||||
|
|
|
@ -15,6 +15,7 @@ use diesel_async::{
|
|||
AsyncPgConnection,
|
||||
RunQueryDsl,
|
||||
};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
/// Returned by `diesel::delete`
|
||||
pub type Delete<T> = DeleteStatement<<T as HasTable>::Table, <T as IntoUpdateTarget>::WhereClause>;
|
||||
|
@ -154,6 +155,14 @@ pub trait Reportable {
|
|||
report_id: Self::IdType,
|
||||
resolver_id: PersonId,
|
||||
) -> 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
|
||||
Self: Sized;
|
||||
async fn resolve_all_for_object(
|
||||
|
|
Loading…
Reference in a new issue