diff --git a/migrations/2018-05-12-213456_likes_add_ap_url/down.sql b/migrations/2018-05-12-213456_likes_add_ap_url/down.sql new file mode 100644 index 00000000..b68f1950 --- /dev/null +++ b/migrations/2018-05-12-213456_likes_add_ap_url/down.sql @@ -0,0 +1,2 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE likes DROP COLUMN ap_url; diff --git a/migrations/2018-05-12-213456_likes_add_ap_url/up.sql b/migrations/2018-05-12-213456_likes_add_ap_url/up.sql new file mode 100644 index 00000000..42b4e967 --- /dev/null +++ b/migrations/2018-05-12-213456_likes_add_ap_url/up.sql @@ -0,0 +1,2 @@ +-- Your SQL goes here +ALTER TABLE likes ADD COLUMN ap_url VARCHAR NOT NULL default ''; diff --git a/src/activity_pub/activity.rs b/src/activity_pub/activity.rs index 50e38e01..3e1954ad 100644 --- a/src/activity_pub/activity.rs +++ b/src/activity_pub/activity.rs @@ -210,12 +210,48 @@ impl Activity for Like { } fn get_type(&self) -> String { - "Follow".to_string() + "Like".to_string() } fn serialize(&self) -> serde_json::Value { json!({ - "type": "Follow", + "type": "Like", + "id": self.id, + "actor": self.actor, + "object": self.object + }) + } +} + +#[derive(Clone)] +pub struct Undo { + id: String, + actor: serde_json::Value, + object: serde_json::Value +} + +impl Undo { + pub fn new(actor: &A, obj: &B, conn: &PgConnection) -> Undo { + Undo { + id: format!("{}/undo", obj.compute_id(conn)), + actor: serde_json::Value::String(actor.compute_id(conn)), + object: obj.serialize(conn) + } + } +} + +impl Activity for Undo { + fn get_id(&self) -> String { + self.id.clone() + } + + fn get_type(&self) -> String { + "Undo".to_string() + } + + fn serialize(&self) -> serde_json::Value { + json!({ + "type": "Undo", "id": self.id, "actor": self.actor, "object": self.object diff --git a/src/activity_pub/inbox.rs b/src/activity_pub/inbox.rs index 377d6b69..9e6586d0 100644 --- a/src/activity_pub/inbox.rs +++ b/src/activity_pub/inbox.rs @@ -64,9 +64,19 @@ pub trait Inbox: Actor + Sized { let post = Post::get_by_ap_url(conn, act["object"].as_str().unwrap().to_string()); Like::insert(conn, NewLike { post_id: post.unwrap().id, - user_id: liker.unwrap().id + user_id: liker.unwrap().id, + ap_url: act["id"].as_str().unwrap().to_string() }); }, + "Undo" => { + match act["object"]["type"].as_str().unwrap() { + "Like" => { + let like = Like::find_by_ap_url(conn, act["object"]["id"].as_str().unwrap().to_string()).unwrap(); + like.delete(conn); + } + x => println!("Wanted to Undo a {}, but it is not supported yet", x) + } + }, x => println!("Received unknow activity type: {}", x) } } diff --git a/src/models/likes.rs b/src/models/likes.rs index c648d443..f822d49b 100644 --- a/src/models/likes.rs +++ b/src/models/likes.rs @@ -13,14 +13,16 @@ pub struct Like { pub id: i32, pub user_id: i32, pub post_id: i32, - pub creation_date: chrono::NaiveDateTime + pub creation_date: chrono::NaiveDateTime, + pub ap_url: String } #[derive(Insertable)] #[table_name = "likes"] pub struct NewLike { pub user_id: i32, - pub post_id: i32 + pub post_id: i32, + pub ap_url: String } impl Like { @@ -31,6 +33,14 @@ impl Like { .expect("Unable to insert new like") } + pub fn update_ap_url(&self, conn: &PgConnection) { + if self.ap_url.len() == 0 { + diesel::update(self) + .set(likes::ap_url.eq(self.compute_id(conn))) + .get_result::(conn).expect("Couldn't update AP URL"); + } + } + pub fn get(conn: &PgConnection, id: i32) -> Option { likes::table.filter(likes::id.eq(id)) .limit(1) @@ -39,6 +49,14 @@ impl Like { .into_iter().nth(0) } + pub fn find_by_ap_url(conn: &PgConnection, ap_url: String) -> Option { + likes::table.filter(likes::ap_url.eq(ap_url)) + .limit(1) + .load::(conn) + .expect("Error loading like by AP URL") + .into_iter().nth(0) + } + pub fn for_user_on_post(conn: &PgConnection, user: &User, post: &Post) -> Option { likes::table.filter(likes::post_id.eq(post.id)) .filter(likes::user_id.eq(user.id)) diff --git a/src/routes/likes.rs b/src/routes/likes.rs index 599a0753..2ccee271 100644 --- a/src/routes/likes.rs +++ b/src/routes/likes.rs @@ -1,6 +1,6 @@ use rocket::response::Redirect; -use activity_pub::activity::{Like, Delete}; +use activity_pub::activity::{Like, Undo}; use activity_pub::outbox::broadcast; use db_conn::DbConn; use models::likes; @@ -12,17 +12,19 @@ fn create(blog: String, slug: String, user: User, conn: DbConn) -> Redirect { let post = Post::find_by_slug(&*conn, slug.clone()).unwrap(); if !user.has_liked(&*conn, &post) { - likes::Like::insert(&*conn, likes::NewLike { + let like = likes::Like::insert(&*conn, likes::NewLike { post_id: post.id, - user_id: user.id + user_id: user.id, + ap_url: "".to_string() }); + like.update_ap_url(&*conn); let act = Like::new(&user, &post, &*conn); broadcast(&*conn, &user, act, user.get_followers(&*conn)); } else { let like = likes::Like::for_user_on_post(&*conn, &user, &post).unwrap(); like.delete(&*conn); - broadcast(&*conn, &user, Delete::new(&user, &like, &*conn), user.get_followers(&*conn)); + broadcast(&*conn, &user, Undo::new(&user, &like, &*conn), user.get_followers(&*conn)); } - + Redirect::to(format!("/~/{}/{}/", blog, slug).as_ref()) } diff --git a/src/schema.rs b/src/schema.rs index ad07691d..af20e6d5 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -62,6 +62,7 @@ table! { user_id -> Int4, post_id -> Int4, creation_date -> Timestamp, + ap_url -> Varchar, } }