summaryrefslogtreecommitdiffstats
path: root/server/src
diff options
context:
space:
mode:
authorFelix Ableitner <me@nutomic.com>2020-03-14 01:05:42 +0100
committerFelix Ableitner <me@nutomic.com>2020-03-14 01:05:42 +0100
commitb01f4f75d6dbbc579099422ccb2a897899dfe343 (patch)
treec79d4525c0bb43202179121ae1b6533795164043 /server/src
parent8f67a3c6340645e00e653655a4977308cf8bfffa (diff)
WIP: federate posts between instances
Diffstat (limited to 'server/src')
-rw-r--r--server/src/api/post.rs2
-rw-r--r--server/src/apub/community.rs51
-rw-r--r--server/src/apub/mod.rs30
-rw-r--r--server/src/apub/post.rs14
-rw-r--r--server/src/apub/puller.rs37
-rw-r--r--server/src/routes/federation.rs4
-rw-r--r--server/src/websocket/server.rs3
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);