diff --git a/docker/federation/docker-compose.yml b/docker/federation/docker-compose.yml index f251a4f9..fa95dc20 100644 --- a/docker/federation/docker-compose.yml +++ b/docker/federation/docker-compose.yml @@ -28,6 +28,7 @@ services: - LEMMY_FEDERATION__TLS_ENABLED=false - LEMMY_PORT=8540 - RUST_BACKTRACE=1 + - RUST_LOG=actix_web=debug restart: always depends_on: - postgres_alpha @@ -58,6 +59,7 @@ services: - LEMMY_FEDERATION__TLS_ENABLED=false - LEMMY_PORT=8550 - RUST_BACKTRACE=1 + - RUST_LOG=actix_web=debug restart: always depends_on: - postgres_beta diff --git a/docker/federation/run-federation-test.bash b/docker/federation/run-federation-test.bash index 7cf26206..62bc1e8b 100755 --- a/docker/federation/run-federation-test.bash +++ b/docker/federation/run-federation-test.bash @@ -1,9 +1,11 @@ #!/bin/bash set -e -pushd ../../ui/ || exit -yarn build -popd || exit +if [ "$1" = "-yarn" ]; then + pushd ../../ui/ || exit + yarn build + popd || exit +fi pushd ../../server/ || exit cargo build diff --git a/server/src/api/post.rs b/server/src/api/post.rs index e0053fe8..eb8909b2 100644 --- a/server/src/api/post.rs +++ b/server/src/api/post.rs @@ -1,9 +1,9 @@ use super::*; -use crate::apub::activities::post_create; +use crate::apub::activities::{post_create, post_update}; use diesel::PgConnection; use std::str::FromStr; -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Debug)] pub struct CreatePost { name: String, url: Option, @@ -150,12 +150,12 @@ impl Perform for Oper { } }; - match Post::update_ap_id(&conn, inserted_post.id) { + let updated_post = match Post::update_ap_id(&conn, inserted_post.id) { Ok(post) => post, Err(_e) => return Err(APIError::err("couldnt_create_post").into()), }; - post_create(&inserted_post, &user, conn)?; + post_create(&updated_post, &user, conn)?; // They like their own post by default let like_form = PostLikeForm { @@ -369,7 +369,8 @@ impl Perform for Oper { } // Check for a site ban - if UserView::read(&conn, user_id)?.banned { + let user = User_::read(&conn, user_id)?; + if user.banned { return Err(APIError::err("site_ban").into()); } @@ -400,7 +401,7 @@ impl Perform for Oper { published: None, }; - let _updated_post = match Post::update(&conn, data.edit_id, &post_form) { + let updated_post = match Post::update(&conn, data.edit_id, &post_form) { Ok(post) => post, Err(e) => { let err_type = if e.to_string() == "value too long for type character varying(200)" { @@ -442,6 +443,8 @@ impl Perform for Oper { ModStickyPost::create(&conn, &form)?; } + post_update(&updated_post, &user, conn)?; + let post_view = PostView::read(&conn, data.edit_id, Some(user_id))?; Ok(PostResponse { post: post_view }) diff --git a/server/src/apub/activities.rs b/server/src/apub/activities.rs index ff0a4fc1..aedc53f5 100644 --- a/server/src/apub/activities.rs +++ b/server/src/apub/activities.rs @@ -3,30 +3,36 @@ use crate::db::community::Community; use crate::db::post::Post; use crate::db::user::User_; use crate::db::Crud; -use activitystreams::activity::Create; -use activitystreams::context; +use activitystreams::activity::{Create, Update}; +use activitystreams::object::properties::ObjectProperties; +use activitystreams::{context, public}; use diesel::PgConnection; use failure::Error; +use failure::_core::fmt::Debug; use isahc::prelude::*; +use serde::Serialize; -pub fn post_create(post: &Post, creator: &User_, conn: &PgConnection) -> Result<(), Error> { - let page = post.as_page(conn)?; - let community = Community::read(conn, post.community_id)?; - let mut create = Create::new(); - create.object_props.set_context_xsd_any_uri(context())?; - create - .object_props - // TODO: seems like the create activity needs its own id (and be fetchable there) - .set_id(page.object_props.get_id().unwrap().to_string())? +fn populate_object_props( + props: &mut ObjectProperties, + addressed_to: &str, + object_id: &str, +) -> Result<(), Error> { + props + .set_context_xsd_any_uri(context())? + // TODO: the activity needs a seperate id from the object + .set_id(object_id)? // TODO: should to/cc go on the Create, or on the Post? or on both? // TODO: handle privacy on the receiving side (at least ignore anything thats not public) - .set_to_xsd_any_uri("https://www.w3.org/ns/activitystreams#Public")? - .set_cc_xsd_any_uri(format!("{}/followers", community.actor_id))?; - create - .create_props - .set_actor_xsd_any_uri(creator.actor_id.to_owned())?; - create.create_props.set_object_base_box(page)?; - let json = serde_json::to_string(&create)?; + .set_to_xsd_any_uri(public())? + .set_cc_xsd_any_uri(addressed_to)?; + Ok(()) +} + +fn send_activity(activity: &A) -> Result<(), Error> +where + A: Serialize + Debug, +{ + let json = serde_json::to_string(&activity)?; for i in get_following_instances() { // TODO: need to send this to the inbox of following users let inbox = format!( @@ -34,6 +40,8 @@ pub fn post_create(post: &Post, creator: &User_, conn: &PgConnection) -> Result< get_apub_protocol_string(), i.domain ); + dbg!(&inbox); + dbg!(&json); let res = Request::post(inbox) .header("Content-Type", "application/json") .body(json.to_owned())? @@ -42,3 +50,37 @@ pub fn post_create(post: &Post, creator: &User_, conn: &PgConnection) -> Result< } Ok(()) } + +pub fn post_create(post: &Post, creator: &User_, conn: &PgConnection) -> Result<(), Error> { + let page = post.as_page(conn)?; + let community = Community::read(conn, post.community_id)?; + let mut create = Create::new(); + populate_object_props( + &mut create.object_props, + &community.get_followers_url(), + &post.ap_id, + )?; + create + .create_props + .set_actor_xsd_any_uri(creator.actor_id.to_owned())? + .set_object_base_box(page)?; + send_activity(&create)?; + Ok(()) +} + +pub fn post_update(post: &Post, creator: &User_, conn: &PgConnection) -> Result<(), Error> { + let page = post.as_page(conn)?; + let community = Community::read(conn, post.community_id)?; + let mut update = Update::new(); + populate_object_props( + &mut update.object_props, + &community.get_followers_url(), + &post.ap_id, + )?; + update + .update_props + .set_actor_xsd_any_uri(creator.actor_id.to_owned())? + .set_object_base_box(page)?; + send_activity(&update)?; + Ok(()) +} diff --git a/server/src/apub/community.rs b/server/src/apub/community.rs index 76a94ed2..f3f74648 100644 --- a/server/src/apub/community.rs +++ b/server/src/apub/community.rs @@ -79,9 +79,9 @@ impl Community { actor_props .set_preferred_username(self.title.to_owned())? - .set_inbox(format!("{}/inbox", &self.actor_id))? - .set_outbox(format!("{}/outbox", &self.actor_id))? - .set_followers(format!("{}/followers", &self.actor_id))?; + .set_inbox(self.get_inbox_url())? + .set_outbox(self.get_outbox_url())? + .set_followers(self.get_followers_url())?; let public_key = PublicKey { id: format!("{}#main-key", self.actor_id), @@ -91,6 +91,16 @@ impl Community { Ok(group.extend(actor_props).extend(public_key.to_ext())) } + + pub fn get_followers_url(&self) -> String { + format!("{}/followers", &self.actor_id) + } + pub fn get_inbox_url(&self) -> String { + format!("{}/inbox", &self.actor_id) + } + pub fn get_outbox_url(&self) -> String { + format!("{}/outbox", &self.actor_id) + } } impl CommunityForm { diff --git a/server/src/apub/fetcher.rs b/server/src/apub/fetcher.rs index 3e9b6a9d..53c97b69 100644 --- a/server/src/apub/fetcher.rs +++ b/server/src/apub/fetcher.rs @@ -78,8 +78,7 @@ fn fetch_remote_community_posts( community: &Community, conn: &PgConnection, ) -> Result, Error> { - // TODO: need to add outbox field to Community - let outbox_url = Url::parse(&format!("{}/outbox", community.actor_id))?; + let outbox_url = Url::parse(&community.get_outbox_url())?; let outbox = fetch_remote_object::(&outbox_url)?; let items = outbox.collection_props.get_many_items_base_boxes(); diff --git a/server/src/apub/inbox.rs b/server/src/apub/inbox.rs index 8b6504a7..cd513540 100644 --- a/server/src/apub/inbox.rs +++ b/server/src/apub/inbox.rs @@ -1,6 +1,6 @@ use crate::db::post::{Post, PostForm}; use crate::db::Crud; -use activitystreams::activity::Create; +use activitystreams::activity::{Create, Update}; use activitystreams::object::Page; use actix_web::{web, HttpResponse}; use diesel::r2d2::{ConnectionManager, Pool}; @@ -9,10 +9,11 @@ use failure::Error; // TODO: need a proper actor that has this inbox -pub async fn create_inbox( +pub async fn inbox_create( create: web::Json, db: web::Data>>, ) -> Result { + dbg!(&create); let page = create .create_props .get_object_base_box() @@ -25,3 +26,23 @@ pub async fn create_inbox( dbg!(&post); Ok(HttpResponse::Ok().finish()) } + +// TODO: invalid type? +pub async fn inbox_update( + update: web::Json, + db: web::Data>>, +) -> Result { + dbg!(&update); + let page = update + .update_props + .get_object_base_box() + .unwrap() + .to_owned() + .to_concrete::()?; + let post = PostForm::from_page(&page, &db.get().unwrap())?; + let id = Post::read_from_apub_id(&db.get().unwrap(), &post.ap_id)?.id; + Post::update(&db.get().unwrap(), id, &post)?; + // TODO: send the new post out via websocket + dbg!(&post); + Ok(HttpResponse::Ok().finish()) +} diff --git a/server/src/routes/federation.rs b/server/src/routes/federation.rs index f4fffdad..4ceefc3c 100644 --- a/server/src/routes/federation.rs +++ b/server/src/routes/federation.rs @@ -13,7 +13,11 @@ pub fn config(cfg: &mut web::ServiceConfig) { // TODO: this needs to be moved to the actors (eg /federation/u/{}/inbox) .route( "/federation/inbox", - web::post().to(apub::inbox::create_inbox), + web::post().to(apub::inbox::inbox_create), + ) + .route( + "/federation/inbox", + web::post().to(apub::inbox::inbox_update), ) .route( "/federation/c/{community_name}",