5c5cf36b0d
* Allow for comment deletion Receive and emit deletion activity Add button to delete comment * Remove debug print and fix copy-past typo * Improve style of comment deletion button
192 lines
7.3 KiB
Rust
192 lines
7.3 KiB
Rust
use activitypub::{
|
|
activity::{
|
|
Announce,
|
|
Create,
|
|
Delete,
|
|
Follow as FollowAct,
|
|
Like,
|
|
Undo,
|
|
Update
|
|
},
|
|
object::Tombstone
|
|
};
|
|
use failure::Error;
|
|
use rocket::{
|
|
data::*,
|
|
http::Status,
|
|
Outcome::{self, *},
|
|
Request,
|
|
};
|
|
use rocket_contrib::json::*;
|
|
use serde::Deserialize;
|
|
use serde_json;
|
|
|
|
use std::io::Read;
|
|
|
|
use plume_common::activity_pub::{
|
|
inbox::{Deletable, FromActivity, InboxError, Notify},
|
|
Id,request::Digest,
|
|
};
|
|
use plume_models::{
|
|
comments::Comment, follows::Follow, instance::Instance, likes, posts::Post, reshares::Reshare,
|
|
users::User, search::Searcher, Connection,
|
|
};
|
|
|
|
pub trait Inbox {
|
|
fn received(&self, conn: &Connection, searcher: &Searcher, act: serde_json::Value) -> Result<(), Error> {
|
|
let actor_id = Id::new(act["actor"].as_str().unwrap_or_else(|| {
|
|
act["actor"]["id"]
|
|
.as_str()
|
|
.expect("Inbox::received: actor_id missing error")
|
|
}));
|
|
match act["type"].as_str() {
|
|
Some(t) => match t {
|
|
"Announce" => {
|
|
Reshare::from_activity(conn, serde_json::from_value(act.clone())?, actor_id);
|
|
Ok(())
|
|
}
|
|
"Create" => {
|
|
let act: Create = serde_json::from_value(act.clone())?;
|
|
if Post::try_from_activity(&(conn, searcher), act.clone())
|
|
|| Comment::try_from_activity(conn, act)
|
|
{
|
|
Ok(())
|
|
} else {
|
|
Err(InboxError::InvalidType)?
|
|
}
|
|
}
|
|
"Delete" => {
|
|
let act: Delete = serde_json::from_value(act.clone())?;
|
|
Post::delete_id(
|
|
&act.delete_props
|
|
.object_object::<Tombstone>()?
|
|
.object_props
|
|
.id_string()?,
|
|
actor_id.as_ref(),
|
|
&(conn, searcher),
|
|
);
|
|
Comment::delete_id(
|
|
&act.delete_props
|
|
.object_object::<Tombstone>()?
|
|
.object_props
|
|
.id_string()?,
|
|
actor_id.as_ref(),
|
|
conn,
|
|
);
|
|
Ok(())
|
|
}
|
|
"Follow" => {
|
|
Follow::from_activity(conn, serde_json::from_value(act.clone())?, actor_id).notify(conn);
|
|
Ok(())
|
|
}
|
|
"Like" => {
|
|
likes::Like::from_activity(
|
|
conn,
|
|
serde_json::from_value(act.clone())?,
|
|
actor_id,
|
|
);
|
|
Ok(())
|
|
}
|
|
"Undo" => {
|
|
let act: Undo = serde_json::from_value(act.clone())?;
|
|
if let Some(t) = act.undo_props.object["type"].as_str() {
|
|
match t {
|
|
"Like" => {
|
|
likes::Like::delete_id(
|
|
&act.undo_props
|
|
.object_object::<Like>()?
|
|
.object_props
|
|
.id_string()?,
|
|
actor_id.as_ref(),
|
|
conn,
|
|
);
|
|
Ok(())
|
|
}
|
|
"Announce" => {
|
|
Reshare::delete_id(
|
|
&act.undo_props
|
|
.object_object::<Announce>()?
|
|
.object_props
|
|
.id_string()?,
|
|
actor_id.as_ref(),
|
|
conn,
|
|
);
|
|
Ok(())
|
|
}
|
|
"Follow" => {
|
|
Follow::delete_id(
|
|
&act.undo_props
|
|
.object_object::<FollowAct>()?
|
|
.object_props
|
|
.id_string()?,
|
|
actor_id.as_ref(),
|
|
conn,
|
|
);
|
|
Ok(())
|
|
}
|
|
_ => Err(InboxError::CantUndo)?,
|
|
}
|
|
} else {
|
|
let link = act.undo_props.object.as_str().expect("Inbox::received: undo don't contain type and isn't Link");
|
|
if let Some(like) = likes::Like::find_by_ap_url(conn, link) {
|
|
likes::Like::delete_id(&like.ap_url, actor_id.as_ref(), conn);
|
|
Ok(())
|
|
} else if let Some(reshare) = Reshare::find_by_ap_url(conn, link) {
|
|
Reshare::delete_id(&reshare.ap_url, actor_id.as_ref(), conn);
|
|
Ok(())
|
|
} else if let Some(follow) = Follow::find_by_ap_url(conn, link) {
|
|
Follow::delete_id(&follow.ap_url, actor_id.as_ref(), conn);
|
|
Ok(())
|
|
} else {
|
|
Err(InboxError::NoType)?
|
|
}
|
|
}
|
|
}
|
|
"Update" => {
|
|
let act: Update = serde_json::from_value(act.clone())?;
|
|
Post::handle_update(conn, &act.update_props.object_object()?, searcher);
|
|
Ok(())
|
|
}
|
|
_ => Err(InboxError::InvalidType)?,
|
|
},
|
|
None => Err(InboxError::NoType)?,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Inbox for Instance {}
|
|
impl Inbox for User {}
|
|
|
|
const JSON_LIMIT: u64 = 1 << 20;
|
|
|
|
pub struct SignedJson<T>(pub Digest, pub Json<T>);
|
|
|
|
impl<'a, T: Deserialize<'a>> FromData<'a> for SignedJson<T> {
|
|
type Error = JsonError<'a>;
|
|
type Owned = String;
|
|
type Borrowed = str;
|
|
|
|
fn transform(r: &Request, d: Data) -> Transform<Outcome<Self::Owned, (Status, Self::Error), Data>> {
|
|
let size_limit = r.limits().get("json").unwrap_or(JSON_LIMIT);
|
|
let mut s = String::with_capacity(512);
|
|
match d.open().take(size_limit).read_to_string(&mut s) {
|
|
Ok(_) => Transform::Borrowed(Success(s)),
|
|
Err(e) => Transform::Borrowed(Failure((Status::BadRequest, JsonError::Io(e))))
|
|
}
|
|
}
|
|
|
|
fn from_data(_: &Request, o: Transformed<'a, Self>) -> Outcome<Self, (Status, Self::Error), Data> {
|
|
let string = o.borrowed()?;
|
|
match serde_json::from_str(&string) {
|
|
Ok(v) => Success(SignedJson(Digest::from_body(&string),Json(v))),
|
|
Err(e) => {
|
|
if e.is_data() {
|
|
Failure((Status::UnprocessableEntity, JsonError::Parse(string, e)))
|
|
} else {
|
|
Failure((Status::BadRequest, JsonError::Parse(string, e)))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|