From da21e4e1c3f0aa28553e7a70b5e48344c23cc304 Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Wed, 11 Mar 2020 12:29:10 +0100 Subject: [PATCH] improved community federation (wip) --- server/src/apub/community.rs | 47 ++++------------- server/src/apub/group_wrapper.rs | 91 ++++++++++++++++++++++++++++++++ server/src/apub/mod.rs | 1 + server/src/apub/puller.rs | 31 +++-------- server/src/websocket/server.rs | 2 - 5 files changed, 111 insertions(+), 61 deletions(-) create mode 100644 server/src/apub/group_wrapper.rs diff --git a/server/src/apub/community.rs b/server/src/apub/community.rs index ee3edc1f4..2a16eeae9 100644 --- a/server/src/apub/community.rs +++ b/server/src/apub/community.rs @@ -1,14 +1,14 @@ +use crate::apub::group_wrapper::GroupHelper; use crate::apub::make_apub_endpoint; use crate::db::community::Community; use crate::db::community_view::CommunityFollowerView; use crate::db::establish_unpooled_connection; -use crate::to_datetime_utc; use activitypub::{actor::Group, collection::UnorderedCollection, context}; use actix_web::body::Body; use actix_web::web::Path; use actix_web::HttpResponse; use serde::Deserialize; -use serde_json::json; +use serde_json::{Value}; impl Community { pub fn as_group(&self) -> Group { @@ -16,42 +16,18 @@ impl Community { let mut group = Group::default(); - // TODO: why the hell is this code so awkward? group.object_props.set_context_object(context()).ok(); - // TODO: id really needs to be a url - group.object_props.set_id_string(self.id.to_string()).ok(); - group - .object_props - .set_name_string(self.title.to_owned()) - .ok(); - group - .object_props - .set_published_utctime(to_datetime_utc(self.published)) - .ok(); - group.object_props.attributed_to = Some(json!(self.creator_id.to_string())); - if let Some(updated) = self.updated { - group - .object_props - .set_updated_utctime(to_datetime_utc(updated)) - .ok(); - } + Group::set_id(&mut group, self.id); + Group::set_title(&mut group, &self.title); + Group::set_published(&mut group, self.published); + Group::set_updated(&mut group, self.updated); + Group::set_creator_id(&mut group, self.creator_id); - if let Some(description) = &self.description { - group.object_props.summary = Some(json!(description.to_string())); - } + Group::set_description(&mut group, &self.description); - group - .ap_actor_props - .set_inbox_string(format!("{}/inbox", &base_url)) - .ok(); - group - .ap_actor_props - .set_outbox_string(format!("{}/outbox", &base_url)) - .ok(); - group - .ap_actor_props - .set_followers_string(format!("{}/followers", &base_url)) - .ok(); + group.ap_actor_props.inbox = Value::String(format!("{}/inbox", &base_url)); + group.ap_actor_props.outbox = Value::String(format!("{}/outbox", &base_url)); + group.ap_actor_props.followers = Some(Value::String(format!("{}/followers", &base_url))); group } @@ -65,7 +41,6 @@ impl Community { let connection = establish_unpooled_connection(); //As we are an object, we validated that the community id was valid - // TODO: add a method that only returns count for better performance let community_followers = CommunityFollowerView::for_community(&connection, self.id).unwrap(); collection diff --git a/server/src/apub/group_wrapper.rs b/server/src/apub/group_wrapper.rs new file mode 100644 index 000000000..dd140df28 --- /dev/null +++ b/server/src/apub/group_wrapper.rs @@ -0,0 +1,91 @@ +use crate::to_datetime_utc; +use activitypub::actor::Group; +use chrono::{DateTime, NaiveDateTime}; +use failure::Error; +use serde_json::Value; + +pub trait GroupHelper { + // TODO: id really needs to be a url + fn set_id(group: &mut Group, id: i32); + fn get_id(group: &Group) -> Result; + + fn set_title(group: &mut Group, title: &str); + fn get_title(group: &Group) -> Result; + + fn set_description(group: &mut Group, description: &Option); + fn get_description(group: &Group) -> Result, Error>; + + // TODO: also needs to be changed to url + fn set_creator_id(group: &mut Group, creator_id: i32); + fn get_creator_id(group: &Group) -> Result; + + fn set_published(group: &mut Group, published: NaiveDateTime); + fn get_published(group: &Group) -> Result; + + fn set_updated(group: &mut Group, updated: Option); + fn get_updated(group: &Group) -> Result, Error>; +} + +// TODO: something is crashing and not reporting the error +impl GroupHelper for Group { + fn set_id(group: &mut Group, id: i32) { + group.object_props.id = Some(Value::String(id.to_string())); + } + fn get_id(group: &Group) -> Result { + Ok(get_string_value(group.clone().object_props.id).parse::()?) + } + + fn set_title(group: &mut Group, title: &str) { + group.object_props.name = Some(Value::String(title.to_string())); + } + fn get_title(group: &Group) -> Result { + Ok(get_string_value(group.to_owned().object_props.name)) + } + + fn set_description(group: &mut Group, description: &Option) { + group.object_props.summary = description.as_ref().map(|d| Value::String(d.to_string())); + } + fn get_description(group: &Group) -> Result, Error> { + Ok(get_string_value_opt(group.to_owned().object_props.summary)) + } + + fn set_creator_id(group: &mut Group, creator_id: i32) { + group.object_props.attributed_to = Some(Value::String(creator_id.to_string())); + } + fn get_creator_id(group: &Group) -> Result { + Ok(get_string_value(group.clone().object_props.attributed_to).parse::()?) + } + + fn set_published(group: &mut Group, published: NaiveDateTime) { + group.object_props.published = Some(Value::String(to_datetime_utc(published).to_string())) + } + fn get_published(group: &Group) -> Result { + let str = get_string_value(group.to_owned().object_props.published); + // TODO: no idea which date format + let date = DateTime::parse_from_rfc2822(&str)?; + Ok(date.naive_local()) + } + + fn set_updated(group: &mut Group, updated: Option) { + group.object_props.updated = updated.map(|u| Value::String(u.to_string())); + } + fn get_updated(group: &Group) -> Result, Error> { + let str = get_string_value_opt(group.to_owned().object_props.updated); + match str { + Some(s) => Ok(Some(DateTime::parse_from_rfc2822(&s)?.naive_local())), + None => Ok(None), + } + } +} + +fn get_string_value_opt(value: Option) -> Option { + value + .as_ref() + .map(Value::as_str) + .flatten() + .map(str::to_string) +} + +fn get_string_value(value: Option) -> String { + get_string_value_opt(value).unwrap() +} diff --git a/server/src/apub/mod.rs b/server/src/apub/mod.rs index 9bac64a6e..31dc3cedb 100644 --- a/server/src/apub/mod.rs +++ b/server/src/apub/mod.rs @@ -1,4 +1,5 @@ pub mod community; +pub mod group_wrapper; pub mod post; pub mod puller; pub mod user; diff --git a/server/src/apub/puller.rs b/server/src/apub/puller.rs index b68779820..ce9469fcc 100644 --- a/server/src/apub/puller.rs +++ b/server/src/apub/puller.rs @@ -3,11 +3,10 @@ extern crate reqwest; use self::reqwest::Error; use crate::api::community::{GetCommunityResponse, ListCommunitiesResponse}; use crate::api::post::GetPosts; +use crate::apub::group_wrapper::GroupHelper; use crate::db::community_view::CommunityView; -use crate::naive_now; use crate::settings::Settings; use activitypub::actor::Group; -use serde_json::Value; // TODO: right now all of the data is requested on demand, for production we will need to store // things in the local database to not ruin the performance @@ -43,22 +42,20 @@ pub fn get_remote_community(identifier: String) -> Result()?, + id: Group::get_id(&community)?, name, - title: get_string_value(community.object_props.name), - description: get_string_value_opt(community.object_props.summary), + title: Group::get_title(&community)?, + description: Group::get_description(&community)?, category_id: -1, - creator_id: get_string_value(community.object_props.attributed_to).parse::()?, + creator_id: Group::get_creator_id(&community)?, removed: false, - published: naive_now(), // TODO: need to handle time conversion (or handle it in apub lib) - updated: Some(naive_now()), // TODO: community.object_props.updated + published: Group::get_published(&community)?, + updated: Group::get_updated(&community)?, deleted: false, nsfw: false, creator_name: "".to_string(), @@ -69,24 +66,12 @@ pub fn get_remote_community(identifier: String) -> Result) -> Option { - value - .as_ref() - .map(Value::as_str) - .flatten() - .map(str::to_string) -} - -fn get_string_value(value: Option) -> String { - get_string_value_opt(value).unwrap() -} - pub fn get_following_instances() -> Result, Error> { let instance_list = match Settings::get().federated_instance.clone() { Some(f) => vec![f, Settings::get().hostname.clone()], diff --git a/server/src/websocket/server.rs b/server/src/websocket/server.rs index 76a55540f..66b10cd91 100644 --- a/server/src/websocket/server.rs +++ b/server/src/websocket/server.rs @@ -557,8 +557,6 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result