improved community federation (wip)

This commit is contained in:
Felix Ableitner 2020-03-11 12:29:10 +01:00
parent b7ed716659
commit da21e4e1c3
5 changed files with 111 additions and 61 deletions

View file

@ -1,14 +1,14 @@
use crate::apub::group_wrapper::GroupHelper;
use crate::apub::make_apub_endpoint; use crate::apub::make_apub_endpoint;
use crate::db::community::Community; use crate::db::community::Community;
use crate::db::community_view::CommunityFollowerView; use crate::db::community_view::CommunityFollowerView;
use crate::db::establish_unpooled_connection; use crate::db::establish_unpooled_connection;
use crate::to_datetime_utc;
use activitypub::{actor::Group, collection::UnorderedCollection, context}; use activitypub::{actor::Group, collection::UnorderedCollection, context};
use actix_web::body::Body; use actix_web::body::Body;
use actix_web::web::Path; use actix_web::web::Path;
use actix_web::HttpResponse; use actix_web::HttpResponse;
use serde::Deserialize; use serde::Deserialize;
use serde_json::json; use serde_json::{Value};
impl Community { impl Community {
pub fn as_group(&self) -> Group { pub fn as_group(&self) -> Group {
@ -16,42 +16,18 @@ impl Community {
let mut group = Group::default(); let mut group = Group::default();
// TODO: why the hell is this code so awkward?
group.object_props.set_context_object(context()).ok(); group.object_props.set_context_object(context()).ok();
// TODO: id really needs to be a url Group::set_id(&mut group, self.id);
group.object_props.set_id_string(self.id.to_string()).ok(); Group::set_title(&mut group, &self.title);
group Group::set_published(&mut group, self.published);
.object_props Group::set_updated(&mut group, self.updated);
.set_name_string(self.title.to_owned()) Group::set_creator_id(&mut group, self.creator_id);
.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();
}
if let Some(description) = &self.description { Group::set_description(&mut group, &self.description);
group.object_props.summary = Some(json!(description.to_string()));
}
group group.ap_actor_props.inbox = Value::String(format!("{}/inbox", &base_url));
.ap_actor_props group.ap_actor_props.outbox = Value::String(format!("{}/outbox", &base_url));
.set_inbox_string(format!("{}/inbox", &base_url)) group.ap_actor_props.followers = Some(Value::String(format!("{}/followers", &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 group
} }
@ -65,7 +41,6 @@ impl Community {
let connection = establish_unpooled_connection(); let connection = establish_unpooled_connection();
//As we are an object, we validated that the community id was valid //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(); let community_followers = CommunityFollowerView::for_community(&connection, self.id).unwrap();
collection collection

View file

@ -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<i32, Error>;
fn set_title(group: &mut Group, title: &str);
fn get_title(group: &Group) -> Result<String, Error>;
fn set_description(group: &mut Group, description: &Option<String>);
fn get_description(group: &Group) -> Result<Option<String>, 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<i32, Error>;
fn set_published(group: &mut Group, published: NaiveDateTime);
fn get_published(group: &Group) -> Result<NaiveDateTime, Error>;
fn set_updated(group: &mut Group, updated: Option<NaiveDateTime>);
fn get_updated(group: &Group) -> Result<Option<NaiveDateTime>, 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<i32, Error> {
Ok(get_string_value(group.clone().object_props.id).parse::<i32>()?)
}
fn set_title(group: &mut Group, title: &str) {
group.object_props.name = Some(Value::String(title.to_string()));
}
fn get_title(group: &Group) -> Result<String, Error> {
Ok(get_string_value(group.to_owned().object_props.name))
}
fn set_description(group: &mut Group, description: &Option<String>) {
group.object_props.summary = description.as_ref().map(|d| Value::String(d.to_string()));
}
fn get_description(group: &Group) -> Result<Option<String>, 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<i32, Error> {
Ok(get_string_value(group.clone().object_props.attributed_to).parse::<i32>()?)
}
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<NaiveDateTime, Error> {
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<NaiveDateTime>) {
group.object_props.updated = updated.map(|u| Value::String(u.to_string()));
}
fn get_updated(group: &Group) -> Result<Option<NaiveDateTime>, 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<Value>) -> Option<String> {
value
.as_ref()
.map(Value::as_str)
.flatten()
.map(str::to_string)
}
fn get_string_value(value: Option<Value>) -> String {
get_string_value_opt(value).unwrap()
}

View file

@ -1,4 +1,5 @@
pub mod community; pub mod community;
pub mod group_wrapper;
pub mod post; pub mod post;
pub mod puller; pub mod puller;
pub mod user; pub mod user;

View file

@ -3,11 +3,10 @@ extern crate reqwest;
use self::reqwest::Error; use self::reqwest::Error;
use crate::api::community::{GetCommunityResponse, ListCommunitiesResponse}; use crate::api::community::{GetCommunityResponse, ListCommunitiesResponse};
use crate::api::post::GetPosts; use crate::api::post::GetPosts;
use crate::apub::group_wrapper::GroupHelper;
use crate::db::community_view::CommunityView; use crate::db::community_view::CommunityView;
use crate::naive_now;
use crate::settings::Settings; use crate::settings::Settings;
use activitypub::actor::Group; 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 // 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 // things in the local database to not ruin the performance
@ -43,22 +42,20 @@ pub fn get_remote_community(identifier: String) -> Result<GetCommunityResponse,
// TODO: looks like a bunch of data is missing from the activitypub response // TODO: looks like a bunch of data is missing from the activitypub response
// TODO: i dont think simple numeric ids are going to work, we probably need something like uuids // TODO: i dont think simple numeric ids are going to work, we probably need something like uuids
// TODO: why are the Group properties not typed?
Ok(GetCommunityResponse { Ok(GetCommunityResponse {
moderators: vec![], moderators: vec![],
admins: vec![], admins: vec![],
community: CommunityView { community: CommunityView {
// TODO: why does the stupid library have everything stored as value without working autocomplete for methods???
// TODO: we need to merge id and name into a single thing (stuff like @user@instance.com) // TODO: we need to merge id and name into a single thing (stuff like @user@instance.com)
id: get_string_value(community.object_props.id).parse::<i32>()?, id: Group::get_id(&community)?,
name, name,
title: get_string_value(community.object_props.name), title: Group::get_title(&community)?,
description: get_string_value_opt(community.object_props.summary), description: Group::get_description(&community)?,
category_id: -1, category_id: -1,
creator_id: get_string_value(community.object_props.attributed_to).parse::<i32>()?, creator_id: Group::get_creator_id(&community)?,
removed: false, removed: false,
published: naive_now(), // TODO: need to handle time conversion (or handle it in apub lib) published: Group::get_published(&community)?,
updated: Some(naive_now()), // TODO: community.object_props.updated updated: Group::get_updated(&community)?,
deleted: false, deleted: false,
nsfw: false, nsfw: false,
creator_name: "".to_string(), creator_name: "".to_string(),
@ -69,24 +66,12 @@ pub fn get_remote_community(identifier: String) -> Result<GetCommunityResponse,
number_of_comments: -1, number_of_comments: -1,
hot_rank: -1, hot_rank: -1,
user_id: None, user_id: None,
subscribed: None, // TODO: looks like lemmy uses None/true for this value subscribed: None,
}, },
online: 0, online: 0,
}) })
} }
fn get_string_value_opt(value: Option<Value>) -> Option<String> {
value
.as_ref()
.map(Value::as_str)
.flatten()
.map(str::to_string)
}
fn get_string_value(value: Option<Value>) -> String {
get_string_value_opt(value).unwrap()
}
pub fn get_following_instances() -> Result<Vec<String>, Error> { pub fn get_following_instances() -> Result<Vec<String>, Error> {
let instance_list = match Settings::get().federated_instance.clone() { let instance_list = match Settings::get().federated_instance.clone() {
Some(f) => vec![f, Settings::get().hostname.clone()], Some(f) => vec![f, Settings::get().hostname.clone()],

View file

@ -557,8 +557,6 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result<Str
if community_name.contains('@') { if community_name.contains('@') {
// TODO: need to support sort, filter etc for remote communities // TODO: need to support sort, filter etc for remote communities
get_remote_community(community_name)? get_remote_community(community_name)?
// TODO what is this about
// get_community.name = Some(name.replace("!", ""));
} else { } else {
Oper::new(get_community).perform(&conn)? Oper::new(get_community).perform(&conn)?
} }