From f7330bf062114853a81d24ab6d15fe2bdf7018a1 Mon Sep 17 00:00:00 2001 From: Felix Date: Thu, 9 Apr 2020 21:04:31 +0200 Subject: [PATCH] Implement create activity --- server/src/api/post.rs | 3 +++ server/src/apub/activities.rs | 36 +++++++++++++++++++++++++++++++++ server/src/apub/inbox.rs | 25 +++++++++++++++++++++++ server/src/apub/mod.rs | 2 ++ server/src/apub/post.rs | 14 +++++++++---- server/src/apub/puller.rs | 14 +++++++++++-- server/src/routes/federation.rs | 24 +++------------------- 7 files changed, 91 insertions(+), 27 deletions(-) create mode 100644 server/src/apub/activities.rs create mode 100644 server/src/apub/inbox.rs diff --git a/server/src/api/post.rs b/server/src/api/post.rs index 18c33dc1a..7e7126a2f 100644 --- a/server/src/api/post.rs +++ b/server/src/api/post.rs @@ -1,4 +1,5 @@ use super::*; +use crate::apub::activities::post_create; use diesel::PgConnection; use std::str::FromStr; @@ -153,6 +154,8 @@ impl Perform for Oper { Err(_e) => return Err(APIError::err("couldnt_create_post").into()), }; + post_create(&inserted_post, conn)?; + // They like their own post by default let like_form = PostLikeForm { post_id: inserted_post.id, diff --git a/server/src/apub/activities.rs b/server/src/apub/activities.rs new file mode 100644 index 000000000..81a624b05 --- /dev/null +++ b/server/src/apub/activities.rs @@ -0,0 +1,36 @@ +use crate::apub::{get_apub_protocol_string, get_following_instances}; +use crate::db::post::Post; +use crate::db::user::User_; +use crate::db::Crud; +use activitystreams::activity::Create; +use activitystreams::context; +use diesel::PgConnection; +use failure::Error; +use isahc::prelude::*; + +pub fn post_create(post: &Post, conn: &PgConnection) -> Result<(), Error> { + let user = User_::read(conn, post.creator_id)?; + let page = post.as_page(conn)?; + let mut create = Create::new(); + create.object_props.set_context_xsd_any_uri(context())?; + // TODO: seems like the create activity needs its own id (and be fetchable there) + create + .object_props + .set_id(page.object_props.get_id().unwrap().to_string())?; + create.create_props.set_actor_xsd_any_uri(user.actor_id)?; + create.create_props.set_object_base_box(page)?; + let json = serde_json::to_string(&create)?; + for i in get_following_instances() { + let inbox = format!( + "{}://{}/federation/inbox", + get_apub_protocol_string(), + i.domain + ); + let res = Request::post(inbox) + .header("Content-Type", "application/json") + .body(json.to_owned())? + .send()?; + dbg!(res); + } + Ok(()) +} diff --git a/server/src/apub/inbox.rs b/server/src/apub/inbox.rs new file mode 100644 index 000000000..53da497f2 --- /dev/null +++ b/server/src/apub/inbox.rs @@ -0,0 +1,25 @@ +use crate::db::post::{Post, PostForm}; +use crate::db::Crud; +use activitystreams::activity::Create; +use activitystreams::object::Page; +use actix_web::{web, HttpResponse}; +use diesel::r2d2::{ConnectionManager, Pool}; +use diesel::PgConnection; +use failure::Error; + +pub async fn inbox( + create: web::Json, + db: web::Data>>, +) -> Result { + let page = create + .create_props + .get_object_base_box() + .unwrap() + .to_owned() + .to_concrete::()?; + let post = PostForm::from_page(&page, &db.get().unwrap())?; + Post::create(&db.get().unwrap(), &post)?; + // TODO: send the new post out via websocket + dbg!(&post); + Ok(HttpResponse::Ok().finish()) +} diff --git a/server/src/apub/mod.rs b/server/src/apub/mod.rs index c9d5e0e57..eb3097c7a 100644 --- a/server/src/apub/mod.rs +++ b/server/src/apub/mod.rs @@ -1,4 +1,6 @@ +pub mod activities; pub mod community; +pub mod inbox; pub mod post; pub mod puller; pub mod user; diff --git a/server/src/apub/post.rs b/server/src/apub/post.rs index 198d9d2fa..8458e8c91 100644 --- a/server/src/apub/post.rs +++ b/server/src/apub/post.rs @@ -1,6 +1,7 @@ -use crate::apub::puller::fetch_remote_user; +use crate::apub::puller::{fetch_remote_community, fetch_remote_user}; use crate::apub::{create_apub_response, make_apub_endpoint, EndpointType}; use crate::convert_datetime; +use crate::db::community::Community; use crate::db::post::{Post, PostForm}; use crate::db::user::User_; use crate::db::Crud; @@ -34,6 +35,7 @@ impl Post { let mut page = Page::default(); let oprops: &mut ObjectProperties = page.as_mut(); let creator = User_::read(conn, self.creator_id)?; + let community = Community::read(conn, self.community_id)?; oprops // Not needed when the Post is embedded in a collection (like for community outbox) @@ -41,6 +43,7 @@ impl Post { .set_id(base_url)? .set_name_xsd_string(self.name.to_owned())? .set_published(convert_datetime(self.published))? + .set_to_xsd_any_uri(community.actor_id)? .set_attributed_to_xsd_any_uri(make_apub_endpoint(EndpointType::User, &creator.name))?; if let Some(body) = &self.body { @@ -65,14 +68,17 @@ impl Post { impl PostForm { pub fn from_page(page: &Page, conn: &PgConnection) -> Result { let oprops = &page.object_props; - let apub_id = Url::parse(&oprops.get_attributed_to_xsd_any_uri().unwrap().to_string())?; - let creator = fetch_remote_user(&apub_id, conn)?; + let creator_id = Url::parse(&oprops.get_attributed_to_xsd_any_uri().unwrap().to_string())?; + let creator = fetch_remote_user(&creator_id, conn)?; + let community_id = Url::parse(&oprops.get_to_xsd_any_uri().unwrap().to_string())?; + let community = fetch_remote_community(&community_id, conn)?; + Ok(PostForm { name: oprops.get_name_xsd_string().unwrap().to_string(), url: oprops.get_url_xsd_any_uri().map(|u| u.to_string()), body: oprops.get_content_xsd_string().map(|c| c.to_string()), creator_id: creator.id, - community_id: -1, + community_id: community.id, removed: None, locked: None, published: oprops diff --git a/server/src/apub/puller.rs b/server/src/apub/puller.rs index a00f4d468..3e9b6a9d1 100644 --- a/server/src/apub/puller.rs +++ b/server/src/apub/puller.rs @@ -91,8 +91,7 @@ fn fetch_remote_community_posts( PostForm::from_page(&page, conn) }) .map(|pf: Result| -> Result { - let mut pf2 = pf?; - pf2.community_id = community.id; + let pf2 = pf?; let existing = Post::read_from_apub_id(conn, &pf2.ap_id); match existing { Err(NotFound {}) => Ok(Post::create(conn, &pf2)?), @@ -104,6 +103,7 @@ fn fetch_remote_community_posts( ) } +// TODO: can probably merge these two methods? pub fn fetch_remote_user(apub_id: &Url, conn: &PgConnection) -> Result { let person = fetch_remote_object::(apub_id)?; let uf = UserForm::from_person(&person)?; @@ -114,6 +114,16 @@ pub fn fetch_remote_user(apub_id: &Url, conn: &PgConnection) -> Result return Err(Error::from(e)), }) } +pub fn fetch_remote_community(apub_id: &Url, conn: &PgConnection) -> Result { + let group = fetch_remote_object::(apub_id)?; + let cf = CommunityForm::from_group(&group, conn)?; + let existing = Community::read_from_actor_id(conn, &cf.actor_id); + Ok(match existing { + Err(NotFound {}) => Community::create(conn, &cf)?, + Ok(u) => Community::update(conn, u.id, &cf)?, + Err(e) => return Err(Error::from(e)), + }) +} // TODO: in the future, this should only be done when an instance is followed for the first time // after that, we should rely in the inbox, and fetch on demand when needed diff --git a/server/src/routes/federation.rs b/server/src/routes/federation.rs index 28041d312..64cc5a814 100644 --- a/server/src/routes/federation.rs +++ b/server/src/routes/federation.rs @@ -1,12 +1,6 @@ -use crate::api::community::ListCommunities; -use crate::api::Oper; -use crate::api::Perform; use crate::apub; use crate::settings::Settings; -use actix_web::web::Query; -use actix_web::{web, HttpResponse}; -use diesel::r2d2::{ConnectionManager, Pool}; -use diesel::PgConnection; +use actix_web::web; pub fn config(cfg: &mut web::ServiceConfig) { if Settings::get().federation.enabled { @@ -16,6 +10,8 @@ pub fn config(cfg: &mut web::ServiceConfig) { "/federation/communities", web::get().to(apub::community::get_apub_community_list), ) + // TODO: need a proper actor that has this inbox + .route("/federation/inbox", web::post().to(apub::inbox::inbox)) .route( "/federation/c/{community_name}", web::get().to(apub::community::get_apub_community_http), @@ -35,20 +31,6 @@ pub fn config(cfg: &mut web::ServiceConfig) { .route( "/federation/p/{post_id}", web::get().to(apub::user::get_apub_user), - ) - // TODO: we should be able to remove this but somehow that breaks the remote community list - .route( - "/api/v1/communities/list", - web::get().to( - |query: Query, db: web::Data>>| { - let res = Oper::new(query.into_inner()) - .perform(&db.get().unwrap()) - .unwrap(); - HttpResponse::Ok() - .content_type("application/json") - .body(serde_json::to_string(&res).unwrap()) - }, - ), ); } }