summaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorFelix <me@nutomic.com>2020-04-13 15:06:41 +0200
committerFelix <me@nutomic.com>2020-04-13 15:06:41 +0200
commitfdaf0b33642fec0e3e47b86cd7284b2fcea13ce9 (patch)
treed35aeb00bcae525aa860de9ec6b6aefadf5e7f28 /server
parentfac1cc7e1dc3b78aa48ae8c945c8c1cbd9e7fc9e (diff)
Get inbox working properly
Diffstat (limited to 'server')
-rw-r--r--server/src/api/post.rs15
-rw-r--r--server/src/apub/activities.rs76
-rw-r--r--server/src/apub/community.rs16
-rw-r--r--server/src/apub/fetcher.rs3
-rw-r--r--server/src/apub/inbox.rs85
-rw-r--r--server/src/apub/post.rs2
-rw-r--r--server/src/routes/federation.rs6
7 files changed, 157 insertions, 46 deletions
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<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 })
diff --git a/server/src/apub/activities.rs b/server/src/apub/activities.rs
index ff0a4fc1..0c1a1901 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<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(())
+}
diff --git a/server/src/apub/community.rs b/server/src/apub/community.rs
index 36b9c703..a56d81d0 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<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();
diff --git a/server/src/apub/inbox.rs b/server/src/apub/inbox.rs
index 8b6504a7..cc844224 100644
--- a/server/src/apub/inbox.rs
+++ b/server/src/apub/inbox.rs
@@ -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>,
+}
diff --git a/server/src/apub/post.rs b/server/src/apub/post.rs
index e45a7d21..e8f53904 100644
--- a/server/src/apub/post.rs
+++ b/server/src/apub/post.rs
@@ -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))?
diff --git a/server/src/routes/federation.rs b/server/src/routes/federation.rs
index f4fffdad..100e548f 100644
--- a/server/src/routes/federation.rs
+++ b/server/src/routes/federation.rs
@@ -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),