]> Untitled Git - lemmy.git/commitdiff
Some fed fixes.
authorDessalines <tyhou13@gmx.com>
Fri, 24 Apr 2020 14:04:36 +0000 (10:04 -0400)
committerDessalines <tyhou13@gmx.com>
Fri, 24 Apr 2020 14:04:36 +0000 (10:04 -0400)
19 files changed:
server/src/api/comment.rs
server/src/api/community.rs
server/src/api/mod.rs
server/src/api/user.rs
server/src/apub/activities.rs
server/src/apub/community.rs
server/src/apub/community_inbox.rs
server/src/apub/fetcher.rs
server/src/apub/mod.rs
server/src/apub/post.rs
server/src/apub/signatures.rs
server/src/apub/user.rs
server/src/apub/user_inbox.rs
server/src/db/community.rs
server/src/db/user.rs
server/src/routes/feeds.rs
server/src/routes/nodeinfo.rs
server/src/routes/webfinger.rs
server/src/routes/websocket.rs

index 0c6d8e89fa66afc9a90fd0c6160f70a6989dc443..eb67d8f2043c88600b2539b092e1be8889985dfd 100644 (file)
@@ -122,7 +122,7 @@ impl Perform for Oper<CreateComment> {
     let extracted_usernames = extract_usernames(&comment_form.content);
 
     for username_mention in &extracted_usernames {
-      if let Ok(mention_user) = User_::read_from_name(&conn, (*username_mention).to_string()) {
+      if let Ok(mention_user) = User_::read_from_name(&conn, username_mention) {
         // You can't mention yourself
         // At some point, make it so you can't tag the parent creator either
         // This can cause two notifications, one for reply and the other for mention
@@ -334,7 +334,7 @@ impl Perform for Oper<EditComment> {
     let extracted_usernames = extract_usernames(&comment_form.content);
 
     for username_mention in &extracted_usernames {
-      let mention_user = User_::read_from_name(&conn, (*username_mention).to_string());
+      let mention_user = User_::read_from_name(&conn, username_mention);
 
       if mention_user.is_ok() {
         let mention_user_id = mention_user?.id;
index 01bfc40b0e10261e910ea9431df5af2b42a5080e..ace5b353e31a3b6ced07a3e185c5499bae428846 100644 (file)
@@ -139,7 +139,7 @@ impl Perform for Oper<GetCommunity> {
       None => {
         match Community::read_from_name(
           &conn,
-          data.name.to_owned().unwrap_or_else(|| "main".to_string()),
+          &data.name.to_owned().unwrap_or_else(|| "main".to_string()),
         ) {
           Ok(community) => community,
           Err(_e) => return Err(APIError::err("couldnt_find_community").into()),
index 8711e0b50a47810f1ad78cc64e5651cdb24edc03..04d6900143ba7dd35d5681c98f10d979bdba7417 100644 (file)
@@ -68,8 +68,8 @@ pub struct Oper<T> {
   data: T,
 }
 
-impl<T> Oper<T> {
-  pub fn new(data: T) -> Oper<T> {
+impl<Data> Oper<Data> {
+  pub fn new(data: Data) -> Oper<Data> {
     Oper { data }
   }
 }
index 0dfca417310871a578b597cbc020928aeee7b47a..ff2760a5cd5d86b73d00a68547af23361e838b36 100644 (file)
@@ -261,7 +261,7 @@ impl Perform for Oper<Register> {
       return Err(APIError::err("admin_already_created").into());
     }
 
-    let keypair = generate_actor_keypair()?;
+    let user_keypair = generate_actor_keypair()?;
 
     // Register the new user
     let user_form = UserForm {
@@ -284,8 +284,8 @@ impl Perform for Oper<Register> {
       actor_id: make_apub_endpoint(EndpointType::User, &data.username).to_string(),
       bio: None,
       local: true,
-      private_key: Some(keypair.private_key),
-      public_key: Some(keypair.public_key),
+      private_key: Some(user_keypair.private_key),
+      public_key: Some(user_keypair.public_key),
       last_refreshed_at: None,
     };
 
@@ -305,7 +305,7 @@ impl Perform for Oper<Register> {
       }
     };
 
-    let keypair = generate_actor_keypair()?;
+    let main_community_keypair = generate_actor_keypair()?;
 
     // Create the main community if it doesn't exist
     let main_community: Community = match Community::read(&conn, 2) {
@@ -324,8 +324,8 @@ impl Perform for Oper<Register> {
           updated: None,
           actor_id: make_apub_endpoint(EndpointType::Community, default_community_name).to_string(),
           local: true,
-          private_key: Some(keypair.private_key),
-          public_key: Some(keypair.public_key),
+          private_key: Some(main_community_keypair.private_key),
+          public_key: Some(main_community_keypair.public_key),
           last_refreshed_at: None,
           published: None,
         };
@@ -504,7 +504,7 @@ impl Perform for Oper<GetUserDetails> {
       None => {
         match User_::read_from_name(
           &conn,
-          data
+          &data
             .username
             .to_owned()
             .unwrap_or_else(|| "admin".to_string()),
index e5980e293f8e0ef4dbf29b56a607ad651578c88f..c842bc3cf87eb5e6e5faf7651a01d13014091bd8 100644 (file)
@@ -1,21 +1,4 @@
-use crate::apub::is_apub_id_valid;
-use crate::apub::signatures::sign;
-use crate::db::community::Community;
-use crate::db::community_view::CommunityFollowerView;
-use crate::db::post::Post;
-use crate::db::user::User_;
-use crate::db::Crud;
-use activitystreams::activity::{Accept, Create, Follow, Update};
-use activitystreams::object::properties::ObjectProperties;
-use activitystreams::BaseBox;
-use activitystreams::{context, public};
-use diesel::PgConnection;
-use failure::Error;
-use failure::_core::fmt::Debug;
-use isahc::prelude::*;
-use log::debug;
-use serde::Serialize;
-use url::Url;
+use super::*;
 
 fn populate_object_props(
   props: &mut ObjectProperties,
@@ -45,6 +28,8 @@ where
 {
   let json = serde_json::to_string(&activity)?;
   debug!("Sending activitypub activity {} to {:?}", json, to);
+  // TODO it needs to expand, the to field needs to expand and dedup the followers urls
+  // The inbox is determined by first retrieving the target actor's JSON-LD representation and then looking up the inbox property. If a recipient is a Collection or OrderedCollection, then the server MUST dereference the collection (with the user's credentials) and discover inboxes for each item in the collection. Servers MUST limit the number of layers of indirections through collections which will be performed, which MAY be one. 
   for t in to {
     let to_url = Url::parse(&t)?;
     if !is_apub_id_valid(&to_url) {
@@ -136,6 +121,7 @@ pub fn follow_community(
     .follow_props
     .set_actor_xsd_any_uri(user.actor_id.clone())?
     .set_object_xsd_any_uri(community.actor_id.clone())?;
+  // TODO this is incorrect, the to field should not be the inbox, but the followers url
   let to = format!("{}/inbox", community.actor_id);
   send_activity(
     &follow,
@@ -153,6 +139,7 @@ pub fn accept_follow(follow: &Follow, conn: &PgConnection) -> Result<(), Error>
     .get_object_xsd_any_uri()
     .unwrap()
     .to_string();
+  let actor_uri = follow.follow_props.get_actor_xsd_any_uri().unwrap().to_string();
   let community = Community::read_from_actor_id(conn, &community_uri)?;
   let mut accept = Accept::new();
   accept
@@ -164,12 +151,14 @@ pub fn accept_follow(follow: &Follow, conn: &PgConnection) -> Result<(), Error>
         .follow_props
         .get_actor_xsd_any_uri()
         .unwrap()
-        .to_string(),
+        .to_string()
     )?;
   accept
     .accept_props
+    .set_actor_xsd_any_uri(community.actor_id.clone())?
     .set_object_base_box(BaseBox::from_concrete(follow.clone())?)?;
-  let to = format!("{}/inbox", community_uri);
+  // TODO this is incorrect, the to field should not be the inbox, but the followers url
+  let to = format!("{}/inbox", actor_uri);
   send_activity(
     &accept,
     &community.private_key.unwrap(),
index a49357a867518b75a90b2566c0bcd330cd958f0d..fe31527fd0d1814998c1689f38ef5f0f3cf20398 100644 (file)
@@ -1,34 +1,11 @@
-use crate::apub::fetcher::{fetch_remote_object, fetch_remote_user};
-use crate::apub::signatures::PublicKey;
-use crate::apub::*;
-use crate::db::community::{Community, CommunityForm};
-use crate::db::community_view::CommunityFollowerView;
-use crate::db::establish_unpooled_connection;
-use crate::db::post::Post;
-use crate::db::user::User_;
-use crate::db::Crud;
-use crate::{convert_datetime, naive_now};
-use activitystreams::actor::properties::ApActorProperties;
-use activitystreams::collection::OrderedCollection;
-use activitystreams::{
-  actor::Group, collection::UnorderedCollection, context, ext::Extensible,
-  object::properties::ObjectProperties,
-};
-use actix_web::body::Body;
-use actix_web::web::Path;
-use actix_web::HttpResponse;
-use actix_web::{web, Result};
-use diesel::r2d2::{ConnectionManager, Pool};
-use diesel::PgConnection;
-use failure::Error;
-use serde::Deserialize;
-use url::Url;
+use super::*;
 
 #[derive(Deserialize)]
 pub struct CommunityQuery {
   community_name: String,
 }
 
+// TODO turn these as_group, as_page, into apub trait... something like to_apub
 impl Community {
   // Turn a Lemmy Community into an ActivityPub group that can be sent out over the network.
   fn as_group(&self, conn: &PgConnection) -> Result<GroupExt, Error> {
@@ -91,8 +68,8 @@ impl CommunityForm {
     let outbox_uri = Url::parse(&aprops.get_outbox().to_string())?;
     let _outbox = fetch_remote_object::<OrderedCollection>(&outbox_uri)?;
     let _followers = fetch_remote_object::<UnorderedCollection>(&followers_uri)?;
-    let apub_id = Url::parse(&oprops.get_attributed_to_xsd_any_uri().unwrap().to_string())?;
-    let creator = fetch_remote_user(&apub_id, conn)?;
+    let apub_id = &oprops.get_attributed_to_xsd_any_uri().unwrap().to_string();
+    let creator = get_or_fetch_and_upsert_remote_user(&apub_id, conn)?;
 
     Ok(CommunityForm {
       name: oprops.get_name_xsd_string().unwrap().to_string(),
@@ -123,19 +100,22 @@ impl CommunityForm {
 /// Return the community json over HTTP.
 pub async fn get_apub_community_http(
   info: Path<CommunityQuery>,
-  db: web::Data<Pool<ConnectionManager<PgConnection>>>,
+  db: DbPoolParam,
+  chat_server: ChatServerParam,
 ) -> Result<HttpResponse<Body>, Error> {
-  let community = Community::read_from_name(&&db.get()?, info.community_name.to_owned())?;
+  let community = Community::read_from_name(&&db.get()?, &info.community_name)?;
   let c = community.as_group(&db.get().unwrap())?;
   Ok(create_apub_response(&c))
 }
 
 /// Returns an empty followers collection, only populating the siz (for privacy).
+// TODO this needs to return the actual followers, and the to: field needs this
 pub async fn get_apub_community_followers(
   info: Path<CommunityQuery>,
-  db: web::Data<Pool<ConnectionManager<PgConnection>>>,
+  db: DbPoolParam,
+  chat_server: ChatServerParam,
 ) -> Result<HttpResponse<Body>, Error> {
-  let community = Community::read_from_name(&&db.get()?, info.community_name.to_owned())?;
+  let community = Community::read_from_name(&&db.get()?, &info.community_name)?;
 
   let connection = establish_unpooled_connection();
   //As we are an object, we validated that the community id was valid
@@ -156,9 +136,10 @@ pub async fn get_apub_community_followers(
 /// Returns an UnorderedCollection with the latest posts from the community.
 pub async fn get_apub_community_outbox(
   info: Path<CommunityQuery>,
-  db: web::Data<Pool<ConnectionManager<PgConnection>>>,
+  db: DbPoolParam,
+  chat_server: ChatServerParam,
 ) -> Result<HttpResponse<Body>, Error> {
-  let community = Community::read_from_name(&&db.get()?, info.community_name.to_owned())?;
+  let community = Community::read_from_name(&&db.get()?, &info.community_name)?;
 
   let conn = establish_unpooled_connection();
   //As we are an object, we validated that the community id was valid
index e7fc856e856d2f356efba9dba4a6a53b51011f4b..473f35f4c1e33725d864455b11366c8eb2418887 100644 (file)
@@ -1,16 +1,4 @@
-use crate::apub::activities::accept_follow;
-use crate::apub::fetcher::fetch_remote_user;
-use crate::apub::signatures::verify;
-use crate::db::community::{Community, CommunityFollower, CommunityFollowerForm};
-use crate::db::Followable;
-use activitystreams::activity::Follow;
-use actix_web::{web, HttpRequest, HttpResponse};
-use diesel::r2d2::{ConnectionManager, Pool};
-use diesel::PgConnection;
-use failure::Error;
-use log::debug;
-use serde::Deserialize;
-use url::Url;
+use super::*;
 
 #[serde(untagged)]
 #[derive(Deserialize, Debug)]
@@ -23,17 +11,18 @@ pub async fn community_inbox(
   request: HttpRequest,
   input: web::Json<CommunityAcceptedObjects>,
   path: web::Path<String>,
-  db: web::Data<Pool<ConnectionManager<PgConnection>>>,
+  db: DbPoolParam,
+  chat_server: ChatServerParam,
 ) -> Result<HttpResponse, Error> {
   let input = input.into_inner();
-  let conn = &db.get().unwrap();
+  let community_name = path.into_inner();
   debug!(
     "Community {} received activity {:?}",
-    &path.into_inner(),
+    &community_name,
     &input
   );
   match input {
-    CommunityAcceptedObjects::Follow(f) => handle_follow(&f, &request, conn),
+    CommunityAcceptedObjects::Follow(f) => handle_follow(&f, &request, &community_name, db, chat_server),
   }
 }
 
@@ -42,28 +31,36 @@ pub async fn community_inbox(
 fn handle_follow(
   follow: &Follow,
   request: &HttpRequest,
-  conn: &PgConnection,
+  community_name: &str,
+  db: DbPoolParam,
+  chat_server: ChatServerParam,
 ) -> Result<HttpResponse, Error> {
   let user_uri = follow
     .follow_props
     .get_actor_xsd_any_uri()
     .unwrap()
     .to_string();
-  let user = fetch_remote_user(&Url::parse(&user_uri)?, conn)?;
-  verify(&request, &user.public_key.unwrap())?;
-
-  // TODO: make sure this is a local community
   let community_uri = follow
     .follow_props
     .get_object_xsd_any_uri()
     .unwrap()
     .to_string();
-  let community = Community::read_from_actor_id(conn, &community_uri)?;
+
+  let conn = db.get()?;
+
+  let user = get_or_fetch_and_upsert_remote_user(&user_uri, &conn)?;
+  let community = Community::read_from_name(&conn, &community_name)?;
+
+  verify(&request, &user.public_key.unwrap())?;
+
   let community_follower_form = CommunityFollowerForm {
     community_id: community.id,
     user_id: user.id,
   };
+
+  // This will fail if they're already a follower
   CommunityFollower::follow(&conn, &community_follower_form)?;
-  accept_follow(&follow, conn)?;
+
+  accept_follow(&follow, &conn)?;
   Ok(HttpResponse::Ok().finish())
 }
index 71453a2a493954d9b3cb22c24816e0a7ca7439f0..b4af70c960b7cc511ac346f131ce48b17bd37174 100644 (file)
@@ -1,23 +1,4 @@
-use crate::api::site::SearchResponse;
-use crate::apub::*;
-use crate::db::community::{Community, CommunityForm};
-use crate::db::community_view::CommunityView;
-use crate::db::post::{Post, PostForm};
-use crate::db::post_view::PostView;
-use crate::db::user::{UserForm, User_};
-use crate::db::user_view::UserView;
-use crate::db::{Crud, SearchType};
-use crate::routes::nodeinfo::{NodeInfo, NodeInfoWellKnown};
-use activitystreams::collection::OrderedCollection;
-use activitystreams::object::Page;
-use activitystreams::BaseBox;
-use diesel::result::Error::NotFound;
-use diesel::PgConnection;
-use failure::Error;
-use isahc::prelude::*;
-use serde::Deserialize;
-use std::time::Duration;
-use url::Url;
+use super::*;
 
 // Fetch nodeinfo metadata from a remote instance.
 fn _fetch_node_info(domain: &str) -> Result<NodeInfo, Error> {
@@ -30,26 +11,27 @@ fn _fetch_node_info(domain: &str) -> Result<NodeInfo, Error> {
   Ok(fetch_remote_object::<NodeInfo>(&well_known.links.href)?)
 }
 
-// TODO: move these to db
-fn upsert_community(
-  community_form: &CommunityForm,
-  conn: &PgConnection,
-) -> Result<Community, Error> {
-  let existing = Community::read_from_actor_id(conn, &community_form.actor_id);
-  match existing {
-    Err(NotFound {}) => Ok(Community::create(conn, &community_form)?),
-    Ok(c) => Ok(Community::update(conn, c.id, &community_form)?),
-    Err(e) => Err(Error::from(e)),
-  }
-}
-fn upsert_user(user_form: &UserForm, conn: &PgConnection) -> Result<User_, Error> {
-  let existing = User_::read_from_apub_id(conn, &user_form.actor_id);
-  Ok(match existing {
-    Err(NotFound {}) => User_::create(conn, &user_form)?,
-    Ok(u) => User_::update(conn, u.id, &user_form)?,
-    Err(e) => return Err(Error::from(e)),
-  })
-}
+// // TODO: move these to db
+// // TODO use the last_refreshed_at
+// fn upsert_community(
+//   community_form: &CommunityForm,
+//   conn: &PgConnection,
+// ) -> Result<Community, Error> {
+//   let existing = Community::read_from_actor_id(conn, &community_form.actor_id);
+//   match existing {
+//     Err(NotFound {}) => Ok(Community::create(conn, &community_form)?),
+//     Ok(c) => Ok(Community::update(conn, c.id, &community_form)?),
+//     Err(e) => Err(Error::from(e)),
+//   }
+// }
+// fn upsert_user(user_form: &UserForm, conn: &PgConnection) -> Result<User_, Error> {
+//   let existing = User_::read_from_actor_id(conn, &user_form.actor_id);
+//   Ok(match existing {
+//     Err(NotFound {}) => User_::create(conn, &user_form)?,
+//     Ok(u) => User_::update(conn, u.id, &user_form)?,
+//     Err(e) => return Err(Error::from(e)),
+//   })
+// }
 
 fn upsert_post(post_form: &PostForm, conn: &PgConnection) -> Result<Post, Error> {
   let existing = Post::read_from_apub_id(conn, &post_form.ap_id);
@@ -89,7 +71,7 @@ where
 pub enum SearchAcceptedObjects {
   Person(Box<PersonExt>),
   Group(Box<GroupExt>),
-  Page(Box<Page>),
+  // Page(Box<Page>),
 }
 
 /// Attempt to parse the query as URL, and fetch an ActivityPub object from it.
@@ -109,22 +91,27 @@ pub fn search_by_apub_id(query: &str, conn: &PgConnection) -> Result<SearchRespo
   };
   match fetch_remote_object::<SearchAcceptedObjects>(&query_url)? {
     SearchAcceptedObjects::Person(p) => {
-      let u = upsert_user(&UserForm::from_person(&p)?, conn)?;
-      response.users = vec![UserView::read(conn, u.id)?];
+      let user = get_or_fetch_and_upsert_remote_user(query, &conn)?;
+      response.users = vec![UserView::read(conn, user.id)?];
     }
     SearchAcceptedObjects::Group(g) => {
-      let c = upsert_community(&CommunityForm::from_group(&g, conn)?, conn)?;
-      fetch_community_outbox(&c, conn)?;
-      response.communities = vec![CommunityView::read(conn, c.id, None)?];
-    }
-    SearchAcceptedObjects::Page(p) => {
-      let p = upsert_post(&PostForm::from_page(&p, conn)?, conn)?;
-      response.posts = vec![PostView::read(conn, p.id, None)?];
+      let community = get_or_fetch_and_upsert_remote_community(query, &conn)?;
+      // fetch_community_outbox(&c, conn)?;
+      response.communities = vec![CommunityView::read(conn, community.id, None)?];
     }
+    // SearchAcceptedObjects::Page(p) => {
+    //   let p = upsert_post(&PostForm::from_page(&p, conn)?, conn)?;
+    //   response.posts = vec![PostView::read(conn, p.id, None)?];
+    // }
   }
   Ok(response)
 }
 
+// TODO It should not be fetching data from a community outbox.
+// All posts, comments, comment likes, etc should be posts to our community_inbox
+// The only data we should be periodically fetching (if it hasn't been fetched in the last day
+// maybe), is community and user actors
+// and user actors
 /// Fetch all posts in the outbox of the given user, and insert them into the database.
 fn fetch_community_outbox(community: &Community, conn: &PgConnection) -> Result<Vec<Post>, Error> {
   let outbox_url = Url::parse(&community.get_outbox_url())?;
@@ -143,16 +130,54 @@ fn fetch_community_outbox(community: &Community, conn: &PgConnection) -> Result<
   )
 }
 
-/// Fetch a user, insert/update it in the database and return the user.
-pub fn fetch_remote_user(apub_id: &Url, conn: &PgConnection) -> Result<User_, Error> {
-  let person = fetch_remote_object::<PersonExt>(apub_id)?;
-  let uf = UserForm::from_person(&person)?;
-  upsert_user(&uf, conn)
+/// Check if a remote user exists, create if not found, if its too old update it.Fetch a user, insert/update it in the database and return the user.
+pub fn get_or_fetch_and_upsert_remote_user(apub_id: &str, conn: &PgConnection) -> Result<User_, Error> {
+  match User_::read_from_actor_id(&conn, &apub_id) {
+    Ok(u) => {
+      // If its older than a day, re-fetch it
+      // TODO the less than needs to be tested
+      if u.last_refreshed_at.lt(&(naive_now() - chrono::Duration::days(1))) {   
+        debug!("Fetching and updating from remote user: {}", apub_id);
+        let person = fetch_remote_object::<PersonExt>(&Url::parse(apub_id)?)?;
+        let uf = UserForm::from_person(&person)?;
+        uf.last_refreshed_at = Some(naive_now());
+        Ok(User_::update(&conn, u.id, &uf)?)
+      } else {
+        Ok(u)
+      }
+    },
+    Err(NotFound {}) => {
+      debug!("Fetching and creating remote user: {}", apub_id);
+      let person = fetch_remote_object::<PersonExt>(&Url::parse(apub_id)?)?;
+      let uf = UserForm::from_person(&person)?;
+      Ok(User_::create(conn, &uf)?)
+    }
+    Err(e) => Err(Error::from(e)),
+  }
 }
 
-/// Fetch a community, insert/update it in the database and return the community.
-pub fn fetch_remote_community(apub_id: &Url, conn: &PgConnection) -> Result<Community, Error> {
-  let group = fetch_remote_object::<GroupExt>(apub_id)?;
-  let cf = CommunityForm::from_group(&group, conn)?;
-  upsert_community(&cf, conn)
+/// Check if a remote community exists, create if not found, if its too old update it.Fetch a community, insert/update it in the database and return the community.
+pub fn get_or_fetch_and_upsert_remote_community(apub_id: &str, conn: &PgConnection) -> Result<Community, Error> {
+  match Community::read_from_actor_id(&conn, &apub_id) {
+    Ok(c) => {
+      // If its older than a day, re-fetch it
+      // TODO the less than needs to be tested
+      if c.last_refreshed_at.lt(&(naive_now() - chrono::Duration::days(1))) {   
+        debug!("Fetching and updating from remote community: {}", apub_id);
+        let group = fetch_remote_object::<GroupExt>(&Url::parse(apub_id)?)?;
+        let cf = CommunityForm::from_group(&group, conn)?;
+        cf.last_refreshed_at = Some(naive_now());
+        Ok(Community::update(&conn, c.id, &cf)?)
+      } else {
+        Ok(c)
+      }
+    },
+    Err(NotFound {}) => {
+      debug!("Fetching and creating remote community: {}", apub_id);
+      let group = fetch_remote_object::<GroupExt>(&Url::parse(apub_id)?)?;
+      let cf = CommunityForm::from_group(&group, conn)?;
+      Ok(Community::create(conn, &cf)?)
+    }
+    Err(e) => Err(Error::from(e)),
+  }
 }
index 846458977e50aa807a220cd15c4975bb7ecab1da..08fd97561446d0413b1102708ca89473c83e1890 100644 (file)
@@ -6,14 +6,66 @@ pub mod post;
 pub mod signatures;
 pub mod user;
 pub mod user_inbox;
-use crate::apub::signatures::PublicKeyExtension;
-use crate::Settings;
-use activitystreams::actor::{properties::ApActorProperties, Group, Person};
-use activitystreams::ext::Ext;
+
+use activitystreams::{
+  context, public, BaseBox,
+  actor::{
+    Actor,
+    Person,
+    Group,
+    properties::ApActorProperties, 
+  },
+  activity::{Accept, Create, Follow, Update},
+  object::{
+    Page,
+    properties::ObjectProperties,
+  },
+  ext::{
+    Ext,
+    Extensible,
+    Extension,
+  },
+  collection::{
+    UnorderedCollection, 
+    OrderedCollection,
+  },
+};
 use actix_web::body::Body;
-use actix_web::HttpResponse;
-use serde::ser::Serialize;
+use actix_web::{web, Result, HttpRequest, HttpResponse};
+use actix_web::web::Path;
 use url::Url;
+use failure::Error;
+use failure::_core::fmt::Debug;
+use log::debug;
+use isahc::prelude::*;
+use diesel::result::Error::NotFound;
+use diesel::PgConnection;
+use http::request::Builder;
+use http_signature_normalization::Config;
+use openssl::hash::MessageDigest;
+use openssl::sign::{Signer, Verifier};
+use openssl::{pkey::PKey, rsa::Rsa};
+use serde::{Deserialize, Serialize};
+use std::collections::BTreeMap;
+use std::time::Duration;
+
+use crate::routes::{DbPoolParam, ChatServerParam};
+use crate::routes::nodeinfo::{NodeInfo, NodeInfoWellKnown};
+use crate::{convert_datetime, naive_now, Settings};
+use crate::db::community::{Community, CommunityForm, CommunityFollower, CommunityFollowerForm};
+use crate::db::community_view::{CommunityFollowerView, CommunityView};
+use crate::db::post::{Post, PostForm};
+use crate::db::post_view::PostView;
+use crate::db::user::{UserForm, User_};
+use crate::db::user_view::UserView;
+// TODO check on unpooled connection
+use crate::db::{Crud, Followable, SearchType, establish_unpooled_connection};
+use crate::api::site::SearchResponse;
+
+use signatures::{PublicKey, PublicKeyExtension, sign};
+use activities::accept_follow;
+use signatures::verify;
+use fetcher::{fetch_remote_object, get_or_fetch_and_upsert_remote_user, get_or_fetch_and_upsert_remote_community};
 
 type GroupExt = Ext<Ext<Group, ApActorProperties>, PublicKeyExtension>;
 type PersonExt = Ext<Ext<Person, ApActorProperties>, PublicKeyExtension>;
index edae92d06909599c3d1a09cef21b13b91d0cf696..b56eeab649af9d798273fb04f9b4402cec72f12c 100644 (file)
@@ -1,19 +1,4 @@
-use crate::apub::create_apub_response;
-use crate::apub::fetcher::{fetch_remote_community, fetch_remote_user};
-use crate::convert_datetime;
-use crate::db::community::Community;
-use crate::db::post::{Post, PostForm};
-use crate::db::user::User_;
-use crate::db::Crud;
-use activitystreams::{context, object::properties::ObjectProperties, object::Page};
-use actix_web::body::Body;
-use actix_web::web::Path;
-use actix_web::{web, HttpResponse};
-use diesel::r2d2::{ConnectionManager, Pool};
-use diesel::PgConnection;
-use failure::Error;
-use serde::Deserialize;
-use url::Url;
+use super::*;
 
 #[derive(Deserialize)]
 pub struct PostQuery {
@@ -23,7 +8,8 @@ pub struct PostQuery {
 /// Return the post json over HTTP.
 pub async fn get_apub_post(
   info: Path<PostQuery>,
-  db: web::Data<Pool<ConnectionManager<PgConnection>>>,
+  db: DbPoolParam,
+  chat_server: ChatServerParam,
 ) -> Result<HttpResponse<Body>, Error> {
   let id = info.post_id.parse::<i32>()?;
   let post = Post::read(&&db.get()?, id)?;
@@ -72,10 +58,10 @@ impl PostForm {
   /// Parse an ActivityPub page received from another instance into a Lemmy post.
   pub fn from_page(page: &Page, conn: &PgConnection) -> Result<PostForm, Error> {
     let oprops = &page.object_props;
-    let creator_id = Url::parse(&oprops.get_attributed_to_xsd_any_uri().unwrap().to_string())?;
-    let creator = fetch_remote_user(&creator_id, conn)?;
-    let community_id = Url::parse(&oprops.get_to_xsd_any_uri().unwrap().to_string())?;
-    let community = fetch_remote_community(&community_id, conn)?;
+    let creator_actor_id = &oprops.get_attributed_to_xsd_any_uri().unwrap().to_string();
+    let creator = get_or_fetch_and_upsert_remote_user(&creator_actor_id, &conn)?;
+    let community_actor_id = &oprops.get_to_xsd_any_uri().unwrap().to_string();
+    let community = get_or_fetch_and_upsert_remote_community(&community_actor_id, &conn)?;
 
     Ok(PostForm {
       name: oprops.get_summary_xsd_string().unwrap().to_string(),
index 40b3c738eff2417b536f7e9556ed7ac370b8c09b..cf064603bb5d5b989143b5d37f5f75688ae8ae70 100644 (file)
@@ -1,14 +1,4 @@
-use activitystreams::{actor::Actor, ext::Extension};
-use actix_web::HttpRequest;
-use failure::Error;
-use http::request::Builder;
-use http_signature_normalization::Config;
-use log::debug;
-use openssl::hash::MessageDigest;
-use openssl::sign::{Signer, Verifier};
-use openssl::{pkey::PKey, rsa::Rsa};
-use serde::{Deserialize, Serialize};
-use std::collections::BTreeMap;
+use super::*;
 
 lazy_static! {
   static ref HTTP_SIG_CONFIG: Config = Config::new();
index b5a819114f0078b1fa62d1ccccb1750bade198f7..acf72221fd8c7e4ff5beb875f56c02a4dd8ac80d 100644 (file)
@@ -1,21 +1,4 @@
-use crate::apub::signatures::PublicKey;
-use crate::apub::{create_apub_response, PersonExt};
-use crate::db::user::{UserForm, User_};
-use crate::{convert_datetime, naive_now};
-use activitystreams::{
-  actor::{properties::ApActorProperties, Person},
-  context,
-  ext::Extensible,
-  object::properties::ObjectProperties,
-};
-use actix_web::body::Body;
-use actix_web::web::Path;
-use actix_web::HttpResponse;
-use actix_web::{web, Result};
-use diesel::r2d2::{ConnectionManager, Pool};
-use diesel::PgConnection;
-use failure::Error;
-use serde::Deserialize;
+use super::*;
 
 #[derive(Deserialize)]
 pub struct UserQuery {
@@ -25,7 +8,8 @@ pub struct UserQuery {
 // Turn a Lemmy user into an ActivityPub person and return it as json.
 pub async fn get_apub_user(
   info: Path<UserQuery>,
-  db: web::Data<Pool<ConnectionManager<PgConnection>>>,
+  db: DbPoolParam,
+  chat_server: ChatServerParam,
 ) -> Result<HttpResponse<Body>, Error> {
   let user = User_::find_by_email_or_username(&&db.get()?, &info.user_name)?;
 
index f9faa0f095e5b70e21cf7e61ee9e1466bf50183d..97cdeece77cf88aec2882a6a7a4ba99ecbacf51c 100644 (file)
@@ -1,16 +1,4 @@
-use crate::apub::fetcher::{fetch_remote_community, fetch_remote_user};
-use crate::apub::signatures::verify;
-use crate::db::post::{Post, PostForm};
-use crate::db::Crud;
-use activitystreams::activity::{Accept, Create, Update};
-use activitystreams::object::Page;
-use actix_web::{web, HttpRequest, HttpResponse};
-use diesel::r2d2::{ConnectionManager, Pool};
-use diesel::PgConnection;
-use failure::Error;
-use log::debug;
-use serde::Deserialize;
-use url::Url;
+use super::*;
 
 #[serde(untagged)]
 #[derive(Deserialize, Debug)]
@@ -25,21 +13,23 @@ pub async fn user_inbox(
   request: HttpRequest,
   input: web::Json<UserAcceptedObjects>,
   path: web::Path<String>,
-  db: web::Data<Pool<ConnectionManager<PgConnection>>>,
+  db: DbPoolParam,
+  chat_server: ChatServerParam,
 ) -> Result<HttpResponse, Error> {
   // TODO: would be nice if we could do the signature check here, but we cant access the actor property
   let input = input.into_inner();
   let conn = &db.get().unwrap();
+  let username = path.into_inner();
   debug!(
     "User {} received activity: {:?}",
-    &path.into_inner(),
+    &username,
     &input
   );
 
   match input {
-    UserAcceptedObjects::Create(c) => handle_create(&c, &request, conn),
-    UserAcceptedObjects::Update(u) => handle_update(&u, &request, conn),
-    UserAcceptedObjects::Accept(a) => handle_accept(&a, &request, conn),
+    UserAcceptedObjects::Create(c) => handle_create(&c, &request, &username, &conn),
+    UserAcceptedObjects::Update(u) => handle_update(&u, &request, &username, &conn),
+    UserAcceptedObjects::Accept(a) => handle_accept(&a, &request, &username, &conn),
   }
 }
 
@@ -47,8 +37,11 @@ pub async fn user_inbox(
 fn handle_create(
   create: &Create,
   request: &HttpRequest,
+  username: &str,
   conn: &PgConnection,
 ) -> Result<HttpResponse, Error> {
+  // TODO before this even gets named, because we don't know what type of object it is, we need
+  // to parse this out
   let community_uri = create
     .create_props
     .get_actor_xsd_any_uri()
@@ -75,6 +68,7 @@ fn handle_create(
 fn handle_update(
   update: &Update,
   request: &HttpRequest,
+  username: &str,
   conn: &PgConnection,
 ) -> Result<HttpResponse, Error> {
   let community_uri = update
@@ -103,6 +97,7 @@ fn handle_update(
 fn handle_accept(
   accept: &Accept,
   request: &HttpRequest,
+  username: &str,
   conn: &PgConnection,
 ) -> Result<HttpResponse, Error> {
   let community_uri = accept
@@ -110,9 +105,21 @@ fn handle_accept(
     .get_actor_xsd_any_uri()
     .unwrap()
     .to_string();
-  let community = fetch_remote_community(&Url::parse(&community_uri)?, conn)?;
+    
+  let community = get_or_fetch_and_upsert_remote_community(&community_uri, conn)?;
   verify(request, &community.public_key.unwrap())?;
 
+  let user = User_::read_from_name(&conn, username)?;
+
+  // Now you need to add this to the community follower
+  let community_follower_form = CommunityFollowerForm {
+    community_id: community.id,
+    user_id: user.id,
+  };
+
+  // This will fail if they're already a follower
+  CommunityFollower::follow(&conn, &community_follower_form)?;
+
   // TODO: make sure that we actually requested a follow
   // TODO: at this point, indicate to the user that they are following the community
   Ok(HttpResponse::Ok().finish())
index ca2fc120a44bcf979f1b8639dd20c59232fc10cd..301fce0322a5ea256b5169952f39f149bde3ba99 100644 (file)
@@ -73,7 +73,7 @@ impl Crud<CommunityForm> for Community {
 }
 
 impl Community {
-  pub fn read_from_name(conn: &PgConnection, community_name: String) -> Result<Self, Error> {
+  pub fn read_from_name(conn: &PgConnection, community_name: &str) -> Result<Self, Error> {
     use crate::schema::community::dsl::*;
     community
       .filter(name.eq(community_name))
index 3a079f0916e31452299cee9942b84c9347c1fda4..065b2c5789644bc364aeaf090ce0be43f3a8f6c9 100644 (file)
@@ -104,7 +104,7 @@ impl User_ {
       .get_result::<Self>(conn)
   }
 
-  pub fn read_from_name(conn: &PgConnection, from_user_name: String) -> Result<Self, Error> {
+  pub fn read_from_name(conn: &PgConnection, from_user_name: &str) -> Result<Self, Error> {
     user_.filter(name.eq(from_user_name)).first::<Self>(conn)
   }
 
@@ -120,7 +120,7 @@ impl User_ {
       .get_result::<Self>(conn)
   }
 
-  pub fn read_from_apub_id(conn: &PgConnection, object_id: &str) -> Result<Self, Error> {
+  pub fn read_from_actor_id(conn: &PgConnection, object_id: &str) -> Result<Self, Error> {
     use crate::schema::user_::dsl::*;
     user_.filter(actor_id.eq(object_id)).first::<Self>(conn)
   }
index 815953c55abb722a8c017aad23dc268db4b790c3..6e28a7f774783eee27c7adc384a1225ed1e6a432 100644 (file)
@@ -27,7 +27,7 @@ pub fn config(cfg: &mut web::ServiceConfig) {
 
 async fn get_all_feed(
   info: web::Query<Params>,
-  db: web::Data<Pool<ConnectionManager<PgConnection>>>,
+  db: DbPoolParam,
 ) -> Result<HttpResponse, Error> {
   let res = web::block(move || {
     let conn = db.get()?;
@@ -144,7 +144,7 @@ fn get_feed_community(
   community_name: String,
 ) -> Result<ChannelBuilder, failure::Error> {
   let site_view = SiteView::read(&conn)?;
-  let community = Community::read_from_name(&conn, community_name)?;
+  let community = Community::read_from_name(&conn, &community_name)?;
   let community_url = community.get_url();
 
   let posts = PostQueryBuilder::create(&conn)
index 32507c6b6eb38980cf9e014bead9a6ed8c5cb140..5004bc932eb6fbb5e03b3527e59a7c9fe6304cff 100644 (file)
@@ -21,7 +21,7 @@ async fn node_info_well_known() -> Result<HttpResponse<Body>, failure::Error> {
 }
 
 async fn node_info(
-  db: web::Data<Pool<ConnectionManager<PgConnection>>>,
+  db: DbPoolParam,
 ) -> Result<HttpResponse, Error> {
   let res = web::block(move || {
     let conn = db.get()?;
index 0f30acaf220c656c21001bd81bdb60905b7232a2..7bdce2067b2f247d21d1fec633cd6e65ddfd8959 100644 (file)
@@ -31,7 +31,7 @@ lazy_static! {
 /// https://radical.town/.well-known/webfinger?resource=acct:felix@radical.town
 async fn get_webfinger_response(
   info: Query<Params>,
-  db: web::Data<Pool<ConnectionManager<PgConnection>>>,
+  db: DbPoolParam,
 ) -> Result<HttpResponse, Error> {
   let res = web::block(move || {
     let conn = db.get()?;
@@ -46,7 +46,7 @@ async fn get_webfinger_response(
     };
 
     // Make sure the requested community exists.
-    let community = match Community::read_from_name(&conn, community_name.to_string()) {
+    let community = match Community::read_from_name(&conn, &community_name) {
       Ok(o) => o,
       Err(_) => return Err(format_err!("not_found")),
     };
index 48b7d08fb0336cf587346771710e9d43510ecc54..a95ff9ee820c16dba14c8b1192711395f278509c 100644 (file)
@@ -32,7 +32,6 @@ struct WSSession {
   /// Client must send ping at least once per 10 seconds (CLIENT_TIMEOUT),
   /// otherwise we drop connection.
   hb: Instant,
-  // db: Pool<ConnectionManager<PgConnection>>,
 }
 
 impl Actor for WSSession {