mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-12-23 19:31:33 +00:00
Merge pull request #687 from LemmyNet/undo_delete_community
Undo delete for community, comment, and post.
This commit is contained in:
commit
67d4daa7a1
13 changed files with 1142 additions and 26 deletions
8
server/Cargo.lock
generated
vendored
8
server/Cargo.lock
generated
vendored
|
@ -2,9 +2,9 @@
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "activitystreams"
|
name = "activitystreams"
|
||||||
version = "0.5.0"
|
version = "0.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ae98a55a86fc3150f278b225644cd46b5359f4d75067eae6dc3a52b409c537fb"
|
checksum = "dd5b29a0f2c64cc56f2b79ec29cab68a9dab3b714d811a55668d072f18a8638e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"activitystreams-derive",
|
"activitystreams-derive",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
@ -17,9 +17,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "activitystreams-derive"
|
name = "activitystreams-derive"
|
||||||
version = "0.5.0"
|
version = "0.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "20d0384ae423a1df266f216e351ce9b40e8d369467d9242c086121154b4327dd"
|
checksum = "985d3ca1ee226e83f4118e0235bc11d9fce39c4eec8d53739a21b01dd0b3f30f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|
2
server/Cargo.toml
vendored
2
server/Cargo.toml
vendored
|
@ -8,7 +8,7 @@ edition = "2018"
|
||||||
diesel = { version = "1.4.4", features = ["postgres","chrono","r2d2","64-column-tables","serde_json"] }
|
diesel = { version = "1.4.4", features = ["postgres","chrono","r2d2","64-column-tables","serde_json"] }
|
||||||
diesel_migrations = "1.4.0"
|
diesel_migrations = "1.4.0"
|
||||||
dotenv = "0.15.0"
|
dotenv = "0.15.0"
|
||||||
activitystreams = "0.5.0-alpha.16"
|
activitystreams = "0.6.0"
|
||||||
bcrypt = "0.6.2"
|
bcrypt = "0.6.2"
|
||||||
chrono = { version = "0.4.7", features = ["serde"] }
|
chrono = { version = "0.4.7", features = ["serde"] }
|
||||||
failure = "0.1.5"
|
failure = "0.1.5"
|
||||||
|
|
|
@ -337,7 +337,15 @@ impl Perform for Oper<EditComment> {
|
||||||
Err(_e) => return Err(APIError::err("couldnt_update_comment").into()),
|
Err(_e) => return Err(APIError::err("couldnt_update_comment").into()),
|
||||||
};
|
};
|
||||||
|
|
||||||
updated_comment.send_update(&user, &conn)?;
|
if let Some(deleted) = data.deleted.to_owned() {
|
||||||
|
if deleted {
|
||||||
|
updated_comment.send_delete(&user, &conn)?;
|
||||||
|
} else {
|
||||||
|
updated_comment.send_undo_delete(&user, &conn)?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
updated_comment.send_update(&user, &conn)?;
|
||||||
|
}
|
||||||
|
|
||||||
let mut recipient_ids = Vec::new();
|
let mut recipient_ids = Vec::new();
|
||||||
|
|
||||||
|
|
|
@ -321,7 +321,8 @@ impl Perform for Oper<EditCommunity> {
|
||||||
let conn = pool.get()?;
|
let conn = pool.get()?;
|
||||||
|
|
||||||
// Check for a site ban
|
// Check for a site ban
|
||||||
if UserView::read(&conn, user_id)?.banned {
|
let user = User_::read(&conn, user_id)?;
|
||||||
|
if user.banned {
|
||||||
return Err(APIError::err("site_ban").into());
|
return Err(APIError::err("site_ban").into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -358,7 +359,7 @@ impl Perform for Oper<EditCommunity> {
|
||||||
published: None,
|
published: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let _updated_community = match Community::update(&conn, data.edit_id, &community_form) {
|
let updated_community = match Community::update(&conn, data.edit_id, &community_form) {
|
||||||
Ok(community) => community,
|
Ok(community) => community,
|
||||||
Err(_e) => return Err(APIError::err("couldnt_update_community").into()),
|
Err(_e) => return Err(APIError::err("couldnt_update_community").into()),
|
||||||
};
|
};
|
||||||
|
@ -379,6 +380,14 @@ impl Perform for Oper<EditCommunity> {
|
||||||
ModRemoveCommunity::create(&conn, &form)?;
|
ModRemoveCommunity::create(&conn, &form)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(deleted) = data.deleted.to_owned() {
|
||||||
|
if deleted {
|
||||||
|
updated_community.send_delete(&user, &conn)?;
|
||||||
|
} else {
|
||||||
|
updated_community.send_undo_delete(&user, &conn)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let community_view = CommunityView::read(&conn, data.edit_id, Some(user_id))?;
|
let community_view = CommunityView::read(&conn, data.edit_id, Some(user_id))?;
|
||||||
|
|
||||||
let res = CommunityResponse {
|
let res = CommunityResponse {
|
||||||
|
@ -701,7 +710,7 @@ impl Perform for Oper<TransferCommunity> {
|
||||||
title: read_community.title,
|
title: read_community.title,
|
||||||
description: read_community.description,
|
description: read_community.description,
|
||||||
category_id: read_community.category_id,
|
category_id: read_community.category_id,
|
||||||
creator_id: data.user_id,
|
creator_id: data.user_id, // This makes the new user the community creator
|
||||||
removed: None,
|
removed: None,
|
||||||
deleted: None,
|
deleted: None,
|
||||||
nsfw: read_community.nsfw,
|
nsfw: read_community.nsfw,
|
||||||
|
|
|
@ -541,7 +541,15 @@ impl Perform for Oper<EditPost> {
|
||||||
ModStickyPost::create(&conn, &form)?;
|
ModStickyPost::create(&conn, &form)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
updated_post.send_update(&user, &conn)?;
|
if let Some(deleted) = data.deleted.to_owned() {
|
||||||
|
if deleted {
|
||||||
|
updated_post.send_delete(&user, &conn)?;
|
||||||
|
} else {
|
||||||
|
updated_post.send_undo_delete(&user, &conn)?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
updated_post.send_update(&user, &conn)?;
|
||||||
|
}
|
||||||
|
|
||||||
let post_view = PostView::read(&conn, data.edit_id, Some(user_id))?;
|
let post_view = PostView::read(&conn, data.edit_id, Some(user_id))?;
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,15 @@ impl ToApub for Comment {
|
||||||
|
|
||||||
Ok(comment)
|
Ok(comment)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn to_tombstone(&self) -> Result<Tombstone, Error> {
|
||||||
|
create_tombstone(
|
||||||
|
self.deleted,
|
||||||
|
&self.ap_id,
|
||||||
|
self.updated,
|
||||||
|
NoteType.to_string(),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromApub for CommentForm {
|
impl FromApub for CommentForm {
|
||||||
|
@ -157,6 +166,96 @@ impl ApubObjectType for Comment {
|
||||||
)?;
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn send_delete(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
|
||||||
|
let note = self.to_apub(&conn)?;
|
||||||
|
let post = Post::read(&conn, self.post_id)?;
|
||||||
|
let community = Community::read(&conn, post.community_id)?;
|
||||||
|
let id = format!("{}/delete/{}", self.ap_id, uuid::Uuid::new_v4());
|
||||||
|
let mut delete = Delete::default();
|
||||||
|
|
||||||
|
populate_object_props(
|
||||||
|
&mut delete.object_props,
|
||||||
|
&community.get_followers_url(),
|
||||||
|
&id,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
delete
|
||||||
|
.delete_props
|
||||||
|
.set_actor_xsd_any_uri(creator.actor_id.to_owned())?
|
||||||
|
.set_object_base_box(note)?;
|
||||||
|
|
||||||
|
// 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)?;
|
||||||
|
|
||||||
|
send_activity(
|
||||||
|
&delete,
|
||||||
|
&creator.private_key.as_ref().unwrap(),
|
||||||
|
&creator.actor_id,
|
||||||
|
community.get_follower_inboxes(&conn)?,
|
||||||
|
)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_undo_delete(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
|
||||||
|
let note = self.to_apub(&conn)?;
|
||||||
|
let post = Post::read(&conn, self.post_id)?;
|
||||||
|
let community = Community::read(&conn, post.community_id)?;
|
||||||
|
|
||||||
|
// Generate a fake delete activity, with the correct object
|
||||||
|
let id = format!("{}/delete/{}", self.ap_id, uuid::Uuid::new_v4());
|
||||||
|
let mut delete = Delete::default();
|
||||||
|
|
||||||
|
populate_object_props(
|
||||||
|
&mut delete.object_props,
|
||||||
|
&community.get_followers_url(),
|
||||||
|
&id,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
delete
|
||||||
|
.delete_props
|
||||||
|
.set_actor_xsd_any_uri(creator.actor_id.to_owned())?
|
||||||
|
.set_object_base_box(note)?;
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// Undo that fake activity
|
||||||
|
let undo_id = format!("{}/undo/delete/{}", self.ap_id, uuid::Uuid::new_v4());
|
||||||
|
let mut undo = Undo::default();
|
||||||
|
|
||||||
|
populate_object_props(
|
||||||
|
&mut undo.object_props,
|
||||||
|
&community.get_followers_url(),
|
||||||
|
&undo_id,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
undo
|
||||||
|
.undo_props
|
||||||
|
.set_actor_xsd_any_uri(creator.actor_id.to_owned())?
|
||||||
|
.set_object_base_box(delete)?;
|
||||||
|
|
||||||
|
// Insert the sent activity into the activity table
|
||||||
|
let activity_form = activity::ActivityForm {
|
||||||
|
user_id: self.creator_id,
|
||||||
|
data: serde_json::to_value(&undo)?,
|
||||||
|
local: true,
|
||||||
|
updated: None,
|
||||||
|
};
|
||||||
|
activity::Activity::create(&conn, &activity_form)?;
|
||||||
|
|
||||||
|
send_activity(
|
||||||
|
&undo,
|
||||||
|
&creator.private_key.as_ref().unwrap(),
|
||||||
|
&creator.actor_id,
|
||||||
|
community.get_follower_inboxes(&conn)?,
|
||||||
|
)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ApubLikeableType for Comment {
|
impl ApubLikeableType for Comment {
|
||||||
|
|
|
@ -45,6 +45,15 @@ impl ToApub for Community {
|
||||||
|
|
||||||
Ok(group.extend(actor_props).extend(self.get_public_key_ext()))
|
Ok(group.extend(actor_props).extend(self.get_public_key_ext()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn to_tombstone(&self) -> Result<Tombstone, Error> {
|
||||||
|
create_tombstone(
|
||||||
|
self.deleted,
|
||||||
|
&self.actor_id,
|
||||||
|
self.updated,
|
||||||
|
GroupType.to_string(),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ActorType for Community {
|
impl ActorType for Community {
|
||||||
|
@ -94,10 +103,86 @@ impl ActorType for Community {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn send_delete(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
|
||||||
|
let group = self.to_apub(conn)?;
|
||||||
|
let id = format!("{}/delete/{}", self.actor_id, uuid::Uuid::new_v4());
|
||||||
|
|
||||||
|
let mut delete = Delete::default();
|
||||||
|
populate_object_props(&mut delete.object_props, &self.get_followers_url(), &id)?;
|
||||||
|
|
||||||
|
delete
|
||||||
|
.delete_props
|
||||||
|
.set_actor_xsd_any_uri(creator.actor_id.to_owned())?
|
||||||
|
.set_object_base_box(group)?;
|
||||||
|
|
||||||
|
// 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)?;
|
||||||
|
|
||||||
|
// Note: For an accept, since it was automatic, no one pushed a button,
|
||||||
|
// the community was the actor.
|
||||||
|
// But for delete, the creator is the actor, and does the signing
|
||||||
|
send_activity(
|
||||||
|
&delete,
|
||||||
|
&creator.private_key.as_ref().unwrap(),
|
||||||
|
&creator.actor_id,
|
||||||
|
self.get_follower_inboxes(&conn)?,
|
||||||
|
)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_undo_delete(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
|
||||||
|
let group = self.to_apub(conn)?;
|
||||||
|
let id = format!("{}/delete/{}", self.actor_id, uuid::Uuid::new_v4());
|
||||||
|
|
||||||
|
let mut delete = Delete::default();
|
||||||
|
populate_object_props(&mut delete.object_props, &self.get_followers_url(), &id)?;
|
||||||
|
|
||||||
|
delete
|
||||||
|
.delete_props
|
||||||
|
.set_actor_xsd_any_uri(creator.actor_id.to_owned())?
|
||||||
|
.set_object_base_box(group)?;
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// Undo that fake activity
|
||||||
|
let undo_id = format!("{}/undo/delete/{}", self.actor_id, uuid::Uuid::new_v4());
|
||||||
|
let mut undo = Undo::default();
|
||||||
|
|
||||||
|
populate_object_props(&mut undo.object_props, &self.get_followers_url(), &undo_id)?;
|
||||||
|
|
||||||
|
undo
|
||||||
|
.undo_props
|
||||||
|
.set_actor_xsd_any_uri(creator.actor_id.to_owned())?
|
||||||
|
.set_object_base_box(delete)?;
|
||||||
|
|
||||||
|
// Insert the sent activity into the activity table
|
||||||
|
let activity_form = activity::ActivityForm {
|
||||||
|
user_id: self.creator_id,
|
||||||
|
data: serde_json::to_value(&undo)?,
|
||||||
|
local: true,
|
||||||
|
updated: None,
|
||||||
|
};
|
||||||
|
activity::Activity::create(&conn, &activity_form)?;
|
||||||
|
|
||||||
|
// Note: For an accept, since it was automatic, no one pushed a button,
|
||||||
|
// the community was the actor.
|
||||||
|
// But for delete, the creator is the actor, and does the signing
|
||||||
|
send_activity(
|
||||||
|
&undo,
|
||||||
|
&creator.private_key.as_ref().unwrap(),
|
||||||
|
&creator.actor_id,
|
||||||
|
self.get_follower_inboxes(&conn)?,
|
||||||
|
)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// For a given community, returns the inboxes of all followers.
|
/// For a given community, returns the inboxes of all followers.
|
||||||
fn get_follower_inboxes(&self, conn: &PgConnection) -> Result<Vec<String>, Error> {
|
fn get_follower_inboxes(&self, conn: &PgConnection) -> Result<Vec<String>, Error> {
|
||||||
debug!("got here.");
|
|
||||||
|
|
||||||
Ok(
|
Ok(
|
||||||
CommunityFollowerView::for_community(conn, self.id)?
|
CommunityFollowerView::for_community(conn, self.id)?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -170,8 +255,13 @@ pub async fn get_apub_community_http(
|
||||||
db: DbPoolParam,
|
db: DbPoolParam,
|
||||||
) -> Result<HttpResponse<Body>, Error> {
|
) -> Result<HttpResponse<Body>, Error> {
|
||||||
let community = Community::read_from_name(&&db.get()?, &info.community_name)?;
|
let community = Community::read_from_name(&&db.get()?, &info.community_name)?;
|
||||||
let c = community.to_apub(&db.get().unwrap())?;
|
if !community.deleted {
|
||||||
Ok(create_apub_response(&c))
|
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).
|
/// Returns an empty followers collection, only populating the siz (for privacy).
|
||||||
|
|
|
@ -9,14 +9,17 @@ pub mod signatures;
|
||||||
pub mod user;
|
pub mod user;
|
||||||
pub mod user_inbox;
|
pub mod user_inbox;
|
||||||
|
|
||||||
|
use crate::api::community::CommunityResponse;
|
||||||
|
use crate::websocket::server::SendCommunityRoomMessage;
|
||||||
|
use activitystreams::object::kind::{NoteType, PageType};
|
||||||
use activitystreams::{
|
use activitystreams::{
|
||||||
activity::{Accept, Create, Dislike, Follow, Like, Update},
|
activity::{Accept, Create, Delete, Dislike, Follow, Like, Undo, Update},
|
||||||
actor::{properties::ApActorProperties, Actor, Group, Person},
|
actor::{kind::GroupType, properties::ApActorProperties, Actor, Group, Person},
|
||||||
collection::UnorderedCollection,
|
collection::UnorderedCollection,
|
||||||
context,
|
context,
|
||||||
endpoint::EndpointProperties,
|
endpoint::EndpointProperties,
|
||||||
ext::{Ext, Extensible, Extension},
|
ext::{Ext, Extensible, Extension},
|
||||||
object::{properties::ObjectProperties, Note, Page},
|
object::{properties::ObjectProperties, Note, Page, Tombstone},
|
||||||
public, BaseBox,
|
public, BaseBox,
|
||||||
};
|
};
|
||||||
use actix_web::body::Body;
|
use actix_web::body::Body;
|
||||||
|
@ -60,6 +63,7 @@ use crate::websocket::{
|
||||||
use crate::{convert_datetime, naive_now, Settings};
|
use crate::{convert_datetime, naive_now, Settings};
|
||||||
|
|
||||||
use activities::{populate_object_props, send_activity};
|
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 fetcher::{get_or_fetch_and_upsert_remote_community, get_or_fetch_and_upsert_remote_user};
|
||||||
use signatures::verify;
|
use signatures::verify;
|
||||||
use signatures::{sign, PublicKey, PublicKeyExtension};
|
use signatures::{sign, PublicKey, PublicKeyExtension};
|
||||||
|
@ -87,6 +91,15 @@ where
|
||||||
.json(data)
|
.json(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_apub_tombstone_response<T>(data: &T) -> HttpResponse<Body>
|
||||||
|
where
|
||||||
|
T: Serialize,
|
||||||
|
{
|
||||||
|
HttpResponse::Gone()
|
||||||
|
.content_type(APUB_JSON_CONTENT_TYPE)
|
||||||
|
.json(data)
|
||||||
|
}
|
||||||
|
|
||||||
/// Generates the ActivityPub ID for a given object type and name.
|
/// Generates the ActivityPub ID for a given object type and name.
|
||||||
///
|
///
|
||||||
/// TODO: we will probably need to change apub endpoint urls so that html and activity+json content
|
/// TODO: we will probably need to change apub endpoint urls so that html and activity+json content
|
||||||
|
@ -142,6 +155,35 @@ fn is_apub_id_valid(apub_id: &Url) -> bool {
|
||||||
pub trait ToApub {
|
pub trait ToApub {
|
||||||
type Response;
|
type Response;
|
||||||
fn to_apub(&self, conn: &PgConnection) -> Result<Self::Response, Error>;
|
fn to_apub(&self, conn: &PgConnection) -> Result<Self::Response, Error>;
|
||||||
|
fn to_tombstone(&self) -> Result<Tombstone, Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Updated is actually the deletion time
|
||||||
|
fn create_tombstone(
|
||||||
|
deleted: bool,
|
||||||
|
object_id: &str,
|
||||||
|
updated: Option<NaiveDateTime>,
|
||||||
|
former_type: String,
|
||||||
|
) -> Result<Tombstone, Error> {
|
||||||
|
if deleted {
|
||||||
|
if let Some(updated) = updated {
|
||||||
|
let mut tombstone = Tombstone::default();
|
||||||
|
tombstone.object_props.set_id(object_id)?;
|
||||||
|
tombstone
|
||||||
|
.tombstone_props
|
||||||
|
.set_former_type_xsd_string(former_type)?
|
||||||
|
.set_deleted(convert_datetime(updated))?;
|
||||||
|
Ok(tombstone)
|
||||||
|
} else {
|
||||||
|
Err(format_err!(
|
||||||
|
"Cant convert to tombstone because updated time was None."
|
||||||
|
))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(format_err!(
|
||||||
|
"Cant convert object to tombstone if it wasnt deleted"
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait FromApub {
|
pub trait FromApub {
|
||||||
|
@ -154,11 +196,14 @@ pub trait FromApub {
|
||||||
pub trait ApubObjectType {
|
pub trait ApubObjectType {
|
||||||
fn send_create(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
|
fn send_create(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
|
||||||
fn send_update(&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_undo_delete(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ApubLikeableType {
|
pub trait ApubLikeableType {
|
||||||
fn send_like(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
|
fn send_like(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
|
||||||
fn send_dislike(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
|
fn send_dislike(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
|
||||||
|
// TODO add send_undo_like / undo_dislike
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_shared_inbox(actor_id: &str) -> String {
|
pub fn get_shared_inbox(actor_id: &str) -> String {
|
||||||
|
@ -192,6 +237,9 @@ pub trait ActorType {
|
||||||
Err(format_err!("Accept not implemented."))
|
Err(format_err!("Accept not implemented."))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn send_delete(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
|
||||||
|
fn send_undo_delete(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
|
||||||
|
|
||||||
// TODO default because there is no user following yet.
|
// TODO default because there is no user following yet.
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
/// For a given community, returns the inboxes of all followers.
|
/// For a given community, returns the inboxes of all followers.
|
||||||
|
|
|
@ -12,7 +12,11 @@ pub async fn get_apub_post(
|
||||||
) -> Result<HttpResponse<Body>, Error> {
|
) -> Result<HttpResponse<Body>, Error> {
|
||||||
let id = info.post_id.parse::<i32>()?;
|
let id = info.post_id.parse::<i32>()?;
|
||||||
let post = Post::read(&&db.get()?, id)?;
|
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 {
|
impl ToApub for Post {
|
||||||
|
@ -53,6 +57,15 @@ impl ToApub for Post {
|
||||||
|
|
||||||
Ok(page)
|
Ok(page)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn to_tombstone(&self) -> Result<Tombstone, Error> {
|
||||||
|
create_tombstone(
|
||||||
|
self.deleted,
|
||||||
|
&self.ap_id,
|
||||||
|
self.updated,
|
||||||
|
PageType.to_string(),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromApub for PostForm {
|
impl FromApub for PostForm {
|
||||||
|
@ -163,6 +176,94 @@ impl ApubObjectType for Post {
|
||||||
)?;
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn send_delete(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
|
||||||
|
let page = self.to_apub(conn)?;
|
||||||
|
let community = Community::read(conn, self.community_id)?;
|
||||||
|
let id = format!("{}/delete/{}", self.ap_id, uuid::Uuid::new_v4());
|
||||||
|
let mut delete = Delete::default();
|
||||||
|
|
||||||
|
populate_object_props(
|
||||||
|
&mut delete.object_props,
|
||||||
|
&community.get_followers_url(),
|
||||||
|
&id,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
delete
|
||||||
|
.delete_props
|
||||||
|
.set_actor_xsd_any_uri(creator.actor_id.to_owned())?
|
||||||
|
.set_object_base_box(page)?;
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
&creator.private_key.as_ref().unwrap(),
|
||||||
|
&creator.actor_id,
|
||||||
|
community.get_follower_inboxes(&conn)?,
|
||||||
|
)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_undo_delete(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
|
||||||
|
let page = self.to_apub(conn)?;
|
||||||
|
let community = Community::read(conn, self.community_id)?;
|
||||||
|
let id = format!("{}/delete/{}", self.ap_id, uuid::Uuid::new_v4());
|
||||||
|
let mut delete = Delete::default();
|
||||||
|
|
||||||
|
populate_object_props(
|
||||||
|
&mut delete.object_props,
|
||||||
|
&community.get_followers_url(),
|
||||||
|
&id,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
delete
|
||||||
|
.delete_props
|
||||||
|
.set_actor_xsd_any_uri(creator.actor_id.to_owned())?
|
||||||
|
.set_object_base_box(page)?;
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// Undo that fake activity
|
||||||
|
let undo_id = format!("{}/undo/delete/{}", self.ap_id, uuid::Uuid::new_v4());
|
||||||
|
let mut undo = Undo::default();
|
||||||
|
|
||||||
|
populate_object_props(
|
||||||
|
&mut undo.object_props,
|
||||||
|
&community.get_followers_url(),
|
||||||
|
&undo_id,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
undo
|
||||||
|
.undo_props
|
||||||
|
.set_actor_xsd_any_uri(creator.actor_id.to_owned())?
|
||||||
|
.set_object_base_box(delete)?;
|
||||||
|
|
||||||
|
// Insert the sent activity into the activity table
|
||||||
|
let activity_form = activity::ActivityForm {
|
||||||
|
user_id: self.creator_id,
|
||||||
|
data: serde_json::to_value(&undo)?,
|
||||||
|
local: true,
|
||||||
|
updated: None,
|
||||||
|
};
|
||||||
|
activity::Activity::create(&conn, &activity_form)?;
|
||||||
|
|
||||||
|
let community = Community::read(conn, self.community_id)?;
|
||||||
|
send_activity(
|
||||||
|
&undo,
|
||||||
|
&creator.private_key.as_ref().unwrap(),
|
||||||
|
&creator.actor_id,
|
||||||
|
community.get_follower_inboxes(&conn)?,
|
||||||
|
)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ApubLikeableType for Post {
|
impl ApubLikeableType for Post {
|
||||||
|
|
|
@ -7,6 +7,8 @@ pub enum SharedAcceptedObjects {
|
||||||
Update(Update),
|
Update(Update),
|
||||||
Like(Like),
|
Like(Like),
|
||||||
Dislike(Dislike),
|
Dislike(Dislike),
|
||||||
|
Delete(Delete),
|
||||||
|
Undo(Undo),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SharedAcceptedObjects {
|
impl SharedAcceptedObjects {
|
||||||
|
@ -16,6 +18,8 @@ impl SharedAcceptedObjects {
|
||||||
SharedAcceptedObjects::Update(u) => u.update_props.get_object_base_box(),
|
SharedAcceptedObjects::Update(u) => u.update_props.get_object_base_box(),
|
||||||
SharedAcceptedObjects::Like(l) => l.like_props.get_object_base_box(),
|
SharedAcceptedObjects::Like(l) => l.like_props.get_object_base_box(),
|
||||||
SharedAcceptedObjects::Dislike(d) => d.dislike_props.get_object_base_box(),
|
SharedAcceptedObjects::Dislike(d) => d.dislike_props.get_object_base_box(),
|
||||||
|
SharedAcceptedObjects::Delete(d) => d.delete_props.get_object_base_box(),
|
||||||
|
SharedAcceptedObjects::Undo(d) => d.undo_props.get_object_base_box(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,6 +53,9 @@ pub async fn shared_inbox(
|
||||||
(SharedAcceptedObjects::Dislike(d), Some("Page")) => {
|
(SharedAcceptedObjects::Dislike(d), Some("Page")) => {
|
||||||
receive_dislike_post(&d, &request, &conn, chat_server)
|
receive_dislike_post(&d, &request, &conn, chat_server)
|
||||||
}
|
}
|
||||||
|
(SharedAcceptedObjects::Delete(d), Some("Page")) => {
|
||||||
|
receive_delete_post(&d, &request, &conn, chat_server)
|
||||||
|
}
|
||||||
(SharedAcceptedObjects::Create(c), Some("Note")) => {
|
(SharedAcceptedObjects::Create(c), Some("Note")) => {
|
||||||
receive_create_comment(&c, &request, &conn, chat_server)
|
receive_create_comment(&c, &request, &conn, chat_server)
|
||||||
}
|
}
|
||||||
|
@ -61,6 +68,15 @@ pub async fn shared_inbox(
|
||||||
(SharedAcceptedObjects::Dislike(d), Some("Note")) => {
|
(SharedAcceptedObjects::Dislike(d), Some("Note")) => {
|
||||||
receive_dislike_comment(&d, &request, &conn, chat_server)
|
receive_dislike_comment(&d, &request, &conn, chat_server)
|
||||||
}
|
}
|
||||||
|
(SharedAcceptedObjects::Delete(d), Some("Note")) => {
|
||||||
|
receive_delete_comment(&d, &request, &conn, chat_server)
|
||||||
|
}
|
||||||
|
(SharedAcceptedObjects::Delete(d), Some("Group")) => {
|
||||||
|
receive_delete_community(&d, &request, &conn, chat_server)
|
||||||
|
}
|
||||||
|
(SharedAcceptedObjects::Undo(u), Some("Delete")) => {
|
||||||
|
receive_undo_delete(&u, &request, &conn, chat_server)
|
||||||
|
}
|
||||||
_ => Err(format_err!("Unknown incoming activity type.")),
|
_ => Err(format_err!("Unknown incoming activity type.")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,7 +93,7 @@ fn receive_create_post(
|
||||||
.to_owned()
|
.to_owned()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_owned()
|
.to_owned()
|
||||||
.to_concrete::<Page>()?;
|
.into_concrete::<Page>()?;
|
||||||
|
|
||||||
let user_uri = create
|
let user_uri = create
|
||||||
.create_props
|
.create_props
|
||||||
|
@ -126,7 +142,7 @@ fn receive_create_comment(
|
||||||
.to_owned()
|
.to_owned()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_owned()
|
.to_owned()
|
||||||
.to_concrete::<Note>()?;
|
.into_concrete::<Note>()?;
|
||||||
|
|
||||||
let user_uri = create
|
let user_uri = create
|
||||||
.create_props
|
.create_props
|
||||||
|
@ -180,7 +196,7 @@ fn receive_update_post(
|
||||||
.to_owned()
|
.to_owned()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_owned()
|
.to_owned()
|
||||||
.to_concrete::<Page>()?;
|
.into_concrete::<Page>()?;
|
||||||
|
|
||||||
let user_uri = update
|
let user_uri = update
|
||||||
.update_props
|
.update_props
|
||||||
|
@ -230,7 +246,7 @@ fn receive_like_post(
|
||||||
.to_owned()
|
.to_owned()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_owned()
|
.to_owned()
|
||||||
.to_concrete::<Page>()?;
|
.into_concrete::<Page>()?;
|
||||||
|
|
||||||
let user_uri = like.like_props.get_actor_xsd_any_uri().unwrap().to_string();
|
let user_uri = like.like_props.get_actor_xsd_any_uri().unwrap().to_string();
|
||||||
|
|
||||||
|
@ -283,7 +299,7 @@ fn receive_dislike_post(
|
||||||
.to_owned()
|
.to_owned()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_owned()
|
.to_owned()
|
||||||
.to_concrete::<Page>()?;
|
.into_concrete::<Page>()?;
|
||||||
|
|
||||||
let user_uri = dislike
|
let user_uri = dislike
|
||||||
.dislike_props
|
.dislike_props
|
||||||
|
@ -340,7 +356,7 @@ fn receive_update_comment(
|
||||||
.to_owned()
|
.to_owned()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_owned()
|
.to_owned()
|
||||||
.to_concrete::<Note>()?;
|
.into_concrete::<Note>()?;
|
||||||
|
|
||||||
let user_uri = update
|
let user_uri = update
|
||||||
.update_props
|
.update_props
|
||||||
|
@ -395,7 +411,7 @@ fn receive_like_comment(
|
||||||
.to_owned()
|
.to_owned()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_owned()
|
.to_owned()
|
||||||
.to_concrete::<Note>()?;
|
.into_concrete::<Note>()?;
|
||||||
|
|
||||||
let user_uri = like.like_props.get_actor_xsd_any_uri().unwrap().to_string();
|
let user_uri = like.like_props.get_actor_xsd_any_uri().unwrap().to_string();
|
||||||
|
|
||||||
|
@ -453,7 +469,7 @@ fn receive_dislike_comment(
|
||||||
.to_owned()
|
.to_owned()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_owned()
|
.to_owned()
|
||||||
.to_concrete::<Note>()?;
|
.into_concrete::<Note>()?;
|
||||||
|
|
||||||
let user_uri = dislike
|
let user_uri = dislike
|
||||||
.dislike_props
|
.dislike_props
|
||||||
|
@ -502,3 +518,449 @@ fn receive_dislike_comment(
|
||||||
|
|
||||||
Ok(HttpResponse::Ok().finish())
|
Ok(HttpResponse::Ok().finish())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn receive_delete_community(
|
||||||
|
delete: &Delete,
|
||||||
|
request: &HttpRequest,
|
||||||
|
conn: &PgConnection,
|
||||||
|
chat_server: ChatServerParam,
|
||||||
|
) -> Result<HttpResponse, Error> {
|
||||||
|
let user_uri = delete
|
||||||
|
.delete_props
|
||||||
|
.get_actor_xsd_any_uri()
|
||||||
|
.unwrap()
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
let group = delete
|
||||||
|
.delete_props
|
||||||
|
.get_object_base_box()
|
||||||
|
.to_owned()
|
||||||
|
.unwrap()
|
||||||
|
.to_owned()
|
||||||
|
.into_concrete::<GroupExt>()?;
|
||||||
|
|
||||||
|
let user = get_or_fetch_and_upsert_remote_user(&user_uri, &conn)?;
|
||||||
|
verify(request, &user.public_key.unwrap())?;
|
||||||
|
|
||||||
|
// Insert the received activity into the activity table
|
||||||
|
let activity_form = activity::ActivityForm {
|
||||||
|
user_id: user.id,
|
||||||
|
data: serde_json::to_value(&delete)?,
|
||||||
|
local: false,
|
||||||
|
updated: None,
|
||||||
|
};
|
||||||
|
activity::Activity::create(&conn, &activity_form)?;
|
||||||
|
|
||||||
|
let community_actor_id = CommunityForm::from_apub(&group, &conn)?.actor_id;
|
||||||
|
let community = Community::read_from_actor_id(conn, &community_actor_id)?;
|
||||||
|
|
||||||
|
let community_form = CommunityForm {
|
||||||
|
name: community.name.to_owned(),
|
||||||
|
title: community.title.to_owned(),
|
||||||
|
description: community.description.to_owned(),
|
||||||
|
category_id: community.category_id, // Note: need to keep this due to foreign key constraint
|
||||||
|
creator_id: community.creator_id, // Note: need to keep this due to foreign key constraint
|
||||||
|
removed: None,
|
||||||
|
published: None,
|
||||||
|
updated: Some(naive_now()),
|
||||||
|
deleted: Some(true),
|
||||||
|
nsfw: community.nsfw,
|
||||||
|
actor_id: community.actor_id,
|
||||||
|
local: community.local,
|
||||||
|
private_key: community.private_key,
|
||||||
|
public_key: community.public_key,
|
||||||
|
last_refreshed_at: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
Community::update(&conn, community.id, &community_form)?;
|
||||||
|
|
||||||
|
let res = CommunityResponse {
|
||||||
|
community: CommunityView::read(&conn, community.id, None)?,
|
||||||
|
};
|
||||||
|
|
||||||
|
chat_server.do_send(SendCommunityRoomMessage {
|
||||||
|
op: UserOperation::EditCommunity,
|
||||||
|
response: res,
|
||||||
|
community_id: community.id,
|
||||||
|
my_id: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(HttpResponse::Ok().finish())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn receive_delete_post(
|
||||||
|
delete: &Delete,
|
||||||
|
request: &HttpRequest,
|
||||||
|
conn: &PgConnection,
|
||||||
|
chat_server: ChatServerParam,
|
||||||
|
) -> Result<HttpResponse, Error> {
|
||||||
|
let user_uri = delete
|
||||||
|
.delete_props
|
||||||
|
.get_actor_xsd_any_uri()
|
||||||
|
.unwrap()
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
let page = delete
|
||||||
|
.delete_props
|
||||||
|
.get_object_base_box()
|
||||||
|
.to_owned()
|
||||||
|
.unwrap()
|
||||||
|
.to_owned()
|
||||||
|
.into_concrete::<Page>()?;
|
||||||
|
|
||||||
|
let user = get_or_fetch_and_upsert_remote_user(&user_uri, &conn)?;
|
||||||
|
verify(request, &user.public_key.unwrap())?;
|
||||||
|
|
||||||
|
// Insert the received activity into the activity table
|
||||||
|
let activity_form = activity::ActivityForm {
|
||||||
|
user_id: user.id,
|
||||||
|
data: serde_json::to_value(&delete)?,
|
||||||
|
local: false,
|
||||||
|
updated: None,
|
||||||
|
};
|
||||||
|
activity::Activity::create(&conn, &activity_form)?;
|
||||||
|
|
||||||
|
let post_ap_id = PostForm::from_apub(&page, conn)?.ap_id;
|
||||||
|
let post = Post::read_from_apub_id(conn, &post_ap_id)?;
|
||||||
|
|
||||||
|
let post_form = PostForm {
|
||||||
|
name: post.name.to_owned(),
|
||||||
|
url: post.url.to_owned(),
|
||||||
|
body: post.body.to_owned(),
|
||||||
|
creator_id: post.creator_id.to_owned(),
|
||||||
|
community_id: post.community_id,
|
||||||
|
removed: None,
|
||||||
|
deleted: Some(true),
|
||||||
|
nsfw: post.nsfw,
|
||||||
|
locked: None,
|
||||||
|
stickied: None,
|
||||||
|
updated: Some(naive_now()),
|
||||||
|
embed_title: post.embed_title,
|
||||||
|
embed_description: post.embed_description,
|
||||||
|
embed_html: post.embed_html,
|
||||||
|
thumbnail_url: post.thumbnail_url,
|
||||||
|
ap_id: post.ap_id,
|
||||||
|
local: post.local,
|
||||||
|
published: None,
|
||||||
|
};
|
||||||
|
Post::update(&conn, post.id, &post_form)?;
|
||||||
|
|
||||||
|
// Refetch the view
|
||||||
|
let post_view = PostView::read(&conn, post.id, None)?;
|
||||||
|
|
||||||
|
let res = PostResponse { post: post_view };
|
||||||
|
|
||||||
|
chat_server.do_send(SendPost {
|
||||||
|
op: UserOperation::EditPost,
|
||||||
|
post: res,
|
||||||
|
my_id: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(HttpResponse::Ok().finish())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn receive_delete_comment(
|
||||||
|
delete: &Delete,
|
||||||
|
request: &HttpRequest,
|
||||||
|
conn: &PgConnection,
|
||||||
|
chat_server: ChatServerParam,
|
||||||
|
) -> Result<HttpResponse, Error> {
|
||||||
|
let user_uri = delete
|
||||||
|
.delete_props
|
||||||
|
.get_actor_xsd_any_uri()
|
||||||
|
.unwrap()
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
let note = delete
|
||||||
|
.delete_props
|
||||||
|
.get_object_base_box()
|
||||||
|
.to_owned()
|
||||||
|
.unwrap()
|
||||||
|
.to_owned()
|
||||||
|
.into_concrete::<Note>()?;
|
||||||
|
|
||||||
|
let user = get_or_fetch_and_upsert_remote_user(&user_uri, &conn)?;
|
||||||
|
verify(request, &user.public_key.unwrap())?;
|
||||||
|
|
||||||
|
// Insert the received activity into the activity table
|
||||||
|
let activity_form = activity::ActivityForm {
|
||||||
|
user_id: user.id,
|
||||||
|
data: serde_json::to_value(&delete)?,
|
||||||
|
local: false,
|
||||||
|
updated: None,
|
||||||
|
};
|
||||||
|
activity::Activity::create(&conn, &activity_form)?;
|
||||||
|
|
||||||
|
let comment_ap_id = CommentForm::from_apub(¬e, &conn)?.ap_id;
|
||||||
|
let comment = Comment::read_from_apub_id(conn, &comment_ap_id)?;
|
||||||
|
let comment_form = CommentForm {
|
||||||
|
content: comment.content.to_owned(),
|
||||||
|
parent_id: comment.parent_id,
|
||||||
|
post_id: comment.post_id,
|
||||||
|
creator_id: comment.creator_id,
|
||||||
|
removed: None,
|
||||||
|
deleted: Some(true),
|
||||||
|
read: None,
|
||||||
|
published: None,
|
||||||
|
updated: Some(naive_now()),
|
||||||
|
ap_id: comment.ap_id,
|
||||||
|
local: comment.local,
|
||||||
|
};
|
||||||
|
Comment::update(&conn, comment.id, &comment_form)?;
|
||||||
|
|
||||||
|
// Refetch the view
|
||||||
|
let comment_view = CommentView::read(&conn, comment.id, None)?;
|
||||||
|
|
||||||
|
// TODO get those recipient actor ids from somewhere
|
||||||
|
let recipient_ids = vec![];
|
||||||
|
let res = CommentResponse {
|
||||||
|
comment: comment_view,
|
||||||
|
recipient_ids,
|
||||||
|
};
|
||||||
|
|
||||||
|
chat_server.do_send(SendComment {
|
||||||
|
op: UserOperation::EditComment,
|
||||||
|
comment: res,
|
||||||
|
my_id: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(HttpResponse::Ok().finish())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn receive_undo_delete(
|
||||||
|
undo: &Undo,
|
||||||
|
request: &HttpRequest,
|
||||||
|
conn: &PgConnection,
|
||||||
|
chat_server: ChatServerParam,
|
||||||
|
) -> Result<HttpResponse, Error> {
|
||||||
|
let delete = undo
|
||||||
|
.undo_props
|
||||||
|
.get_object_base_box()
|
||||||
|
.to_owned()
|
||||||
|
.unwrap()
|
||||||
|
.to_owned()
|
||||||
|
.into_concrete::<Delete>()?;
|
||||||
|
|
||||||
|
let type_ = delete
|
||||||
|
.delete_props
|
||||||
|
.get_object_base_box()
|
||||||
|
.to_owned()
|
||||||
|
.unwrap()
|
||||||
|
.kind()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
match type_ {
|
||||||
|
"Note" => receive_undo_delete_comment(&delete, &request, &conn, chat_server),
|
||||||
|
"Page" => receive_undo_delete_post(&delete, &request, &conn, chat_server),
|
||||||
|
"Group" => receive_undo_delete_community(&delete, &request, &conn, chat_server),
|
||||||
|
d => Err(format_err!("Undo Delete type {} not supported", d)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn receive_undo_delete_comment(
|
||||||
|
delete: &Delete,
|
||||||
|
request: &HttpRequest,
|
||||||
|
conn: &PgConnection,
|
||||||
|
chat_server: ChatServerParam,
|
||||||
|
) -> Result<HttpResponse, Error> {
|
||||||
|
let user_uri = delete
|
||||||
|
.delete_props
|
||||||
|
.get_actor_xsd_any_uri()
|
||||||
|
.unwrap()
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
let note = delete
|
||||||
|
.delete_props
|
||||||
|
.get_object_base_box()
|
||||||
|
.to_owned()
|
||||||
|
.unwrap()
|
||||||
|
.to_owned()
|
||||||
|
.into_concrete::<Note>()?;
|
||||||
|
|
||||||
|
let user = get_or_fetch_and_upsert_remote_user(&user_uri, &conn)?;
|
||||||
|
verify(request, &user.public_key.unwrap())?;
|
||||||
|
|
||||||
|
// Insert the received activity into the activity table
|
||||||
|
let activity_form = activity::ActivityForm {
|
||||||
|
user_id: user.id,
|
||||||
|
data: serde_json::to_value(&delete)?,
|
||||||
|
local: false,
|
||||||
|
updated: None,
|
||||||
|
};
|
||||||
|
activity::Activity::create(&conn, &activity_form)?;
|
||||||
|
|
||||||
|
let comment_ap_id = CommentForm::from_apub(¬e, &conn)?.ap_id;
|
||||||
|
let comment = Comment::read_from_apub_id(conn, &comment_ap_id)?;
|
||||||
|
let comment_form = CommentForm {
|
||||||
|
content: comment.content.to_owned(),
|
||||||
|
parent_id: comment.parent_id,
|
||||||
|
post_id: comment.post_id,
|
||||||
|
creator_id: comment.creator_id,
|
||||||
|
removed: None,
|
||||||
|
deleted: Some(false),
|
||||||
|
read: None,
|
||||||
|
published: None,
|
||||||
|
updated: Some(naive_now()),
|
||||||
|
ap_id: comment.ap_id,
|
||||||
|
local: comment.local,
|
||||||
|
};
|
||||||
|
Comment::update(&conn, comment.id, &comment_form)?;
|
||||||
|
|
||||||
|
// Refetch the view
|
||||||
|
let comment_view = CommentView::read(&conn, comment.id, None)?;
|
||||||
|
|
||||||
|
// TODO get those recipient actor ids from somewhere
|
||||||
|
let recipient_ids = vec![];
|
||||||
|
let res = CommentResponse {
|
||||||
|
comment: comment_view,
|
||||||
|
recipient_ids,
|
||||||
|
};
|
||||||
|
|
||||||
|
chat_server.do_send(SendComment {
|
||||||
|
op: UserOperation::EditComment,
|
||||||
|
comment: res,
|
||||||
|
my_id: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(HttpResponse::Ok().finish())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn receive_undo_delete_post(
|
||||||
|
delete: &Delete,
|
||||||
|
request: &HttpRequest,
|
||||||
|
conn: &PgConnection,
|
||||||
|
chat_server: ChatServerParam,
|
||||||
|
) -> Result<HttpResponse, Error> {
|
||||||
|
let user_uri = delete
|
||||||
|
.delete_props
|
||||||
|
.get_actor_xsd_any_uri()
|
||||||
|
.unwrap()
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
let page = delete
|
||||||
|
.delete_props
|
||||||
|
.get_object_base_box()
|
||||||
|
.to_owned()
|
||||||
|
.unwrap()
|
||||||
|
.to_owned()
|
||||||
|
.into_concrete::<Page>()?;
|
||||||
|
|
||||||
|
let user = get_or_fetch_and_upsert_remote_user(&user_uri, &conn)?;
|
||||||
|
verify(request, &user.public_key.unwrap())?;
|
||||||
|
|
||||||
|
// Insert the received activity into the activity table
|
||||||
|
let activity_form = activity::ActivityForm {
|
||||||
|
user_id: user.id,
|
||||||
|
data: serde_json::to_value(&delete)?,
|
||||||
|
local: false,
|
||||||
|
updated: None,
|
||||||
|
};
|
||||||
|
activity::Activity::create(&conn, &activity_form)?;
|
||||||
|
|
||||||
|
let post_ap_id = PostForm::from_apub(&page, conn)?.ap_id;
|
||||||
|
let post = Post::read_from_apub_id(conn, &post_ap_id)?;
|
||||||
|
|
||||||
|
let post_form = PostForm {
|
||||||
|
name: post.name.to_owned(),
|
||||||
|
url: post.url.to_owned(),
|
||||||
|
body: post.body.to_owned(),
|
||||||
|
creator_id: post.creator_id.to_owned(),
|
||||||
|
community_id: post.community_id,
|
||||||
|
removed: None,
|
||||||
|
deleted: Some(false),
|
||||||
|
nsfw: post.nsfw,
|
||||||
|
locked: None,
|
||||||
|
stickied: None,
|
||||||
|
updated: Some(naive_now()),
|
||||||
|
embed_title: post.embed_title,
|
||||||
|
embed_description: post.embed_description,
|
||||||
|
embed_html: post.embed_html,
|
||||||
|
thumbnail_url: post.thumbnail_url,
|
||||||
|
ap_id: post.ap_id,
|
||||||
|
local: post.local,
|
||||||
|
published: None,
|
||||||
|
};
|
||||||
|
Post::update(&conn, post.id, &post_form)?;
|
||||||
|
|
||||||
|
// Refetch the view
|
||||||
|
let post_view = PostView::read(&conn, post.id, None)?;
|
||||||
|
|
||||||
|
let res = PostResponse { post: post_view };
|
||||||
|
|
||||||
|
chat_server.do_send(SendPost {
|
||||||
|
op: UserOperation::EditPost,
|
||||||
|
post: res,
|
||||||
|
my_id: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(HttpResponse::Ok().finish())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn receive_undo_delete_community(
|
||||||
|
delete: &Delete,
|
||||||
|
request: &HttpRequest,
|
||||||
|
conn: &PgConnection,
|
||||||
|
chat_server: ChatServerParam,
|
||||||
|
) -> Result<HttpResponse, Error> {
|
||||||
|
let user_uri = delete
|
||||||
|
.delete_props
|
||||||
|
.get_actor_xsd_any_uri()
|
||||||
|
.unwrap()
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
let group = delete
|
||||||
|
.delete_props
|
||||||
|
.get_object_base_box()
|
||||||
|
.to_owned()
|
||||||
|
.unwrap()
|
||||||
|
.to_owned()
|
||||||
|
.into_concrete::<GroupExt>()?;
|
||||||
|
|
||||||
|
let user = get_or_fetch_and_upsert_remote_user(&user_uri, &conn)?;
|
||||||
|
verify(request, &user.public_key.unwrap())?;
|
||||||
|
|
||||||
|
// Insert the received activity into the activity table
|
||||||
|
let activity_form = activity::ActivityForm {
|
||||||
|
user_id: user.id,
|
||||||
|
data: serde_json::to_value(&delete)?,
|
||||||
|
local: false,
|
||||||
|
updated: None,
|
||||||
|
};
|
||||||
|
activity::Activity::create(&conn, &activity_form)?;
|
||||||
|
|
||||||
|
let community_actor_id = CommunityForm::from_apub(&group, &conn)?.actor_id;
|
||||||
|
let community = Community::read_from_actor_id(conn, &community_actor_id)?;
|
||||||
|
|
||||||
|
let community_form = CommunityForm {
|
||||||
|
name: community.name.to_owned(),
|
||||||
|
title: community.title.to_owned(),
|
||||||
|
description: community.description.to_owned(),
|
||||||
|
category_id: community.category_id, // Note: need to keep this due to foreign key constraint
|
||||||
|
creator_id: community.creator_id, // Note: need to keep this due to foreign key constraint
|
||||||
|
removed: None,
|
||||||
|
published: None,
|
||||||
|
updated: Some(naive_now()),
|
||||||
|
deleted: Some(false),
|
||||||
|
nsfw: community.nsfw,
|
||||||
|
actor_id: community.actor_id,
|
||||||
|
local: community.local,
|
||||||
|
private_key: community.private_key,
|
||||||
|
public_key: community.public_key,
|
||||||
|
last_refreshed_at: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
Community::update(&conn, community.id, &community_form)?;
|
||||||
|
|
||||||
|
let res = CommunityResponse {
|
||||||
|
community: CommunityView::read(&conn, community.id, None)?,
|
||||||
|
};
|
||||||
|
|
||||||
|
chat_server.do_send(SendCommunityRoomMessage {
|
||||||
|
op: UserOperation::EditCommunity,
|
||||||
|
response: res,
|
||||||
|
community_id: community.id,
|
||||||
|
my_id: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(HttpResponse::Ok().finish())
|
||||||
|
}
|
||||||
|
|
|
@ -43,6 +43,9 @@ impl ToApub for User_ {
|
||||||
|
|
||||||
Ok(person.extend(actor_props).extend(self.get_public_key_ext()))
|
Ok(person.extend(actor_props).extend(self.get_public_key_ext()))
|
||||||
}
|
}
|
||||||
|
fn to_tombstone(&self) -> Result<Tombstone, Error> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ActorType for User_ {
|
impl ActorType for User_ {
|
||||||
|
@ -87,6 +90,14 @@ impl ActorType for User_ {
|
||||||
)?;
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn send_delete(&self, _creator: &User_, _conn: &PgConnection) -> Result<(), Error> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_undo_delete(&self, _creator: &User_, _conn: &PgConnection) -> Result<(), Error> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromApub for UserForm {
|
impl FromApub for UserForm {
|
||||||
|
|
|
@ -22,6 +22,7 @@ pub struct Community {
|
||||||
pub last_refreshed_at: chrono::NaiveDateTime,
|
pub last_refreshed_at: chrono::NaiveDateTime,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO add better delete, remove, lock actions here.
|
||||||
#[derive(Insertable, AsChangeset, Clone, Serialize, Deserialize, Debug)]
|
#[derive(Insertable, AsChangeset, Clone, Serialize, Deserialize, Debug)]
|
||||||
#[table_name = "community"]
|
#[table_name = "community"]
|
||||||
pub struct CommunityForm {
|
pub struct CommunityForm {
|
||||||
|
|
279
ui/src/api_tests/api.spec.ts
vendored
279
ui/src/api_tests/api.spec.ts
vendored
|
@ -13,6 +13,9 @@ import {
|
||||||
GetPostResponse,
|
GetPostResponse,
|
||||||
CommentForm,
|
CommentForm,
|
||||||
CommentResponse,
|
CommentResponse,
|
||||||
|
CommunityForm,
|
||||||
|
GetCommunityForm,
|
||||||
|
GetCommunityResponse,
|
||||||
} from '../interfaces';
|
} from '../interfaces';
|
||||||
|
|
||||||
let lemmyAlphaUrl = 'http://localhost:8540';
|
let lemmyAlphaUrl = 'http://localhost:8540';
|
||||||
|
@ -326,6 +329,282 @@ describe('main', () => {
|
||||||
expect(getPostRes.comments[1].creator_local).toBe(false);
|
expect(getPostRes.comments[1].creator_local).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('delete things', () => {
|
||||||
|
test('/u/lemmy_beta deletes and undeletes a federated comment, post, and community, lemmy_alpha sees its deleted.', async () => {
|
||||||
|
// Create a test community
|
||||||
|
let communityName = 'test_community';
|
||||||
|
let communityForm: CommunityForm = {
|
||||||
|
name: communityName,
|
||||||
|
title: communityName,
|
||||||
|
category_id: 1,
|
||||||
|
nsfw: false,
|
||||||
|
auth: lemmyBetaAuth,
|
||||||
|
};
|
||||||
|
|
||||||
|
let createCommunityRes: CommunityResponse = await fetch(
|
||||||
|
`${lemmyBetaApiUrl}/community`,
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: wrapper(communityForm),
|
||||||
|
}
|
||||||
|
).then(d => d.json());
|
||||||
|
|
||||||
|
expect(createCommunityRes.community.name).toBe(communityName);
|
||||||
|
|
||||||
|
// Cache it on lemmy_alpha
|
||||||
|
let searchUrl = `${lemmyAlphaApiUrl}/search?q=http://lemmy_beta:8550/c/${communityName}&type_=All&sort=TopAll`;
|
||||||
|
let searchResponse: SearchResponse = await fetch(searchUrl, {
|
||||||
|
method: 'GET',
|
||||||
|
}).then(d => d.json());
|
||||||
|
|
||||||
|
let communityOnAlphaId = searchResponse.communities[0].id;
|
||||||
|
|
||||||
|
// Follow it
|
||||||
|
let followForm: FollowCommunityForm = {
|
||||||
|
community_id: communityOnAlphaId,
|
||||||
|
follow: true,
|
||||||
|
auth: lemmyAlphaAuth,
|
||||||
|
};
|
||||||
|
|
||||||
|
let followRes: CommunityResponse = await fetch(
|
||||||
|
`${lemmyAlphaApiUrl}/community/follow`,
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: wrapper(followForm),
|
||||||
|
}
|
||||||
|
).then(d => d.json());
|
||||||
|
|
||||||
|
// Make sure the follow response went through
|
||||||
|
expect(followRes.community.local).toBe(false);
|
||||||
|
expect(followRes.community.name).toBe(communityName);
|
||||||
|
|
||||||
|
// Lemmy beta creates a test post
|
||||||
|
let postName = 'A jest test post with delete';
|
||||||
|
let createPostForm: PostForm = {
|
||||||
|
name: postName,
|
||||||
|
auth: lemmyBetaAuth,
|
||||||
|
community_id: createCommunityRes.community.id,
|
||||||
|
creator_id: 2,
|
||||||
|
nsfw: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
let createPostRes: PostResponse = await fetch(`${lemmyBetaApiUrl}/post`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: wrapper(createPostForm),
|
||||||
|
}).then(d => d.json());
|
||||||
|
expect(createPostRes.post.name).toBe(postName);
|
||||||
|
|
||||||
|
// Lemmy beta creates a test comment
|
||||||
|
let commentContent = 'A jest test federated comment with delete';
|
||||||
|
let createCommentForm: CommentForm = {
|
||||||
|
content: commentContent,
|
||||||
|
post_id: createPostRes.post.id,
|
||||||
|
auth: lemmyBetaAuth,
|
||||||
|
};
|
||||||
|
|
||||||
|
let createCommentRes: CommentResponse = await fetch(
|
||||||
|
`${lemmyBetaApiUrl}/comment`,
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: wrapper(createCommentForm),
|
||||||
|
}
|
||||||
|
).then(d => d.json());
|
||||||
|
|
||||||
|
expect(createCommentRes.comment.content).toBe(commentContent);
|
||||||
|
|
||||||
|
// lemmy_beta deletes the comment
|
||||||
|
let deleteCommentForm: CommentForm = {
|
||||||
|
content: commentContent,
|
||||||
|
edit_id: createCommentRes.comment.id,
|
||||||
|
post_id: createPostRes.post.id,
|
||||||
|
deleted: true,
|
||||||
|
auth: lemmyBetaAuth,
|
||||||
|
creator_id: createCommentRes.comment.creator_id,
|
||||||
|
};
|
||||||
|
|
||||||
|
let deleteCommentRes: CommentResponse = await fetch(
|
||||||
|
`${lemmyBetaApiUrl}/comment`,
|
||||||
|
{
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: wrapper(deleteCommentForm),
|
||||||
|
}
|
||||||
|
).then(d => d.json());
|
||||||
|
expect(deleteCommentRes.comment.deleted).toBe(true);
|
||||||
|
|
||||||
|
// lemmy_alpha sees that the comment is deleted
|
||||||
|
let getPostUrl = `${lemmyAlphaApiUrl}/post?id=3`;
|
||||||
|
let getPostRes: GetPostResponse = await fetch(getPostUrl, {
|
||||||
|
method: 'GET',
|
||||||
|
}).then(d => d.json());
|
||||||
|
expect(getPostRes.comments[0].deleted).toBe(true);
|
||||||
|
|
||||||
|
// lemmy_beta undeletes the comment
|
||||||
|
let undeleteCommentForm: CommentForm = {
|
||||||
|
content: commentContent,
|
||||||
|
edit_id: createCommentRes.comment.id,
|
||||||
|
post_id: createPostRes.post.id,
|
||||||
|
deleted: false,
|
||||||
|
auth: lemmyBetaAuth,
|
||||||
|
creator_id: createCommentRes.comment.creator_id,
|
||||||
|
};
|
||||||
|
|
||||||
|
let undeleteCommentRes: CommentResponse = await fetch(
|
||||||
|
`${lemmyBetaApiUrl}/comment`,
|
||||||
|
{
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: wrapper(undeleteCommentForm),
|
||||||
|
}
|
||||||
|
).then(d => d.json());
|
||||||
|
expect(undeleteCommentRes.comment.deleted).toBe(false);
|
||||||
|
|
||||||
|
// lemmy_alpha sees that the comment is undeleted
|
||||||
|
let getPostUndeleteRes: GetPostResponse = await fetch(getPostUrl, {
|
||||||
|
method: 'GET',
|
||||||
|
}).then(d => d.json());
|
||||||
|
expect(getPostUndeleteRes.comments[0].deleted).toBe(false);
|
||||||
|
|
||||||
|
// lemmy_beta deletes the post
|
||||||
|
let deletePostForm: PostForm = {
|
||||||
|
name: postName,
|
||||||
|
edit_id: createPostRes.post.id,
|
||||||
|
auth: lemmyBetaAuth,
|
||||||
|
community_id: createPostRes.post.community_id,
|
||||||
|
creator_id: createPostRes.post.creator_id,
|
||||||
|
nsfw: false,
|
||||||
|
deleted: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
let deletePostRes: PostResponse = await fetch(`${lemmyBetaApiUrl}/post`, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: wrapper(deletePostForm),
|
||||||
|
}).then(d => d.json());
|
||||||
|
expect(deletePostRes.post.deleted).toBe(true);
|
||||||
|
|
||||||
|
// Make sure lemmy_alpha sees the post is deleted
|
||||||
|
let getPostResAgain: GetPostResponse = await fetch(getPostUrl, {
|
||||||
|
method: 'GET',
|
||||||
|
}).then(d => d.json());
|
||||||
|
expect(getPostResAgain.post.deleted).toBe(true);
|
||||||
|
|
||||||
|
// lemmy_beta undeletes the post
|
||||||
|
let undeletePostForm: PostForm = {
|
||||||
|
name: postName,
|
||||||
|
edit_id: createPostRes.post.id,
|
||||||
|
auth: lemmyBetaAuth,
|
||||||
|
community_id: createPostRes.post.community_id,
|
||||||
|
creator_id: createPostRes.post.creator_id,
|
||||||
|
nsfw: false,
|
||||||
|
deleted: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
let undeletePostRes: PostResponse = await fetch(
|
||||||
|
`${lemmyBetaApiUrl}/post`,
|
||||||
|
{
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: wrapper(undeletePostForm),
|
||||||
|
}
|
||||||
|
).then(d => d.json());
|
||||||
|
expect(undeletePostRes.post.deleted).toBe(false);
|
||||||
|
|
||||||
|
// Make sure lemmy_alpha sees the post is undeleted
|
||||||
|
let getPostResAgainTwo: GetPostResponse = await fetch(getPostUrl, {
|
||||||
|
method: 'GET',
|
||||||
|
}).then(d => d.json());
|
||||||
|
expect(getPostResAgainTwo.post.deleted).toBe(false);
|
||||||
|
|
||||||
|
// lemmy_beta deletes the community
|
||||||
|
let deleteCommunityForm: CommunityForm = {
|
||||||
|
name: communityName,
|
||||||
|
title: communityName,
|
||||||
|
category_id: 1,
|
||||||
|
edit_id: createCommunityRes.community.id,
|
||||||
|
nsfw: false,
|
||||||
|
deleted: true,
|
||||||
|
auth: lemmyBetaAuth,
|
||||||
|
};
|
||||||
|
|
||||||
|
let deleteResponse: CommunityResponse = await fetch(
|
||||||
|
`${lemmyBetaApiUrl}/community`,
|
||||||
|
{
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: wrapper(deleteCommunityForm),
|
||||||
|
}
|
||||||
|
).then(d => d.json());
|
||||||
|
|
||||||
|
// Make sure the delete went through
|
||||||
|
expect(deleteResponse.community.deleted).toBe(true);
|
||||||
|
|
||||||
|
// Re-get it from alpha, make sure its deleted there too
|
||||||
|
let getCommunityUrl = `${lemmyAlphaApiUrl}/community?id=${communityOnAlphaId}&auth=${lemmyAlphaAuth}`;
|
||||||
|
let getCommunityRes: GetCommunityResponse = await fetch(getCommunityUrl, {
|
||||||
|
method: 'GET',
|
||||||
|
}).then(d => d.json());
|
||||||
|
|
||||||
|
expect(getCommunityRes.community.deleted).toBe(true);
|
||||||
|
|
||||||
|
// lemmy_beta undeletes the community
|
||||||
|
let undeleteCommunityForm: CommunityForm = {
|
||||||
|
name: communityName,
|
||||||
|
title: communityName,
|
||||||
|
category_id: 1,
|
||||||
|
edit_id: createCommunityRes.community.id,
|
||||||
|
nsfw: false,
|
||||||
|
deleted: false,
|
||||||
|
auth: lemmyBetaAuth,
|
||||||
|
};
|
||||||
|
|
||||||
|
let undeleteCommunityRes: CommunityResponse = await fetch(
|
||||||
|
`${lemmyBetaApiUrl}/community`,
|
||||||
|
{
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: wrapper(undeleteCommunityForm),
|
||||||
|
}
|
||||||
|
).then(d => d.json());
|
||||||
|
|
||||||
|
// Make sure the delete went through
|
||||||
|
expect(undeleteCommunityRes.community.deleted).toBe(false);
|
||||||
|
|
||||||
|
// Re-get it from alpha, make sure its deleted there too
|
||||||
|
let getCommunityResAgain: GetCommunityResponse = await fetch(
|
||||||
|
getCommunityUrl,
|
||||||
|
{
|
||||||
|
method: 'GET',
|
||||||
|
}
|
||||||
|
).then(d => d.json());
|
||||||
|
expect(getCommunityResAgain.community.deleted).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function wrapper(form: any): string {
|
function wrapper(form: any): string {
|
||||||
|
|
Loading…
Reference in a new issue