From 38d99ad5aff4fbe2b2160ba267ff10bb6d6ec5a2 Mon Sep 17 00:00:00 2001 From: Bat Date: Fri, 27 Jul 2018 12:53:21 +0200 Subject: [PATCH] Try to fetch followers --- .../down.sql | 2 + .../up.sql | 2 + plume-models/src/schema.rs | 1 + plume-models/src/users.rs | 44 ++++++++++++++++--- src/routes/user.rs | 21 +++++++-- 5 files changed, 60 insertions(+), 10 deletions(-) create mode 100644 migrations/2018-07-27-102221_user_add_followers_endpoint/down.sql create mode 100644 migrations/2018-07-27-102221_user_add_followers_endpoint/up.sql diff --git a/migrations/2018-07-27-102221_user_add_followers_endpoint/down.sql b/migrations/2018-07-27-102221_user_add_followers_endpoint/down.sql new file mode 100644 index 00000000..3beaffac --- /dev/null +++ b/migrations/2018-07-27-102221_user_add_followers_endpoint/down.sql @@ -0,0 +1,2 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE users DROP COLUMN followers_endpoint; diff --git a/migrations/2018-07-27-102221_user_add_followers_endpoint/up.sql b/migrations/2018-07-27-102221_user_add_followers_endpoint/up.sql new file mode 100644 index 00000000..f565e8ed --- /dev/null +++ b/migrations/2018-07-27-102221_user_add_followers_endpoint/up.sql @@ -0,0 +1,2 @@ +-- Your SQL goes here +ALTER TABLE users ADD COLUMN followers_endpoint VARCHAR NOT NULL DEFAULT ''; diff --git a/plume-models/src/schema.rs b/plume-models/src/schema.rs index c19136d0..fce52e1e 100644 --- a/plume-models/src/schema.rs +++ b/plume-models/src/schema.rs @@ -135,6 +135,7 @@ table! { private_key -> Nullable, public_key -> Text, shared_inbox_url -> Nullable, + followers_endpoint -> Varchar, } } diff --git a/plume-models/src/users.rs b/plume-models/src/users.rs index 63a8b26b..332d7580 100644 --- a/plume-models/src/users.rs +++ b/plume-models/src/users.rs @@ -63,7 +63,8 @@ pub struct User { pub ap_url: String, pub private_key: Option, pub public_key: String, - pub shared_inbox_url: Option + pub shared_inbox_url: Option, + pub followers_endpoint: String } #[derive(Insertable)] @@ -81,7 +82,8 @@ pub struct NewUser { pub ap_url: String, pub private_key: Option, pub public_key: String, - pub shared_inbox_url: Option + pub shared_inbox_url: Option, + pub followers_endpoint: String } const USER_PREFIX: &'static str = "@"; @@ -152,7 +154,7 @@ impl User { } } - fn fetch_from_url(conn: &PgConnection, url: String) -> Option { + pub fn fetch_from_url(conn: &PgConnection, url: String) -> Option { let req = Client::new() .get(&url[..]) .header(Accept(ap_accept_header().into_iter().map(|h| qitem(h.parse::().expect("Invalid Content-Type"))).collect())) @@ -186,7 +188,6 @@ impl User { }) } }; - println!("User from act : {:?}", acct.custom_props); User::insert(conn, NewUser { username: acct.object.ap_actor_props.preferred_username_string().expect("User::from_activity: preferredUsername error"), display_name: acct.object.object_props.name_string().expect("User::from_activity: name error"), @@ -202,7 +203,8 @@ impl User { .public_key_pem_string().expect("User::from_activity: publicKey.publicKeyPem error"), private_key: None, shared_inbox_url: acct.object.ap_actor_props.endpoints_endpoint() - .and_then(|e| e.shared_inbox_string()).ok() + .and_then(|e| e.shared_inbox_string()).ok(), + followers_endpoint: acct.object.ap_actor_props.followers_string().expect("User::from_activity: followers error") }) } @@ -243,6 +245,12 @@ impl User { .set(users::shared_inbox_url.eq(ap_url(format!("{}/inbox", Instance::get_local(conn).unwrap().public_domain)))) .get_result::(conn).expect("Couldn't update shared inbox URL"); } + + if self.followers_endpoint.len() == 0 { + diesel::update(self) + .set(users::followers_endpoint.eq(instance.compute_box(USER_PREFIX, self.username.clone(), "followers"))) + .get_result::(conn).expect("Couldn't update followers endpoint"); + } } pub fn outbox(&self, conn: &PgConnection) -> ActivityStream { @@ -276,6 +284,28 @@ impl User { } } + pub fn fetch_followers_ids(&self) -> Vec { + let req = Client::new() + .get(&self.followers_endpoint[..]) + .header(Accept(ap_accept_header().into_iter().map(|h| qitem(h.parse::().expect("Invalid Content-Type"))).collect())) + .send(); + match req { + Ok(mut res) => { + let text = &res.text().unwrap(); + let json: serde_json::Value = serde_json::from_str(text).unwrap(); + json["items"].as_array() + .expect("Followers.items is not an array") + .into_iter() + .filter_map(|j| serde_json::from_value(j.clone()).ok()) + .collect::>() + }, + Err(e) => { + println!("User followers fetch error: {:?}", e); + vec![] + } + } + } + fn get_activities(&self, conn: &PgConnection) -> Vec { use schema::posts; use schema::post_authors; @@ -377,6 +407,7 @@ impl User { actor.ap_actor_props.set_inbox_string(self.inbox_url.clone()).expect("User::into_activity: inbox error"); actor.ap_actor_props.set_outbox_string(self.outbox_url.clone()).expect("User::into_activity: outbox error"); actor.ap_actor_props.set_preferred_username_string(self.username.clone()).expect("User::into_activity: preferredUsername error"); + actor.ap_actor_props.set_followers_string(self.followers_endpoint.clone()).expect("User::into_activity: followers error"); let mut endpoints = Endpoint::default(); endpoints.set_shared_inbox_string(ap_url(format!("{}/inbox/", BASE_URL.as_str()))).expect("User::into_activity: endpoints.sharedInbox error"); @@ -517,7 +548,8 @@ impl NewUser { ap_url: String::from(""), public_key: String::from_utf8(pub_key).unwrap(), private_key: Some(String::from_utf8(priv_key).unwrap()), - shared_inbox_url: None + shared_inbox_url: None, + followers_endpoint: String::from("") }) } } diff --git a/src/routes/user.rs b/src/routes/user.rs index e72b6725..2688b38b 100644 --- a/src/routes/user.rs +++ b/src/routes/user.rs @@ -39,27 +39,40 @@ fn me(user: Option) -> Result> { } #[get("/@/", rank = 2)] -fn details<'r>(name: String, conn: DbConn, account: Option, worker: State>>, other_conn: DbConn) -> Template { +fn details<'r>(name: String, conn: DbConn, account: Option, worker: State>>, fecth_articles_conn: DbConn, fecth_followers_conn: DbConn) -> Template { may_fail!(account, User::find_by_fqn(&*conn, name), "Couldn't find requested user", |user| { let recents = Post::get_recents_for_author(&*conn, &user, 6); let reshares = Reshare::get_recents_for_author(&*conn, &user, 6); let user_id = user.id.clone(); let n_followers = user.get_followers(&*conn).len(); - // Fetch new articles if !user.get_instance(&*conn).local { + // Fetch new articles let user_clone = user.clone(); worker.execute(Thunk::of(move || { for create_act in user_clone.fetch_outbox::() { match create_act.create_props.object_object::
() { Ok(article) => { - Post::from_activity(&*other_conn, article, user_clone.clone().into_id()); + Post::from_activity(&*fecth_articles_conn, article, user_clone.clone().into_id()); println!("Fetched article from remote user"); } Err(e) => println!("Error while fetching articles in background: {:?}", e) } } })); + + // Fetch followers + let user_clone = user.clone(); + worker.execute(Thunk::of(move || { + for user_id in user_clone.fetch_followers_ids() { + let follower = User::find_by_ap_url(&*fecth_followers_conn, user_id.clone()) + .unwrap_or_else(|| User::fetch_from_url(&*fecth_followers_conn, user_id).expect("Couldn't fetch follower")); + follows::Follow::insert(&*fecth_followers_conn, follows::NewFollow { + follower_id: follower.id, + following_id: user_clone.id + }); + } + })); } Template::render("users/details", json!({ @@ -258,7 +271,7 @@ fn ap_followers(name: String, conn: DbConn, _ap: ApRequest) -> ActivityStream>(); let mut coll = OrderedCollection::default(); - coll.object_props.set_id_string(format!("{}/followers", user.ap_url)).expect("Follower collection: id error"); + coll.object_props.set_id_string(user.followers_endpoint).expect("Follower collection: id error"); coll.collection_props.set_total_items_u64(followers.len() as u64).expect("Follower collection: totalItems error"); coll.collection_props.set_items_link_vec(followers).expect("Follower collection: items error"); ActivityStream::new(coll)