summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDessalines <tyhou13@gmx.com>2020-05-05 22:06:24 -0400
committerDessalines <tyhou13@gmx.com>2020-05-05 22:06:24 -0400
commit15f1920b2515f1f7046d4e6cbb57487d41817163 (patch)
tree82c4cfdefdd43c4faec9db158817e4d0195c3ebd
parent21407260a4bfc3c173d4f17b6540805fe60f3519 (diff)
Federated private messages.
-rw-r--r--server/migrations/2020-05-05-210233_add_activitypub_for_private_messages/down.sql21
-rw-r--r--server/migrations/2020-05-05-210233_add_activitypub_for_private_messages/up.sql25
-rw-r--r--server/src/api/user.rs60
-rw-r--r--server/src/apub/mod.rs8
-rw-r--r--server/src/apub/private_message.rs234
-rw-r--r--server/src/apub/user_inbox.rs261
-rw-r--r--server/src/db/code_migrations.rs22
-rw-r--r--server/src/db/private_message.rs37
-rw-r--r--server/src/db/private_message_view.rs18
-rw-r--r--server/src/routes/api.rs8
-rw-r--r--server/src/schema.rs510
-rw-r--r--ui/src/api_tests/api.spec.ts142
-rw-r--r--ui/src/interfaces.ts6
13 files changed, 1081 insertions, 271 deletions
diff --git a/server/migrations/2020-05-05-210233_add_activitypub_for_private_messages/down.sql b/server/migrations/2020-05-05-210233_add_activitypub_for_private_messages/down.sql
new file mode 100644
index 00000000..15c92859
--- /dev/null
+++ b/server/migrations/2020-05-05-210233_add_activitypub_for_private_messages/down.sql
@@ -0,0 +1,21 @@
+drop materialized view private_message_mview;
+drop view private_message_view;
+
+alter table private_message
+drop column ap_id,
+drop column local;
+
+create view private_message_view as
+select
+pm.*,
+u.name as creator_name,
+u.avatar as creator_avatar,
+u2.name as recipient_name,
+u2.avatar as recipient_avatar
+from private_message pm
+inner join user_ u on u.id = pm.creator_id
+inner join user_ u2 on u2.id = pm.recipient_id;
+
+create materialized view private_message_mview as select * from private_message_view;
+
+create unique index idx_private_message_mview_id on private_message_mview (id);
diff --git a/server/migrations/2020-05-05-210233_add_activitypub_for_private_messages/up.sql b/server/migrations/2020-05-05-210233_add_activitypub_for_private_messages/up.sql
new file mode 100644
index 00000000..627be1f3
--- /dev/null
+++ b/server/migrations/2020-05-05-210233_add_activitypub_for_private_messages/up.sql
@@ -0,0 +1,25 @@
+alter table private_message
+add column ap_id character varying(255) not null default 'changeme', -- This needs to be checked and updated in code, building from the site url if local
+add column local boolean not null default true
+;
+
+drop materialized view private_message_mview;
+drop view private_message_view;
+create view private_message_view as
+select
+pm.*,
+u.name as creator_name,
+u.avatar as creator_avatar,
+u.actor_id as creator_actor_id,
+u.local as creator_local,
+u2.name as recipient_name,
+u2.avatar as recipient_avatar,
+u2.actor_id as recipient_actor_id,
+u2.local as recipient_local
+from private_message pm
+inner join user_ u on u.id = pm.creator_id
+inner join user_ u2 on u2.id = pm.recipient_id;
+
+create materialized view private_message_mview as select * from private_message_view;
+
+create unique index idx_private_message_mview_id on private_message_mview (id);
diff --git a/server/src/api/user.rs b/server/src/api/user.rs
index ff2760a5..b5336609 100644
--- a/server/src/api/user.rs
+++ b/server/src/api/user.rs
@@ -186,7 +186,7 @@ pub struct PrivateMessagesResponse {
#[derive(Serialize, Deserialize, Clone)]
pub struct PrivateMessageResponse {
- message: PrivateMessageView,
+ pub message: PrivateMessageView,
}
#[derive(Serialize, Deserialize, Debug)]
@@ -861,12 +861,15 @@ impl Perform for Oper<MarkAllAsRead> {
for message in &messages {
let private_message_form = PrivateMessageForm {
- content: None,
+ content: message.to_owned().content,
creator_id: message.to_owned().creator_id,
recipient_id: message.to_owned().recipient_id,
deleted: None,
read: Some(true),
updated: None,
+ ap_id: message.to_owned().ap_id,
+ local: message.local,
+ published: None,
};
let _updated_message = match PrivateMessage::update(&conn, message.id, &private_message_form)
@@ -1034,19 +1037,23 @@ impl Perform for Oper<CreatePrivateMessage> {
let conn = pool.get()?;
// 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());
}
let content_slurs_removed = remove_slurs(&data.content.to_owned());
let private_message_form = PrivateMessageForm {
- content: Some(content_slurs_removed.to_owned()),
+ content: content_slurs_removed.to_owned(),
creator_id: user_id,
recipient_id: data.recipient_id,
deleted: None,
read: None,
updated: None,
+ ap_id: "changeme".into(),
+ local: true,
+ published: None,
};
let inserted_private_message = match PrivateMessage::create(&conn, &private_message_form) {
@@ -1056,6 +1063,14 @@ impl Perform for Oper<CreatePrivateMessage> {
}
};
+ let updated_private_message =
+ match PrivateMessage::update_ap_id(&conn, inserted_private_message.id) {
+ Ok(private_message) => private_message,
+ Err(_e) => return Err(APIError::err("couldnt_create_private_message").into()),
+ };
+
+ updated_private_message.send_create(&user, &conn)?;
+
// Send notifications to the recipient
let recipient_user = User_::read(&conn, data.recipient_id)?;
if recipient_user.send_notifications_to_email {
@@ -1099,7 +1114,7 @@ impl Perform for Oper<EditPrivateMessage> {
fn perform(
&self,
pool: Pool<ConnectionManager<PgConnection>>,
- _websocket_info: Option<WebsocketInfo>,
+ websocket_info: Option<WebsocketInfo>,
) -> Result<PrivateMessageResponse, Error> {
let data: &EditPrivateMessage = &self.data;
@@ -1115,7 +1130,8 @@ impl Perform for Oper<EditPrivateMessage> {
let orig_private_message = PrivateMessage::read(&conn, data.edit_id)?;
// 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());
}
@@ -1127,8 +1143,8 @@ impl Perform for Oper<EditPrivateMessage> {
}
let content_slurs_removed = match &data.content {
- Some(content) => Some(remove_slurs(content)),
- None => None,
+ Some(content) => remove_slurs(content),
+ None => orig_private_message.content,
};
let private_message_form = PrivateMessageForm {
@@ -1142,17 +1158,41 @@ impl Perform for Oper<EditPrivateMessage> {
} else {
Some(naive_now())
},
+ ap_id: orig_private_message.ap_id,
+ local: orig_private_message.local,
+ published: None,
};
- let _updated_private_message =
+ let updated_private_message =
match PrivateMessage::update(&conn, data.edit_id, &private_message_form) {
Ok(private_message) => private_message,
Err(_e) => return Err(APIError::err("couldnt_update_private_message").into()),
};
+ if let Some(deleted) = data.deleted.to_owned() {
+ if deleted {
+ updated_private_message.send_delete(&user, &conn)?;
+ } else {
+ updated_private_message.send_undo_delete(&user, &conn)?;
+ }
+ } else {
+ updated_private_message.send_update(&user, &conn)?;
+ }
+
let message = PrivateMessageView::read(&conn, data.edit_id)?;
- Ok(PrivateMessageResponse { message })
+ let res = PrivateMessageResponse { message };
+
+ if let Some(ws) = websocket_info {
+ ws.chatserver.do_send(SendUserRoomMessage {
+ op: UserOperation::EditPrivateMessage,
+ response: res.clone(),
+ recipient_id: orig_private_message.recipient_id,
+ my_id: ws.id,
+ });
+ }
+
+ Ok(res)
}
}
diff --git a/server/src/apub/mod.rs b/server/src/apub/mod.rs
index 3c6a0060..e955f7ed 100644
--- a/server/src/apub/mod.rs
+++ b/server/src/apub/mod.rs
@@ -5,6 +5,7 @@ pub mod community_inbox;
pub mod fetcher;
pub mod page_extension;
pub mod post;
+pub mod private_message;
pub mod shared_inbox;
pub mod signatures;
pub mod user;
@@ -46,6 +47,7 @@ use url::Url;
use crate::api::comment::CommentResponse;
use crate::api::post::PostResponse;
use crate::api::site::SearchResponse;
+use crate::api::user::PrivateMessageResponse;
use crate::db::comment::{Comment, CommentForm, CommentLike, CommentLikeForm};
use crate::db::comment_view::CommentView;
use crate::db::community::{
@@ -55,13 +57,15 @@ use crate::db::community::{
use crate::db::community_view::{CommunityFollowerView, CommunityModeratorView, CommunityView};
use crate::db::post::{Post, PostForm, PostLike, PostLikeForm};
use crate::db::post_view::PostView;
+use crate::db::private_message::{PrivateMessage, PrivateMessageForm};
+use crate::db::private_message_view::PrivateMessageView;
use crate::db::user::{UserForm, User_};
use crate::db::user_view::UserView;
use crate::db::{activity, Crud, Followable, Joinable, Likeable, SearchType};
use crate::routes::nodeinfo::{NodeInfo, NodeInfoWellKnown};
use crate::routes::{ChatServerParam, DbPoolParam};
use crate::websocket::{
- server::{SendComment, SendPost},
+ server::{SendComment, SendPost, SendUserRoomMessage},
UserOperation,
};
use crate::{convert_datetime, naive_now, Settings};
@@ -85,6 +89,7 @@ pub enum EndpointType {
User,
Post,
Comment,
+ PrivateMessage,
}
/// Convert the data to json and turn it into an HTTP Response with the correct ActivityPub
@@ -120,6 +125,7 @@ pub fn make_apub_endpoint(endpoint_type: EndpointType, name: &str) -> Url {
// TODO I have to change this else my update advanced_migrations crashes the
// server if a comment exists.
EndpointType::Comment => "comment",
+ EndpointType::PrivateMessage => "private_message",
};
Url::parse(&format!(
diff --git a/server/src/apub/private_message.rs b/server/src/apub/private_message.rs
new file mode 100644
index 00000000..2fb8f6ac
--- /dev/null
+++ b/server/src/apub/private_message.rs
@@ -0,0 +1,234 @@
+use super::*;
+
+impl ToApub for PrivateMessage {
+ type Response = Note;
+
+ fn to_apub(&self, conn: &PgConnection) -> Result<Note, Error> {
+ let mut private_message = Note::default();
+ let oprops: &mut ObjectProperties = private_message.as_mut();
+ let creator = User_::read(&conn, self.creator_id)?;
+ let recipient = User_::read(&conn, self.recipient_id)?;
+
+ oprops
+ .set_context_xsd_any_uri(context())?
+ .set_id(self.ap_id.to_owned())?
+ .set_published(convert_datetime(self.published))?
+ .set_content_xsd_string(self.content.to_owned())?
+ .set_to_xsd_any_uri(recipient.actor_id)?
+ .set_attributed_to_xsd_any_uri(creator.actor_id)?;
+
+ if let Some(u) = self.updated {
+ oprops.set_updated(convert_datetime(u))?;
+ }
+
+ Ok(private_message)
+ }
+
+ fn to_tombstone(&self) -> Result<Tombstone, Error> {
+ create_tombstone(
+ self.deleted,
+ &self.ap_id,
+ self.updated,
+ NoteType.to_string(),
+ )
+ }
+}
+
+impl FromApub for PrivateMessageForm {
+ type ApubType = Note;
+
+ /// Parse an ActivityPub note received from another instance into a Lemmy Private message
+ fn from_apub(note: &Note, conn: &PgConnection) -> Result<PrivateMessageForm, 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 recipient_actor_id = &oprops.get_to_xsd_any_uri().unwrap().to_string();
+ let recipient = get_or_fetch_and_upsert_remote_user(&recipient_actor_id, &conn)?;
+
+ Ok(PrivateMessageForm {
+ creator_id: creator.id,
+ recipient_id: recipient.id,
+ content: oprops
+ .get_content_xsd_string()
+ .map(|c| c.to_string())
+ .unwrap(),
+ 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,
+ read: None,
+ ap_id: oprops.get_id().unwrap().to_string(),
+ local: false,
+ })
+ }
+}
+
+impl ApubObjectType for PrivateMessage {
+ /// Send out information about a newly created private message
+ fn send_create(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
+ let note = self.to_apub(conn)?;
+ let id = format!("{}/create/{}", self.ap_id, uuid::Uuid::new_v4());
+ let recipient = User_::read(&conn, self.recipient_id)?;
+
+ let mut create = Create::new();
+ create
+ .object_props
+ .set_context_xsd_any_uri(context())?
+ .set_id(id)?;
+ let to = format!("{}/inbox", recipient.actor_id);
+
+ create
+ .create_props
+ .set_actor_xsd_any_uri(creator.actor_id.to_owned())?
+ .set_object_base_box(note)?;
+
+ // Insert the sent activity into the activity table
+ let activity_form = activity::ActivityForm {
+ user_id: creator.id,
+ data: serde_json::to_value(&create)?,
+ local: true,
+ updated: None,
+ };
+ activity::Activity::create(&conn, &activity_form)?;
+
+ send_activity(
+ &create,
+ &creator.private_key.as_ref().unwrap(),
+ &creator.actor_id,
+ vec![to],
+ )?;
+ 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 id = format!("{}/update/{}", self.ap_id, uuid::Uuid::new_v4());
+ let recipient = User_::read(&conn, self.recipient_id)?;
+
+ let mut update = Update::new();
+ update
+ .object_props
+ .set_context_xsd_any_uri(context())?
+ .set_id(id)?;
+ let to = format!("{}/inbox", recipient.actor_id);
+
+ update
+ .update_props
+ .set_actor_xsd_any_uri(creator.actor_id.to_owned())?
+ .set_object_base_box(note)?;
+
+ // Insert the sent activity into the activity table
+ let activity_form = activity::ActivityForm {
+ user_id: creator.id,
+ data: serde_json::to_value(&update)?,
+ local: true,
+ updated: None,
+ };
+ activity::Activity::create(&conn, &activity_form)?;
+
+ send_activity(
+ &update,
+ &creator.private_key.as_ref().unwrap(),
+ &creator.actor_id,
+ vec![to],
+ )?;
+ Ok(())
+ }
+
+ fn send_delete(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
+ let note = self.to_apub(conn)?;
+ let id = format!("{}/delete/{}", self.ap_id, uuid::Uuid::new_v4());
+ let recipient = User_::read(&conn, self.recipient_id)?;
+
+ let mut delete = Delete::new();
+ delete
+ .object_props
+ .set_context_xsd_any_uri(context())?
+ .set_id(id)?;
+ let to = format!("{}/inbox", recipient.actor_id);
+
+ delete
+ .delete_props
+ .set_actor_xsd_any_uri(creator.actor_id.to_owned())?
+ .set_object_base_box(note)?;
+
+ // Insert the sent activity into the activity table
+ let activity_form = activity::ActivityForm {
+ user_id: creator.id,
+ data: serde_json::to_value(&delete)?,
+ local: true,
+ updated: None,
+ };
+ activity::Activity::create(&conn, &activity_form)?;
+
+ send_activity(
+ &delete,
+ &creator.private_key.as_ref().unwrap(),
+ &creator.actor_id,
+ vec![to],
+ )?;
+ Ok(())
+ }
+
+ fn send_undo_delete(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
+ let note = self.to_apub(conn)?;
+ let id = format!("{}/delete/{}", self.ap_id, uuid::Uuid::new_v4());
+ let recipient = User_::read(&conn, self.recipient_id)?;
+
+ let mut delete = Delete::new();
+ delete
+ .object_props
+ .set_context_xsd_any_uri(context())?
+ .set_id(id)?;
+ let to = format!("{}/inbox", recipient.actor_id);
+
+ delete
+ .delete_props
+ .set_actor_xsd_any_uri(creator.actor_id.to_owned())?
+ .set_object_base_box(note)?;
+
+ // TODO
+ // Undo that fake activity
+ let undo_id = format!("{}/undo/delete/{}", self.ap_id, uuid::Uuid::new_v4());
+ let mut undo = Undo::default();
+
+ undo
+ .object_props
+ .set_context_xsd_any_uri(context())?
+ .set_id(undo_id)?;
+
+ undo
+ .undo_props
+ .set_actor_xsd_any_uri(creator.actor_id.to_owned())?
+ .set_object_base_box(delete)?;
+
+ // Insert the sent activity into the activity table
+ let activity_form = activity::ActivityForm {
+ user_id: creator.id,
+ data: serde_json::to_value(&undo)?,
+ local: true,
+ updated: None,
+ };
+ activity::Activity::create(&conn, &activity_form)?;
+
+ send_activity(
+ &undo,
+ &creator.private_key.as_ref().unwrap(),
+ &creator.actor_id,
+ vec![to],
+ )?;
+ Ok(())
+ }
+
+ fn send_remove(&self, _mod_: &User_, _conn: &PgConnection) -> Result<(), Error> {
+ unimplemented!()
+ }
+
+ fn send_undo_remove(&self, _mod_: &User_, _conn: &PgConnection) -> Result<(), Error> {
+ unimplemented!()
+ }
+}
diff --git a/server/src/apub/user_inbox.rs b/server/src/apub/user_inbox.rs
index 4dd40161..9c25d805 100644
--- a/server/src/apub/user_inbox.rs
+++ b/server/src/apub/user_inbox.rs
@@ -3,7 +3,11 @@ use super::*;
#[serde(untagged)]
#[derive(Deserialize, Debug)]
pub enum UserAcceptedObjects {
- Accept(Accept),
+ Accept(Box<Accept>),
+ Create(Box<Create>),
+ Update(Box<Update>),
+ Delete(Box<Delete>),
+ Undo(Box<Undo>),
}
/// Handler for all incoming activities to user inboxes.
@@ -12,7 +16,7 @@ pub async fn user_inbox(
input: web::Json<UserAcceptedObjects>,
path: web::Path<String>,
db: DbPoolParam,
- _chat_server: ChatServerParam,
+ 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();
@@ -21,12 +25,24 @@ pub async fn user_inbox(
debug!("User {} received activity: {:?}", &username, &input);
match input {
- UserAcceptedObjects::Accept(a) => handle_accept(&a, &request, &username, &conn),
+ UserAcceptedObjects::Accept(a) => receive_accept(&a, &request, &username, &conn),
+ UserAcceptedObjects::Create(c) => {
+ receive_create_private_message(&c, &request, &conn, chat_server)
+ }
+ UserAcceptedObjects::Update(u) => {
+ receive_update_private_message(&u, &request, &conn, chat_server)
+ }
+ UserAcceptedObjects::Delete(d) => {
+ receive_delete_private_message(&d, &request, &conn, chat_server)
+ }
+ UserAcceptedObjects::Undo(u) => {
+ receive_undo_delete_private_message(&u, &request, &conn, chat_server)
+ }
}
}
/// Handle accepted follows.
-fn handle_accept(
+fn receive_accept(
accept: &Accept,
request: &HttpRequest,
username: &str,
@@ -65,3 +81,240 @@ fn handle_accept(
// TODO: at this point, indicate to the user that they are following the community
Ok(HttpResponse::Ok().finish())
}
+
+fn receive_create_private_message(
+ create: &Create,
+ request: &HttpRequest,
+ conn: &PgConnection,
+ chat_server: ChatServerParam,
+) -> Result<HttpResponse, Error> {
+ let note = create
+ .create_props
+ .get_object_base_box()
+ .to_owned()
+ .unwrap()
+ .to_owned()
+ .into_concrete::<Note>()?;
+
+ 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())?;
+
+ // Insert the received activity into the activity table
+ let activity_form = activity::ActivityForm {
+ user_id: user.id,
+ data: serde_json::to_value(&create)?,
+ local: false,
+ updated: None,
+ };
+ activity::Activity::create(&conn, &activity_form)?;
+
+ let private_message = PrivateMessageForm::from_apub(&note, &conn)?;
+ let inserted_private_message = PrivateMessage::create(&conn, &private_message)?;
+
+ let message = PrivateMessageView::read(&conn, inserted_private_message.id)?;
+
+ let res = PrivateMessageResponse {
+ message: message.to_owned(),
+ };
+
+ chat_server.do_send(SendUserRoomMessage {
+ op: UserOperation::CreatePrivateMessage,
+ response: res,
+ recipient_id: message.recipient_id,
+ my_id: None,
+ });
+
+ Ok(HttpResponse::Ok().finish())
+}
+
+fn receive_update_private_message(
+ update: &Update,
+ request: &HttpRequest,
+ conn: &PgConnection,
+ chat_server: ChatServerParam,
+) -> Result<HttpResponse, Error> {
+ let note = update
+ .update_props
+ .get_object_base_box()
+ .to_owned()
+ .unwrap()
+ .to_owned()
+ .into_concrete::<Note>()?;
+
+ 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())?;
+
+ // Insert the received activity into the activity table
+ let activity_form = activity::ActivityForm {
+ user_id: user.id,
+ data: serde_json::to_value(&update)?,
+ local: false,
+ updated: None,
+ };
+ activity::Activity::create(&conn, &activity_form)?;
+
+ let private_message = PrivateMessageForm::from_apub(&note, &conn)?;
+ let private_message_id = PrivateMessage::read_from_apub_id(&conn, &private_message.ap_id)?.id;
+ PrivateMessage::update(conn, private_message_id, &private_message)?;
+
+ let message = PrivateMessageView::read(&conn, private_message_id)?;
+
+ let res = PrivateMessageResponse {
+ message: message.to_owned(),
+ };
+
+ chat_server.do_send(SendUserRoomMessage {
+ op: UserOperation::EditPrivateMessage,
+ response: res,
+ recipient_id: message.recipient_id,
+ my_id: None,
+ });
+
+ Ok(HttpResponse::Ok().finish())
+}
+
+fn receive_delete_private_message(
+ delete: &Delete,
+ request: &HttpRequest,
+ conn: &PgConnection,
+ chat_server: ChatServerParam,
+) -> Result<HttpResponse, Error> {
+ let note = delete
+ .delete_props
+ .get_object_base_box()
+ .to_owned()
+ .unwrap()
+ .to_owned()
+ .into_concrete::<Note>()?;
+
+ let user_uri = delete
+ .delete_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())?;
+
+ // Insert the received activity into the activity table
+ let activity_form = activity::ActivityForm {
+ user_id: user.id,
+ data: serde_json::to_value(&delete)?,
+ local: false,
+ updated: None,
+ };
+ activity::Activity::create(&conn, &activity_form)?;
+
+ let private_message = PrivateMessageForm::from_apub(&note, &conn)?;
+ let private_message_id = PrivateMessage::read_from_apub_id(&conn, &private_message.ap_id)?.id;
+ let private_message_form = PrivateMessageForm {
+ content: private_message.content,
+ recipient_id: private_message.recipient_id,
+ creator_id: private_message.creator_id,
+ deleted: Some(true),
+ read: None,
+ ap_id: private_message.ap_id,
+ local: private_message.local,
+ published: None,
+ updated: Some(naive_now()),
+ };
+ PrivateMessage::update(conn, private_message_id, &private_message_form)?;
+
+ let message = PrivateMessageView::read(&conn, private_message_id)?;
+
+ let res = PrivateMessageResponse {
+ message: message.to_owned(),
+ };
+
+ chat_server.do_send(SendUserRoomMessage {
+ op: UserOperation::EditPrivateMessage,
+ response: res,
+ recipient_id: message.recipient_id,
+ my_id: None,
+ });
+
+ Ok(HttpResponse::Ok().finish())
+}
+
+fn receive_undo_delete_private_message(
+ undo: &Undo,
+ request: &HttpRequest,
+ conn: &PgConnection,
+ chat_server: ChatServerParam,
+) -> Result<HttpResponse, Error> {
+ let delete = undo
+ .undo_props
+ .get_object_base_box()
+ .to_owned()
+ .unwrap()
+ .to_owned()
+ .into_concrete::<Delete>()?;
+
+ let note = delete
+ .delete_props
+ .get_object_base_box()
+ .to_owned()
+ .unwrap()
+ .to_owned()
+ .into_concrete::<Note>()?;
+
+ let user_uri = delete
+ .delete_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())?;
+
+ // Insert the received activity into the activity table
+ let activity_form = activity::ActivityForm {
+ user_id: user.id,
+ data: serde_json::to_value(&delete)?,
+ local: false,
+ updated: None,
+ };
+ activity::Activity::create(&conn, &activity_form)?;
+
+ let private_message = PrivateMessageForm::from_apub(&note, &conn)?;
+ let private_message_id = PrivateMessage::read_from_apub_id(&conn, &private_message.ap_id)?.id;
+ let private_message_form = PrivateMessageForm {
+ content: private_message.content,
+ recipient_id: private_message.recipient_id,
+ creator_id: private_message.creator_id,
+ deleted: Some(false),
+ read: None,
+ ap_id: private_message.ap_id,
+ local: private_message.local,
+ published: None,
+ updated: Some(naive_now()),
+ };
+ PrivateMessage::update(conn, private_message_id, &private_message_form)?;
+
+ let message = PrivateMessageView::read(&conn, private_message_id)?;
+
+ let res = PrivateMessageResponse {
+ message: message.to_owned(),
+ };
+
+ chat_server.do_send(SendUserRoomMessage {
+ op: UserOperation::EditPrivateMessage,
+ response: res,
+ recipient_id: message.recipient_id,
+ my_id: None,
+ });
+
+ Ok(HttpResponse::Ok().finish())
+}
diff --git a/server/src/db/code_migrations.rs b/server/src/db/code_migrations.rs
index 60597199..c7f0e4b9 100644
--- a/server/src/db/code_migrations.rs
+++ b/server/src/db/code_migrations.rs
@@ -2,6 +2,7 @@
use super::comment::Comment;
use super::community::{Community, CommunityForm};
use super::post::Post;
+use super::private_message::PrivateMessage;
use super::user::{UserForm, User_};
use super::*;
use crate::apub::signatures::generate_actor_keypair;
@@ -15,6 +16,7 @@ pub fn run_advanced_migrations(conn: &PgConnection) -> Result<(), Error> {
community_updates_2020_04_02(conn)?;
post_updates_2020_04_03(conn)?;
comment_updates_2020_04_03(conn)?;
+ private_message_updates_2020_05_05(conn)?;
Ok(())
}
@@ -145,3 +147,23 @@ fn comment_updates_2020_04_03(conn: &PgConnection) -> Result<(), Error> {
Ok(())
}
+
+fn private_message_updates_2020_05_05(conn: &PgConnection) -> Result<(), Error> {
+ use crate::schema::private_message::dsl::*;
+
+ info!("Running private_message_updates_2020_05_05");
+
+ // Update the ap_id
+ let incorrect_pms = private_message
+ .filter(ap_id.eq("changeme"))
+ .filter(local.eq(true))
+ .load::<PrivateMessage>(conn)?;
+
+ for cpm in &incorrect_pms {
+ PrivateMessage::update_ap_id(&conn, cpm.id)?;
+ }
+
+ info!("{} private message rows updated.", incorrect_pms.len());
+
+ Ok(())
+}
diff --git a/server/src/db/private_message.rs b/server/src/db/private_message.rs
index 63607547..b765bef4 100644
--- a/server/src/db/private_message.rs
+++ b/server/src/db/private_message.rs
@@ -1,4 +1,5 @@
use super::*;
+use crate::apub::{make_apub_endpoint, EndpointType};
use crate::schema::private_message;
#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
@@ -12,6 +13,8 @@ pub struct PrivateMessage {
pub read: bool,
pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>,
+ pub ap_id: String,
+ pub local: bool,
}
#[derive(Insertable, AsChangeset, Clone)]
@@ -19,10 +22,13 @@ pub struct PrivateMessage {
pub struct PrivateMessageForm {
pub creator_id: i32,
pub recipient_id: i32,
- pub content: Option<String>,
+ pub content: String,
pub deleted: Option<bool>,
pub read: Option<bool>,
+ pub published: Option<chrono::NaiveDateTime>,
pub updated: Option<chrono::NaiveDateTime>,
+ pub ap_id: String,
+ pub local: bool,
}
impl Crud<PrivateMessageForm> for PrivateMessage {
@@ -55,6 +61,28 @@ impl Crud<PrivateMessageForm> for PrivateMessage {
}
}
+impl PrivateMessage {
+ pub fn update_ap_id(conn: &PgConnection, private_message_id: i32) -> Result<Self, Error> {
+ use crate::schema::private_message::dsl::*;
+
+ let apid = make_apub_endpoint(
+ EndpointType::PrivateMessage,
+ &private_message_id.to_string(),
+ )
+ .to_string();
+ diesel::update(private_message.find(private_message_id))
+ .set(ap_id.eq(apid))
+ .get_result::<Self>(conn)
+ }
+
+ pub fn read_from_apub_id(conn: &PgConnection, object_id: &str) -> Result<Self, Error> {
+ use crate::schema::private_message::dsl::*;
+ private_message
+ .filter(ap_id.eq(object_id))
+ .first::<Self>(conn)
+ }
+}
+
#[cfg(test)]
mod tests {
use super::super::user::*;
@@ -118,12 +146,15 @@ mod tests {
let inserted_recipient = User_::create(&conn, &recipient_form).unwrap();
let private_message_form = PrivateMessageForm {
- content: Some("A test private message".into()),
+ content: "A test private message".into(),
creator_id: inserted_creator.id,
recipient_id: inserted_recipient.id,
deleted: None,
read: None,
+ published: None,
updated: None,
+ ap_id: "changeme".into(),
+ local: true,
};
let inserted_private_message = PrivateMessage::create(&conn, &private_message_form).unwrap();
@@ -137,6 +168,8 @@ mod tests {
read: false,
updated: None,
published: inserted_private_message.published,
+ ap_id: "changeme".into(),
+ local: true,
};
let read_private_message = PrivateMessage::read(&conn, inserted_private_message.id).unwrap();
diff --git a/server/src/db/private_message_view.rs b/server/src/db/private_message_view.rs
index e22bef50..436178e1 100644
--- a/server/src/db/private_message_view.rs
+++ b/server/src/db/private_message_view.rs
@@ -12,10 +12,16 @@ table! {
read -> Bool,
published -> Timestamp,
updated -> Nullable<Timestamp>,
+ ap_id -> Text,
+ local -> Bool,
creator_name -> Varchar,
creator_avatar -> Nullable<Text>,
+ creator_actor_id -> Text,
+ creator_local -> Bool,
recipient_name -> Varchar,
recipient_avatar -> Nullable<Text>,
+ recipient_actor_id -> Text,
+ recipient_local -> Bool,
}
}
@@ -29,10 +35,16 @@ table! {
read -> Bool,
published -> Timestamp,
updated -> Nullable<Timestamp>,
+ ap_id -> Text,
+ local -> Bool,
creator_name -> Varchar,
creator_avatar -> Nullable<Text>,
+ creator_actor_id -> Text,
+ creator_local -> Bool,
recipient_name -> Varchar,
recipient_avatar -> Nullable<Text>,
+ recipient_actor_id -> Text,
+ recipient_local -> Bool,
}
}
@@ -49,10 +61,16 @@ pub struct PrivateMessageView {
pub read: bool,
pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>,
+ pub ap_id: String,
+ pub local: bool,
pub creator_name: String,
pub creator_avatar: Option<String>,
+ pub creator_actor_id: String,
+ pub creator_local: bool,
pub recipient_name: String,
pub recipient_avatar: Option<String>,
+ pub recipient_actor_id: String,
+ pub recipient_local: bool,
}
pub struct PrivateMessageQueryBuilder<'a> {