diff --git a/crates/apub/src/fetcher/deletable_apub_object.rs b/crates/apub/src/fetcher/deletable_apub_object.rs new file mode 100644 index 000000000..5df90dd18 --- /dev/null +++ b/crates/apub/src/fetcher/deletable_apub_object.rs @@ -0,0 +1,85 @@ +use crate::fetcher::post_or_comment::PostOrComment; +use lemmy_api_common::blocking; +use lemmy_db_queries::source::{ + comment::Comment_, + community::Community_, + person::Person_, + post::Post_, +}; +use lemmy_db_schema::source::{comment::Comment, community::Community, person::Person, post::Post}; +use lemmy_utils::LemmyError; +use lemmy_websocket::LemmyContext; + +// TODO: merge this trait with ApubObject (means that db_schema needs to depend on apub_lib) +#[async_trait::async_trait(?Send)] +pub trait DeletableApubObject { + // TODO: pass in tombstone with summary field, to decide between remove/delete + async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError>; +} + +#[async_trait::async_trait(?Send)] +impl DeletableApubObject for Community { + async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> { + let id = self.id; + blocking(context.pool(), move |conn| { + Community::update_deleted(conn, id, true) + }) + .await??; + Ok(()) + } +} + +#[async_trait::async_trait(?Send)] +impl DeletableApubObject for Person { + async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> { + let id = self.id; + blocking(context.pool(), move |conn| Person::delete_account(conn, id)).await??; + Ok(()) + } +} + +#[async_trait::async_trait(?Send)] +impl DeletableApubObject for Post { + async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> { + let id = self.id; + blocking(context.pool(), move |conn| { + Post::update_deleted(conn, id, true) + }) + .await??; + Ok(()) + } +} + +#[async_trait::async_trait(?Send)] +impl DeletableApubObject for Comment { + async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> { + let id = self.id; + blocking(context.pool(), move |conn| { + Comment::update_deleted(conn, id, true) + }) + .await??; + Ok(()) + } +} + +#[async_trait::async_trait(?Send)] +impl DeletableApubObject for PostOrComment { + async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> { + match self { + PostOrComment::Comment(c) => { + blocking(context.pool(), move |conn| { + Comment::update_deleted(conn, c.id, true) + }) + .await??; + } + PostOrComment::Post(p) => { + blocking(context.pool(), move |conn| { + Post::update_deleted(conn, p.id, true) + }) + .await??; + } + } + + Ok(()) + } +} diff --git a/crates/apub/src/fetcher/mod.rs b/crates/apub/src/fetcher/mod.rs index 3077c2e30..8929dfc80 100644 --- a/crates/apub/src/fetcher/mod.rs +++ b/crates/apub/src/fetcher/mod.rs @@ -1,4 +1,5 @@ pub mod community; +pub mod deletable_apub_object; mod fetch; pub mod object_id; pub mod post_or_comment; diff --git a/crates/apub/src/fetcher/object_id.rs b/crates/apub/src/fetcher/object_id.rs index 6290c718c..82f805bc8 100644 --- a/crates/apub/src/fetcher/object_id.rs +++ b/crates/apub/src/fetcher/object_id.rs @@ -1,4 +1,8 @@ -use crate::{fetcher::should_refetch_actor, objects::FromApub, APUB_JSON_CONTENT_TYPE}; +use crate::{ + fetcher::{deletable_apub_object::DeletableApubObject, should_refetch_actor}, + objects::FromApub, + APUB_JSON_CONTENT_TYPE, +}; use anyhow::anyhow; use diesel::NotFound; use lemmy_api_common::blocking; @@ -23,18 +27,18 @@ static REQUEST_LIMIT: i32 = 25; #[derive(Clone, PartialEq, Serialize, Deserialize, Debug)] pub struct ObjectId(Url, #[serde(skip)] PhantomData) where - Kind: FromApub + ApubObject + Send + 'static, + Kind: FromApub + ApubObject + DeletableApubObject + Send + 'static, for<'de2> ::ApubType: serde::Deserialize<'de2>; impl ObjectId where - Kind: FromApub + ApubObject + Send + 'static, + Kind: FromApub + ApubObject + DeletableApubObject + Send + 'static, for<'de> ::ApubType: serde::Deserialize<'de>, { pub fn new(url: T) -> ObjectId where T: Into, - K: FromApub + ApubObject + Send + 'static, + K: FromApub + ApubObject + DeletableApubObject + Send + 'static, for<'de> ::ApubType: serde::Deserialize<'de>, { ObjectId(url.into(), PhantomData::) @@ -64,13 +68,17 @@ where // TODO: rename to should_refetch_object() if should_refetch_actor(last_refreshed_at) { debug!("Refetching remote object {}", self.0.as_str()); - return self.dereference_remotely(context, request_counter).await; + return self + .dereference_remotely(context, request_counter, Some(object)) + .await; } } Ok(object) } else { debug!("Fetching remote object {}", self.0.as_str()); - self.dereference_remotely(context, request_counter).await + self + .dereference_remotely(context, request_counter, None) + .await } } @@ -89,6 +97,7 @@ where &self, context: &LemmyContext, request_counter: &mut i32, + db_object: Option, ) -> Result { // dont fetch local objects this way debug_assert!(self.0.domain() != Some(&Settings::get().hostname)); @@ -109,31 +118,21 @@ where .await?; if res.status() == StatusCode::GONE { - self.mark_object_deleted(context).await?; - return Err( - anyhow!( - "Fetched remote object {} which was deleted", - self.0.as_str() - ) - .into(), - ); + if let Some(db_object) = db_object { + db_object.delete(context).await?; + } + return Err(anyhow!("Fetched remote object {} which was deleted", self).into()); } let res2: Kind::ApubType = res.json().await?; Ok(Kind::from_apub(&res2, context, self.inner(), request_counter).await?) } - - async fn mark_object_deleted(&self, _context: &LemmyContext) -> Result<(), LemmyError> { - // TODO: need to move methods update_deleted, update_removed etc into a trait to use them here. - // also, how do we know if the object was deleted vs removed? - todo!() - } } impl Display for ObjectId where - Kind: FromApub + ApubObject + Send + 'static, + Kind: FromApub + ApubObject + DeletableApubObject + Send + 'static, for<'de> ::ApubType: serde::Deserialize<'de>, { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { @@ -143,7 +142,7 @@ where impl From> for Url where - Kind: FromApub + ApubObject + Send + 'static, + Kind: FromApub + ApubObject + DeletableApubObject + Send + 'static, for<'de> ::ApubType: serde::Deserialize<'de>, { fn from(id: ObjectId) -> Self { @@ -153,7 +152,7 @@ where impl From> for DbUrl where - Kind: FromApub + ApubObject + Send + 'static, + Kind: FromApub + ApubObject + DeletableApubObject + Send + 'static, for<'de> ::ApubType: serde::Deserialize<'de>, { fn from(id: ObjectId) -> Self {