Start refactoring activities
This commit is contained in:
parent
58fad0d414
commit
afe98ab1c3
@ -1,69 +1,150 @@
|
|||||||
use chrono;
|
use chrono;
|
||||||
use diesel::PgConnection;
|
use diesel::PgConnection;
|
||||||
use serde_json;
|
use serde_json;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
use activity_pub::actor::Actor;
|
use activity_pub::actor::Actor;
|
||||||
use activity_pub::object::Object;
|
use activity_pub::object::Object;
|
||||||
|
|
||||||
#[derive(Clone)]
|
pub trait Activity: ActivityClone {
|
||||||
pub enum Activity {
|
fn get_id(&self) -> String;
|
||||||
Create(Payload),
|
|
||||||
Accept(Payload),
|
fn serialize(&self) -> serde_json::Value;
|
||||||
Follow(Payload)
|
|
||||||
}
|
// fn deserialize(serde_json::Value) -> Self;
|
||||||
impl Activity {
|
|
||||||
pub fn serialize(&self) -> serde_json::Value {
|
|
||||||
json!({
|
|
||||||
"type": self.get_type(),
|
|
||||||
"actor": self.payload().by,
|
|
||||||
"object": self.payload().object,
|
|
||||||
"published": self.payload().date.to_rfc3339()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_type(&self) -> String {
|
trait ActivityClone {
|
||||||
match self {
|
fn clone_box(&self) -> Box<Activity>;
|
||||||
Activity::Accept(_) => String::from("Accept"),
|
}
|
||||||
Activity::Create(_) => String::from("Create"),
|
|
||||||
Activity::Follow(_) => String::from("Follow")
|
impl<T> ActivityClone for T
|
||||||
|
where
|
||||||
|
T: 'static + Activity + Clone,
|
||||||
|
{
|
||||||
|
fn clone_box(&self) -> Box<Activity> {
|
||||||
|
Box::new(self.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn payload(&self) -> Payload {
|
// We can now implement Clone manually by forwarding to clone_box.
|
||||||
match self {
|
impl Clone for Box<Activity> {
|
||||||
Activity::Accept(p) => p.clone(),
|
fn clone(&self) -> Box<Activity> {
|
||||||
Activity::Create(p) => p.clone(),
|
self.clone_box()
|
||||||
Activity::Follow(p) => p.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create<T: Object, U: Actor>(by: &U, obj: T, conn: &PgConnection) -> Activity {
|
|
||||||
Activity::Create(Payload::new(serde_json::Value::String(by.compute_id(conn)), obj.serialize(conn)))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn accept<A: Actor>(by: &A, what: String, conn: &PgConnection) -> Activity {
|
|
||||||
Activity::Accept(Payload::new(serde_json::Value::String(by.compute_id(conn)), serde_json::Value::String(what)))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn follow<A: Actor, B: Actor>(by: &A, obj: &B, conn: &PgConnection) -> Activity {
|
|
||||||
Activity::Follow(Payload::new(serde_json::Value::String(by.compute_id(conn)), serde_json::Value::String(obj.compute_id(conn))))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Payload {
|
pub struct Accept {
|
||||||
by: serde_json::Value,
|
id: String,
|
||||||
|
actor: serde_json::Value,
|
||||||
object: serde_json::Value,
|
object: serde_json::Value,
|
||||||
date: chrono::DateTime<chrono::Utc>
|
date: chrono::DateTime<chrono::Utc>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Payload {
|
impl Accept {
|
||||||
pub fn new(by: serde_json::Value, obj: serde_json::Value) -> Payload {
|
pub fn new<A: Activity, B: Actor>(who: &B, what: &A, conn: &PgConnection) -> Accept {
|
||||||
Payload {
|
Accept {
|
||||||
by: by,
|
id: "TODO".to_string(),
|
||||||
object: obj,
|
actor: serde_json::Value::String(who.compute_id(conn)),
|
||||||
|
object: serde_json::Value::String(what.get_id()),
|
||||||
date: chrono::Utc::now()
|
date: chrono::Utc::now()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Activity for Accept {
|
||||||
|
fn get_id(&self) -> String {
|
||||||
|
self.id.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize(&self) -> serde_json::Value {
|
||||||
|
json!({
|
||||||
|
"type": "Accept",
|
||||||
|
"actor": self.actor,
|
||||||
|
"object": self.object,
|
||||||
|
"published": self.date.to_rfc3339()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Create {
|
||||||
|
id: String,
|
||||||
|
actor: serde_json::Value,
|
||||||
|
object: serde_json::Value,
|
||||||
|
date: chrono::DateTime<chrono::Utc>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Create {
|
||||||
|
pub fn new<A: Actor, B: Object>(actor: &A, obj: &B, conn: &PgConnection) -> Create {
|
||||||
|
Create {
|
||||||
|
id: "TODO".to_string(),
|
||||||
|
actor: serde_json::Value::String(actor.compute_id(conn)),
|
||||||
|
object: obj.serialize(conn),
|
||||||
|
date: chrono::Utc::now()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Activity for Create {
|
||||||
|
fn get_id(&self) -> String {
|
||||||
|
self.id.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize(&self) -> serde_json::Value {
|
||||||
|
json!({
|
||||||
|
"type": "Create",
|
||||||
|
"actor": self.actor,
|
||||||
|
"object": self.object,
|
||||||
|
"published": self.date.to_rfc3339()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Follow {
|
||||||
|
id: String,
|
||||||
|
actor: serde_json::Value,
|
||||||
|
object: serde_json::Value,
|
||||||
|
date: chrono::DateTime<chrono::Utc>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Follow {
|
||||||
|
pub fn new<A: Actor, B: Actor>(follower: &A, following: &B, conn: &PgConnection) -> Follow {
|
||||||
|
Follow {
|
||||||
|
id: "TODO".to_string(),
|
||||||
|
actor: serde_json::Value::String(follower.compute_id(conn)),
|
||||||
|
object: serde_json::Value::String(following.compute_id(conn)),
|
||||||
|
date: chrono::Utc::now()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize(json: serde_json::Value) -> Follow {
|
||||||
|
Follow {
|
||||||
|
id: json["id"].as_str().unwrap().to_string(),
|
||||||
|
actor: json["actor"].clone(),
|
||||||
|
object: json["object"].clone(),
|
||||||
|
date: chrono::DateTime::from_str(json["published"].as_str().unwrap()).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_target_id(&self) -> String {
|
||||||
|
self.object.as_str().unwrap().to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Activity for Follow {
|
||||||
|
fn get_id(&self) -> String {
|
||||||
|
self.id.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize(&self) -> serde_json::Value {
|
||||||
|
json!({
|
||||||
|
"type": "Follow",
|
||||||
|
"actor": self.actor,
|
||||||
|
"object": self.object,
|
||||||
|
"published": self.date.to_rfc3339()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -67,7 +67,7 @@ pub trait Actor: Sized {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_to_inbox(&self, conn: &PgConnection, act: Activity) {
|
fn send_to_inbox<A: Activity>(&self, conn: &PgConnection, act: A) {
|
||||||
let res = Client::new()
|
let res = Client::new()
|
||||||
.post(&self.compute_inbox(conn)[..])
|
.post(&self.compute_inbox(conn)[..])
|
||||||
.body(act.serialize().to_string())
|
.body(act.serialize().to_string())
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use diesel::PgConnection;
|
use diesel::PgConnection;
|
||||||
use serde_json;
|
use serde_json;
|
||||||
|
|
||||||
use activity_pub::activity::Activity;
|
use activity_pub::activity;
|
||||||
use activity_pub::actor::Actor;
|
use activity_pub::actor::Actor;
|
||||||
use models::blogs::Blog;
|
use models::blogs::Blog;
|
||||||
use models::follows::{Follow, NewFollow};
|
use models::follows::{Follow, NewFollow};
|
||||||
@ -29,13 +29,13 @@ pub trait Inbox: Actor + Sized {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Follow" => {
|
"Follow" => {
|
||||||
let follow_id = act["object"].as_str().unwrap().to_string();
|
let follow_act = activity::Follow::deserialize(act.clone());
|
||||||
let from = User::from_url(conn, act["actor"].as_str().unwrap().to_string()).unwrap();
|
let from = User::from_url(conn, act["actor"].as_str().unwrap().to_string()).unwrap();
|
||||||
match User::from_url(conn, act["object"].as_str().unwrap().to_string()) {
|
match User::from_url(conn, act["object"].as_str().unwrap().to_string()) {
|
||||||
Some(u) => self.accept_follow(conn, &from, &u, follow_id, from.id, u.id),
|
Some(u) => self.accept_follow(conn, &from, &u, &follow_act, from.id, u.id),
|
||||||
None => {
|
None => {
|
||||||
let blog = Blog::from_url(conn, follow_id.clone()).unwrap();
|
let blog = Blog::from_url(conn, follow_act.get_target_id()).unwrap();
|
||||||
self.accept_follow(conn, &from, &blog, follow_id, from.id, blog.id)
|
self.accept_follow(conn, &from, &blog, &follow_act, from.id, blog.id)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -45,13 +45,13 @@ pub trait Inbox: Actor + Sized {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn accept_follow<A: Actor, B: Actor>(&self, conn: &PgConnection, from: &A, target: &B, follow_id: String, from_id: i32, target_id: i32) {
|
fn accept_follow<A: Actor, B: Actor, T: activity::Activity>(&self, conn: &PgConnection, from: &A, target: &B, follow: &T, from_id: i32, target_id: i32) {
|
||||||
Follow::insert(conn, NewFollow {
|
Follow::insert(conn, NewFollow {
|
||||||
follower_id: from_id,
|
follower_id: from_id,
|
||||||
following_id: target_id
|
following_id: target_id
|
||||||
});
|
});
|
||||||
|
|
||||||
let accept = Activity::accept(target, follow_id, conn);
|
let accept = activity::Accept::new(target, follow, conn);
|
||||||
from.send_to_inbox(conn, accept)
|
from.send_to_inbox(conn, accept)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,13 +9,13 @@ use activity_pub::activity::Activity;
|
|||||||
use activity_pub::actor::Actor;
|
use activity_pub::actor::Actor;
|
||||||
use models::users::User;
|
use models::users::User;
|
||||||
|
|
||||||
pub struct Outbox {
|
pub struct Outbox<A> where A: Activity + Clone {
|
||||||
id: String,
|
id: String,
|
||||||
items: Vec<Activity>
|
items: Vec<Box<A>>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Outbox {
|
impl<A: Activity + Clone + 'static> Outbox<A> {
|
||||||
pub fn new(id: String, items: Vec<Activity>) -> Outbox {
|
pub fn new(id: String, items: Vec<Box<A>>) -> Outbox<A> {
|
||||||
Outbox {
|
Outbox {
|
||||||
id: id,
|
id: id,
|
||||||
items: items
|
items: items
|
||||||
@ -23,24 +23,24 @@ impl Outbox {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn serialize(&self) -> ActivityPub {
|
fn serialize(&self) -> ActivityPub {
|
||||||
let items = self.items.clone();
|
let items = self.items.clone().into_iter().map(|i| i.serialize()).collect::<Vec<serde_json::Value>>();
|
||||||
activity_pub(json!({
|
activity_pub(json!({
|
||||||
"@context": context(),
|
"@context": context(),
|
||||||
"type": "OrderedCollection",
|
"type": "OrderedCollection",
|
||||||
"id": self.id,
|
"id": self.id,
|
||||||
"totalItems": items.len(),
|
"totalItems": items.len(),
|
||||||
"orderedItems": items.into_iter().map(|i| i.serialize()).collect::<Vec<serde_json::Value>>()
|
"orderedItems": items
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r> Responder<'r> for Outbox {
|
impl<'r, A: Activity + Clone + 'static> Responder<'r> for Outbox<A> {
|
||||||
fn respond_to(self, request: &Request) -> Result<Response<'r>, Status> {
|
fn respond_to(self, request: &Request) -> Result<Response<'r>, Status> {
|
||||||
self.serialize().respond_to(request)
|
self.serialize().respond_to(request)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn broadcast(conn: &PgConnection, act: Activity, to: Vec<User>) {
|
pub fn broadcast<A: Activity + Clone>(conn: &PgConnection, act: A, to: Vec<User>) {
|
||||||
for user in to {
|
for user in to {
|
||||||
user.send_to_inbox(conn, act.clone()); // TODO: run it in Sidekiq or something like that
|
user.send_to_inbox(conn, act.clone()); // TODO: run it in Sidekiq or something like that
|
||||||
}
|
}
|
||||||
|
@ -78,11 +78,11 @@ impl Blog {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn outbox(&self, conn: &PgConnection) -> Outbox {
|
pub fn outbox<A: Activity + Clone + 'static>(&self, conn: &PgConnection) -> Outbox<A> {
|
||||||
Outbox::new(self.compute_outbox(conn), self.get_activities(conn))
|
Outbox::new(self.compute_outbox(conn), self.get_activities(conn))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_activities(&self, _conn: &PgConnection) -> Vec<Activity> {
|
fn get_activities<A: Activity + Clone>(&self, _conn: &PgConnection) -> Vec<Box<A>> {
|
||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ use serde_json;
|
|||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use BASE_URL;
|
use BASE_URL;
|
||||||
use activity_pub::activity::Activity;
|
use activity_pub::activity::{Create, Activity};
|
||||||
use activity_pub::actor::{ActorType, Actor};
|
use activity_pub::actor::{ActorType, Actor};
|
||||||
use activity_pub::inbox::Inbox;
|
use activity_pub::inbox::Inbox;
|
||||||
use activity_pub::outbox::Outbox;
|
use activity_pub::outbox::Outbox;
|
||||||
@ -184,16 +184,16 @@ impl User {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn outbox(&self, conn: &PgConnection) -> Outbox {
|
pub fn outbox<A: Activity + Clone + 'static>(&self, conn: &PgConnection) -> Outbox<A> {
|
||||||
Outbox::new(self.compute_outbox(conn), self.get_activities(conn))
|
Outbox::new(self.compute_outbox(conn), self.get_activities(conn))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_activities(&self, conn: &PgConnection) -> Vec<Activity> {
|
fn get_activities<A: Activity>(&self, conn: &PgConnection) -> Vec<Box<A>> {
|
||||||
use schema::posts;
|
use schema::posts;
|
||||||
use schema::post_authors;
|
use schema::post_authors;
|
||||||
let posts_by_self = PostAuthor::belonging_to(self).select(post_authors::post_id);
|
let posts_by_self = PostAuthor::belonging_to(self).select(post_authors::post_id);
|
||||||
let posts = posts::table.filter(posts::id.eq(any(posts_by_self))).load::<Post>(conn).unwrap();
|
let posts = posts::table.filter(posts::id.eq(any(posts_by_self))).load::<Post>(conn).unwrap();
|
||||||
posts.into_iter().map(|p| Activity::create(self, p, conn)).collect::<Vec<Activity>>()
|
posts.into_iter().map(|p| Box::new(Create::new(self, &p, conn)) as Box<A>).collect::<Vec<Box<A>>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_followers(&self, conn: &PgConnection) -> Vec<User> {
|
pub fn get_followers(&self, conn: &PgConnection) -> Vec<User> {
|
||||||
|
@ -4,6 +4,7 @@ use rocket_contrib::Template;
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use activity_pub::ActivityPub;
|
use activity_pub::ActivityPub;
|
||||||
|
use activity_pub::activity::Activity;
|
||||||
use activity_pub::actor::Actor;
|
use activity_pub::actor::Actor;
|
||||||
use activity_pub::outbox::Outbox;
|
use activity_pub::outbox::Outbox;
|
||||||
use db_conn::DbConn;
|
use db_conn::DbConn;
|
||||||
@ -57,7 +58,7 @@ fn create(conn: DbConn, data: Form<NewBlogForm>, user: User) -> Redirect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[get("/~/<name>/outbox")]
|
#[get("/~/<name>/outbox")]
|
||||||
fn outbox(name: String, conn: DbConn) -> Outbox {
|
fn outbox<A: Activity + Clone + 'static>(name: String, conn: DbConn) -> Outbox<A> {
|
||||||
let blog = Blog::find_by_actor_id(&*conn, name).unwrap();
|
let blog = Blog::find_by_actor_id(&*conn, name).unwrap();
|
||||||
blog.outbox(&*conn)
|
blog.outbox(&*conn)
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ use rocket::response::Redirect;
|
|||||||
use rocket_contrib::Template;
|
use rocket_contrib::Template;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use activity_pub::activity::Activity;
|
use activity_pub::activity::Create;
|
||||||
use activity_pub::outbox::broadcast;
|
use activity_pub::outbox::broadcast;
|
||||||
use db_conn::DbConn;
|
use db_conn::DbConn;
|
||||||
use models::blogs::*;
|
use models::blogs::*;
|
||||||
@ -55,7 +55,7 @@ fn create(blog_name: String, data: Form<NewPostForm>, user: User, conn: DbConn)
|
|||||||
author_id: user.id
|
author_id: user.id
|
||||||
});
|
});
|
||||||
|
|
||||||
let act = Activity::create(&user, post, &*conn);
|
let act = Create::new(&user, &post, &*conn);
|
||||||
broadcast(&*conn, act, user.get_followers(&*conn));
|
broadcast(&*conn, act, user.get_followers(&*conn));
|
||||||
|
|
||||||
Redirect::to(format!("/~/{}/{}", blog_name, slug).as_str())
|
Redirect::to(format!("/~/{}/{}", blog_name, slug).as_str())
|
||||||
|
@ -5,7 +5,7 @@ use serde_json;
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use activity_pub::ActivityPub;
|
use activity_pub::ActivityPub;
|
||||||
use activity_pub::activity::Activity;
|
use activity_pub::activity;
|
||||||
use activity_pub::actor::Actor;
|
use activity_pub::actor::Actor;
|
||||||
use activity_pub::inbox::Inbox;
|
use activity_pub::inbox::Inbox;
|
||||||
use activity_pub::outbox::Outbox;
|
use activity_pub::outbox::Outbox;
|
||||||
@ -34,7 +34,7 @@ fn follow(name: String, conn: DbConn, user: User) -> Redirect {
|
|||||||
follower_id: user.id,
|
follower_id: user.id,
|
||||||
following_id: target.id
|
following_id: target.id
|
||||||
});
|
});
|
||||||
target.send_to_inbox(&*conn, Activity::follow(&user, &target, &*conn));
|
target.send_to_inbox(&*conn, activity::Follow::new(&user, &target, &*conn));
|
||||||
Redirect::to(format!("/@/{}", name).as_ref())
|
Redirect::to(format!("/@/{}", name).as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user