summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDessalines <tyhou13@gmx.com>2020-04-27 12:57:00 -0400
committerDessalines <tyhou13@gmx.com>2020-04-27 12:57:00 -0400
commit22abbebd41d586298c62bb6a45efa7a96d998049 (patch)
tree60c31f49e9cee360cd06edc18eb53f93e953d6b7
parent3ce061836242813730ec0b63240097347647dfc4 (diff)
Lots of additions to federation.
- Added a shared inbox. - Added federated comments, comment updates, and tests. - Abstracted ap object sends into a common trait.
-rwxr-xr-xdocker/federation-test/run-tests.sh3
-rw-r--r--server/src/api/comment.rs17
-rw-r--r--server/src/api/mod.rs6
-rw-r--r--server/src/api/post.rs4
-rw-r--r--server/src/apub/activities.rs62
-rw-r--r--server/src/apub/comment.rs139
-rw-r--r--server/src/apub/community.rs26
-rw-r--r--server/src/apub/mod.rs56
-rw-r--r--server/src/apub/post.rs48
-rw-r--r--server/src/apub/shared_inbox.rs242
-rw-r--r--server/src/apub/user_inbox.rs65
-rw-r--r--server/src/db/comment.rs8
-rw-r--r--server/src/db/comment_view.rs1
-rw-r--r--server/src/db/moderator.rs1
-rw-r--r--server/src/db/user_mention.rs1
-rw-r--r--server/src/routes/federation.rs4
-rw-r--r--ui/jest.config.js1
-rw-r--r--ui/src/api_tests/api.spec.ts300
18 files changed, 810 insertions, 174 deletions
diff --git a/docker/federation-test/run-tests.sh b/docker/federation-test/run-tests.sh
index 4206f060..9d8a7e58 100755
--- a/docker/federation-test/run-tests.sh
+++ b/docker/federation-test/run-tests.sh
@@ -12,7 +12,8 @@ sudo docker-compose --file ../federation/docker-compose.yml --project-directory
pushd ../../ui
yarn
echo "Waiting for Lemmy to start..."
-while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8540/api/v1/site')" != "200" ]]; do sleep 5; done
+while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8540/api/v1/site')" != "200" ]]; do sleep 1; done
+while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'localhost:8550/api/v1/site')" != "200" ]]; do sleep 1; done
yarn api-test || true
popd
diff --git a/server/src/api/comment.rs b/server/src/api/comment.rs
index eb67d8f2..fddb42ab 100644
--- a/server/src/api/comment.rs
+++ b/server/src/api/comment.rs
@@ -87,7 +87,8 @@ impl Perform for Oper<CreateComment> {
}
// 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());
}
@@ -101,6 +102,7 @@ impl Perform for Oper<CreateComment> {
removed: None,
deleted: None,
read: None,
+ published: None,
updated: None,
ap_id: "changeme".into(),
local: true,
@@ -111,11 +113,13 @@ impl Perform for Oper<CreateComment> {
Err(_e) => return Err(APIError::err("couldnt_create_comment").into()),
};
- match Comment::update_ap_id(&conn, inserted_comment.id) {
+ let updated_comment = match Comment::update_ap_id(&conn, inserted_comment.id) {
Ok(comment) => comment,
Err(_e) => return Err(APIError::err("couldnt_create_comment").into()),
};
+ updated_comment.send_create(&user, &conn)?;
+
let mut recipient_ids = Vec::new();
// Scan the comment for user mentions, add those rows
@@ -273,6 +277,8 @@ impl Perform for Oper<EditComment> {
let conn = pool.get()?;
+ let user = User_::read(&conn, user_id)?;
+
let orig_comment = CommentView::read(&conn, data.edit_id, None)?;
// You are allowed to mark the comment as read even if you're banned.
@@ -297,7 +303,7 @@ impl Perform for Oper<EditComment> {
}
// Check for a site ban
- if UserView::read(&conn, user_id)?.banned {
+ if user.banned {
return Err(APIError::err("site_ban").into());
}
}
@@ -314,6 +320,7 @@ impl Perform for Oper<EditComment> {
removed: data.removed.to_owned(),
deleted: data.deleted.to_owned(),
read: data.read.to_owned(),
+ published: None,
updated: if data.read.is_some() {
orig_comment.updated
} else {
@@ -323,11 +330,13 @@ impl Perform for Oper<EditComment> {
local: read_comment.local,
};
- let _updated_comment = match Comment::update(&conn, data.edit_id, &comment_form) {
+ let updated_comment = match Comment::update(&conn, data.edit_id, &comment_form) {
Ok(comment) => comment,
Err(_e) => return Err(APIError::err("couldnt_update_comment").into()),
};
+ updated_comment.send_update(&user, &conn)?;
+
let mut recipient_ids = Vec::new();
// Scan the comment for user mentions, add those rows
diff --git a/server/src/api/mod.rs b/server/src/api/mod.rs
index 0595f2a4..70ff2bfe 100644
--- a/server/src/api/mod.rs
+++ b/server/src/api/mod.rs
@@ -23,19 +23,17 @@ use crate::{
};
use crate::apub::{
- activities::{send_post_create, send_post_update},
fetcher::search_by_apub_id,
signatures::generate_actor_keypair,
- {make_apub_endpoint, ActorType, EndpointType},
+ {make_apub_endpoint, ActorType, ApubObjectType, EndpointType},
};
use crate::settings::Settings;
-use crate::websocket::UserOperation;
use crate::websocket::{
server::{
JoinCommunityRoom, JoinPostRoom, JoinUserRoom, SendAllMessage, SendComment,
SendCommunityRoomMessage, SendPost, SendUserRoomMessage,
},
- WebsocketInfo,
+ UserOperation, WebsocketInfo,
};
use diesel::r2d2::{ConnectionManager, Pool};
use diesel::PgConnection;
diff --git a/server/src/api/post.rs b/server/src/api/post.rs
index 89f1dd1d..5be227d8 100644
--- a/server/src/api/post.rs
+++ b/server/src/api/post.rs
@@ -160,7 +160,7 @@ impl Perform for Oper<CreatePost> {
Err(_e) => return Err(APIError::err("couldnt_create_post").into()),
};
- send_post_create(&updated_post, &user, &conn)?;
+ updated_post.send_create(&user, &conn)?;
// They like their own post by default
let like_form = PostLikeForm {
@@ -531,7 +531,7 @@ impl Perform for Oper<EditPost> {
ModStickyPost::create(&conn, &form)?;
}
- send_post_update(&updated_post, &user, &conn)?;
+ updated_post.send_update(&user, &conn)?;
let post_view = PostView::read(&conn, data.edit_id, Some(user_id))?;
diff --git a/server/src/apub/activities.rs b/server/src/apub/activities.rs
index cb98e973..517fd248 100644
--- a/server/src/apub/activities.rs
+++ b/server/src/apub/activities.rs
@@ -1,6 +1,6 @@
use super::*;
-fn populate_object_props(
+pub fn populate_object_props(
props: &mut ObjectProperties,
addressed_to: &str,
object_id: &str,
@@ -47,63 +47,3 @@ where
}
Ok(())
}
-
-/// For a given community, returns the inboxes of all followers.
-fn get_follower_inboxes(conn: &PgConnection, community: &Community) -> Result<Vec<String>, Error> {
- Ok(
- CommunityFollowerView::for_community(conn, community.id)?
- .into_iter()
- .filter(|c| !c.user_local)
- // TODO eventually this will have to use the inbox or shared_inbox column, meaning that view
- // will have to change
- .map(|c| format!("{}/inbox", c.user_actor_id.to_owned()))
- .unique()
- .collect(),
- )
-}
-
-/// Send out information about a newly created post, to the followers of the community.
-pub fn send_post_create(post: &Post, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
- let page = post.to_apub(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,
- &creator.private_key.as_ref().unwrap(),
- &creator.actor_id,
- get_follower_inboxes(conn, &community)?,
- )?;
- Ok(())
-}
-
-/// Send out information about an edited post, to the followers of the community.
-pub fn send_post_update(post: &Post, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
- let page = post.to_apub(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,
- &creator.private_key.as_ref().unwrap(),
- &creator.actor_id,
- get_follower_inboxes(conn, &community)?,
- )?;
- Ok(())
-}
diff --git a/server/src/apub/comment.rs b/server/src/apub/comment.rs
new file mode 100644
index 00000000..3b7c0dfe
--- /dev/null
+++ b/server/src/apub/comment.rs
@@ -0,0 +1,139 @@
+use super::*;
+
+impl ToApub for Comment {
+ type Response = Note;
+
+ fn to_apub(&self, conn: &PgConnection) -> Result<Note, Error> {
+ let mut comment = Note::default();
+ let oprops: &mut ObjectProperties = comment.as_mut();
+ let creator = User_::read(&conn, self.creator_id)?;
+ let post = Post::read(&conn, self.post_id)?;
+ let community = Community::read(&conn, post.community_id)?;
+
+ // Add a vector containing some important info to the "in_reply_to" field
+ // [post_ap_id, Option(parent_comment_ap_id)]
+ let mut in_reply_to_vec = vec![post.ap_id];
+
+ if let Some(parent_id) = self.parent_id {
+ let parent_comment = Comment::read(&conn, parent_id)?;
+ in_reply_to_vec.push(parent_comment.ap_id);
+ }
+
+ oprops
+ // Not needed when the Post is embedded in a collection (like for community outbox)
+ .set_context_xsd_any_uri(context())?
+ .set_id(self.ap_id.to_owned())?
+ // 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))?
+ .set_to_xsd_any_uri(community.actor_id)?
+ .set_many_in_reply_to_xsd_any_uris(in_reply_to_vec)?
+ .set_content_xsd_string(self.content.to_owned())?
+ .set_attributed_to_xsd_any_uri(creator.actor_id)?;
+
+ if let Some(u) = self.updated {
+ oprops.set_updated(convert_datetime(u))?;
+ }
+
+ Ok(comment)
+ }
+}
+
+impl FromApub for CommentForm {
+ type ApubType = Note;
+
+ /// Parse an ActivityPub note received from another instance into a Lemmy comment
+ fn from_apub(note: &Note, conn: &PgConnection) -> Result<CommentForm, Error> {
+ let oprops = &note.object_props;
+ let creator_actor_id = &oprops.get_attributed_to_xsd_any_uri().unwrap().to_string();
+ let creator = get_or_fetch_and_upsert_remote_user(&creator_actor_id, &conn)?;
+
+ let mut in_reply_tos = oprops.get_many_in_reply_to_xsd_any_uris().unwrap();
+ let post_ap_id = in_reply_tos.next().unwrap().to_string();
+
+ // The 2nd item, if it exists, is the parent comment apub_id
+ let parent_id: Option<i32> = match in_reply_tos.next() {
+ Some(parent_comment_uri) => {
+ let parent_comment_uri_str = &parent_comment_uri.to_string();
+ let parent_comment = Comment::read_from_apub_id(&conn, &parent_comment_uri_str)?;
+
+ Some(parent_comment.id)
+ }
+ None => None,
+ };
+
+ let post = Post::read_from_apub_id(&conn, &post_ap_id)?;
+
+ Ok(CommentForm {
+ creator_id: creator.id,
+ post_id: post.id,
+ parent_id,
+ content: oprops
+ .get_content_xsd_string()
+ .map(|c| c.to_string())
+ .unwrap(),
+ removed: None,
+ read: None,
+ published: oprops
+ .get_published()
+ .map(|u| u.as_ref().to_owned().naive_local()),
+ updated: oprops
+ .get_updated()
+ .map(|u| u.as_ref().to_owned().naive_local()),
+ deleted: None,
+ ap_id: oprops.get_id().unwrap().to_string(),
+ local: false,
+ })
+ }
+}
+
+impl ApubObjectType for Comment {
+ /// Send out information about a newly created comment, to the followers of the community.
+ fn send_create(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
+ let note = self.to_apub(conn)?;
+ let post = Post::read(&conn, self.post_id)?;
+ let community = Community::read(conn, post.community_id)?;
+ let mut create = Create::new();
+ populate_object_props(
+ &mut create.object_props,
+ &community.get_followers_url(),
+ &self.ap_id,
+ )?;
+ create
+ .create_props
+ .set_actor_xsd_any_uri(creator.actor_id.to_owned())?
+ .set_object_base_box(note)?;
+ send_activity(
+ &create,
+ &creator.private_key.as_ref().unwrap(),
+ &creator.actor_id,
+ community.get_follower_inboxes(&conn)?,
+ )?;
+ Ok(())
+ }
+
+ /// Send out information about an edited post, to the followers of the community.
+ fn send_update(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
+ let note = self.to_apub(&conn)?;
+ let post = Post::read(&conn, self.post_id)?;
+ let community = Community::read(&conn, post.community_id)?;
+ let mut update = Update::new();
+ populate_object_props(
+ &mut update.object_props,
+ &community.get_followers_url(),
+ &self.ap_id,
+ )?;
+ update
+ .update_props
+ .set_actor_xsd_any_uri(creator.actor_id.to_owned())?
+ .set_object_base_box(note)?;
+ send_activity(
+ &update,
+ &creator.private_key.as_ref().unwrap(),
+ &creator.actor_id,
+ community.get_follower_inboxes(&conn)?,
+ )?;
+ Ok(())
+ }
+}
diff --git a/server/src/apub/community.rs b/server/src/apub/community.rs
index bc984b25..d66bbc01 100644
--- a/server/src/apub/community.rs
+++ b/server/src/apub/community.rs
@@ -89,6 +89,32 @@ impl ActorType for Community {
)?;
Ok(())
}
+
+ /// For a given community, returns the inboxes of all followers.
+ fn get_follower_inboxes(&self, conn: &PgConnection) -> Result<Vec<String>, Error> {
+ debug!("got here.");
+
+ Ok(
+ CommunityFollowerView::for_community(conn, self.id)?
+ .into_iter()
+ // TODO eventually this will have to use the inbox or shared_inbox column, meaning that view
+ // will have to change
+ .map(|c| {
+ // If the user is local, but the community isn't, get the community shared inbox
+ // and vice versa
+ if c.user_local && !c.community_local {
+ get_shared_inbox(&c.community_actor_id)
+ } else if !c.user_local && c.community_local {
+ get_shared_inbox(&c.user_actor_id)
+ } else {
+ "".to_string()
+ }
+ })
+ .filter(|s| !s.is_empty())
+ .unique()
+ .collect(),
+ )
+ }
}
impl FromApub for CommunityForm {
diff --git a/server/src/apub/mod.rs b/server/src/apub/mod.rs
index 5c585299..a861156f 100644
--- a/server/src/apub/mod.rs
+++ b/server/src/apub/mod.rs
@@ -1,4 +1,5 @@
pub mod activities;
+pub mod comment;
pub mod community;
pub mod community_inbox;
pub mod fetcher;
@@ -15,7 +16,11 @@ use activitystreams::{
context,
endpoint::EndpointProperties,
ext::{Ext, Extensible, Extension},
- object::{properties::ObjectProperties, Page},
+ object::{
+ kind::{NoteType, PageType},
+ properties::ObjectProperties,
+ Note, Page,
+ },
public, BaseBox,
};
use actix_web::body::Body;
@@ -38,7 +43,11 @@ use std::collections::BTreeMap;
use std::time::Duration;
use url::Url;
+use crate::api::comment::CommentResponse;
+use crate::api::post::PostResponse;
use crate::api::site::SearchResponse;
+use crate::db::comment::{Comment, CommentForm};
+use crate::db::comment_view::CommentView;
use crate::db::community::{Community, CommunityFollower, CommunityFollowerForm, CommunityForm};
use crate::db::community_view::{CommunityFollowerView, CommunityView};
use crate::db::post::{Post, PostForm};
@@ -48,9 +57,13 @@ use crate::db::user_view::UserView;
use crate::db::{Crud, Followable, SearchType};
use crate::routes::nodeinfo::{NodeInfo, NodeInfoWellKnown};
use crate::routes::{ChatServerParam, DbPoolParam};
+use crate::websocket::{
+ server::{SendComment, SendPost},
+ UserOperation,
+};
use crate::{convert_datetime, naive_now, Settings};
-use activities::send_activity;
+use activities::{populate_object_props, send_activity};
use fetcher::{get_or_fetch_and_upsert_remote_community, get_or_fetch_and_upsert_remote_user};
use signatures::verify;
use signatures::{sign, PublicKey, PublicKeyExtension};
@@ -142,6 +155,25 @@ pub trait FromApub {
Self: Sized;
}
+pub trait ApubObjectType {
+ fn send_create(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
+ fn send_update(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
+}
+
+pub fn get_shared_inbox(actor_id: &str) -> String {
+ let url = Url::parse(actor_id).unwrap();
+ format!(
+ "{}://{}{}/inbox",
+ &url.scheme(),
+ &url.host_str().unwrap(),
+ if let Some(port) = url.port() {
+ format!(":{}", port)
+ } else {
+ "".to_string()
+ },
+ )
+}
+
pub trait ActorType {
fn actor_id(&self) -> String;
@@ -159,24 +191,20 @@ pub trait ActorType {
Ok(())
}
+ // TODO default because there is no user following yet.
+ #[allow(unused_variables)]
+ /// For a given community, returns the inboxes of all followers.
+ fn get_follower_inboxes(&self, conn: &PgConnection) -> Result<Vec<String>, Error> {
+ Ok(vec![])
+ }
+
// TODO move these to the db rows
fn get_inbox_url(&self) -> String {
format!("{}/inbox", &self.actor_id())
}
fn get_shared_inbox_url(&self) -> String {
- let url = Url::parse(&self.actor_id()).unwrap();
- let url_str = format!(
- "{}://{}{}/inbox",
- &url.scheme(),
- &url.host_str().unwrap(),
- if let Some(port) = url.port() {
- format!(":{}", port)
- } else {
- "".to_string()
- },
- );
- format!("{}/inbox", &url_str)
+ get_shared_inbox(&self.actor_id())
}
fn get_outbox_url(&self) -> String {
diff --git a/server/src/apub/post.rs b/server/src/apub/post.rs
index 51ba861e..0a054431 100644
--- a/server/src/apub/post.rs
+++ b/server/src/apub/post.rs
@@ -92,3 +92,51 @@ impl FromApub for PostForm {
})
}
}
+
+impl ApubObjectType for Post {
+ /// Send out information about a newly created post, to the followers of the community.
+ fn send_create(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
+ let page = self.to_apub(conn)?;
+ let community = Community::read(conn, self.community_id)?;
+ let mut create = Create::new();
+ populate_object_props(
+ &mut create.object_props,
+ &community.get_followers_url(),
+ &self.ap_id,
+ )?;
+ create
+ .create_props
+ .set_actor_xsd_any_uri(creator.actor_id.to_owned())?
+ .set_object_base_box(page)?;
+ send_activity(
+ &create,
+ &creator.private_key.as_ref().unwrap(),
+ &creator.actor_id,
+ community.get_follower_inboxes(&conn)?,
+ )?;
+ Ok(())
+ }
+
+ /// Send out information about an edited post, to the followers of the community.
+ fn send_update(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
+ let page = self.to_apub(conn)?;
+ let community = Community::read(conn, self.community_id)?;
+ let mut update = Update::new();
+ populate_object_props(
+ &mut update.object_props,
+ &community.get_followers_url(),
+ &self.ap_id,
+ )?;
+ update
+ .update_props
+ .set_actor_xsd_any_uri(creator.actor_id.to_owned())?
+ .set_object_base_box(page)?;
+ send_activity(
+ &update,
+ &creator.private_key.as_ref().unwrap(),
+ &creator.actor_id,
+ community.get_follower_inboxes(&conn)?,
+ )?;
+ Ok(())
+ }
+}
diff --git a/server/src/apub/shared_inbox.rs b/server/src/apub/shared_inbox.rs
index 35ba3908..f0cfc990 100644
--- a/server/src/apub/shared_inbox.rs
+++ b/server/src/apub/shared_inbox.rs
@@ -1 +1,241 @@
-// use super::*;
+use super::*;
+
+#[serde(untagged)]
+#[derive(Serialize, Deserialize, Debug)]
+pub enum SharedAcceptedObjects {
+ Create(Create),
+ Update(Update),
+}
+
+/// Handler for all incoming activities to user inboxes.
+pub async fn shared_inbox(
+ request: HttpRequest,
+ input: web::Json<SharedAcceptedObjects>,
+ db: DbPoolParam,
+ chat_server: ChatServerParam,
+) -> Result<HttpResponse, Error> {
+ // TODO: would be nice if we could do the signature check here, but we cant access the actor property
+ let input = input.into_inner();
+ let conn = &db.get().unwrap();
+
+ let json = serde_json::to_string(&input)?;
+ debug!("Shared inbox received activity: {:?}", &json);
+
+ match input {
+ SharedAcceptedObjects::Create(c) => handle_create(&c, &request, &conn, chat_server),
+ SharedAcceptedObjects::Update(u) => handle_update(&u, &request, &conn, chat_server),
+ }
+}
+
+/// Handle create activities and insert them in the database.
+fn handle_create(
+ create: &Create,
+ request: &HttpRequest,
+ conn: &PgConnection,
+ chat_server: ChatServerParam,
+) -> Result<HttpResponse, Error> {
+ let base_box = create.create_props.get_object_base_box().unwrap();
+
+ if base_box.is_kind(PageType) {
+ let page = create
+ .create_props
+ .get_object_base_box()
+ .to_owned()
+ .unwrap()
+ .to_owned()
+ .to_concrete::<Page>()?;
+ receive_create_post(&create, &page, &request, &conn, chat_server)?;
+ } else if base_box.is_kind(NoteType) {
+ let note = create
+ .create_props
+ .get_object_base_box()
+ .to_owned()
+ .unwrap()
+ .to_owned()
+ .to_concrete::<Note>()?;
+ receive_create_comment(&create, &note, &request, &conn, chat_server)?;
+ } else {
+ return Err(format_err!("Unknown base box type"));
+ }
+
+ Ok(HttpResponse::Ok().finish())
+}
+
+fn receive_create_post(
+ create: &Create,
+ page: &Page,
+ request: &HttpRequest,
+ conn: &PgConnection,
+ chat_server: ChatServerParam,
+) -> Result<(), Error> {
+ let user_uri = create
+ .create_props
+ .get_actor_xsd_any_uri()
+ .unwrap()
+ .to_string();
+
+ let user = get_or_fetch_and_upsert_remote_user(&user_uri, &conn)?;
+ verify(request, &user.public_key.unwrap())?;
+
+ let post = PostForm::from_apub(&page, &conn)?;
+ let inserted_post = Post::create(conn, &post)?;
+
+ // Refetch the view
+ let post_view = PostView::read(&conn, inserted_post.id, None)?;
+
+ let res = PostResponse { post: post_view };
+
+ chat_server.do_send(SendPost {
+ op: UserOperation::CreatePost,
+ post: res,
+ my_id: None,
+ });
+
+ Ok(())
+}
+
+fn receive_create_comment(
+ create: &Create,
+ note: &Note,
+ request: &HttpRequest,
+ conn: &PgConnection,
+ chat_server: ChatServerParam,
+) -> Result<(), Error> {
+ let user_uri = create
+ .create_props
+ .get_actor_xsd_any_uri()
+ .unwrap()
+ .to_string();
+
+ let user = get_or_fetch_and_upsert_remote_user(&user_uri, &conn)?;
+ verify(request, &user.public_key.unwrap())?;
+
+ let comment = CommentForm::from_apub(&note, &conn)?;
+ let inserted_comment = Comment::create(conn, &comment)?;
+
+ // Refetch the view
+ let comment_view = CommentView::read(&conn, inserted_comment.id, None)?;
+
+ // TODO get those recipient actor ids from somewhere
+ let recipient_ids = vec![];
+ let res = CommentResponse {
+ comment: comment_view,
+ recipient_ids,
+ };
+
+ chat_server.do_send(SendComment {
+ op: UserOperation::CreateComment,
+ comment: res,
+ my_id: None,
+ });
+
+ Ok(())
+}
+
+/// Handle create activities and insert them in the database.
+fn handle_update(
+ update: &Update,
+ request: &HttpRequest,
+ conn: &PgConnection,
+ chat_server: ChatServerParam,
+) -> Result<HttpResponse, Error> {
+ let base_box = update.update_props.get_object_base_box().unwrap();
+
+ if base_box.is_kind(PageType) {
+ let page = update
+ .update_props
+ .get_object_base_box()
+ .to_owned()
+ .unwrap()
+ .to_owned()
+ .to_concrete::<Page>()?;
+
+ receive_update_post(&update, &page, &request, &conn, chat_server)?;
+ } else if base_box.is_kind(NoteType) {
+ let note = update
+ .update_props
+ .get_object_base_box()
+ .to_owned()
+ .unwrap()
+ .to_owned()
+ .to_concrete::<Note>()?;
+ receive_update_comment(&update, &note, &request, &conn, chat_server)?;
+ } else {
+ return Err(format_err!("Unknown base box type"));
+ }
+
+ Ok(HttpResponse::Ok().finish())
+}
+
+fn receive_update_post(
+ update: &Update,
+ page: &Page,
+ request: &HttpRequest,
+ conn: &PgConnection,
+ chat_server: ChatServerParam,
+) -> Result<(), Error> {
+ let user_uri = update
+ .update_props
+ .get_actor_xsd_any_uri()
+ .unwrap()
+ .to_string();
+
+ let user = get_or_fetch_and_upsert_remote_user(&user_uri, &conn)?;
+ verify(request, &user.public_key.unwrap())?;
+
+ let post = PostForm::from_apub(&page, conn)?;
+ let post_id = Post::read_from_apub_id(conn, &post.ap_id)?.id;
+ Post::update(conn, post_id, &post)?;
+
+ // Refetch the view
+ let post_view = PostView::read(&conn, post_id, None)?;
+
+ let res = PostResponse { post: post_view };
+
+ chat_server.do_send(SendPost {
+ op: UserOperation::EditPost,
+ post: res,
+ my_id: None,
+ });
+
+ Ok(())
+}
+
+fn receive_update_comment(
+ update: &Update,
+ note: &Note,
+ request: &HttpRequest,
+ conn: &PgConnection,
+ chat_server: ChatServerParam,
+) -> Result<(), Error> {
+ let user_uri = update
+ .update_props
+ .get_actor_xsd_any_uri()
+ .unwrap()
+ .to_string();
+
+ let user = get_or_fetch_and_upsert_remote_user(&user_uri, &conn)?;
+ verify(request, &user.public_key.unwrap())?;
+
+ let comment = CommentForm::from_apub(&note, &conn)?;
+ let comment_id = Comment::read_from_apub_id(conn, &comment.ap_id)?.id;
+ Comment::update(conn, comment_id, &comment)?;
+
+ // Refetch the view
+ let comment_view = CommentView::read(&conn, comment_id, None)?;
+
+ // TODO get those recipient actor ids from somewhere
+ let recipient_ids = vec![];
+ let res = CommentResponse {
+ comment: comment_view,
+ recipient_ids,
+ };
+
+ chat_server.do_send(SendComment {
+ op: UserOperation::EditComment,
+ comment: res,
+ my_id: None,
+ });
+
+ Ok(())
+}
diff --git a/server/src/apub/user_inbox.rs b/server/src/apub/user_inbox.rs
index 251a221c..7c00b5bb 100644
--- a/server/src/apub/user_inbox.rs
+++ b/server/src/apub/user_inbox.rs
@@ -3,8 +3,6 @@ use super::*;
#[serde(untagged)]
#[derive(Deserialize, Debug)]
pub enum UserAcceptedObjects {
- Create(Create),
- Update(Update),
Accept(Accept),
}
@@ -23,73 +21,10 @@ pub async fn user_inbox(
debug!("User {} received activity: {:?}", &username, &input);
match input {
- UserAcceptedObjects::Create(c) => handle_create(&c, &request, &username, &conn),
- UserAcceptedObjects::Update(u) => handle_update(&u, &request, &username, &conn),
UserAcceptedObjects::Accept(a) => handle_accept(&a, &request, &username, &conn),
}
}
-/// Handle create activities and insert them in the database.
-fn handle_create(
- create: &Create,
- request: &HttpRequest,
- _username: &str,
- conn: &PgConnection,
-) -> Result<HttpResponse, Error> {
- // TODO before this even gets named, because we don't know what type of object it is, we need
- // to parse this out
- let user_uri = create
- .create_props
- .get_actor_xsd_any_uri()
- .unwrap()
- .to_string();
-
- let user = get_or_fetch_and_upsert_remote_user(&user_uri, &conn)?;
- verify(request, &user.public_key.unwrap())?;
-
- let page = create
- .create_props
- .get_object_base_box()
- .to_owned()
- .unwrap()
- .to_owned()
- .to_concrete::<Page>()?;
- let post = PostForm::from_apub(&page, conn)?;
- Post::create(conn, &post)?;
- // TODO: send the new post out via websocket
- Ok(HttpResponse::Ok().finish())
-}
-
-/// Handle update activities and insert them in the database.
-fn handle_update(
- update: &Update,
- request: &HttpRequest,
- _username: &str,
- conn: &PgConnection,
-) -> Result<HttpResponse, Error> {
- let user_uri = update
- .update_props
- .get_actor_xsd_any_uri()
- .unwrap()
- .to_string();
-
- let user = get_or_fetch_and_upsert_remote_user(&user_uri, &conn)?;
- verify(request, &user.public_key.unwrap())?;
-
- let page = update
- .update_props
- .get_object_base_box()
- .to_owned()
- .unwrap()
- .to_owned()
- .to_concrete::<Page>()?;
- let post = PostForm::from_apub(&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
- Ok(HttpResponse::Ok().finish())
-}
-
/// Handle accepted follows.
fn handle_accept(
accept: &Accept,
diff --git a/server/src/db/comment.rs b/server/src/db/comment.rs
index 0b8a2e20..59c2ccd2 100644
--- a/server/src/db/comment.rs
+++ b/server/src/db/comment.rs
@@ -38,6 +38,7 @@ pub struct CommentForm {
pub content: String,
pub removed: Option<bool>,
pub read: Option<bool>,
+ pub published: Option<chrono::NaiveDateTime>,
pub updated: Option<chrono::NaiveDateTime>,
pub deleted: Option<bool>,
pub ap_id: String,
@@ -84,6 +85,11 @@ impl Comment {
.get_result::<Self>(conn)
}
+ pub fn read_from_apub_id(conn: &PgConnection, object_id: &str) -> Result<Self, Error> {
+ use crate::schema::comment::dsl::*;
+ comment.filter(ap_id.eq(object_id)).first::<Self>(conn)
+ }
+
pub fn mark_as_read(conn: &PgConnection, comment_id: i32) -> Result<Self, Error> {
use crate::schema::comment::dsl::*;
@@ -283,6 +289,7 @@ mod tests {
deleted: None,
read: None,
parent_id: None,
+ published: None,
updated: None,
ap_id: "changeme".into(),
local: true,
@@ -313,6 +320,7 @@ mod tests {
removed: None,
deleted: None,
read: None,
+ published: None,
updated: None,
ap_id: "changeme".into(),
local: true,
diff --git a/server/src/db/comment_view.rs b/server/src/db/comment_view.rs
index f0b97cb5..a94aa157 100644
--- a/