2 captcha_espeak_wav_base64,
4 collect_moderated_communities,
10 use actix_web::web::Data;
13 use captcha::{gen, Difficulty};
16 generate_apub_endpoint,
17 generate_followers_url,
19 generate_shared_inbox_url,
23 use lemmy_db_queries::{
24 diesel_option_overwrite,
27 community::Community_,
28 password_reset_request::PasswordResetRequest_,
30 private_message::PrivateMessage_,
33 user_mention::UserMention_,
41 use lemmy_db_schema::{
47 password_reset_request::*,
56 comment_report_view::CommentReportView,
57 comment_view::CommentQueryBuilder,
58 post_report_view::PostReportView,
59 post_view::PostQueryBuilder,
60 private_message_view::{PrivateMessageQueryBuilder, PrivateMessageView},
62 use lemmy_db_views_actor::{
63 community_follower_view::CommunityFollowerView,
64 community_moderator_view::CommunityModeratorView,
65 user_mention_view::{UserMentionQueryBuilder, UserMentionView},
66 user_view::UserViewSafe,
68 use lemmy_structs::{blocking, send_email_to_user, user::*};
70 apub::generate_actor_keypair,
77 generate_random_string,
78 is_valid_preferred_username,
87 use lemmy_websocket::{
88 messages::{CaptchaItem, CheckCaptcha, SendAllMessage, SendUserRoomMessage},
92 use std::str::FromStr;
94 #[async_trait::async_trait(?Send)]
95 impl Perform for Login {
96 type Response = LoginResponse;
100 context: &Data<LemmyContext>,
101 _websocket_id: Option<ConnectionId>,
102 ) -> Result<LoginResponse, LemmyError> {
103 let data: &Login = &self;
105 // Fetch that username / email
106 let username_or_email = data.username_or_email.clone();
107 let user = match blocking(context.pool(), move |conn| {
108 User_::find_by_email_or_username(conn, &username_or_email)
113 Err(_e) => return Err(ApiError::err("couldnt_find_that_username_or_email").into()),
116 // Verify the password
117 let valid: bool = verify(&data.password, &user.password_encrypted).unwrap_or(false);
119 return Err(ApiError::err("password_incorrect").into());
124 jwt: Claims::jwt(user.id, Settings::get().hostname)?,
129 #[async_trait::async_trait(?Send)]
130 impl Perform for Register {
131 type Response = LoginResponse;
135 context: &Data<LemmyContext>,
136 _websocket_id: Option<ConnectionId>,
137 ) -> Result<LoginResponse, LemmyError> {
138 let data: &Register = &self;
140 // Make sure site has open registration
141 if let Ok(site) = blocking(context.pool(), move |conn| Site::read_simple(conn)).await? {
142 if !site.open_registration {
143 return Err(ApiError::err("registration_closed").into());
147 // Password length check
148 if data.password.len() > 60 {
149 return Err(ApiError::err("invalid_password").into());
152 // Make sure passwords match
153 if data.password != data.password_verify {
154 return Err(ApiError::err("passwords_dont_match").into());
157 // Check if there are admins. False if admins exist
158 let no_admins = blocking(context.pool(), move |conn| {
159 UserViewSafe::admins(conn).map(|a| a.is_empty())
163 // If its not the admin, check the captcha
164 if !no_admins && Settings::get().captcha.enabled {
171 .unwrap_or_else(|| "".to_string()),
175 .unwrap_or_else(|| "".to_string()),
179 return Err(ApiError::err("captcha_incorrect").into());
183 check_slurs(&data.username)?;
185 let user_keypair = generate_actor_keypair()?;
186 if !is_valid_username(&data.username) {
187 return Err(ApiError::err("invalid_username").into());
189 let user_actor_id = generate_apub_endpoint(EndpointType::User, &data.username)?;
191 // Register the new user
192 let user_form = UserForm {
193 name: data.username.to_owned(),
194 email: Some(data.email.to_owned()),
195 matrix_user_id: None,
198 password_encrypted: data.password.to_owned(),
199 preferred_username: None,
204 show_nsfw: data.show_nsfw,
205 theme: "browser".into(),
206 default_sort_type: SortType::Active as i16,
207 default_listing_type: ListingType::Subscribed as i16,
208 lang: "browser".into(),
210 send_notifications_to_email: false,
211 actor_id: Some(user_actor_id.clone()),
214 private_key: Some(user_keypair.private_key),
215 public_key: Some(user_keypair.public_key),
216 last_refreshed_at: None,
217 inbox_url: Some(generate_inbox_url(&user_actor_id)?),
218 shared_inbox_url: Some(Some(generate_shared_inbox_url(&user_actor_id)?)),
222 let inserted_user = match blocking(context.pool(), move |conn| {
223 User_::register(conn, &user_form)
229 let err_type = if e.to_string()
230 == "duplicate key value violates unique constraint \"user__email_key\""
232 "email_already_exists"
234 "user_already_exists"
237 return Err(ApiError::err(err_type).into());
241 let main_community_keypair = generate_actor_keypair()?;
243 // Create the main community if it doesn't exist
245 match blocking(context.pool(), move |conn| Community::read(conn, 2)).await? {
248 let default_community_name = "main";
249 let actor_id = generate_apub_endpoint(EndpointType::Community, default_community_name)?;
250 let community_form = CommunityForm {
251 name: default_community_name.to_string(),
252 title: "The Default Community".to_string(),
253 description: Some("The Default Community".to_string()),
256 creator_id: inserted_user.id,
260 actor_id: Some(actor_id.to_owned()),
262 private_key: Some(main_community_keypair.private_key),
263 public_key: Some(main_community_keypair.public_key),
264 last_refreshed_at: None,
268 followers_url: Some(generate_followers_url(&actor_id)?),
269 inbox_url: Some(generate_inbox_url(&actor_id)?),
270 shared_inbox_url: Some(Some(generate_shared_inbox_url(&actor_id)?)),
272 blocking(context.pool(), move |conn| {
273 Community::create(conn, &community_form)
279 // Sign them up for main community no matter what
280 let community_follower_form = CommunityFollowerForm {
281 community_id: main_community.id,
282 user_id: inserted_user.id,
286 let follow = move |conn: &'_ _| CommunityFollower::follow(conn, &community_follower_form);
287 if blocking(context.pool(), follow).await?.is_err() {
288 return Err(ApiError::err("community_follower_already_exists").into());
291 // If its an admin, add them as a mod and follower to main
293 let community_moderator_form = CommunityModeratorForm {
294 community_id: main_community.id,
295 user_id: inserted_user.id,
298 let join = move |conn: &'_ _| CommunityModerator::join(conn, &community_moderator_form);
299 if blocking(context.pool(), join).await?.is_err() {
300 return Err(ApiError::err("community_moderator_already_exists").into());
306 jwt: Claims::jwt(inserted_user.id, Settings::get().hostname)?,
311 #[async_trait::async_trait(?Send)]
312 impl Perform for GetCaptcha {
313 type Response = GetCaptchaResponse;
317 context: &Data<LemmyContext>,
318 _websocket_id: Option<ConnectionId>,
319 ) -> Result<Self::Response, LemmyError> {
320 let captcha_settings = Settings::get().captcha;
322 if !captcha_settings.enabled {
323 return Ok(GetCaptchaResponse { ok: None });
326 let captcha = match captcha_settings.difficulty.as_str() {
327 "easy" => gen(Difficulty::Easy),
328 "medium" => gen(Difficulty::Medium),
329 "hard" => gen(Difficulty::Hard),
330 _ => gen(Difficulty::Medium),
333 let answer = captcha.chars_as_string();
335 let png_byte_array = captcha.as_png().expect("failed to generate captcha");
337 let png = base64::encode(png_byte_array);
339 let uuid = uuid::Uuid::new_v4().to_string();
341 let wav = captcha_espeak_wav_base64(&answer).ok();
343 let captcha_item = CaptchaItem {
345 uuid: uuid.to_owned(),
346 expires: naive_now() + Duration::minutes(10), // expires in 10 minutes
349 // Stores the captcha item on the queue
350 context.chat_server().do_send(captcha_item);
352 Ok(GetCaptchaResponse {
353 ok: Some(CaptchaResponse { png, uuid, wav }),
358 #[async_trait::async_trait(?Send)]
359 impl Perform for SaveUserSettings {
360 type Response = LoginResponse;
364 context: &Data<LemmyContext>,
365 _websocket_id: Option<ConnectionId>,
366 ) -> Result<LoginResponse, LemmyError> {
367 let data: &SaveUserSettings = &self;
368 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
370 let avatar = diesel_option_overwrite(&data.avatar);
371 let banner = diesel_option_overwrite(&data.banner);
372 let email = diesel_option_overwrite(&data.email);
373 let bio = diesel_option_overwrite(&data.bio);
374 let preferred_username = diesel_option_overwrite(&data.preferred_username);
375 let matrix_user_id = diesel_option_overwrite(&data.matrix_user_id);
377 // Check to make sure the avatar and banners are urls
378 check_optional_url(&avatar)?;
379 check_optional_url(&banner)?;
381 if let Some(Some(bio)) = &bio {
382 if bio.chars().count() > 300 {
383 return Err(ApiError::err("bio_length_overflow").into());
387 if let Some(Some(preferred_username)) = &preferred_username {
388 if !is_valid_preferred_username(preferred_username.trim()) {
389 return Err(ApiError::err("invalid_username").into());
393 let user_id = user.id;
394 let password_encrypted = match &data.new_password {
395 Some(new_password) => {
396 match &data.new_password_verify {
397 Some(new_password_verify) => {
398 // Make sure passwords match
399 if new_password != new_password_verify {
400 return Err(ApiError::err("passwords_dont_match").into());
403 // Check the old password
404 match &data.old_password {
405 Some(old_password) => {
406 let valid: bool = verify(old_password, &user.password_encrypted).unwrap_or(false);
408 return Err(ApiError::err("password_incorrect").into());
410 let new_password = new_password.to_owned();
411 let user = blocking(context.pool(), move |conn| {
412 User_::update_password(conn, user_id, &new_password)
415 user.password_encrypted
417 None => return Err(ApiError::err("password_incorrect").into()),
420 None => return Err(ApiError::err("passwords_dont_match").into()),
423 None => user.password_encrypted,
426 let default_listing_type = data.default_listing_type;
427 let default_sort_type = data.default_sort_type;
429 let user_form = UserForm {
438 published: Some(user.published),
439 updated: Some(naive_now()),
441 banned: Some(user.banned),
442 show_nsfw: data.show_nsfw,
443 theme: data.theme.to_owned(),
445 default_listing_type,
446 lang: data.lang.to_owned(),
447 show_avatars: data.show_avatars,
448 send_notifications_to_email: data.send_notifications_to_email,
449 actor_id: Some(user.actor_id),
452 private_key: user.private_key,
453 public_key: user.public_key,
454 last_refreshed_at: None,
455 shared_inbox_url: None,
458 let res = blocking(context.pool(), move |conn| {
459 User_::update(conn, user_id, &user_form)
462 let updated_user: User_ = match res {
465 let err_type = if e.to_string()
466 == "duplicate key value violates unique constraint \"user__email_key\""
468 "email_already_exists"
470 "user_already_exists"
473 return Err(ApiError::err(err_type).into());
479 jwt: Claims::jwt(updated_user.id, Settings::get().hostname)?,
484 #[async_trait::async_trait(?Send)]
485 impl Perform for GetUserDetails {
486 type Response = GetUserDetailsResponse;
490 context: &Data<LemmyContext>,
491 _websocket_id: Option<ConnectionId>,
492 ) -> Result<GetUserDetailsResponse, LemmyError> {
493 let data: &GetUserDetails = &self;
494 let user = get_user_from_jwt_opt(&data.auth, context.pool()).await?;
496 let show_nsfw = match &user {
497 Some(user) => user.show_nsfw,
501 let sort = SortType::from_str(&data.sort)?;
506 .unwrap_or_else(|| "admin".to_string());
507 let user_details_id = match data.user_id {
510 let user = blocking(context.pool(), move |conn| {
511 User_::read_from_name(conn, &username)
516 Err(_e) => return Err(ApiError::err("couldnt_find_that_username_or_email").into()),
521 let user_id = user.map(|u| u.id);
523 // You don't need to return settings for the user, since this comes back with GetSite
525 let user_view = blocking(context.pool(), move |conn| {
526 UserViewSafe::read(conn, user_details_id)
530 let page = data.page;
531 let limit = data.limit;
532 let saved_only = data.saved_only;
533 let community_id = data.community_id;
535 let (posts, comments) = blocking(context.pool(), move |conn| {
536 let mut posts_query = PostQueryBuilder::create(conn)
538 .show_nsfw(show_nsfw)
539 .saved_only(saved_only)
540 .community_id(community_id)
545 let mut comments_query = CommentQueryBuilder::create(conn)
548 .saved_only(saved_only)
552 // If its saved only, you don't care what creator it was
553 // Or, if its not saved, then you only want it for that specific creator
555 posts_query = posts_query.creator_id(user_details_id);
556 comments_query = comments_query.creator_id(user_details_id);
559 let posts = posts_query.list()?;
560 let comments = comments_query.list()?;
562 Ok((posts, comments)) as Result<_, LemmyError>
566 let mut follows = vec![];
567 if let Some(uid) = user_id {
568 if uid == user_details_id {
569 follows = blocking(context.pool(), move |conn| {
570 CommunityFollowerView::for_user(conn, user_details_id)
575 let moderates = blocking(context.pool(), move |conn| {
576 CommunityModeratorView::for_user(conn, user_details_id)
581 Ok(GetUserDetailsResponse {
591 #[async_trait::async_trait(?Send)]
592 impl Perform for AddAdmin {
593 type Response = AddAdminResponse;
597 context: &Data<LemmyContext>,
598 websocket_id: Option<ConnectionId>,
599 ) -> Result<AddAdminResponse, LemmyError> {
600 let data: &AddAdmin = &self;
601 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
603 // Make sure user is an admin
604 is_admin(context.pool(), user.id).await?;
606 let added = data.added;
607 let added_user_id = data.user_id;
608 let add_admin = move |conn: &'_ _| User_::add_admin(conn, added_user_id, added);
609 if blocking(context.pool(), add_admin).await?.is_err() {
610 return Err(ApiError::err("couldnt_update_user").into());
614 let form = ModAddForm {
615 mod_user_id: user.id,
616 other_user_id: data.user_id,
617 removed: Some(!data.added),
620 blocking(context.pool(), move |conn| ModAdd::create(conn, &form)).await??;
622 let site_creator_id = blocking(context.pool(), move |conn| {
623 Site::read(conn, 1).map(|s| s.creator_id)
627 let mut admins = blocking(context.pool(), move |conn| UserViewSafe::admins(conn)).await??;
628 let creator_index = admins
630 .position(|r| r.user.id == site_creator_id)
631 .context(location_info!())?;
632 let creator_user = admins.remove(creator_index);
633 admins.insert(0, creator_user);
635 let res = AddAdminResponse { admins };
637 context.chat_server().do_send(SendAllMessage {
638 op: UserOperation::AddAdmin,
639 response: res.clone(),
647 #[async_trait::async_trait(?Send)]
648 impl Perform for BanUser {
649 type Response = BanUserResponse;
653 context: &Data<LemmyContext>,
654 websocket_id: Option<ConnectionId>,
655 ) -> Result<BanUserResponse, LemmyError> {
656 let data: &BanUser = &self;
657 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
659 // Make sure user is an admin
660 is_admin(context.pool(), user.id).await?;
663 let banned_user_id = data.user_id;
664 let ban_user = move |conn: &'_ _| User_::ban_user(conn, banned_user_id, ban);
665 if blocking(context.pool(), ban_user).await?.is_err() {
666 return Err(ApiError::err("couldnt_update_user").into());
669 // Remove their data if that's desired
670 if data.remove_data {
672 blocking(context.pool(), move |conn: &'_ _| {
673 Post::update_removed_for_creator(conn, banned_user_id, None, true)
678 blocking(context.pool(), move |conn: &'_ _| {
679 Community::update_removed_for_creator(conn, banned_user_id, true)
684 blocking(context.pool(), move |conn: &'_ _| {
685 Comment::update_removed_for_creator(conn, banned_user_id, true)
691 let expires = match data.expires {
692 Some(time) => Some(naive_from_unix(time)),
696 let form = ModBanForm {
697 mod_user_id: user.id,
698 other_user_id: data.user_id,
699 reason: data.reason.to_owned(),
700 banned: Some(data.ban),
704 blocking(context.pool(), move |conn| ModBan::create(conn, &form)).await??;
706 let user_id = data.user_id;
707 let user_view = blocking(context.pool(), move |conn| {
708 UserViewSafe::read(conn, user_id)
712 let res = BanUserResponse {
717 context.chat_server().do_send(SendAllMessage {
718 op: UserOperation::BanUser,
719 response: res.clone(),
727 #[async_trait::async_trait(?Send)]
728 impl Perform for GetReplies {
729 type Response = GetRepliesResponse;
733 context: &Data<LemmyContext>,
734 _websocket_id: Option<ConnectionId>,
735 ) -> Result<GetRepliesResponse, LemmyError> {
736 let data: &GetReplies = &self;
737 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
739 let sort = SortType::from_str(&data.sort)?;
741 let page = data.page;
742 let limit = data.limit;
743 let unread_only = data.unread_only;
744 let user_id = user.id;
745 let replies = blocking(context.pool(), move |conn| {
746 CommentQueryBuilder::create(conn)
748 .unread_only(unread_only)
749 .recipient_id(user_id)
757 Ok(GetRepliesResponse { replies })
761 #[async_trait::async_trait(?Send)]
762 impl Perform for GetUserMentions {
763 type Response = GetUserMentionsResponse;
767 context: &Data<LemmyContext>,
768 _websocket_id: Option<ConnectionId>,
769 ) -> Result<GetUserMentionsResponse, LemmyError> {
770 let data: &GetUserMentions = &self;
771 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
773 let sort = SortType::from_str(&data.sort)?;
775 let page = data.page;
776 let limit = data.limit;
777 let unread_only = data.unread_only;
778 let user_id = user.id;
779 let mentions = blocking(context.pool(), move |conn| {
780 UserMentionQueryBuilder::create(conn)
781 .recipient_id(user_id)
784 .unread_only(unread_only)
791 Ok(GetUserMentionsResponse { mentions })
795 #[async_trait::async_trait(?Send)]
796 impl Perform for MarkUserMentionAsRead {
797 type Response = UserMentionResponse;
801 context: &Data<LemmyContext>,
802 _websocket_id: Option<ConnectionId>,
803 ) -> Result<UserMentionResponse, LemmyError> {
804 let data: &MarkUserMentionAsRead = &self;
805 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
807 let user_mention_id = data.user_mention_id;
808 let read_user_mention = blocking(context.pool(), move |conn| {
809 UserMention::read(conn, user_mention_id)
813 if user.id != read_user_mention.recipient_id {
814 return Err(ApiError::err("couldnt_update_comment").into());
817 let user_mention_id = read_user_mention.id;
818 let read = data.read;
819 let update_mention = move |conn: &'_ _| UserMention::update_read(conn, user_mention_id, read);
820 if blocking(context.pool(), update_mention).await?.is_err() {
821 return Err(ApiError::err("couldnt_update_comment").into());
824 let user_mention_id = read_user_mention.id;
825 let user_id = user.id;
826 let user_mention_view = blocking(context.pool(), move |conn| {
827 UserMentionView::read(conn, user_mention_id, Some(user_id))
831 Ok(UserMentionResponse { user_mention_view })
835 #[async_trait::async_trait(?Send)]
836 impl Perform for MarkAllAsRead {
837 type Response = GetRepliesResponse;
841 context: &Data<LemmyContext>,
842 _websocket_id: Option<ConnectionId>,
843 ) -> Result<GetRepliesResponse, LemmyError> {
844 let data: &MarkAllAsRead = &self;
845 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
847 let user_id = user.id;
848 let replies = blocking(context.pool(), move |conn| {
849 CommentQueryBuilder::create(conn)
851 .recipient_id(user_id)
859 // TODO: this should probably be a bulk operation
860 // Not easy to do as a bulk operation,
861 // because recipient_id isn't in the comment table
862 for comment_view in &replies {
863 let reply_id = comment_view.comment.id;
864 let mark_as_read = move |conn: &'_ _| Comment::update_read(conn, reply_id, true);
865 if blocking(context.pool(), mark_as_read).await?.is_err() {
866 return Err(ApiError::err("couldnt_update_comment").into());
870 // Mark all user mentions as read
871 let update_user_mentions = move |conn: &'_ _| UserMention::mark_all_as_read(conn, user_id);
872 if blocking(context.pool(), update_user_mentions)
876 return Err(ApiError::err("couldnt_update_comment").into());
879 // Mark all private_messages as read
880 let update_pm = move |conn: &'_ _| PrivateMessage::mark_all_as_read(conn, user_id);
881 if blocking(context.pool(), update_pm).await?.is_err() {
882 return Err(ApiError::err("couldnt_update_private_message").into());
885 Ok(GetRepliesResponse { replies: vec![] })
889 #[async_trait::async_trait(?Send)]
890 impl Perform for DeleteAccount {
891 type Response = LoginResponse;
895 context: &Data<LemmyContext>,
896 _websocket_id: Option<ConnectionId>,
897 ) -> Result<LoginResponse, LemmyError> {
898 let data: &DeleteAccount = &self;
899 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
901 // Verify the password
902 let valid: bool = verify(&data.password, &user.password_encrypted).unwrap_or(false);
904 return Err(ApiError::err("password_incorrect").into());
908 let user_id = user.id;
909 let permadelete = move |conn: &'_ _| Comment::permadelete_for_creator(conn, user_id);
910 if blocking(context.pool(), permadelete).await?.is_err() {
911 return Err(ApiError::err("couldnt_update_comment").into());
915 let permadelete = move |conn: &'_ _| Post::permadelete_for_creator(conn, user_id);
916 if blocking(context.pool(), permadelete).await?.is_err() {
917 return Err(ApiError::err("couldnt_update_post").into());
920 blocking(context.pool(), move |conn| {
921 User_::delete_account(conn, user_id)
926 jwt: data.auth.to_owned(),
931 #[async_trait::async_trait(?Send)]
932 impl Perform for PasswordReset {
933 type Response = PasswordResetResponse;
937 context: &Data<LemmyContext>,
938 _websocket_id: Option<ConnectionId>,
939 ) -> Result<PasswordResetResponse, LemmyError> {
940 let data: &PasswordReset = &self;
943 let email = data.email.clone();
944 let user = match blocking(context.pool(), move |conn| {
945 User_::find_by_email(conn, &email)
950 Err(_e) => return Err(ApiError::err("couldnt_find_that_username_or_email").into()),
953 // Generate a random token
954 let token = generate_random_string();
957 let token2 = token.clone();
958 let user_id = user.id;
959 blocking(context.pool(), move |conn| {
960 PasswordResetRequest::create_token(conn, user_id, &token2)
964 // Email the pure token to the user.
965 // TODO no i18n support here.
966 let user_email = &user.email.expect("email");
967 let subject = &format!("Password reset for {}", user.name);
968 let hostname = &Settings::get().get_protocol_and_hostname();
969 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);
970 match send_email(subject, user_email, &user.name, html) {
972 Err(_e) => return Err(ApiError::err(&_e).into()),
975 Ok(PasswordResetResponse {})
979 #[async_trait::async_trait(?Send)]
980 impl Perform for PasswordChange {
981 type Response = LoginResponse;
985 context: &Data<LemmyContext>,
986 _websocket_id: Option<ConnectionId>,
987 ) -> Result<LoginResponse, LemmyError> {
988 let data: &PasswordChange = &self;
990 // Fetch the user_id from the token
991 let token = data.token.clone();
992 let user_id = blocking(context.pool(), move |conn| {
993 PasswordResetRequest::read_from_token(conn, &token).map(|p| p.user_id)
997 // Make sure passwords match
998 if data.password != data.password_verify {
999 return Err(ApiError::err("passwords_dont_match").into());
1002 // Update the user with the new password
1003 let password = data.password.clone();
1004 let updated_user = match blocking(context.pool(), move |conn| {
1005 User_::update_password(conn, user_id, &password)
1010 Err(_e) => return Err(ApiError::err("couldnt_update_user").into()),
1015 jwt: Claims::jwt(updated_user.id, Settings::get().hostname)?,
1020 #[async_trait::async_trait(?Send)]
1021 impl Perform for CreatePrivateMessage {
1022 type Response = PrivateMessageResponse;
1026 context: &Data<LemmyContext>,
1027 websocket_id: Option<ConnectionId>,
1028 ) -> Result<PrivateMessageResponse, LemmyError> {
1029 let data: &CreatePrivateMessage = &self;
1030 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
1032 let content_slurs_removed = remove_slurs(&data.content.to_owned());
1034 let private_message_form = PrivateMessageForm {
1035 content: content_slurs_removed.to_owned(),
1036 creator_id: user.id,
1037 recipient_id: data.recipient_id,
1046 let inserted_private_message = match blocking(context.pool(), move |conn| {
1047 PrivateMessage::create(conn, &private_message_form)
1051 Ok(private_message) => private_message,
1053 return Err(ApiError::err("couldnt_create_private_message").into());
1057 let inserted_private_message_id = inserted_private_message.id;
1058 let updated_private_message = match blocking(
1060 move |conn| -> Result<PrivateMessage, LemmyError> {
1061 let apub_id = generate_apub_endpoint(
1062 EndpointType::PrivateMessage,
1063 &inserted_private_message_id.to_string(),
1065 Ok(PrivateMessage::update_ap_id(
1067 inserted_private_message_id,
1074 Ok(private_message) => private_message,
1075 Err(_e) => return Err(ApiError::err("couldnt_create_private_message").into()),
1078 updated_private_message.send_create(&user, context).await?;
1080 // Send notifications to the recipient
1081 let recipient_id = data.recipient_id;
1082 let recipient_user =
1083 blocking(context.pool(), move |conn| User_::read(conn, recipient_id)).await??;
1084 if recipient_user.send_notifications_to_email {
1087 "Private Message from",
1089 &content_slurs_removed,
1093 let message = blocking(context.pool(), move |conn| {
1094 PrivateMessageView::read(conn, inserted_private_message.id)
1098 let res = PrivateMessageResponse {
1099 private_message_view: message,
1102 context.chat_server().do_send(SendUserRoomMessage {
1103 op: UserOperation::CreatePrivateMessage,
1104 response: res.clone(),
1113 #[async_trait::async_trait(?Send)]
1114 impl Perform for EditPrivateMessage {
1115 type Response = PrivateMessageResponse;
1119 context: &Data<LemmyContext>,
1120 websocket_id: Option<ConnectionId>,
1121 ) -> Result<PrivateMessageResponse, LemmyError> {
1122 let data: &EditPrivateMessage = &self;
1123 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
1125 // Checking permissions
1126 let private_message_id = data.private_message_id;
1127 let orig_private_message = blocking(context.pool(), move |conn| {
1128 PrivateMessage::read(conn, private_message_id)
1131 if user.id != orig_private_message.creator_id {
1132 return Err(ApiError::err("no_private_message_edit_allowed").into());
1136 let content_slurs_removed = remove_slurs(&data.content);
1137 let private_message_id = data.private_message_id;
1138 let updated_private_message = match blocking(context.pool(), move |conn| {
1139 PrivateMessage::update_content(conn, private_message_id, &content_slurs_removed)
1143 Ok(private_message) => private_message,
1144 Err(_e) => return Err(ApiError::err("couldnt_update_private_message").into()),
1147 // Send the apub update
1148 updated_private_message.send_update(&user, context).await?;
1150 let private_message_id = data.private_message_id;
1151 let message = blocking(context.pool(), move |conn| {
1152 PrivateMessageView::read(conn, private_message_id)
1155 let recipient_id = message.recipient.id;
1157 let res = PrivateMessageResponse {
1158 private_message_view: message,
1161 context.chat_server().do_send(SendUserRoomMessage {
1162 op: UserOperation::EditPrivateMessage,
1163 response: res.clone(),
1172 #[async_trait::async_trait(?Send)]
1173 impl Perform for DeletePrivateMessage {
1174 type Response = PrivateMessageResponse;
1178 context: &Data<LemmyContext>,
1179 websocket_id: Option<ConnectionId>,
1180 ) -> Result<PrivateMessageResponse, LemmyError> {
1181 let data: &DeletePrivateMessage = &self;
1182 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
1184 // Checking permissions
1185 let private_message_id = data.private_message_id;
1186 let orig_private_message = blocking(context.pool(), move |conn| {
1187 PrivateMessage::read(conn, private_message_id)
1190 if user.id != orig_private_message.creator_id {
1191 return Err(ApiError::err("no_private_message_edit_allowed").into());
1195 let private_message_id = data.private_message_id;
1196 let deleted = data.deleted;
1197 let updated_private_message = match blocking(context.pool(), move |conn| {
1198 PrivateMessage::update_deleted(conn, private_message_id, deleted)
1202 Ok(private_message) => private_message,
1203 Err(_e) => return Err(ApiError::err("couldnt_update_private_message").into()),
1206 // Send the apub update
1208 updated_private_message.send_delete(&user, context).await?;
1210 updated_private_message
1211 .send_undo_delete(&user, context)
1215 let private_message_id = data.private_message_id;
1216 let message = blocking(context.pool(), move |conn| {
1217 PrivateMessageView::read(conn, private_message_id)
1220 let recipient_id = message.recipient.id;
1222 let res = PrivateMessageResponse {
1223 private_message_view: message,
1226 context.chat_server().do_send(SendUserRoomMessage {
1227 op: UserOperation::DeletePrivateMessage,
1228 response: res.clone(),
1237 #[async_trait::async_trait(?Send)]
1238 impl Perform for MarkPrivateMessageAsRead {
1239 type Response = PrivateMessageResponse;
1243 context: &Data<LemmyContext>,
1244 websocket_id: Option<ConnectionId>,
1245 ) -> Result<PrivateMessageResponse, LemmyError> {
1246 let data: &MarkPrivateMessageAsRead = &self;
1247 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
1249 // Checking permissions
1250 let private_message_id = data.private_message_id;
1251 let orig_private_message = blocking(context.pool(), move |conn| {
1252 PrivateMessage::read(conn, private_message_id)
1255 if user.id != orig_private_message.recipient_id {
1256 return Err(ApiError::err("couldnt_update_private_message").into());
1260 let private_message_id = data.private_message_id;
1261 let read = data.read;
1262 match blocking(context.pool(), move |conn| {
1263 PrivateMessage::update_read(conn, private_message_id, read)
1267 Ok(private_message) => private_message,
1268 Err(_e) => return Err(ApiError::err("couldnt_update_private_message").into()),
1271 // No need to send an apub update
1273 let private_message_id = data.private_message_id;
1274 let message = blocking(context.pool(), move |conn| {
1275 PrivateMessageView::read(conn, private_message_id)
1278 let recipient_id = message.recipient.id;
1280 let res = PrivateMessageResponse {
1281 private_message_view: message,
1284 context.chat_server().do_send(SendUserRoomMessage {
1285 op: UserOperation::MarkPrivateMessageAsRead,
1286 response: res.clone(),
1295 #[async_trait::async_trait(?Send)]
1296 impl Perform for GetPrivateMessages {
1297 type Response = PrivateMessagesResponse;
1301 context: &Data<LemmyContext>,
1302 _websocket_id: Option<ConnectionId>,
1303 ) -> Result<PrivateMessagesResponse, LemmyError> {
1304 let data: &GetPrivateMessages = &self;
1305 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
1306 let user_id = user.id;
1308 let page = data.page;
1309 let limit = data.limit;
1310 let unread_only = data.unread_only;
1311 let messages = blocking(context.pool(), move |conn| {
1312 PrivateMessageQueryBuilder::create(&conn, user_id)
1315 .unread_only(unread_only)
1320 Ok(PrivateMessagesResponse {
1321 private_messages: messages,
1326 #[async_trait::async_trait(?Send)]
1327 impl Perform for GetReportCount {
1328 type Response = GetReportCountResponse;
1332 context: &Data<LemmyContext>,
1333 websocket_id: Option<ConnectionId>,
1334 ) -> Result<GetReportCountResponse, LemmyError> {
1335 let data: &GetReportCount = &self;
1336 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
1338 let user_id = user.id;
1339 let community_id = data.community;
1341 collect_moderated_communities(user_id, community_id, context.pool()).await?;
1344 if community_ids.is_empty() {
1345 GetReportCountResponse {
1351 let ids = community_ids.clone();
1352 let comment_reports = blocking(context.pool(), move |conn| {
1353 CommentReportView::get_report_count(conn, &ids)
1357 let ids = community_ids.clone();
1358 let post_reports = blocking(context.pool(), move |conn| {
1359 PostReportView::get_report_count(conn, &ids)
1363 GetReportCountResponse {
1364 community: data.community,
1371 context.chat_server().do_send(SendUserRoomMessage {
1372 op: UserOperation::GetReportCount,
1373 response: res.clone(),
1374 recipient_id: user.id,