summaryrefslogtreecommitdiffstats
path: root/server/src
diff options
context:
space:
mode:
authorDessalines <tyhou13@gmx.com>2020-01-22 16:35:29 -0500
committerDessalines <tyhou13@gmx.com>2020-01-22 16:38:16 -0500
commit253bc3e0afb6adf64b79f334a8bc1f972aa45eba (patch)
treebdbbb36ee59ea4331c7bf7cf89e8ef554eb2d63e /server/src
parenta964b4ce21cc19eb42ae4da1a1aef8bfc0a1df5c (diff)
Adding private messaging, and matrix user ids.
- Fixes #244
Diffstat (limited to 'server/src')
-rw-r--r--server/src/api/comment.rs4
-rw-r--r--server/src/api/mod.rs5
-rw-r--r--server/src/api/user.rs214
-rw-r--r--server/src/apub/mod.rs1
-rw-r--r--server/src/db/comment.rs1
-rw-r--r--server/src/db/comment_view.rs1
-rw-r--r--server/src/db/community.rs1
-rw-r--r--server/src/db/mod.rs2
-rw-r--r--server/src/db/moderator.rs2
-rw-r--r--server/src/db/password_reset_request.rs1
-rw-r--r--server/src/db/post.rs1
-rw-r--r--server/src/db/post_view.rs1
-rw-r--r--server/src/db/private_message.rs144
-rw-r--r--server/src/db/private_message_view.rs140
-rw-r--r--server/src/db/user.rs4
-rw-r--r--server/src/db/user_mention.rs2
-rw-r--r--server/src/db/user_view.rs3
-rw-r--r--server/src/routes/index.rs1
-rw-r--r--server/src/schema.rs15
-rw-r--r--server/src/websocket/server.rs16
20 files changed, 557 insertions, 2 deletions
diff --git a/server/src/api/comment.rs b/server/src/api/comment.rs
index 61cc9506..382afb5b 100644
--- a/server/src/api/comment.rs
+++ b/server/src/api/comment.rs
@@ -7,7 +7,7 @@ use diesel::PgConnection;
pub struct CreateComment {
content: String,
parent_id: Option<i32>,
- edit_id: Option<i32>,
+ edit_id: Option<i32>, // TODO this isn't used
pub post_id: i32,
auth: String,
}
@@ -15,7 +15,7 @@ pub struct CreateComment {
#[derive(Serialize, Deserialize)]
pub struct EditComment {
content: String,
- parent_id: Option<i32>,
+ parent_id: Option<i32>, // TODO why are the parent_id, creator_id, post_id, etc fields required? They aren't going to change
edit_id: i32,
creator_id: i32,
pub post_id: i32,
diff --git a/server/src/api/mod.rs b/server/src/api/mod.rs
index e3580447..3b2466ac 100644
--- a/server/src/api/mod.rs
+++ b/server/src/api/mod.rs
@@ -8,6 +8,8 @@ use crate::db::moderator_views::*;
use crate::db::password_reset_request::*;
use crate::db::post::*;
use crate::db::post_view::*;
+use crate::db::private_message::*;
+use crate::db::private_message_view::*;
use crate::db::site::*;
use crate::db::site_view::*;
use crate::db::user::*;
@@ -67,6 +69,9 @@ pub enum UserOperation {
DeleteAccount,
PasswordReset,
PasswordChange,
+ CreatePrivateMessage,
+ EditPrivateMessage,
+ GetPrivateMessages,
}
#[derive(Fail, Debug)]
diff --git a/server/src/api/user.rs b/server/src/api/user.rs
index ac700aca..046da6fb 100644
--- a/server/src/api/user.rs
+++ b/server/src/api/user.rs
@@ -30,6 +30,7 @@ pub struct SaveUserSettings {
lang: String,
avatar: Option<String>,
email: Option<String>,
+ matrix_user_id: Option<String>,
new_password: Option<String>,
new_password_verify: Option<String>,
old_password: Option<String>,
@@ -167,6 +168,42 @@ pub struct PasswordChange {
password_verify: String,
}
+#[derive(Serialize, Deserialize)]
+pub struct CreatePrivateMessage {
+ content: String,
+ recipient_id: i32,
+ auth: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct EditPrivateMessage {
+ edit_id: i32,
+ content: Option<String>,
+ deleted: Option<bool>,
+ read: Option<bool>,
+ auth: String,
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct GetPrivateMessages {
+ unread_only: bool,
+ page: Option<i64>,
+ limit: Option<i64>,
+ auth: String,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+pub struct PrivateMessagesResponse {
+ op: String,
+ messages: Vec<PrivateMessageView>,
+}
+
+#[derive(Serialize, Deserialize, Clone)]
+pub struct PrivateMessageResponse {
+ op: String,
+ message: PrivateMessageView,
+}
+
impl Perform<LoginResponse> for Oper<Login> {
fn perform(&self, conn: &PgConnection) -> Result<LoginResponse, Error> {
let data: &Login = &self.data;
@@ -221,6 +258,7 @@ impl Perform<LoginResponse> for Oper<Register> {
name: data.username.to_owned(),
fedi_name: Settings::get().hostname.to_owned(),
email: data.email.to_owned(),
+ matrix_user_id: None,
avatar: None,
password_encrypted: data.password.to_owned(),
preferred_username: None,
@@ -357,6 +395,7 @@ impl Perform<LoginResponse> for Oper<SaveUserSettings> {
name: read_user.name,
fedi_name: read_user.fedi_name,
email,
+ matrix_user_id: data.matrix_user_id.to_owned(),
avatar: data.avatar.to_owned(),
password_encrypted,
preferred_username: read_user.preferred_username,
@@ -504,10 +543,12 @@ impl Perform<AddAdminResponse> for Oper<AddAdmin> {
let read_user = User_::read(&conn, data.user_id)?;
+ // TODO make addadmin easier
let user_form = UserForm {
name: read_user.name,
fedi_name: read_user.fedi_name,
email: read_user.email,
+ matrix_user_id: read_user.matrix_user_id,
avatar: read_user.avatar,
password_encrypted: read_user.password_encrypted,
preferred_username: read_user.preferred_username,
@@ -568,10 +609,12 @@ impl Perform<BanUserResponse> for Oper<BanUser> {
let read_user = User_::read(&conn, data.user_id)?;
+ // TODO make bans and addadmins easier
let user_form = UserForm {
name: read_user.name,
fedi_name: read_user.fedi_name,
email: read_user.email,
+ matrix_user_id: read_user.matrix_user_id,
avatar: read_user.avatar,
password_encrypted: read_user.password_encrypted,
preferred_username: read_user.preferred_username,
@@ -762,6 +805,30 @@ impl Perform<GetRepliesResponse> for Oper<MarkAllAsRead> {
};
}
+ // messages
+ let messages = PrivateMessageQueryBuilder::create(&conn, user_id)
+ .page(1)
+ .limit(999)
+ .unread_only(true)
+ .list()?;
+
+ for message in &messages {
+ let private_message_form = PrivateMessageForm {
+ content: None,
+ creator_id: message.to_owned().creator_id,
+ recipient_id: message.to_owned().recipient_id,
+ deleted: None,
+ read: Some(true),
+ updated: None,
+ };
+
+ let _updated_message = match PrivateMessage::update(&conn, message.id, &private_message_form)
+ {
+ Ok(message) => message,
+ Err(_e) => return Err(APIError::err(&self.op, "couldnt_update_private_message").into()),
+ };
+ }
+
Ok(GetRepliesResponse {
op: self.op.to_string(),
replies: vec![],
@@ -905,3 +972,150 @@ impl Perform<LoginResponse> for Oper<PasswordChange> {
})
}
}
+
+impl Perform<PrivateMessageResponse> for Oper<CreatePrivateMessage> {
+ fn perform(&self, conn: &PgConnection) -> Result<PrivateMessageResponse, Error> {
+ let data: &CreatePrivateMessage = &self.data;
+
+ let claims = match Claims::decode(&data.auth) {
+ Ok(claims) => claims.claims,
+ Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()),
+ };
+
+ let user_id = claims.id;
+
+ let hostname = &format!("https://{}", Settings::get().hostname);
+
+ // Check for a site ban
+ if UserView::read(&conn, user_id)?.banned {
+ return Err(APIError::err(&self.op, "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()),
+ creator_id: user_id,
+ recipient_id: data.recipient_id,
+ deleted: None,
+ read: None,
+ updated: None,
+ };
+
+ let inserted_private_message = match PrivateMessage::create(&conn, &private_message_form) {
+ Ok(private_message) => private_message,
+ Err(_e) => {
+ return Err(APIError::err(&self.op, "couldnt_create_private_message").into());
+ }
+ };
+
+ // Send notifications to the recipient
+ let recipient_user = User_::read(&conn, data.recipient_id)?;
+ if recipient_user.send_notifications_to_email {
+ if let Some(email) = recipient_user.email {
+ let subject = &format!(
+ "{} - Private Message from {}",
+ Settings::get().hostname,
+ claims.username
+ );
+ let html = &format!(
+ "<h1>Private Message</h1><br><div>{} - {}</div><br><a href={}/inbox>inbox</a>",
+ claims.username, &content_slurs_removed, hostname
+ );
+ match send_email(subject, &email, &recipient_user.name, html) {
+ Ok(_o) => _o,
+ Err(e) => eprintln!("{}", e),
+ };
+ }
+ }
+
+ let private_message_view = PrivateMessageView::read(&conn, inserted_private_message.id)?;
+
+ Ok(PrivateMessageResponse {
+ op: self.op.to_string(),
+ message: private_message_view,
+ })
+ }
+}
+
+impl Perform<PrivateMessageResponse> for Oper<EditPrivateMessage> {
+ fn perform(&self, conn: &PgConnection) -> Result<PrivateMessageResponse, Error> {
+ let data: &EditPrivateMessage = &self.data;
+
+ let claims = match Claims::decode(&data.auth) {
+ Ok(claims) => claims.claims,
+ Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()),
+ };
+
+ let user_id = claims.id;
+
+ let orig_private_message = PrivateMessage::read(&conn, data.edit_id)?;
+
+ // Check for a site ban
+ if UserView::read(&conn, user_id)?.banned {
+ return Err(APIError::err(&self.op, "site_ban").into());
+ }
+
+ // Check to make sure they are the creator (or the recipient marking as read
+ if !(data.read.is_some() && orig_private_message.recipient_id.eq(&user_id)
+ || orig_private_message.creator_id.eq(&user_id))
+ {
+ return Err(APIError::err(&self.op, "no_private_message_edit_allowed").into());
+ }
+
+ let content_slurs_removed = match &data.content {
+ Some(content) => Some(remove_slurs(content)),
+ None => None,
+ };
+
+ let private_message_form = PrivateMessageForm {
+ content: content_slurs_removed,
+ creator_id: orig_private_message.creator_id,
+ recipient_id: orig_private_message.recipient_id,
+ deleted: data.deleted.to_owned(),
+ read: data.read.to_owned(),
+ updated: if data.read.is_some() {
+ orig_private_message.updated
+ } else {
+ Some(naive_now())
+ },
+ };
+
+ 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(&self.op, "couldnt_update_private_message").into()),
+ };
+
+ let private_message_view = PrivateMessageView::read(&conn, data.edit_id)?;
+
+ Ok(PrivateMessageResponse {
+ op: self.op.to_string(),
+ message: private_message_view,
+ })
+ }
+}
+
+impl Perform<PrivateMessagesResponse> for Oper<GetPrivateMessages> {
+ fn perform(&self, conn: &PgConnection) -> Result<PrivateMessagesResponse, Error> {
+ let data: &GetPrivateMessages = &self.data;
+
+ let claims = match Claims::decode(&data.auth) {
+ Ok(claims) => claims.claims,
+ Err(_e) => return Err(APIError::err(&self.op, "not_logged_in").into()),
+ };
+
+ let user_id = claims.id;
+
+ let messages = PrivateMessageQueryBuilder::create(&conn, user_id)
+ .page(data.page)
+ .limit(data.limit)
+ .unread_only(data.unread_only)
+ .list()?;
+
+ Ok(PrivateMessagesResponse {
+ op: self.op.to_string(),
+ messages,
+ })
+ }
+}
diff --git a/server/src/apub/mod.rs b/server/src/apub/mod.rs
index 2d2e5ad3..c5a0b2f0 100644
--- a/server/src/apub/mod.rs
+++ b/server/src/apub/mod.rs
@@ -22,6 +22,7 @@ mod tests {
preferred_username: None,
password_encrypted: "here".into(),
email: None,
+ matrix_user_id: None,
avatar: None,
published: naive_now(),
admin: false,
diff --git a/server/src/db/comment.rs b/server/src/db/comment.rs
index a9c7d81d..efba07a5 100644
--- a/server/src/db/comment.rs
+++ b/server/src/db/comment.rs
@@ -174,6 +174,7 @@ mod tests {
preferred_username: None,
password_encrypted: "nope".into(),
email: None,
+ matrix_user_id: None,
avatar: None,
admin: false,
banned: false,
diff --git a/server/src/db/comment_view.rs b/server/src/db/comment_view.rs
index 3b06e8e3..d4a65c9a 100644
--- a/server/src/db/comment_view.rs
+++ b/server/src/db/comment_view.rs
@@ -398,6 +398,7 @@ mod tests {
preferred_username: None,
password_encrypted: "nope".into(),
email: None,
+ matrix_user_id: None,
avatar: None,
admin: false,
banned: false,
diff --git a/server/src/db/community.rs b/server/src/db/community.rs
index b482ca4a..63500963 100644
--- a/server/src/db/community.rs
+++ b/server/src/db/community.rs
@@ -220,6 +220,7 @@ mod tests {
preferred_username: None,
password_encrypted: "nope".into(),
email: None,
+ matrix_user_id: None,
avatar: None,
admin: false,
banned: false,
diff --git a/server/src/db/mod.rs b/server/src/db/mod.rs
index fef3ffce..dacdb6f6 100644
--- a/server/src/db/mod.rs
+++ b/server/src/db/mod.rs
@@ -15,6 +15,8 @@ pub mod moderator_views;
pub mod password_reset_request;
pub mod post;
pub mod post_view;
+pub mod private_message;
+pub mod private_message_view;
pub mod site;
pub mod site_view;
pub mod user;
diff --git a/server/src/db/moderator.rs b/server/src/db/moderator.rs
index 3c6233cb..4fd532af 100644
--- a/server/src/db/moderator.rs
+++ b/server/src/db/moderator.rs
@@ -442,6 +442,7 @@ mod tests {
preferred_username: None,
password_encrypted: "nope".into(),
email: None,
+ matrix_user_id: None,
avatar: None,
admin: false,
banned: false,
@@ -463,6 +464,7 @@ mod tests {
preferred_username: None,
password_encrypted: "nope".into(),
email: None,
+ matrix_user_id: None,
avatar: None,
admin: false,
banned: false,
diff --git a/server/src/db/password_reset_request.rs b/server/src/db/password_reset_request.rs
index fa060a59..6951fd39 100644
--- a/server/src/db/password_reset_request.rs
+++ b/server/src/db/password_reset_request.rs
@@ -92,6 +92,7 @@ mod tests {
preferred_username: None,
password_encrypted: "nope".into(),
email: None,
+ matrix_user_id: None,
avatar: None,
admin: false,
banned: false,
diff --git a/server/src/db/post.rs b/server/src/db/post.rs
index d3fba4da..9e7a4341 100644
--- a/server/src/db/post.rs
+++ b/server/src/db/post.rs
@@ -187,6 +187,7 @@ mod tests {
preferred_username: None,
password_encrypted: "nope".into(),
email: None,
+ matrix_user_id: None,
avatar: None,
admin: false,
banned: false,
diff --git a/server/src/db/post_view.rs b/server/src/db/post_view.rs
index f6cc274f..c80d1696 100644
--- a/server/src/db/post_view.rs
+++ b/server/src/db/post_view.rs
@@ -339,6 +339,7 @@ mod tests {
preferred_username: None,
password_encrypted: "nope".into(),
email: None,
+ matrix_user_id: None,
avatar: None,
updated: None,
admin: false,
diff --git a/server/src/db/private_message.rs b/server/src/db/private_message.rs
new file mode 100644
index 00000000..cc073b59
--- /dev/null
+++ b/server/src/db/private_message.rs
@@ -0,0 +1,144 @@
+use super::*;
+use crate::schema::private_message;
+
+#[derive(Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize)]
+#[table_name = "private_message"]
+pub struct PrivateMessage {
+ pub id: i32,
+ pub creator_id: i32,
+ pub recipient_id: i32,
+ pub content: String,
+ pub deleted: bool,
+ pub read: bool,
+ pub published: chrono::NaiveDateTime,
+ pub updated: Option<chrono::NaiveDateTime>,
+}
+
+#[derive(Insertable, AsChangeset, Clone)]
+#[table_name = "private_message"]
+pub struct PrivateMessageForm {
+ pub creator_id: i32,
+ pub recipient_id: i32,
+ pub content: Option<String>,
+ pub deleted: Option<bool>,
+ pub read: Option<bool>,
+ pub updated: Option<chrono::NaiveDateTime>,
+}
+
+impl Crud<PrivateMessageForm> for PrivateMessage {
+ fn read(conn: &PgConnection, private_message_id: i32) -> Result<Self, Error> {
+ use crate::schema::private_message::dsl::*;
+ private_message.find(private_message_id).first::<Self>(conn)
+ }
+
+ fn delete(conn: &PgConnection, private_message_id: i32) -> Result<usize, Error> {
+ use crate::schema::private_message::dsl::*;
+ diesel::delete(private_message.find(private_message_id)).execute(conn)
+ }
+
+ fn create(conn: &PgConnection, private_message_form: &PrivateMessageForm) -> Result<Self, Error> {
+ use crate::schema::private_message::dsl::*;
+ insert_into(private_message)
+ .values(private_message_form)
+ .get_result::<Self>(conn)
+ }
+
+ fn update(
+ conn: &PgConnection,
+ private_message_id: i32,
+ private_message_form: &PrivateMessageForm,
+ ) -> Result<Self, Error> {
+ use crate::schema::private_message::dsl::*;
+ diesel::update(private_message.find(private_message_id))
+ .set(private_message_form)
+ .get_result::<Self>(conn)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::super::user::*;
+ use super::*;
+ #[test]
+ fn test_crud() {
+ let conn = establish_unpooled_connection();
+
+ let creator_form = UserForm {
+ name: "creator_pm".into(),
+ fedi_name: "rrf".into(),
+ preferred_username: None,
+ password_encrypted: "nope".into(),
+ email: None,
+ matrix_user_id: None,
+ avatar: None,
+ admin: false,
+ banned: false,
+ updated: None,
+ show_nsfw: false,
+ theme: "darkly".into(),
+ default_sort_type: SortType::Hot as i16,
+ default_listing_type: ListingType::Subscribed as i16,
+ lang: "browser".into(),
+ show_avatars: true,
+ send_notifications_to_email: false,
+ };
+
+ let inserted_creator = User_::create(&conn, &creator_form).unwrap();
+
+ let recipient_form = UserForm {
+ name: "recipient_pm".into(),
+ fedi_name: "rrf".into(),
+ preferred_username: None,
+ password_encrypted: "nope".into(),
+ email: None,
+ matrix_user_id: None,
+ avatar: None,
+ admin: false,
+ banned: false,
+ updated: None,
+ show_nsfw: false,
+ theme: "darkly".into(),
+ default_sort_type: SortType::Hot as i16,
+ default_listing_type: ListingType::Subscribed as i16,
+ lang: "browser".into(),
+ show_avatars: true,
+ send_notifications_to_email: false,
+ };
+
+ let inserted_recipient = User_::create(&conn, &recipient_form).unwrap();
+
+ let private_message_form = PrivateMessageForm {
+ content: Some("A test private message".into()),
+ creator_id: inserted_creator.id,
+ recipient_id: inserted_recipient.id,
+ deleted: None,
+ read: None,
+ updated: None,
+ };
+
+ let inserted_private_message = PrivateMessage::create(&conn, &private_message_form).unwrap();
+
+ let expected_private_message = PrivateMessage {
+ id: inserted_private_message.id,
+ content: "A test private message".into(),
+ creator_id: inserted_creator.id,
+ recipient_id: inserted_recipient.id,
+ deleted: false,
+ read: false,
+ updated: None,
+ published: inserted_private_message.published,
+ };
+
+ let read_private_message = PrivateMessage::read(&conn, inserted_private_message.id).unwrap();
+ let updated_private_message =
+ PrivateMessage::update(&conn, inserted_private_message.id, &private_message_form).unwrap();
+ let num_deleted = PrivateMessage::delete(&conn, inserted_private_message.id).unwrap();
+ User_::delete(&conn, inserted_creator.id).unwrap();
+ User_::delete(&conn, inserted_recipient.id).unwrap();
+
+ assert_eq!(expected_private_message, read_private_message);
+ assert_eq!(expected_private_message, updated_private_message);
+ assert_eq!(expected_private_message, inserted_private_message);
+ assert_eq!(1, num_deleted);
+ }
+}
diff --git a/server/src/db/private_message_view.rs b/server/src/db/private_message_view.rs
new file mode 100644
index 00000000..59a573f4
--- /dev/null
+++ b/server/src/db/private_message_view.rs
@@ -0,0 +1,140 @@
+use super::*;
+use diesel::pg::Pg;
+
+// The faked schema since diesel doesn't do views
+table! {
+ private_message_view (id) {
+ id -> Int4,
+ creator_id -> Int4,
+ recipient_id -> Int4,
+ content -> Text,
+ deleted -> Bool,
+ read -> Bool,
+ published -> Timestamp,
+ updated -> Nullable<Timestamp>,
+ creator_name -> Varchar,
+ creator_avatar -> Nullable<Text>,
+ recipient_name -> Varchar,
+ recipient_avatar -> Nullable<Text>,
+ }
+}
+
+table! {
+ private_message_mview (id) {
+ id -> Int4,
+ creator_id -> Int4,
+ recipient_id -> Int4,
+ content -> Text,
+ deleted -> Bool,
+ read -> Bool,
+ published -> Timestamp,
+ updated -> Nullable<Timestamp>,
+ creator_name -> Varchar,
+ creator_avatar -> Nullable<Text>,
+ recipient_name -> Varchar,
+ recipient_avatar -> Nullable<Text>,
+ }
+}
+
+#[derive(
+ Queryable, Identifiable, PartialEq, Debug, Serialize, Deserialize, QueryableByName, Clone,
+)]
+#[table_name = "private_message_view"]
+pub struct PrivateMessageView {
+ pub id: i32,
+ pub creator_id: i32,
+ pub recipient_id: i32,
+ pub content: String,
+ pub deleted: bool,
+ pub read: bool,
+ pub published: chrono::NaiveDateTime,
+ pub updated: Option<chrono::NaiveDateTime>,
+ pub creator_name: String,
+ pub creator_avatar: Option<String>,
+ pub recipient_name: String,
+ pub recipient_avatar: Option<String>,
+}
+
+pub struct PrivateMessageQueryBuilder<'a> {
+ conn: &'a PgConnection,
+ query: super::private_message_view::private_message_mview::BoxedQuery<'a, Pg>,
+ for_recipient_id: i32,
+ unread_only: bool,
+ page: Option<i64>,
+ limit: Option<i64>,
+}
+
+impl<'a> PrivateMessageQueryBuilder<'a> {
+ pub fn create(conn: &'a PgConnection, for_recipient_id: i32) -> Self {
+ use super::private_message_view::private_message_mview::dsl::*;
+
+ let query = private_message_mview.into_boxed();
+
+ PrivateMessageQueryBuilder {
+ conn,
+ query,
+ for_recipient_id,
+ unread_only: false,
+ page: None,
+ limit: None,
+ }
+ }
+
+ pub fn unread_only(mut self, unread_only: bool) -> Self {
+ self.unread_only = unread_only;
+ self
+ }
+
+ pub fn page<T: MaybeOptional<i64>>(mut self, page: T) -> Self {
+ self.page = page.get_optional();
+ self
+ }
+
+ pub fn limit<T: MaybeOptional<i64>>(mut self, limit: T) -> Self {
+ self.limit = limit.get_optional();
+ self
+ }
+
+ pub fn list(self) -> Result<Vec<PrivateMessageView>, Error> {
+ use super::private_message_view::private_message_mview::dsl::*;
+
+ let mut query = self.query;
+
+ // If its unread, I only want the ones to me
+ if self.unread_only {
+ query = query
+ .filter(read.eq(false))
+ .filter(recipient_id.eq(self.for_recipient_id));
+ }
+ // Otherwise, I want the ALL view to show both sent and received
+ else {
+ query = query.filter(
+ recipient_id
+ .eq(self.for_recipient_id)
+ .or(creator_id.eq(self.for_recipient_id)),
+ )
+ }
+
+ let (limit, offset) = limit_and_offset(self.page, self.limit);
+
+ query
+ .limit(limit)
+ .offset(offset)
+ .order_by(published.desc())
+ .load::<PrivateMessageView>(self.conn)
+ }
+}
+
+impl PrivateMessageView {
+ pub fn read(conn: &PgConnection, from_private_message_id: i32) -> Result<Self, Error> {
+ use super::private_message_view::private_message_view::dsl::*;
+
+ let mut query = private_message_view.into_boxed();
+
+ query = query
+ .filter(id.eq(from_private_message_id))
+ .order_by(published.desc());
+
+ query.first::<Self>(conn)
+ }
+}
diff --git a/server/src/db/user.rs b/server/src/db/user.rs
index 71b63d74..b36c07be 100644
--- a/server/src/db/user.rs
+++ b/server/src/db/user.rs
@@ -26,6 +26,7 @@ pub struct User_ {
pub lang: String,
pub show_avatars: bool,
pub send_notifications_to_email: bool,
+ pub matrix_user_id: Option<String>,
}
#[derive(Insertable, AsChangeset, Clone)]
@@ -47,6 +48,7 @@ pub struct UserForm {
pub lang: String,
pub show_avatars: bool,
pub send_notifications_to_email: bool,
+ pub matrix_user_id: Option<String>,
}
impl Crud<UserForm> for User_ {
@@ -184,6 +186,7 @@ mod tests {
preferred_username: None,
password_encrypted: "nope".into(),
email: None,
+ matrix_user_id: None,
avatar: None,
admin: false,
banned: false,
@@ -206,6 +209,7 @@ mod tests {
preferred_username: None,
password_encrypted: "nope".into(),
email: None,
+ matrix_user_id: None,
avatar: None,
admin: false,
banned: false,
diff --git a/server/src/db/user_mention.rs b/server/src/db/user_mention.rs
index 21dd1675..3b10fd0f 100644
--- a/server/src/db/user_mention.rs
+++ b/server/src/db/user_mention.rs
@@ -68,6 +68,7 @@ mod tests {
preferred_username: None,
password_encrypted: "nope".into(),
email: None,
+ matrix_user_id: None,
avatar: None,
admin: false,
banned: false,
@@ -89,6 +90,7 @@ mod tests {
preferred_username: None,
password_encrypted: "nope".into(),
email: None,
+ matrix_user_id: None,
avatar: None,
admin: false,
banned: false,
diff --git a/server/src/db/user_view.rs b/server/src/db/user_view.rs
index 23e47d4b..3ea506e7 100644
--- a/server/src/db/user_view.rs
+++ b/server/src/db/user_view.rs
@@ -8,6 +8,7 @@ table! {
name -> Varchar,
avatar -> Nullable<Text>,
email -> Nullable<Text>,
+ matrix_user_id -> Nullable<Text>,
fedi_name -> Varchar,
admin -> Bool,
banned -> Bool,
@@ -27,6 +28,7 @@ table! {
name -> Varchar,
avatar -> Nullable<Text>,
email -> Nullable<Text>,
+ matrix_user_id -> Nullable<Text>,
fedi_name -> Varchar,
admin -> Bool,
banned -> Bool,
@@ -49,6 +51,7 @@ pub struct UserView {
pub name: String,
pub avatar: Option<String>,
pub email: Option<String>,
+ pub matrix_user_id: Option<String>,
pub fedi_name: String,
pub admin: bool,
pub banned: bool,
diff --git a/server/src/routes/index.rs b/server/src/routes/index.rs
index 2453a1b2..b044833e 100644
--- a/server/src/routes/index.rs
+++ b/server/src/routes/index.rs
@@ -12,6 +12,7 @@ pub fn config(cfg: &mut web::ServiceConfig) {
.route("/login", web::get().to(index))
.route("/create_post", web::get().to(index))
.route("/create_community", web::get().to(index))
+ .route("/create_private_message", web::get().to(index))
.route("/communities/page/{page}", web::get().to(index))
.route("/communities", web::get().to(index))
.route("/post/{id}/comment/{id2}", web::get().to(index))
diff --git a/server/src/schema.rs b/server/src/schema.rs
index 61957067..5330ed07 100644
--- a/server/src/schema.rs
+++ b/server/src/schema.rs
@@ -239,6 +239,19 @@ table! {
}
table! {
+ private_message (id) {
+ id -> Int4,
+ creator_id -> Int4,
+ recipient_id -> Int4,
+ content -> Text,
+ deleted -> Bool,
+ read -> Bool,
+ published -> Timestamp,
+ updated -> Nullable<Timestamp>,
+ }
+}
+
+table! {
site (id) {
id -> Int4,
name -> Varchar,
@@ -272,6 +285,7 @@ table! {
lang -> Varchar,
show_avatars -> Bool,
send_notifications_to_email -> Bool,
+ matrix_user_id -> Nullable<Text>,
}
}
@@ -357,6 +371,7 @@ allow_tables_to_appear_in_same_query!(
post_like,
post_read,
post_saved,
+ private_message,
site,
user_,
user_ban,
diff --git a/server/src/websocket/server.rs b/server/src/websocket/server.rs
index 957c5f64..5efcb7bf 100644
--- a/server/src/websocket/server.rs
+++ b/server/src/websocket/server.rs
@@ -547,5 +547,21 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result<Str
let res = Oper::new(user_operation, password_change).perform(&conn)?;
Ok(serde_json::to_string(&res)?)
}
+ UserOperation::CreatePrivateMessage => {
+ chat.check_rate_limit_message(msg.id)?;
+ let create_private_message: CreatePrivateMessage = serde_json::from_str(data)?;
+ let res = Oper::new(user_operation, create_private_message).perform(&conn)?;
+ Ok(serde_json::to_string(&res)?)
+ }
+ UserOperation::EditPrivateMessage => {
+ let edit_private_message: EditPrivateMessage = serde_json::from_str(data)?;
+ let res = Oper::new(user_operation, edit_private_message).perform(&conn)?;
+ Ok(serde_json::to_string(&res)?)
+ }
+ UserOperation::GetPrivateMessages => {
+ let messages: GetPrivateMessages = serde_json::from_str(data)?;
+ let res = Oper::new(user_operation, messages).perform(&conn)?;
+ Ok(serde_jso