diff options
author | Felix Ableitner <me@nutomic.com> | 2020-03-14 01:05:42 +0100 |
---|---|---|
committer | Felix Ableitner <me@nutomic.com> | 2020-03-14 01:05:42 +0100 |
commit | b01f4f75d6dbbc579099422ccb2a897899dfe343 (patch) | |
tree | c79d4525c0bb43202179121ae1b6533795164043 /server/src | |
parent | 8f67a3c6340645e00e653655a4977308cf8bfffa (diff) |
WIP: federate posts between instances
Diffstat (limited to 'server/src')
-rw-r--r-- | server/src/api/post.rs | 2 | ||||
-rw-r--r-- | server/src/apub/community.rs | 51 | ||||
-rw-r--r-- | server/src/apub/mod.rs | 30 | ||||
-rw-r--r-- | server/src/apub/post.rs | 14 | ||||
-rw-r--r-- | server/src/apub/puller.rs | 37 | ||||
-rw-r--r-- | server/src/routes/federation.rs | 4 | ||||
-rw-r--r-- | server/src/websocket/server.rs | 3 |
7 files changed, 88 insertions, 53 deletions
diff --git a/server/src/api/post.rs b/server/src/api/post.rs index fb022589..cefa5b07 100644 --- a/server/src/api/post.rs +++ b/server/src/api/post.rs @@ -33,7 +33,7 @@ pub struct GetPostResponse { pub online: usize, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Debug)] pub struct GetPosts { type_: String, sort: String, diff --git a/server/src/apub/community.rs b/server/src/apub/community.rs index b5b365d0..61b0b2ce 100644 --- a/server/src/apub/community.rs +++ b/server/src/apub/community.rs @@ -3,6 +3,8 @@ use crate::convert_datetime; use crate::db::community::Community; use crate::db::community_view::CommunityFollowerView; use crate::db::establish_unpooled_connection; +use crate::db::post_view::{PostQueryBuilder, PostView}; +use activitystreams::collection::apub::OrderedCollection; use activitystreams::{ actor::apub::Group, collection::apub::UnorderedCollection, context, object::properties::ObjectProperties, @@ -43,7 +45,7 @@ impl Community { Ok(group) } - pub fn followers_as_collection(&self) -> Result<UnorderedCollection, Error> { + pub fn get_followers(&self) -> Result<UnorderedCollection, Error> { let base_url = make_apub_endpoint("c", &self.name); let connection = establish_unpooled_connection(); @@ -60,6 +62,34 @@ impl Community { .set_total_items(community_followers.len() as u64)?; Ok(collection) } + + pub fn get_outbox(&self) -> Result<OrderedCollection, Error> { + let base_url = make_apub_endpoint("c", &self.name); + + let connection = establish_unpooled_connection(); + //As we are an object, we validated that the community id was valid + let community_posts: Vec<PostView> = PostQueryBuilder::create(&connection) + .for_community_id(self.id) + .list() + .unwrap(); + + let mut collection = OrderedCollection::default(); + let oprops: &mut ObjectProperties = collection.as_mut(); + oprops + .set_context_xsd_any_uri(context())? + .set_id(base_url)?; + collection + .collection_props + .set_many_items_object_boxs( + community_posts + .iter() + .map(|c| c.as_page().unwrap()) + .collect(), + )? + .set_total_items(community_posts.len() as u64)?; + + Ok(collection) + } } #[derive(Deserialize)] @@ -67,6 +97,7 @@ pub struct CommunityQuery { community_name: String, } +// TODO: move all this boilerplate code to routes::federation or such pub async fn get_apub_community(info: Path<CommunityQuery>) -> Result<HttpResponse<Body>, Error> { let connection = establish_unpooled_connection(); @@ -90,7 +121,23 @@ pub async fn get_apub_community_followers( Ok( HttpResponse::Ok() .content_type("application/activity+json") - .body(serde_json::to_string(&community.followers_as_collection()?).unwrap()), + .body(serde_json::to_string(&community.get_followers()?).unwrap()), + ) + } else { + Ok(HttpResponse::NotFound().finish()) + } +} + +pub async fn get_apub_community_outbox( + info: Path<CommunityQuery>, +) -> Result<HttpResponse<Body>, Error> { + let connection = establish_unpooled_connection(); + + if let Ok(community) = Community::read_from_name(&connection, info.community_name.to_owned()) { + Ok( + HttpResponse::Ok() + .content_type("application/activity+json") + .body(serde_json::to_string(&community.get_outbox()?).unwrap()), ) } else { Ok(HttpResponse::NotFound().finish()) diff --git a/server/src/apub/mod.rs b/server/src/apub/mod.rs index 0fa665c5..6ce59992 100644 --- a/server/src/apub/mod.rs +++ b/server/src/apub/mod.rs @@ -11,7 +11,6 @@ use url::Url; #[cfg(test)] mod tests { use crate::db::community::Community; - use crate::db::post::Post; use crate::db::user::User_; use crate::db::{ListingType, SortType}; use crate::{naive_now, Settings}; @@ -69,35 +68,6 @@ mod tests { group.unwrap().object_props.get_id().unwrap().to_string() ); } - - #[test] - fn test_post() { - let post = Post { - id: 62, - name: "A test post".into(), - url: None, - body: None, - creator_id: 52, - community_id: 42, - published: naive_now(), - removed: false, - locked: false, - stickied: false, - nsfw: false, - deleted: false, - updated: None, - embed_title: None, - embed_description: None, - embed_html: None, - thumbnail_url: None, - }; - - let page = post.as_page(); - assert_eq!( - format!("https://{}/federation/post/62", Settings::get().hostname), - page.unwrap().object_props.get_id().unwrap().to_string() - ); - } } // TODO: this should take an enum community/user/post for `point` diff --git a/server/src/apub/post.rs b/server/src/apub/post.rs index f0599eef..6950e561 100644 --- a/server/src/apub/post.rs +++ b/server/src/apub/post.rs @@ -1,17 +1,18 @@ use crate::apub::make_apub_endpoint; use crate::convert_datetime; -use crate::db::post::Post; -use activitystreams::{context, object::apub::Page, object::properties::ObjectProperties}; +use crate::db::post_view::PostView; +use activitystreams::{object::apub::Page, object::properties::ObjectProperties}; use failure::Error; -impl Post { +impl PostView { pub fn as_page(&self) -> Result<Page, Error> { let base_url = make_apub_endpoint("post", self.id); let mut page = Page::default(); let oprops: &mut ObjectProperties = page.as_mut(); oprops - .set_context_xsd_any_uri(context())? + // Not needed when the Post is embedded in a collection (like for community outbox) + //.set_context_xsd_any_uri(context())? .set_id(base_url)? .set_name_xsd_string(self.name.to_owned())? .set_published(convert_datetime(self.published))? @@ -21,8 +22,9 @@ impl Post { oprops.set_content_xsd_string(body.to_owned())?; } - if let Some(url) = &self.url { - oprops.set_url_xsd_any_uri(url.to_owned())?; + if let Some(_url) = &self.url { + // TODO: crashes here + //oprops.set_url_xsd_any_uri(url.to_owned())?; } if let Some(u) = self.updated { diff --git a/server/src/apub/puller.rs b/server/src/apub/puller.rs index aa34ce50..941fbc3f 100644 --- a/server/src/apub/puller.rs +++ b/server/src/apub/puller.rs @@ -1,11 +1,11 @@ extern crate reqwest; use crate::api::community::{GetCommunityResponse, ListCommunitiesResponse}; -use crate::api::post::GetPosts; +use crate::api::post::GetPostsResponse; use crate::db::community_view::CommunityView; use crate::settings::Settings; use activitystreams::actor::apub::Group; -use activitystreams::collection::apub::UnorderedCollection; +use activitystreams::collection::apub::{OrderedCollection, UnorderedCollection}; use failure::Error; // TODO: right now all of the data is requested on demand, for production we will need to store @@ -25,24 +25,33 @@ fn fetch_communities_from_instance(domain: &str) -> Result<Vec<CommunityView>, E Ok(communities2) } -pub fn get_remote_community_posts(name: String) -> Result<GetPosts, Error> { - // TODO: this is for urls like /c/!main@example.com, activitypub exposes it through the outbox - // https://www.w3.org/TR/activitypub/#outbox - dbg!(name); +// TODO: this should be cached or stored in the database +fn get_remote_community_uri(identifier: String) -> String { + let x: Vec<&str> = identifier.split('@').collect(); + let name = x[0].replace("!", ""); + let instance = x[1]; + format!("http://{}/federation/c/{}", instance, name) +} + +pub fn get_remote_community_posts(identifier: String) -> Result<GetPostsResponse, Error> { + let community: Group = reqwest::get(&get_remote_community_uri(identifier))?.json()?; + let outbox_uri = &community.ap_actor_props.get_outbox().to_string(); + let outbox: OrderedCollection = reqwest::get(outbox_uri)?.json()?; + let items = outbox.collection_props.get_many_items_object_boxs(); + dbg!(items); unimplemented!() } pub fn get_remote_community(identifier: String) -> Result<GetCommunityResponse, failure::Error> { - let x: Vec<&str> = identifier.split('@').collect(); - let name = x[0].replace("!", ""); - let instance = x[1]; - let community_uri = format!("http://{}/federation/c/{}", instance, name); - let community: Group = reqwest::get(&community_uri)?.json()?; + let community: Group = reqwest::get(&get_remote_community_uri(identifier.clone()))?.json()?; let followers_uri = &community .ap_actor_props .get_followers() .unwrap() .to_string(); + let outbox_uri = &community.ap_actor_props.get_outbox().to_string(); + let outbox: OrderedCollection = reqwest::get(outbox_uri)?.json()?; + // TODO: this need to be done in get_remote_community_posts() (meaning we need to store the outbox uri?) let followers: UnorderedCollection = reqwest::get(followers_uri)?.json()?; // TODO: looks like a bunch of data is missing from the activitypub response @@ -52,8 +61,8 @@ pub fn get_remote_community(identifier: String) -> Result<GetCommunityResponse, admins: vec![], community: CommunityView { // TODO: we need to merge id and name into a single thing (stuff like @user@instance.com) - id: -1, //community.object_props.get_id() - name, + id: 1337, //community.object_props.get_id() + name: identifier, title: community .object_props .get_name_xsd_string() @@ -87,7 +96,7 @@ pub fn get_remote_community(identifier: String) -> Result<GetCommunityResponse, .get_total_items() .unwrap() .as_ref() as i64, // TODO: need to use the same type - number_of_posts: -1, + number_of_posts: *outbox.collection_props.get_total_items().unwrap().as_ref() as i64, number_of_comments: -1, hot_rank: -1, user_id: None, diff --git a/server/src/routes/federation.rs b/server/src/routes/federation.rs index 6816f1bc..0be051eb 100644 --- a/server/src/routes/federation.rs +++ b/server/src/routes/federation.rs @@ -21,6 +21,10 @@ pub fn config(cfg: &mut web::ServiceConfig) { web::get().to(apub::community::get_apub_community_followers), ) .route( + "/federation/c/{community_name}/outbox", + web::get().to(apub::community::get_apub_community_outbox), + ) + .route( "/federation/u/{user_name}", web::get().to(apub::user::get_apub_user), ) diff --git a/server/src/websocket/server.rs b/server/src/websocket/server.rs index 727eb7d8..21588a58 100644 --- a/server/src/websocket/server.rs +++ b/server/src/websocket/server.rs @@ -648,6 +648,9 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result<Str } UserOperation::GetPosts => { let get_posts: GetPosts = serde_json::from_str(data)?; + dbg!(&get_posts); + // TODO: intercept here (but the type is wrong) + //get_remote_community_posts(get_posts.community_id.unwrap()) if get_posts.community_id.is_none() { // 0 is the "all" community chat.join_community_room(0, msg.id); |