2 captcha_espeak_wav_base64,
5 collect_moderated_communities,
11 use actix_web::web::Data;
14 use captcha::{gen, Difficulty};
16 use lemmy_apub::ApubObjectType;
18 diesel_option_overwrite,
21 community::Community_,
22 password_reset_request::PasswordResetRequest_,
24 private_message::PrivateMessage_,
26 user_mention::UserMention_,
29 comment_report_view::CommentReportView,
30 comment_view::CommentQueryBuilder,
32 community_follower_view::CommunityFollowerView,
33 community_moderator_view::CommunityModeratorView,
35 post_report_view::PostReportView,
36 post_view::PostQueryBuilder,
37 private_message_view::{PrivateMessageQueryBuilder, PrivateMessageView},
39 user_mention_view::{UserMentionQueryBuilder, UserMentionView},
40 user_view::{UserViewDangerous, UserViewSafe},
48 use lemmy_db_schema::{
54 password_reset_request::*,
62 use lemmy_structs::{blocking, send_email_to_user, user::*};
64 apub::{generate_actor_keypair, make_apub_endpoint, EndpointType},
70 generate_random_string,
71 is_valid_preferred_username,
80 use lemmy_websocket::{
81 messages::{CaptchaItem, CheckCaptcha, JoinUserRoom, SendAllMessage, SendUserRoomMessage},
85 use std::str::FromStr;
87 #[async_trait::async_trait(?Send)]
88 impl Perform for Login {
89 type Response = LoginResponse;
93 context: &Data<LemmyContext>,
94 _websocket_id: Option<ConnectionId>,
95 ) -> Result<LoginResponse, LemmyError> {
96 let data: &Login = &self;
98 // Fetch that username / email
99 let username_or_email = data.username_or_email.clone();
100 let user = match blocking(context.pool(), move |conn| {
101 User_::find_by_email_or_username(conn, &username_or_email)
106 Err(_e) => return Err(APIError::err("couldnt_find_that_username_or_email").into()),
109 // Verify the password
110 let valid: bool = verify(&data.password, &user.password_encrypted).unwrap_or(false);
112 return Err(APIError::err("password_incorrect").into());
117 jwt: Claims::jwt(user, Settings::get().hostname)?,
122 #[async_trait::async_trait(?Send)]
123 impl Perform for Register {
124 type Response = LoginResponse;
128 context: &Data<LemmyContext>,
129 _websocket_id: Option<ConnectionId>,
130 ) -> Result<LoginResponse, LemmyError> {
131 let data: &Register = &self;
133 // Make sure site has open registration
134 if let Ok(site_view) = blocking(context.pool(), move |conn| SiteView::read(conn)).await? {
135 if !site_view.site.open_registration {
136 return Err(APIError::err("registration_closed").into());
140 // Password length check
141 if data.password.len() > 60 {
142 return Err(APIError::err("invalid_password").into());
145 // Make sure passwords match
146 if data.password != data.password_verify {
147 return Err(APIError::err("passwords_dont_match").into());
150 // If its not the admin, check the captcha
151 if !data.admin && Settings::get().captcha.enabled {
158 .unwrap_or_else(|| "".to_string()),
162 .unwrap_or_else(|| "".to_string()),
166 return Err(APIError::err("captcha_incorrect").into());
170 check_slurs(&data.username)?;
172 // Make sure there are no admins
173 let any_admins = blocking(context.pool(), move |conn| {
174 UserViewSafe::admins(conn).map(|a| a.is_empty())
177 if data.admin && !any_admins {
178 return Err(APIError::err("admin_already_created").into());
181 let user_keypair = generate_actor_keypair()?;
182 if !is_valid_username(&data.username) {
183 return Err(APIError::err("invalid_username").into());
186 // Register the new user
187 let user_form = UserForm {
188 name: data.username.to_owned(),
189 email: Some(data.email.to_owned()),
190 matrix_user_id: None,
193 password_encrypted: data.password.to_owned(),
194 preferred_username: None,
199 show_nsfw: data.show_nsfw,
200 theme: "browser".into(),
201 default_sort_type: SortType::Active as i16,
202 default_listing_type: ListingType::Subscribed as i16,
203 lang: "browser".into(),
205 send_notifications_to_email: false,
206 actor_id: Some(make_apub_endpoint(EndpointType::User, &data.username).to_string()),
209 private_key: Some(user_keypair.private_key),
210 public_key: Some(user_keypair.public_key),
211 last_refreshed_at: None,
215 let inserted_user = match blocking(context.pool(), move |conn| {
216 User_::register(conn, &user_form)
222 let err_type = if e.to_string()
223 == "duplicate key value violates unique constraint \"user__email_key\""
225 "email_already_exists"
227 "user_already_exists"
230 return Err(APIError::err(err_type).into());
234 let main_community_keypair = generate_actor_keypair()?;
236 // Create the main community if it doesn't exist
238 match blocking(context.pool(), move |conn| Community::read(conn, 2)).await? {
241 let default_community_name = "main";
242 let community_form = CommunityForm {
243 name: default_community_name.to_string(),
244 title: "The Default Community".to_string(),
245 description: Some("The Default Community".to_string()),
248 creator_id: inserted_user.id,
253 make_apub_endpoint(EndpointType::Community, default_community_name).to_string(),
256 private_key: Some(main_community_keypair.private_key),
257 public_key: Some(main_community_keypair.public_key),
258 last_refreshed_at: None,
263 blocking(context.pool(), move |conn| {
264 Community::create(conn, &community_form)
270 // Sign them up for main community no matter what
271 let community_follower_form = CommunityFollowerForm {
272 community_id: main_community.id,
273 user_id: inserted_user.id,
277 let follow = move |conn: &'_ _| CommunityFollower::follow(conn, &community_follower_form);
278 if blocking(context.pool(), follow).await?.is_err() {
279 return Err(APIError::err("community_follower_already_exists").into());
282 // If its an admin, add them as a mod and follower to main
284 let community_moderator_form = CommunityModeratorForm {
285 community_id: main_community.id,
286 user_id: inserted_user.id,
289 let join = move |conn: &'_ _| CommunityModerator::join(conn, &community_moderator_form);
290 if blocking(context.pool(), join).await?.is_err() {
291 return Err(APIError::err("community_moderator_already_exists").into());
297 jwt: Claims::jwt(inserted_user, Settings::get().hostname)?,
302 #[async_trait::async_trait(?Send)]
303 impl Perform for GetCaptcha {
304 type Response = GetCaptchaResponse;
308 context: &Data<LemmyContext>,
309 _websocket_id: Option<ConnectionId>,
310 ) -> Result<Self::Response, LemmyError> {
311 let captcha_settings = Settings::get().captcha;
313 if !captcha_settings.enabled {
314 return Ok(GetCaptchaResponse { ok: None });
317 let captcha = match captcha_settings.difficulty.as_str() {
318 "easy" => gen(Difficulty::Easy),
319 "medium" => gen(Difficulty::Medium),
320 "hard" => gen(Difficulty::Hard),
321 _ => gen(Difficulty::Medium),
324 let answer = captcha.chars_as_string();
326 let png_byte_array = captcha.as_png().expect("failed to generate captcha");
328 let png = base64::encode(png_byte_array);
330 let uuid = uuid::Uuid::new_v4().to_string();
332 let wav = captcha_espeak_wav_base64(&answer).ok();
334 let captcha_item = CaptchaItem {
336 uuid: uuid.to_owned(),
337 expires: naive_now() + Duration::minutes(10), // expires in 10 minutes
340 // Stores the captcha item on the queue
341 context.chat_server().do_send(captcha_item);
343 Ok(GetCaptchaResponse {
344 ok: Some(CaptchaResponse { png, uuid, wav }),
349 #[async_trait::async_trait(?Send)]
350 impl Perform for SaveUserSettings {
351 type Response = LoginResponse;
355 context: &Data<LemmyContext>,
356 _websocket_id: Option<ConnectionId>,
357 ) -> Result<LoginResponse, LemmyError> {
358 let data: &SaveUserSettings = &self;
359 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
361 let user_id = user.id;
362 let read_user = blocking(context.pool(), move |conn| User_::read(conn, user_id)).await??;
364 let avatar = diesel_option_overwrite(&data.avatar);
365 let banner = diesel_option_overwrite(&data.banner);
366 let email = diesel_option_overwrite(&data.email);
367 let bio = diesel_option_overwrite(&data.bio);
368 let preferred_username = diesel_option_overwrite(&data.preferred_username);
369 let matrix_user_id = diesel_option_overwrite(&data.matrix_user_id);
371 // Check to make sure the avatar and banners are urls
372 check_optional_url(&avatar)?;
373 check_optional_url(&banner)?;
375 if let Some(Some(bio)) = &bio {
376 if bio.chars().count() > 300 {
377 return Err(APIError::err("bio_length_overflow").into());
381 if let Some(Some(preferred_username)) = &preferred_username {
382 if !is_valid_preferred_username(preferred_username.trim()) {
383 return Err(APIError::err("invalid_username").into());
387 let password_encrypted = match &data.new_password {
388 Some(new_password) => {
389 match &data.new_password_verify {
390 Some(new_password_verify) => {
391 // Make sure passwords match
392 if new_password != new_password_verify {
393 return Err(APIError::err("passwords_dont_match").into());
396 // Check the old password
397 match &data.old_password {
398 Some(old_password) => {
400 verify(old_password, &read_user.password_encrypted).unwrap_or(false);
402 return Err(APIError::err("password_incorrect").into());
404 let new_password = new_password.to_owned();
405 let user = blocking(context.pool(), move |conn| {
406 User_::update_password(conn, user_id, &new_password)
409 user.password_encrypted
411 None => return Err(APIError::err("password_incorrect").into()),
414 None => return Err(APIError::err("passwords_dont_match").into()),
417 None => read_user.password_encrypted,
420 let user_form = UserForm {
421 name: read_user.name,
428 published: Some(read_user.published),
429 updated: Some(naive_now()),
430 admin: read_user.admin,
431 banned: Some(read_user.banned),
432 show_nsfw: data.show_nsfw,
433 theme: data.theme.to_owned(),
434 default_sort_type: data.default_sort_type,
435 default_listing_type: data.default_listing_type,
436 lang: data.lang.to_owned(),
437 show_avatars: data.show_avatars,
438 send_notifications_to_email: data.send_notifications_to_email,
439 actor_id: Some(read_user.actor_id),
441 local: read_user.local,
442 private_key: read_user.private_key,
443 public_key: read_user.public_key,
444 last_refreshed_at: None,
447 let res = blocking(context.pool(), move |conn| {
448 User_::update(conn, user_id, &user_form)
451 let updated_user: User_ = match res {
454 let err_type = if e.to_string()
455 == "duplicate key value violates unique constraint \"user__email_key\""
457 "email_already_exists"
459 "user_already_exists"
462 return Err(APIError::err(err_type).into());
468 jwt: Claims::jwt(updated_user, Settings::get().hostname)?,
473 #[async_trait::async_trait(?Send)]
474 impl Perform for GetUserDetails {
475 type Response = GetUserDetailsResponse;
479 context: &Data<LemmyContext>,
480 _websocket_id: Option<ConnectionId>,
481 ) -> Result<GetUserDetailsResponse, LemmyError> {
482 let data: &GetUserDetails = &self;
483 let user = get_user_from_jwt_opt(&data.auth, context.pool()).await?;
485 let show_nsfw = match &user {
486 Some(user) => user.show_nsfw,
490 let sort = SortType::from_str(&data.sort)?;
495 .unwrap_or_else(|| "admin".to_string());
496 let user_details_id = match data.user_id {
499 let user = blocking(context.pool(), move |conn| {
500 User_::read_from_name(conn, &username)
505 Err(_e) => return Err(APIError::err("couldnt_find_that_username_or_email").into()),
510 let user_id = user.map(|u| u.id);
512 let (user_view, user_dangerous) = if let Some(auth_user_id) = user_id {
513 if user_details_id == auth_user_id {
517 blocking(context.pool(), move |conn| {
518 UserViewDangerous::read(conn, auth_user_id)
526 blocking(context.pool(), move |conn| {
527 UserViewSafe::read(conn, user_details_id)
537 blocking(context.pool(), move |conn| {
538 UserViewSafe::read(conn, user_details_id)
546 let page = data.page;
547 let limit = data.limit;
548 let saved_only = data.saved_only;
549 let community_id = data.community_id;
551 let (posts, comments) = blocking(context.pool(), move |conn| {
552 let mut posts_query = PostQueryBuilder::create(conn)
554 .show_nsfw(show_nsfw)
555 .saved_only(saved_only)
556 .community_id(community_id)
561 let mut comments_query = CommentQueryBuilder::create(conn)
564 .saved_only(saved_only)
568 // If its saved only, you don't care what creator it was
569 // Or, if its not saved, then you only want it for that specific creator
571 posts_query = posts_query.creator_id(user_details_id);
572 comments_query = comments_query.creator_id(user_details_id);
575 let posts = posts_query.list()?;
576 let comments = comments_query.list()?;
578 Ok((posts, comments)) as Result<_, LemmyError>
582 let follows = blocking(context.pool(), move |conn| {
583 CommunityFollowerView::for_user(conn, user_details_id)
586 let moderates = blocking(context.pool(), move |conn| {
587 CommunityModeratorView::for_user(conn, user_details_id)
592 Ok(GetUserDetailsResponse {
593 // TODO need to figure out dangerous user view here
604 #[async_trait::async_trait(?Send)]
605 impl Perform for AddAdmin {
606 type Response = AddAdminResponse;
610 context: &Data<LemmyContext>,
611 websocket_id: Option<ConnectionId>,
612 ) -> Result<AddAdminResponse, LemmyError> {
613 let data: &AddAdmin = &self;
614 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
616 // Make sure user is an admin
617 is_admin(context.pool(), user.id).await?;
619 let added = data.added;
620 let added_user_id = data.user_id;
621 let add_admin = move |conn: &'_ _| User_::add_admin(conn, added_user_id, added);
622 if blocking(context.pool(), add_admin).await?.is_err() {
623 return Err(APIError::err("couldnt_update_user").into());
627 let form = ModAddForm {
628 mod_user_id: user.id,
629 other_user_id: data.user_id,
630 removed: Some(!data.added),
633 blocking(context.pool(), move |conn| ModAdd::create(conn, &form)).await??;
635 let site_creator_id = blocking(context.pool(), move |conn| {
636 Site::read(conn, 1).map(|s| s.creator_id)
640 let mut admins = blocking(context.pool(), move |conn| UserViewSafe::admins(conn)).await??;
641 let creator_index = admins
643 .position(|r| r.user.id == site_creator_id)
644 .context(location_info!())?;
645 let creator_user = admins.remove(creator_index);
646 admins.insert(0, creator_user);
648 let res = AddAdminResponse { admins };
650 context.chat_server().do_send(SendAllMessage {
651 op: UserOperation::AddAdmin,
652 response: res.clone(),
660 #[async_trait::async_trait(?Send)]
661 impl Perform for BanUser {
662 type Response = BanUserResponse;
666 context: &Data<LemmyContext>,
667 websocket_id: Option<ConnectionId>,
668 ) -> Result<BanUserResponse, LemmyError> {
669 let data: &BanUser = &self;
670 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
672 // Make sure user is an admin
673 is_admin(context.pool(), user.id).await?;
676 let banned_user_id = data.user_id;
677 let ban_user = move |conn: &'_ _| User_::ban_user(conn, banned_user_id, ban);
678 if blocking(context.pool(), ban_user).await?.is_err() {
679 return Err(APIError::err("couldnt_update_user").into());
682 // Remove their data if that's desired
683 if let Some(remove_data) = data.remove_data {
685 blocking(context.pool(), move |conn: &'_ _| {
686 Post::update_removed_for_creator(conn, banned_user_id, None, remove_data)
691 blocking(context.pool(), move |conn: &'_ _| {
692 Community::update_removed_for_creator(conn, banned_user_id, remove_data)
697 blocking(context.pool(), move |conn: &'_ _| {
698 Comment::update_removed_for_creator(conn, banned_user_id, remove_data)
704 let expires = match data.expires {
705 Some(time) => Some(naive_from_unix(time)),
709 let form = ModBanForm {
710 mod_user_id: user.id,
711 other_user_id: data.user_id,
712 reason: data.reason.to_owned(),
713 banned: Some(data.ban),
717 blocking(context.pool(), move |conn| ModBan::create(conn, &form)).await??;
719 let user_id = data.user_id;
720 let user_view = blocking(context.pool(), move |conn| {
721 UserViewSafe::read(conn, user_id)
725 let res = BanUserResponse {
730 context.chat_server().do_send(SendAllMessage {
731 op: UserOperation::BanUser,
732 response: res.clone(),
740 #[async_trait::async_trait(?Send)]
741 impl Perform for GetReplies {
742 type Response = GetRepliesResponse;
746 context: &Data<LemmyContext>,
747 _websocket_id: Option<ConnectionId>,
748 ) -> Result<GetRepliesResponse, LemmyError> {
749 let data: &GetReplies = &self;
750 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
752 let sort = SortType::from_str(&data.sort)?;
754 let page = data.page;
755 let limit = data.limit;
756 let unread_only = data.unread_only;
757 let user_id = user.id;
758 let replies = blocking(context.pool(), move |conn| {
759 CommentQueryBuilder::create(conn)
761 .unread_only(unread_only)
762 .recipient_id(user_id)
770 Ok(GetRepliesResponse { replies })
774 #[async_trait::async_trait(?Send)]
775 impl Perform for GetUserMentions {
776 type Response = GetUserMentionsResponse;
780 context: &Data<LemmyContext>,
781 _websocket_id: Option<ConnectionId>,
782 ) -> Result<GetUserMentionsResponse, LemmyError> {
783 let data: &GetUserMentions = &self;
784 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
786 let sort = SortType::from_str(&data.sort)?;
788 let page = data.page;
789 let limit = data.limit;
790 let unread_only = data.unread_only;
791 let user_id = user.id;
792 let mentions = blocking(context.pool(), move |conn| {
793 UserMentionQueryBuilder::create(conn)
794 .recipient_id(user_id)
797 .unread_only(unread_only)
804 Ok(GetUserMentionsResponse { mentions })
808 #[async_trait::async_trait(?Send)]
809 impl Perform for MarkUserMentionAsRead {
810 type Response = UserMentionResponse;
814 context: &Data<LemmyContext>,
815 _websocket_id: Option<ConnectionId>,
816 ) -> Result<UserMentionResponse, LemmyError> {
817 let data: &MarkUserMentionAsRead = &self;
818 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
820 let user_mention_id = data.user_mention_id;
821 let read_user_mention = blocking(context.pool(), move |conn| {
822 UserMention::read(conn, user_mention_id)
826 if user.id != read_user_mention.recipient_id {
827 return Err(APIError::err("couldnt_update_comment").into());
830 let user_mention_id = read_user_mention.id;
831 let read = data.read;
832 let update_mention = move |conn: &'_ _| UserMention::update_read(conn, user_mention_id, read);
833 if blocking(context.pool(), update_mention).await?.is_err() {
834 return Err(APIError::err("couldnt_update_comment").into());
837 let user_mention_id = read_user_mention.id;
838 let user_id = user.id;
839 let user_mention_view = blocking(context.pool(), move |conn| {
840 UserMentionView::read(conn, user_mention_id, Some(user_id))
844 Ok(UserMentionResponse { user_mention_view })
848 #[async_trait::async_trait(?Send)]
849 impl Perform for MarkAllAsRead {
850 type Response = GetRepliesResponse;
854 context: &Data<LemmyContext>,
855 _websocket_id: Option<ConnectionId>,
856 ) -> Result<GetRepliesResponse, LemmyError> {
857 let data: &MarkAllAsRead = &self;
858 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
860 let user_id = user.id;
861 let replies = blocking(context.pool(), move |conn| {
862 CommentQueryBuilder::create(conn)
864 .recipient_id(user_id)
872 // TODO: this should probably be a bulk operation
873 // Not easy to do as a bulk operation,
874 // because recipient_id isn't in the comment table
875 for comment_view in &replies {
876 let reply_id = comment_view.comment.id;
877 let mark_as_read = move |conn: &'_ _| Comment::update_read(conn, reply_id, true);
878 if blocking(context.pool(), mark_as_read).await?.is_err() {
879 return Err(APIError::err("couldnt_update_comment").into());
883 // Mark all user mentions as read
884 let update_user_mentions = move |conn: &'_ _| UserMention::mark_all_as_read(conn, user_id);
885 if blocking(context.pool(), update_user_mentions)
889 return Err(APIError::err("couldnt_update_comment").into());
892 // Mark all private_messages as read
893 let update_pm = move |conn: &'_ _| PrivateMessage::mark_all_as_read(conn, user_id);
894 if blocking(context.pool(), update_pm).await?.is_err() {
895 return Err(APIError::err("couldnt_update_private_message").into());
898 Ok(GetRepliesResponse { replies: vec![] })
902 #[async_trait::async_trait(?Send)]
903 impl Perform for DeleteAccount {
904 type Response = LoginResponse;
908 context: &Data<LemmyContext>,
909 _websocket_id: Option<ConnectionId>,
910 ) -> Result<LoginResponse, LemmyError> {
911 let data: &DeleteAccount = &self;
912 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
914 // Verify the password
915 let valid: bool = verify(&data.password, &user.password_encrypted).unwrap_or(false);
917 return Err(APIError::err("password_incorrect").into());
921 let user_id = user.id;
922 let permadelete = move |conn: &'_ _| Comment::permadelete_for_creator(conn, user_id);
923 if blocking(context.pool(), permadelete).await?.is_err() {
924 return Err(APIError::err("couldnt_update_comment").into());
928 let permadelete = move |conn: &'_ _| Post::permadelete_for_creator(conn, user_id);
929 if blocking(context.pool(), permadelete).await?.is_err() {
930 return Err(APIError::err("couldnt_update_post").into());
933 blocking(context.pool(), move |conn| {
934 User_::delete_account(conn, user_id)
939 jwt: data.auth.to_owned(),
944 #[async_trait::async_trait(?Send)]
945 impl Perform for PasswordReset {
946 type Response = PasswordResetResponse;
950 context: &Data<LemmyContext>,
951 _websocket_id: Option<ConnectionId>,
952 ) -> Result<PasswordResetResponse, LemmyError> {
953 let data: &PasswordReset = &self;
956 let email = data.email.clone();
957 let user = match blocking(context.pool(), move |conn| {
958 User_::find_by_email(conn, &email)
963 Err(_e) => return Err(APIError::err("couldnt_find_that_username_or_email").into()),
966 // Generate a random token
967 let token = generate_random_string();
970 let token2 = token.clone();
971 let user_id = user.id;
972 blocking(context.pool(), move |conn| {
973 PasswordResetRequest::create_token(conn, user_id, &token2)
977 // Email the pure token to the user.
978 // TODO no i18n support here.
979 let user_email = &user.email.expect("email");
980 let subject = &format!("Password reset for {}", user.name);
981 let hostname = &Settings::get().get_protocol_and_hostname();
982 let html = &format!("<h1>Password Reset Request for {}</h1><br><a href={}/password_change/{}>Click here to reset your password</a>", user.name, hostname, &token);
983 match send_email(subject, user_email, &user.name, html) {
985 Err(_e) => return Err(APIError::err(&_e).into()),
988 Ok(PasswordResetResponse {})
992 #[async_trait::async_trait(?Send)]
993 impl Perform for PasswordChange {
994 type Response = LoginResponse;
998 context: &Data<LemmyContext>,
999 _websocket_id: Option<ConnectionId>,
1000 ) -> Result<LoginResponse, LemmyError> {
1001 let data: &PasswordChange = &self;
1003 // Fetch the user_id from the token
1004 let token = data.token.clone();
1005 let user_id = blocking(context.pool(), move |conn| {
1006 PasswordResetRequest::read_from_token(conn, &token).map(|p| p.user_id)
1010 // Make sure passwords match
1011 if data.password != data.password_verify {
1012 return Err(APIError::err("passwords_dont_match").into());
1015 // Update the user with the new password
1016 let password = data.password.clone();
1017 let updated_user = match blocking(context.pool(), move |conn| {
1018 User_::update_password(conn, user_id, &password)
1023 Err(_e) => return Err(APIError::err("couldnt_update_user").into()),
1028 jwt: Claims::jwt(updated_user, Settings::get().hostname)?,
1033 #[async_trait::async_trait(?Send)]
1034 impl Perform for CreatePrivateMessage {
1035 type Response = PrivateMessageResponse;
1039 context: &Data<LemmyContext>,
1040 websocket_id: Option<ConnectionId>,
1041 ) -> Result<PrivateMessageResponse, LemmyError> {
1042 let data: &CreatePrivateMessage = &self;
1043 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
1045 let content_slurs_removed = remove_slurs(&data.content.to_owned());
1047 let private_message_form = PrivateMessageForm {
1048 content: content_slurs_removed.to_owned(),
1049 creator_id: user.id,
1050 recipient_id: data.recipient_id,
1059 let inserted_private_message = match blocking(context.pool(), move |conn| {
1060 PrivateMessage::create(conn, &private_message_form)
1064 Ok(private_message) => private_message,
1066 return Err(APIError::err("couldnt_create_private_message").into());
1070 let inserted_private_message_id = inserted_private_message.id;
1071 let updated_private_message = match blocking(context.pool(), move |conn| {
1072 let apub_id = make_apub_endpoint(
1073 EndpointType::PrivateMessage,
1074 &inserted_private_message_id.to_string(),
1077 PrivateMessage::update_ap_id(&conn, inserted_private_message_id, apub_id)
1081 Ok(private_message) => private_message,
1082 Err(_e) => return Err(APIError::err("couldnt_create_private_message").into()),
1085 updated_private_message.send_create(&user, context).await?;
1087 // Send notifications to the recipient
1088 let recipient_id = data.recipient_id;
1089 let recipient_user =
1090 blocking(context.pool(), move |conn| User_::read(conn, recipient_id)).await??;
1091 if recipient_user.send_notifications_to_email {
1094 "Private Message from",
1096 &content_slurs_removed,
1100 let message = blocking(context.pool(), move |conn| {
1101 PrivateMessageView::read(conn, inserted_private_message.id)
1105 let res = PrivateMessageResponse { message };
1107 context.chat_server().do_send(SendUserRoomMessage {
1108 op: UserOperation::CreatePrivateMessage,
1109 response: res.clone(),
1118 #[async_trait::async_trait(?Send)]
1119 impl Perform for EditPrivateMessage {
1120 type Response = PrivateMessageResponse;
1124 context: &Data<LemmyContext>,
1125 websocket_id: Option<ConnectionId>,
1126 ) -> Result<PrivateMessageResponse, LemmyError> {
1127 let data: &EditPrivateMessage = &self;
1128 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
1130 // Checking permissions
1131 let edit_id = data.edit_id;
1132 let orig_private_message = blocking(context.pool(), move |conn| {
1133 PrivateMessage::read(conn, edit_id)
1136 if user.id != orig_private_message.creator_id {
1137 return Err(APIError::err("no_private_message_edit_allowed").into());
1141 let content_slurs_removed = remove_slurs(&data.content);
1142 let edit_id = data.edit_id;
1143 let updated_private_message = match blocking(context.pool(), move |conn| {
1144 PrivateMessage::update_content(conn, edit_id, &content_slurs_removed)
1148 Ok(private_message) => private_message,
1149 Err(_e) => return Err(APIError::err("couldnt_update_private_message").into()),
1152 // Send the apub update
1153 updated_private_message.send_update(&user, context).await?;
1155 let edit_id = data.edit_id;
1156 let message = blocking(context.pool(), move |conn| {
1157 PrivateMessageView::read(conn, edit_id)
1160 let recipient_id = message.recipient.id;
1162 let res = PrivateMessageResponse { message };
1164 context.chat_server().do_send(SendUserRoomMessage {
1165 op: UserOperation::EditPrivateMessage,
1166 response: res.clone(),
1175 #[async_trait::async_trait(?Send)]
1176 impl Perform for DeletePrivateMessage {
1177 type Response = PrivateMessageResponse;
1181 context: &Data<LemmyContext>,
1182 websocket_id: Option<ConnectionId>,
1183 ) -> Result<PrivateMessageResponse, LemmyError> {
1184 let data: &DeletePrivateMessage = &self;
1185 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
1187 // Checking permissions
1188 let edit_id = data.edit_id;
1189 let orig_private_message = blocking(context.pool(), move |conn| {
1190 PrivateMessage::read(conn, edit_id)
1193 if user.id != orig_private_message.creator_id {
1194 return Err(APIError::err("no_private_message_edit_allowed").into());
1198 let edit_id = data.edit_id;
1199 let deleted = data.deleted;
1200 let updated_private_message = match blocking(context.pool(), move |conn| {
1201 PrivateMessage::update_deleted(conn, edit_id, deleted)
1205 Ok(private_message) => private_message,
1206 Err(_e) => return Err(APIError::err("couldnt_update_private_message").into()),
1209 // Send the apub update
1211 updated_private_message.send_delete(&user, context).await?;
1213 updated_private_message
1214 .send_undo_delete(&user, context)
1218 let edit_id = data.edit_id;
1219 let message = blocking(context.pool(), move |conn| {
1220 PrivateMessageView::read(conn, edit_id)
1223 let recipient_id = message.recipient.id;
1225 let res = PrivateMessageResponse { message };
1227 context.chat_server().do_send(SendUserRoomMessage {
1228 op: UserOperation::DeletePrivateMessage,
1229 response: res.clone(),
1238 #[async_trait::async_trait(?Send)]
1239 impl Perform for MarkPrivateMessageAsRead {
1240 type Response = PrivateMessageResponse;
1244 context: &Data<LemmyContext>,
1245 websocket_id: Option<ConnectionId>,
1246 ) -> Result<PrivateMessageResponse, LemmyError> {
1247 let data: &MarkPrivateMessageAsRead = &self;
1248 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
1250 // Checking permissions
1251 let edit_id = data.edit_id;
1252 let orig_private_message = blocking(context.pool(), move |conn| {
1253 PrivateMessage::read(conn, edit_id)
1256 if user.id != orig_private_message.recipient_id {
1257 return Err(APIError::err("couldnt_update_private_message").into());
1261 let edit_id = data.edit_id;
1262 let read = data.read;
1263 match blocking(context.pool(), move |conn| {
1264 PrivateMessage::update_read(conn, edit_id, read)
1268 Ok(private_message) => private_message,
1269 Err(_e) => return Err(APIError::err("couldnt_update_private_message").into()),
1272 // No need to send an apub update
1274 let edit_id = data.edit_id;
1275 let message = blocking(context.pool(), move |conn| {
1276 PrivateMessageView::read(conn, edit_id)
1279 let recipient_id = message.recipient.id;
1281 let res = PrivateMessageResponse { message };
1283 context.chat_server().do_send(SendUserRoomMessage {
1284 op: UserOperation::MarkPrivateMessageAsRead,
1285 response: res.clone(),
1294 #[async_trait::async_trait(?Send)]
1295 impl Perform for GetPrivateMessages {
1296 type Response = PrivateMessagesResponse;
1300 context: &Data<LemmyContext>,
1301 _websocket_id: Option<ConnectionId>,
1302 ) -> Result<PrivateMessagesResponse, LemmyError> {
1303 let data: &GetPrivateMessages = &self;
1304 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
1305 let user_id = user.id;
1307 let page = data.page;
1308 let limit = data.limit;
1309 let unread_only = data.unread_only;
1310 let messages = blocking(context.pool(), move |conn| {
1311 PrivateMessageQueryBuilder::create(&conn, user_id)
1314 .unread_only(unread_only)
1319 Ok(PrivateMessagesResponse { messages })
1323 #[async_trait::async_trait(?Send)]
1324 impl Perform for UserJoin {
1325 type Response = UserJoinResponse;
1329 context: &Data<LemmyContext>,
1330 websocket_id: Option<ConnectionId>,
1331 ) -> Result<UserJoinResponse, LemmyError> {
1332 let data: &UserJoin = &self;
1333 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
1335 if let Some(ws_id) = websocket_id {
1336 context.chat_server().do_send(JoinUserRoom {
1342 Ok(UserJoinResponse { joined: true })
1346 #[async_trait::async_trait(?Send)]
1347 impl Perform for GetReportCount {
1348 type Response = GetReportCountResponse;
1352 context: &Data<LemmyContext>,
1353 websocket_id: Option<ConnectionId>,
1354 ) -> Result<GetReportCountResponse, LemmyError> {
1355 let data: &GetReportCount = &self;
1356 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
1358 let user_id = user.id;
1359 let community_id = data.community;
1361 collect_moderated_communities(user_id, community_id, context.pool()).await?;
1364 if community_ids.is_empty() {
1365 GetReportCountResponse {
1371 let ids = community_ids.clone();
1372 let comment_reports = blocking(context.pool(), move |conn| {
1373 CommentReportView::get_report_count(conn, &ids)
1377 let ids = community_ids.clone();
1378 let post_reports = blocking(context.pool(), move |conn| {
1379 PostReportView::get_report_count(conn, &ids)
1383 GetReportCountResponse {
1384 community: data.community,
1391 context.chat_server().do_send(SendUserRoomMessage {
1392 op: UserOperation::GetReportCount,
1393 response: res.clone(),
1394 recipient_id: user.id,