]> Untitled Git - lemmy.git/commitdiff
Get inbox working properly
authorFelix <me@nutomic.com>
Mon, 13 Apr 2020 13:06:41 +0000 (15:06 +0200)
committerFelix <me@nutomic.com>
Mon, 13 Apr 2020 13:06:41 +0000 (15:06 +0200)
docker/federation/docker-compose.yml
docker/federation/run-federation-test.bash
server/src/api/post.rs
server/src/apub/activities.rs
server/src/apub/community.rs
server/src/apub/fetcher.rs
server/src/apub/inbox.rs
server/src/apub/post.rs
server/src/routes/federation.rs

index f251a4f971b4c8105dd685534553bbd6b2915c54..fa95dc206a976ccdd99e2725f759a0b4b74b6cd1 100644 (file)
@@ -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
index 7cf26206f812ec78581a2f55590f38eb6be45580..62bc1e8bfa92431c7fb8b0a50f68ef034d1c2e93 100755 (executable)
@@ -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
index e0053fe8192f85805bcf96479ae999dc47e56d1a..eb8909b2d290ffac582e10a750febaa9cc4574e7 100644 (file)
@@ -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<String>,
@@ -150,12 +150,12 @@ impl Perform<PostResponse> for Oper<CreatePost> {
       }
     };
 
-    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<PostResponse> for Oper<EditPost> {
     }
 
     // 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<PostResponse> for Oper<EditPost> {
       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<PostResponse> for Oper<EditPost> {
       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 })
index ff0a4fc16bfb0c20188ada9b73a8aa014f83c0af..0c1a1901d9543812dec87127d5618b41bb3ed3a6 100644 (file)
@@ -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<A>(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!(
@@ -42,3 +48,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(())
+}
index 36b9c70337dbadd5bfb737df3232291591087471..a56d81d0c386be23b3b80588a8885a804174faf7 100644 (file)
@@ -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 {
index 3e9b6a9d1987797025062659bd5b7c6662a0506d..53c97b690ade0cbbbf7f6ada21402f04242a4c77 100644 (file)
@@ -78,8 +78,7 @@ fn fetch_remote_community_posts(
   community: &Community,
   conn: &PgConnection,
 ) -> Result<Vec<Post>, 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::<OrderedCollection>(&outbox_url)?;
   let items = outbox.collection_props.get_many_items_base_boxes();
 
index 8b6504a7f56b133cdb09927873ce7c86ac3bc74a..cc844224a2c771eb281323dabcd9f47e89d08226 100644 (file)
@@ -1,27 +1,88 @@
 use crate::db::post::{Post, PostForm};
 use crate::db::Crud;
-use activitystreams::activity::Create;
 use activitystreams::object::Page;
+use activitystreams::{
+  object::{Object, ObjectBox},
+  primitives::XsdAnyUri,
+  Base, BaseBox, PropRefs,
+};
 use actix_web::{web, HttpResponse};
 use diesel::r2d2::{ConnectionManager, Pool};
 use diesel::PgConnection;
 use failure::Error;
+use std::collections::HashMap;
 
 // TODO: need a proper actor that has this inbox
 
-pub async fn create_inbox(
-  create: web::Json<Create>,
+pub async fn inbox(
+  input: web::Json<AcceptedObjects>,
   db: web::Data<Pool<ConnectionManager<PgConnection>>>,
 ) -> Result<HttpResponse, Error> {
-  let page = create
-    .create_props
-    .get_object_base_box()
-    .unwrap()
-    .to_owned()
-    .to_concrete::<Page>()?;
-  let post = PostForm::from_page(&page, &db.get().unwrap())?;
-  Post::create(&db.get().unwrap(), &post)?;
+  let input = input.into_inner();
+  let conn = &db.get().unwrap();
+  match input.kind {
+    ValidTypes::Create => handle_create(&input, conn),
+    ValidTypes::Update => handle_update(&input, conn),
+  }
+}
+
+fn handle_create(create: &AcceptedObjects, conn: &PgConnection) -> Result<HttpResponse, Error> {
+  let page = create.object.to_owned().to_concrete::<Page>()?;
+  let post = PostForm::from_page(&page, conn)?;
+  Post::create(conn, &post)?;
+  // TODO: send the new post out via websocket
+  Ok(HttpResponse::Ok().finish())
+}
+
+fn handle_update(update: &AcceptedObjects, conn: &PgConnection) -> Result<HttpResponse, Error> {
+  let page = update.object.to_owned().to_concrete::<Page>()?;
+  let post = PostForm::from_page(&page, conn)?;
+  let id = Post::read_from_apub_id(conn, &post.ap_id)?.id;
+  Post::update(conn, id, &post)?;
   // TODO: send the new post out via websocket
-  dbg!(&post);
   Ok(HttpResponse::Ok().finish())
 }
+
+#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
+#[serde(rename_all = "camelCase")]
+pub struct AcceptedObjects {
+  pub id: XsdAnyUri,
+
+  #[serde(rename = "type")]
+  pub kind: ValidTypes,
+
+  pub actor: XsdAnyUri,
+
+  pub object: BaseBox,
+
+  #[serde(flatten)]
+  ext: HashMap<String, serde_json::Value>,
+}
+
+#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Deserialize, serde::Serialize)]
+#[serde(rename_all = "PascalCase")]
+pub enum ValidTypes {
+  Create,
+  Update,
+}
+
+#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
+#[serde(untagged)]
+#[serde(rename_all = "camelCase")]
+pub enum ValidObjects {
+  Id(XsdAnyUri),
+  Object(AnyExistingObject),
+}
+
+#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize, PropRefs)]
+#[serde(rename_all = "camelCase")]
+#[prop_refs(Object)]
+pub struct AnyExistingObject {
+  pub id: XsdAnyUri,
+
+  #[serde(rename = "type")]
+  pub kind: String,
+
+  #[serde(flatten)]
+  ext: HashMap<String, serde_json::Value>,
+}
index e45a7d2102dddeb8d1d8825b7d83563888f9f78b..e8f539049eb72941a22d9f6b80e3f34a0f169566 100644 (file)
@@ -41,7 +41,7 @@ impl Post {
       // Not needed when the Post is embedded in a collection (like for community outbox)
       .set_context_xsd_any_uri(context())?
       .set_id(base_url)?
-        // Use summary field to be consistent with mastodon content warning.
+      // Use summary field to be consistent with mastodon content warning.
       // https://mastodon.xyz/@Louisa/103987265222901387.json
       .set_summary_xsd_string(self.name.to_owned())?
       .set_published(convert_datetime(self.published))?
index f4fffdad4ba2da13088925b4b2ded7b586c3acee..100e548f67fd45404b8bf894a15fe53ba6e1631e 100644 (file)
@@ -11,10 +11,8 @@ pub fn config(cfg: &mut web::ServiceConfig) {
         web::get().to(apub::community::get_apub_community_list),
       )
       // TODO: this needs to be moved to the actors (eg /federation/u/{}/inbox)
-      .route(
-        "/federation/inbox",
-        web::post().to(apub::inbox::create_inbox),
-      )
+      .route("/federation/inbox", web::post().to(apub::inbox::inbox))
+      .route("/federation/inbox", web::post().to(apub::inbox::inbox))
       .route(
         "/federation/c/{community_name}",
         web::get().to(apub::community::get_apub_community_http),