From c43f06124a00f3af8559d849541c2b84d3383f13 Mon Sep 17 00:00:00 2001 From: Felix Date: Wed, 29 Apr 2020 16:51:25 +0200 Subject: [PATCH] Address comments, implement delete for posts and comments --- server/src/api/comment.rs | 8 +++++ server/src/api/community.rs | 9 +++-- server/src/api/post.rs | 8 +++++ server/src/apub/comment.rs | 46 ++++++++++++++++++++---- server/src/apub/community.rs | 36 +++++++++---------- server/src/apub/mod.rs | 64 +++++++++++++++++++++------------ server/src/apub/post.rs | 50 ++++++++++++++++++++++---- server/src/apub/shared_inbox.rs | 1 + server/src/apub/user.rs | 6 ++-- 9 files changed, 165 insertions(+), 63 deletions(-) diff --git a/server/src/api/comment.rs b/server/src/api/comment.rs index a6742e4c0..17b52d2f5 100644 --- a/server/src/api/comment.rs +++ b/server/src/api/comment.rs @@ -339,6 +339,14 @@ impl Perform for Oper { updated_comment.send_update(&user, &conn)?; + if let Some(deleted) = data.deleted.to_owned() { + if deleted { + updated_comment.send_delete(&user, &conn)?; + } else { + // TODO: undo delete + } + } + let mut recipient_ids = Vec::new(); // Scan the comment for user mentions, add those rows diff --git a/server/src/api/community.rs b/server/src/api/community.rs index 7610d1b78..d7f16c50c 100644 --- a/server/src/api/community.rs +++ b/server/src/api/community.rs @@ -377,11 +377,14 @@ impl Perform for Oper { expires, }; ModRemoveCommunity::create(&conn, &form)?; - updated_community.send_delete(&conn)?; } - if let Some(_deleted) = data.deleted.to_owned() { - updated_community.send_delete(&conn)?; + if let Some(deleted) = data.deleted.to_owned() { + if deleted { + updated_community.send_delete(&conn)?; + } else { + // TODO: undo delete + } } let community_view = CommunityView::read(&conn, data.edit_id, Some(user_id))?; diff --git a/server/src/api/post.rs b/server/src/api/post.rs index 306365fac..56c133737 100644 --- a/server/src/api/post.rs +++ b/server/src/api/post.rs @@ -543,6 +543,14 @@ impl Perform for Oper { updated_post.send_update(&user, &conn)?; + if let Some(deleted) = data.deleted.to_owned() { + if deleted { + updated_post.send_delete(&user, &conn)?; + } else { + // TODO: undo delete + } + } + let post_view = PostView::read(&conn, data.edit_id, Some(user_id))?; let res = PostResponse { post: post_view }; diff --git a/server/src/apub/comment.rs b/server/src/apub/comment.rs index 9fa5731ba..4a5f18b7c 100644 --- a/server/src/apub/comment.rs +++ b/server/src/apub/comment.rs @@ -3,7 +3,7 @@ use super::*; impl ToApub for Comment { type Response = Note; - fn to_apub(&self, conn: &PgConnection) -> Result, Error> { + fn to_apub(&self, conn: &PgConnection) -> Result { let mut comment = Note::default(); let oprops: &mut ObjectProperties = comment.as_mut(); let creator = User_::read(&conn, self.creator_id)?; @@ -33,7 +33,13 @@ impl ToApub for Comment { oprops.set_updated(convert_datetime(u))?; } - Ok(ResponseOrTombstone::Response(comment)) + Ok(comment) + } +} + +impl ToTombstone for Comment { + fn to_tombstone(&self) -> Result { + create_tombstone(self.deleted, &self.ap_id, self.published, self.updated) } } @@ -102,7 +108,7 @@ impl ApubObjectType for Comment { create .create_props .set_actor_xsd_any_uri(creator.actor_id.to_owned())? - .set_object_base_box(note.as_response()?.to_owned())?; + .set_object_base_box(note)?; // Insert the sent activity into the activity table let activity_form = activity::ActivityForm { @@ -138,7 +144,7 @@ impl ApubObjectType for Comment { update .update_props .set_actor_xsd_any_uri(creator.actor_id.to_owned())? - .set_object_base_box(note.as_response()?.to_owned())?; + .set_object_base_box(note)?; // Insert the sent activity into the activity table let activity_form = activity::ActivityForm { @@ -157,6 +163,34 @@ impl ApubObjectType for Comment { )?; Ok(()) } + + // TODO: this code is literally copied from post.rs + fn send_delete(&self, actor: &User_, conn: &PgConnection) -> Result<(), Error> { + let mut delete = Delete::default(); + delete + .delete_props + .set_actor_xsd_any_uri(actor.actor_id.to_owned())? + .set_object_base_box(BaseBox::from_concrete(self.to_tombstone()?)?)?; + + // Insert the sent activity into the activity table + let activity_form = activity::ActivityForm { + user_id: self.creator_id, + data: serde_json::to_value(&delete)?, + local: true, + updated: None, + }; + activity::Activity::create(&conn, &activity_form)?; + + let post = Post::read(conn, self.post_id)?; + let community = Community::read(conn, post.community_id)?; + send_activity( + &delete, + &actor.private_key.to_owned().unwrap(), + &actor.actor_id, + community.get_follower_inboxes(&conn)?, + )?; + Ok(()) + } } impl ApubLikeableType for Comment { @@ -171,7 +205,7 @@ impl ApubLikeableType for Comment { like .like_props .set_actor_xsd_any_uri(creator.actor_id.to_owned())? - .set_object_base_box(note.as_response()?.to_owned())?; + .set_object_base_box(note)?; // Insert the sent activity into the activity table let activity_form = activity::ActivityForm { @@ -206,7 +240,7 @@ impl ApubLikeableType for Comment { dislike .dislike_props .set_actor_xsd_any_uri(creator.actor_id.to_owned())? - .set_object_base_box(note.as_response()?.to_owned())?; + .set_object_base_box(note)?; // Insert the sent activity into the activity table let activity_form = activity::ActivityForm { diff --git a/server/src/apub/community.rs b/server/src/apub/community.rs index ee3199954..36e33c895 100644 --- a/server/src/apub/community.rs +++ b/server/src/apub/community.rs @@ -9,17 +9,7 @@ impl ToApub for Community { type Response = GroupExt; // Turn a Lemmy Community into an ActivityPub group that can be sent out over the network. - fn to_apub(&self, conn: &PgConnection) -> Result, Error> { - if self.deleted || self.removed { - let mut tombstone = Tombstone::default(); - // TODO: might want to include updated/deleted times as well - tombstone - .object_props - .set_id(self.actor_id.to_owned())? - .set_published(convert_datetime(self.published))?; - return Ok(ResponseOrTombstone::Tombstone(Box::new(tombstone))); - } - + fn to_apub(&self, conn: &PgConnection) -> Result { let mut group = Group::default(); let oprops: &mut ObjectProperties = group.as_mut(); @@ -53,9 +43,13 @@ impl ToApub for Community { .set_endpoints(endpoint_props)? .set_followers(self.get_followers_url())?; - Ok(ResponseOrTombstone::Response( - group.extend(actor_props).extend(self.get_public_key_ext()), - )) + Ok(group.extend(actor_props).extend(self.get_public_key_ext())) + } +} + +impl ToTombstone for Community { + fn to_tombstone(&self) -> Result { + create_tombstone(self.deleted, &self.actor_id, self.published, self.updated) } } @@ -107,14 +101,11 @@ impl ActorType for Community { } fn send_delete(&self, conn: &PgConnection) -> Result<(), Error> { - let community = self.to_apub(conn)?; let mut delete = Delete::default(); delete .delete_props .set_actor_xsd_any_uri(self.actor_id.to_owned())? - .set_object_base_box(BaseBox::from_concrete( - community.as_tombstone()?.to_owned(), - )?)?; + .set_object_base_box(BaseBox::from_concrete(self.to_tombstone()?)?)?; // Insert the sent activity into the activity table let activity_form = activity::ActivityForm { @@ -208,8 +199,13 @@ pub async fn get_apub_community_http( db: DbPoolParam, ) -> Result, Error> { let community = Community::read_from_name(&&db.get()?, &info.community_name)?; - let c = community.to_apub(&db.get().unwrap())?; - Ok(create_apub_response(&c)) + if !community.deleted { + Ok(create_apub_response( + &community.to_apub(&db.get().unwrap())?, + )) + } else { + Ok(create_apub_tombstone_response(&community.to_tombstone()?)) + } } /// Returns an empty followers collection, only populating the siz (for privacy). diff --git a/server/src/apub/mod.rs b/server/src/apub/mod.rs index 9232c2d7e..9d312cc3d 100644 --- a/server/src/apub/mod.rs +++ b/server/src/apub/mod.rs @@ -60,6 +60,7 @@ use crate::websocket::{ use crate::{convert_datetime, naive_now, Settings}; use activities::{populate_object_props, send_activity}; +use chrono::NaiveDateTime; use fetcher::{get_or_fetch_and_upsert_remote_community, get_or_fetch_and_upsert_remote_user}; use signatures::verify; use signatures::{sign, PublicKey, PublicKeyExtension}; @@ -86,6 +87,14 @@ where .content_type(APUB_JSON_CONTENT_TYPE) .json(data) } +fn create_apub_tombstone_response(data: &T) -> HttpResponse +where + T: Serialize, +{ + HttpResponse::Gone() + .content_type(APUB_JSON_CONTENT_TYPE) + .json(data) +} /// Generates the ActivityPub ID for a given object type and name. /// @@ -138,31 +147,40 @@ fn is_apub_id_valid(apub_id: &Url) -> bool { } } -#[derive(Serialize)] -pub enum ResponseOrTombstone { - Response(Response), - Tombstone(Box), -} - -impl ResponseOrTombstone { - fn as_response(&self) -> Result<&Response, Error> { - match self { - ResponseOrTombstone::Response(r) => Ok(r), - ResponseOrTombstone::Tombstone(_t) => Err(format_err!("Value is a tombstone")), - } - } - fn as_tombstone(&self) -> Result<&Tombstone, Error> { - match self { - ResponseOrTombstone::Tombstone(t) => Ok(t), - ResponseOrTombstone::Response(_r) => Err(format_err!("Value is a response")), - } - } -} - // TODO Not sure good names for these pub trait ToApub { type Response; - fn to_apub(&self, conn: &PgConnection) -> Result, Error>; + fn to_apub(&self, conn: &PgConnection) -> Result; +} + +fn create_tombstone( + deleted: bool, + object_id: &str, + published: NaiveDateTime, + updated: Option, +) -> Result { + if deleted { + let mut tombstone = Tombstone::default(); + // TODO: might want to include deleted time as well + tombstone + .object_props + .set_id(object_id)? + .set_published(convert_datetime(published))?; + if let Some(updated) = updated { + tombstone + .object_props + .set_updated(convert_datetime(updated))?; + } + Ok(tombstone) + } else { + Err(format_err!( + "Cant convert object to tombstone if it wasnt deleted" + )) + } +} + +pub trait ToTombstone { + fn to_tombstone(&self) -> Result; } pub trait FromApub { @@ -175,7 +193,7 @@ pub trait FromApub { pub trait ApubObjectType { fn send_create(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>; fn send_update(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>; - //fn send_delete(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>; + fn send_delete(&self, actor: &User_, conn: &PgConnection) -> Result<(), Error>; } pub trait ApubLikeableType { diff --git a/server/src/apub/post.rs b/server/src/apub/post.rs index 381ba3c6c..77f8d9842 100644 --- a/server/src/apub/post.rs +++ b/server/src/apub/post.rs @@ -12,14 +12,18 @@ pub async fn get_apub_post( ) -> Result, Error> { let id = info.post_id.parse::()?; let post = Post::read(&&db.get()?, id)?; - Ok(create_apub_response(&post.to_apub(&db.get().unwrap())?)) + if !post.deleted { + Ok(create_apub_response(&post.to_apub(&db.get().unwrap())?)) + } else { + Ok(create_apub_tombstone_response(&post.to_tombstone()?)) + } } impl ToApub for Post { type Response = Page; // Turn a Lemmy post into an ActivityPub page that can be sent out over the network. - fn to_apub(&self, conn: &PgConnection) -> Result, Error> { + fn to_apub(&self, conn: &PgConnection) -> Result { let mut page = Page::default(); let oprops: &mut ObjectProperties = page.as_mut(); let creator = User_::read(conn, self.creator_id)?; @@ -51,7 +55,13 @@ impl ToApub for Post { oprops.set_updated(convert_datetime(u))?; } - Ok(ResponseOrTombstone::Response(page)) + Ok(page) + } +} + +impl ToTombstone for Post { + fn to_tombstone(&self) -> Result { + create_tombstone(self.deleted, &self.ap_id, self.published, self.updated) } } @@ -109,7 +119,7 @@ impl ApubObjectType for Post { create .create_props .set_actor_xsd_any_uri(creator.actor_id.to_owned())? - .set_object_base_box(page.as_response()?.to_owned())?; + .set_object_base_box(page)?; // Insert the sent activity into the activity table let activity_form = activity::ActivityForm { @@ -144,7 +154,7 @@ impl ApubObjectType for Post { update .update_props .set_actor_xsd_any_uri(creator.actor_id.to_owned())? - .set_object_base_box(page.as_response()?.to_owned())?; + .set_object_base_box(page)?; // Insert the sent activity into the activity table let activity_form = activity::ActivityForm { @@ -163,6 +173,32 @@ impl ApubObjectType for Post { )?; Ok(()) } + + fn send_delete(&self, actor: &User_, conn: &PgConnection) -> Result<(), Error> { + let mut delete = Delete::default(); + delete + .delete_props + .set_actor_xsd_any_uri(actor.actor_id.to_owned())? + .set_object_base_box(BaseBox::from_concrete(self.to_tombstone()?)?)?; + + // Insert the sent activity into the activity table + let activity_form = activity::ActivityForm { + user_id: self.creator_id, + data: serde_json::to_value(&delete)?, + local: true, + updated: None, + }; + activity::Activity::create(&conn, &activity_form)?; + + let community = Community::read(conn, self.community_id)?; + send_activity( + &delete, + &actor.private_key.to_owned().unwrap(), + &actor.actor_id, + community.get_follower_inboxes(&conn)?, + )?; + Ok(()) + } } impl ApubLikeableType for Post { @@ -176,7 +212,7 @@ impl ApubLikeableType for Post { like .like_props .set_actor_xsd_any_uri(creator.actor_id.to_owned())? - .set_object_base_box(page.as_response()?.to_owned())?; + .set_object_base_box(page)?; // Insert the sent activity into the activity table let activity_form = activity::ActivityForm { @@ -210,7 +246,7 @@ impl ApubLikeableType for Post { dislike .dislike_props .set_actor_xsd_any_uri(creator.actor_id.to_owned())? - .set_object_base_box(page.as_response()?.to_owned())?; + .set_object_base_box(page)?; // Insert the sent activity into the activity table let activity_form = activity::ActivityForm { diff --git a/server/src/apub/shared_inbox.rs b/server/src/apub/shared_inbox.rs index 10ed1122d..28cb71e2a 100644 --- a/server/src/apub/shared_inbox.rs +++ b/server/src/apub/shared_inbox.rs @@ -66,6 +66,7 @@ pub async fn shared_inbox( receive_dislike_comment(&d, &request, &conn, chat_server) } (SharedAcceptedObjects::Delete(d), Some("Tombstone")) => { + // TODO: is this deleting a community, post, comment or what? receive_delete_community(&d, &request, &conn, chat_server) } _ => Err(format_err!("Unknown incoming activity type.")), diff --git a/server/src/apub/user.rs b/server/src/apub/user.rs index 36147f7a0..7426efd5e 100644 --- a/server/src/apub/user.rs +++ b/server/src/apub/user.rs @@ -9,7 +9,7 @@ impl ToApub for User_ { type Response = PersonExt; // Turn a Lemmy Community into an ActivityPub group that can be sent out over the network. - fn to_apub(&self, _conn: &PgConnection) -> Result, Error> { + fn to_apub(&self, _conn: &PgConnection) -> Result { // TODO go through all these to_string and to_owned() let mut person = Person::default(); let oprops: &mut ObjectProperties = person.as_mut(); @@ -41,9 +41,7 @@ impl ToApub for User_ { .set_following(self.get_following_url())? .set_liked(self.get_liked_url())?; - Ok(ResponseOrTombstone::Response( - person.extend(actor_props).extend(self.get_public_key_ext()), - )) + Ok(person.extend(actor_props).extend(self.get_public_key_ext())) } }