mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-12-25 12:21:31 +00:00
improved community federation (wip)
This commit is contained in:
parent
b7ed716659
commit
da21e4e1c3
5 changed files with 111 additions and 61 deletions
|
@ -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
|
||||||
|
|
91
server/src/apub/group_wrapper.rs
Normal file
91
server/src/apub/group_wrapper.rs
Normal 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()
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
|
@ -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()],
|
||||||
|
|
|
@ -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)?
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue