2 captcha_espeak_wav_base64,
3 collect_moderated_communities,
9 use actix_web::web::Data;
12 use captcha::{gen, Difficulty};
14 use lemmy_api_structs::{blocking, send_email_to_user, user::*};
16 generate_apub_endpoint,
17 generate_followers_url,
19 generate_shared_inbox_url,
23 use lemmy_db_queries::{
24 diesel_option_overwrite,
25 diesel_option_overwrite_to_url,
28 community::Community_,
29 password_reset_request::PasswordResetRequest_,
31 private_message::PrivateMessage_,
34 user_mention::UserMention_,
42 use lemmy_db_schema::{
48 password_reset_request::*,
57 comment_report_view::CommentReportView,
58 comment_view::CommentQueryBuilder,
59 post_report_view::PostReportView,
60 post_view::PostQueryBuilder,
61 private_message_view::{PrivateMessageQueryBuilder, PrivateMessageView},
63 use lemmy_db_views_actor::{
64 community_follower_view::CommunityFollowerView,
65 community_moderator_view::CommunityModeratorView,
66 user_mention_view::{UserMentionQueryBuilder, UserMentionView},
67 user_view::UserViewSafe,
70 apub::generate_actor_keypair,
74 settings::structs::Settings,
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()),
255 creator_id: inserted_user.id,
259 actor_id: Some(actor_id.to_owned()),
261 private_key: Some(main_community_keypair.private_key),
262 public_key: Some(main_community_keypair.public_key),
263 last_refreshed_at: None,
267 followers_url: Some(generate_followers_url(&actor_id)?),
268 inbox_url: Some(generate_inbox_url(&actor_id)?),
269 shared_inbox_url: Some(Some(generate_shared_inbox_url(&actor_id)?)),
271 blocking(context.pool(), move |conn| {
272 Community::create(conn, &community_form)
278 // Sign them up for main community no matter what
279 let community_follower_form = CommunityFollowerForm {
280 community_id: main_community.id,
281 user_id: inserted_user.id,
285 let follow = move |conn: &'_ _| CommunityFollower::follow(conn, &community_follower_form);
286 if blocking(context.pool(), follow).await?.is_err() {
287 return Err(ApiError::err("community_follower_already_exists").into());
290 // If its an admin, add them as a mod and follower to main
292 let community_moderator_form = CommunityModeratorForm {
293 community_id: main_community.id,
294 user_id: inserted_user.id,
297 let join = move |conn: &'_ _| CommunityModerator::join(conn, &community_moderator_form);
298 if blocking(context.pool(), join).await?.is_err() {
299 return Err(ApiError::err("community_moderator_already_exists").into());
305 jwt: Claims::jwt(inserted_user.id, Settings::get().hostname())?,
310 #[async_trait::async_trait(?Send)]
311 impl Perform for GetCaptcha {
312 type Response = GetCaptchaResponse;
316 context: &Data<LemmyContext>,
317 _websocket_id: Option<ConnectionId>,
318 ) -> Result<Self::Response, LemmyError> {
319 let captcha_settings = Settings::get().captcha();
321 if !captcha_settings.enabled {
322 return Ok(GetCaptchaResponse { ok: None });
325 let captcha = match captcha_settings.difficulty.as_str() {
326 "easy" => gen(Difficulty::Easy),
327 "medium" => gen(Difficulty::Medium),
328 "hard" => gen(Difficulty::Hard),
329 _ => gen(Difficulty::Medium),
332 let answer = captcha.chars_as_string();
334 let png_byte_array = captcha.as_png().expect("failed to generate captcha");
336 let png = base64::encode(png_byte_array);
338 let uuid = uuid::Uuid::new_v4().to_string();
340 let wav = captcha_espeak_wav_base64(&answer).ok();
342 let captcha_item = CaptchaItem {
344 uuid: uuid.to_owned(),
345 expires: naive_now() + Duration::minutes(10), // expires in 10 minutes
348 // Stores the captcha item on the queue
349 context.chat_server().do_send(captcha_item);
351 Ok(GetCaptchaResponse {
352 ok: Some(CaptchaResponse { png, uuid, wav }),
357 #[async_trait::async_trait(?Send)]
358 impl Perform for SaveUserSettings {
359 type Response = LoginResponse;
363 context: &Data<LemmyContext>,
364 _websocket_id: Option<ConnectionId>,
365 ) -> Result<LoginResponse, LemmyError> {
366 let data: &SaveUserSettings = &self;
367 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
369 let avatar = diesel_option_overwrite_to_url(&data.avatar)?;
370 let banner = diesel_option_overwrite_to_url(&data.banner)?;
371 let email = diesel_option_overwrite(&data.email);
372 let bio = diesel_option_overwrite(&data.bio);
373 let preferred_username = diesel_option_overwrite(&data.preferred_username);
374 let matrix_user_id = diesel_option_overwrite(&data.matrix_user_id);
376 if let Some(Some(bio)) = &bio {
377 if bio.chars().count() > 300 {
378 return Err(ApiError::err("bio_length_overflow").into());
382 if let Some(Some(preferred_username)) = &preferred_username {
383 if !is_valid_preferred_username(preferred_username.trim()) {
384 return Err(ApiError::err("invalid_username").into());
388 let user_id = user.id;
389 let password_encrypted = match &data.new_password {
390 Some(new_password) => {
391 match &data.new_password_verify {
392 Some(new_password_verify) => {
393 // Make sure passwords match
394 if new_password != new_password_verify {
395 return Err(ApiError::err("passwords_dont_match").into());
398 // Check the old password
399 match &data.old_password {
400 Some(old_password) => {
401 let valid: bool = verify(old_password, &user.password_encrypted).unwrap_or(false);
403 return Err(ApiError::err("password_incorrect").into());
405 let new_password = new_password.to_owned();
406 let user = blocking(context.pool(), move |conn| {
407 User_::update_password(conn, user_id, &new_password)
410 user.password_encrypted
412 None => return Err(ApiError::err("password_incorrect").into()),
415 None => return Err(ApiError::err("passwords_dont_match").into()),
418 None => user.password_encrypted,
421 let default_listing_type = data.default_listing_type;
422 let default_sort_type = data.default_sort_type;
424 let user_form = UserForm {
433 published: Some(user.published),
434 updated: Some(naive_now()),
436 banned: Some(user.banned),
437 show_nsfw: data.show_nsfw,
438 theme: data.theme.to_owned(),
440 default_listing_type,
441 lang: data.lang.to_owned(),
442 show_avatars: data.show_avatars,
443 send_notifications_to_email: data.send_notifications_to_email,
444 actor_id: Some(user.actor_id),
447 private_key: user.private_key,
448 public_key: user.public_key,
449 last_refreshed_at: None,
450 shared_inbox_url: None,
453 let res = blocking(context.pool(), move |conn| {
454 User_::update(conn, user_id, &user_form)
457 let updated_user: User_ = match res {
460 let err_type = if e.to_string()
461 == "duplicate key value violates unique constraint \"user__email_key\""
463 "email_already_exists"
465 "user_already_exists"
468 return Err(ApiError::err(err_type).into());
474 jwt: Claims::jwt(updated_user.id, Settings::get().hostname())?,
479 #[async_trait::async_trait(?Send)]
480 impl Perform for GetUserDetails {
481 type Response = GetUserDetailsResponse;
485 context: &Data<LemmyContext>,
486 _websocket_id: Option<ConnectionId>,
487 ) -> Result<GetUserDetailsResponse, LemmyError> {
488 let data: &GetUserDetails = &self;
489 let user = get_user_from_jwt_opt(&data.auth, context.pool()).await?;
491 let show_nsfw = match &user {
492 Some(user) => user.show_nsfw,
496 let sort = SortType::from_str(&data.sort)?;
501 .unwrap_or_else(|| "admin".to_string());
502 let user_details_id = match data.user_id {
505 let user = blocking(context.pool(), move |conn| {
506 User_::read_from_name(conn, &username)
511 Err(_e) => return Err(ApiError::err("couldnt_find_that_username_or_email").into()),
516 let user_id = user.map(|u| u.id);
518 // You don't need to return settings for the user, since this comes back with GetSite
520 let user_view = blocking(context.pool(), move |conn| {
521 UserViewSafe::read(conn, user_details_id)
525 let page = data.page;
526 let limit = data.limit;
527 let saved_only = data.saved_only;
528 let community_id = data.community_id;
530 let (posts, comments) = blocking(context.pool(), move |conn| {
531 let mut posts_query = PostQueryBuilder::create(conn)
533 .show_nsfw(show_nsfw)
534 .saved_only(saved_only)
535 .community_id(community_id)
540 let mut comments_query = CommentQueryBuilder::create(conn)
543 .saved_only(saved_only)
547 // If its saved only, you don't care what creator it was
548 // Or, if its not saved, then you only want it for that specific creator
550 posts_query = posts_query.creator_id(user_details_id);
551 comments_query = comments_query.creator_id(user_details_id);
554 let posts = posts_query.list()?;
555 let comments = comments_query.list()?;
557 Ok((posts, comments)) as Result<_, LemmyError>
561 let mut follows = vec![];
562 if let Some(uid) = user_id {
563 if uid == user_details_id {
564 follows = blocking(context.pool(), move |conn| {
565 CommunityFollowerView::for_user(conn, user_details_id)
570 let moderates = blocking(context.pool(), move |conn| {
571 CommunityModeratorView::for_user(conn, user_details_id)
576 Ok(GetUserDetailsResponse {
586 #[async_trait::async_trait(?Send)]
587 impl Perform for AddAdmin {
588 type Response = AddAdminResponse;
592 context: &Data<LemmyContext>,
593 websocket_id: Option<ConnectionId>,
594 ) -> Result<AddAdminResponse, LemmyError> {
595 let data: &AddAdmin = &self;
596 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
598 // Make sure user is an admin
599 is_admin(context.pool(), user.id).await?;
601 let added = data.added;
602 let added_user_id = data.user_id;
603 let add_admin = move |conn: &'_ _| User_::add_admin(conn, added_user_id, added);
604 if blocking(context.pool(), add_admin).await?.is_err() {
605 return Err(ApiError::err("couldnt_update_user").into());
609 let form = ModAddForm {
610 mod_user_id: user.id,
611 other_user_id: data.user_id,
612 removed: Some(!data.added),
615 blocking(context.pool(), move |conn| ModAdd::create(conn, &form)).await??;
617 let site_creator_id = blocking(context.pool(), move |conn| {
618 Site::read(conn, 1).map(|s| s.creator_id)
622 let mut admins = blocking(context.pool(), move |conn| UserViewSafe::admins(conn)).await??;
623 let creator_index = admins
625 .position(|r| r.user.id == site_creator_id)
626 .context(location_info!())?;
627 let creator_user = admins.remove(creator_index);
628 admins.insert(0, creator_user);
630 let res = AddAdminResponse { admins };
632 context.chat_server().do_send(SendAllMessage {
633 op: UserOperation::AddAdmin,
634 response: res.clone(),
642 #[async_trait::async_trait(?Send)]
643 impl Perform for BanUser {
644 type Response = BanUserResponse;
648 context: &Data<LemmyContext>,
649 websocket_id: Option<ConnectionId>,
650 ) -> Result<BanUserResponse, LemmyError> {
651 let data: &BanUser = &self;
652 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
654 // Make sure user is an admin
655 is_admin(context.pool(), user.id).await?;
658 let banned_user_id = data.user_id;
659 let ban_user = move |conn: &'_ _| User_::ban_user(conn, banned_user_id, ban);
660 if blocking(context.pool(), ban_user).await?.is_err() {
661 return Err(ApiError::err("couldnt_update_user").into());
664 // Remove their data if that's desired
665 if data.remove_data {
667 blocking(context.pool(), move |conn: &'_ _| {
668 Post::update_removed_for_creator(conn, banned_user_id, None, true)
673 blocking(context.pool(), move |conn: &'_ _| {
674 Community::update_removed_for_creator(conn, banned_user_id, true)
679 blocking(context.pool(), move |conn: &'_ _| {
680 Comment::update_removed_for_creator(conn, banned_user_id, true)
686 let expires = match data.expires {
687 Some(time) => Some(naive_from_unix(time)),
691 let form = ModBanForm {
692 mod_user_id: user.id,
693 other_user_id: data.user_id,
694 reason: data.reason.to_owned(),
695 banned: Some(data.ban),
699 blocking(context.pool(), move |conn| ModBan::create(conn, &form)).await??;
701 let user_id = data.user_id;
702 let user_view = blocking(context.pool(), move |conn| {
703 UserViewSafe::read(conn, user_id)
707 let res = BanUserResponse {
712 context.chat_server().do_send(SendAllMessage {
713 op: UserOperation::BanUser,
714 response: res.clone(),
722 #[async_trait::async_trait(?Send)]
723 impl Perform for GetReplies {
724 type Response = GetRepliesResponse;
728 context: &Data<LemmyContext>,
729 _websocket_id: Option<ConnectionId>,
730 ) -> Result<GetRepliesResponse, LemmyError> {
731 let data: &GetReplies = &self;
732 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
734 let sort = SortType::from_str(&data.sort)?;
736 let page = data.page;
737 let limit = data.limit;
738 let unread_only = data.unread_only;
739 let user_id = user.id;
740 let replies = blocking(context.pool(), move |conn| {
741 CommentQueryBuilder::create(conn)
743 .unread_only(unread_only)
744 .recipient_id(user_id)
752 Ok(GetRepliesResponse { replies })
756 #[async_trait::async_trait(?Send)]
757 impl Perform for GetUserMentions {
758 type Response = GetUserMentionsResponse;
762 context: &Data<LemmyContext>,
763 _websocket_id: Option<ConnectionId>,
764 ) -> Result<GetUserMentionsResponse, LemmyError> {
765 let data: &GetUserMentions = &self;
766 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
768 let sort = SortType::from_str(&data.sort)?;
770 let page = data.page;
771 let limit = data.limit;
772 let unread_only = data.unread_only;
773 let user_id = user.id;
774 let mentions = blocking(context.pool(), move |conn| {
775 UserMentionQueryBuilder::create(conn)
776 .recipient_id(user_id)
779 .unread_only(unread_only)
786 Ok(GetUserMentionsResponse { mentions })
790 #[async_trait::async_trait(?Send)]
791 impl Perform for MarkUserMentionAsRead {
792 type Response = UserMentionResponse;
796 context: &Data<LemmyContext>,
797 _websocket_id: Option<ConnectionId>,
798 ) -> Result<UserMentionResponse, LemmyError> {
799 let data: &MarkUserMentionAsRead = &self;
800 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
802 let user_mention_id = data.user_mention_id;
803 let read_user_mention = blocking(context.pool(), move |conn| {
804 UserMention::read(conn, user_mention_id)
808 if user.id != read_user_mention.recipient_id {
809 return Err(ApiError::err("couldnt_update_comment").into());
812 let user_mention_id = read_user_mention.id;
813 let read = data.read;
814 let update_mention = move |conn: &'_ _| UserMention::update_read(conn, user_mention_id, read);
815 if blocking(context.pool(), update_mention).await?.is_err() {
816 return Err(ApiError::err("couldnt_update_comment").into());
819 let user_mention_id = read_user_mention.id;
820 let user_id = user.id;
821 let user_mention_view = blocking(context.pool(), move |conn| {
822 UserMentionView::read(conn, user_mention_id, Some(user_id))
826 Ok(UserMentionResponse { user_mention_view })
830 #[async_trait::async_trait(?Send)]
831 impl Perform for MarkAllAsRead {
832 type Response = GetRepliesResponse;
836 context: &Data<LemmyContext>,
837 _websocket_id: Option<ConnectionId>,
838 ) -> Result<GetRepliesResponse, LemmyError> {
839 let data: &MarkAllAsRead = &self;
840 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
842 let user_id = user.id;
843 let replies = blocking(context.pool(), move |conn| {
844 CommentQueryBuilder::create(conn)
846 .recipient_id(user_id)
854 // TODO: this should probably be a bulk operation
855 // Not easy to do as a bulk operation,
856 // because recipient_id isn't in the comment table
857 for comment_view in &replies {
858 let reply_id = comment_view.comment.id;
859 let mark_as_read = move |conn: &'_ _| Comment::update_read(conn, reply_id, true);
860 if blocking(context.pool(), mark_as_read).await?.is_err() {
861 return Err(ApiError::err("couldnt_update_comment").into());
865 // Mark all user mentions as read
866 let update_user_mentions = move |conn: &'_ _| UserMention::mark_all_as_read(conn, user_id);
867 if blocking(context.pool(), update_user_mentions)
871 return Err(ApiError::err("couldnt_update_comment").into());
874 // Mark all private_messages as read
875 let update_pm = move |conn: &'_ _| PrivateMessage::mark_all_as_read(conn, user_id);
876 if blocking(context.pool(), update_pm).await?.is_err() {
877 return Err(ApiError::err("couldnt_update_private_message").into());
880 Ok(GetRepliesResponse { replies: vec![] })
884 #[async_trait::async_trait(?Send)]
885 impl Perform for DeleteAccount {
886 type Response = LoginResponse;
890 context: &Data<LemmyContext>,
891 _websocket_id: Option<ConnectionId>,
892 ) -> Result<LoginResponse, LemmyError> {
893 let data: &DeleteAccount = &self;
894 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
896 // Verify the password
897 let valid: bool = verify(&data.password, &user.password_encrypted).unwrap_or(false);
899 return Err(ApiError::err("password_incorrect").into());
903 let user_id = user.id;
904 let permadelete = move |conn: &'_ _| Comment::permadelete_for_creator(conn, user_id);
905 if blocking(context.pool(), permadelete).await?.is_err() {
906 return Err(ApiError::err("couldnt_update_comment").into());
910 let permadelete = move |conn: &'_ _| Post::permadelete_for_creator(conn, user_id);
911 if blocking(context.pool(), permadelete).await?.is_err() {
912 return Err(ApiError::err("couldnt_update_post").into());
915 blocking(context.pool(), move |conn| {
916 User_::delete_account(conn, user_id)
921 jwt: data.auth.to_owned(),
926 #[async_trait::async_trait(?Send)]
927 impl Perform for PasswordReset {
928 type Response = PasswordResetResponse;
932 context: &Data<LemmyContext>,
933 _websocket_id: Option<ConnectionId>,
934 ) -> Result<PasswordResetResponse, LemmyError> {
935 let data: &PasswordReset = &self;
938 let email = data.email.clone();
939 let user = match blocking(context.pool(), move |conn| {
940 User_::find_by_email(conn, &email)
945 Err(_e) => return Err(ApiError::err("couldnt_find_that_username_or_email").into()),
948 // Generate a random token
949 let token = generate_random_string();
952 let token2 = token.clone();
953 let user_id = user.id;
954 blocking(context.pool(), move |conn| {
955 PasswordResetRequest::create_token(conn, user_id, &token2)
959 // Email the pure token to the user.
960 // TODO no i18n support here.
961 let user_email = &user.email.expect("email");
962 let subject = &format!("Password reset for {}", user.name);
963 let hostname = &Settings::get().get_protocol_and_hostname();
964 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);
965 match send_email(subject, user_email, &user.name, html) {
967 Err(_e) => return Err(ApiError::err(&_e).into()),
970 Ok(PasswordResetResponse {})
974 #[async_trait::async_trait(?Send)]
975 impl Perform for PasswordChange {
976 type Response = LoginResponse;
980 context: &Data<LemmyContext>,
981 _websocket_id: Option<ConnectionId>,
982 ) -> Result<LoginResponse, LemmyError> {
983 let data: &PasswordChange = &self;
985 // Fetch the user_id from the token
986 let token = data.token.clone();
987 let user_id = blocking(context.pool(), move |conn| {
988 PasswordResetRequest::read_from_token(conn, &token).map(|p| p.user_id)
992 // Make sure passwords match
993 if data.password != data.password_verify {
994 return Err(ApiError::err("passwords_dont_match").into());
997 // Update the user with the new password
998 let password = data.password.clone();
999 let updated_user = match blocking(context.pool(), move |conn| {
1000 User_::update_password(conn, user_id, &password)
1005 Err(_e) => return Err(ApiError::err("couldnt_update_user").into()),
1010 jwt: Claims::jwt(updated_user.id, Settings::get().hostname())?,
1015 #[async_trait::async_trait(?Send)]
1016 impl Perform for CreatePrivateMessage {
1017 type Response = PrivateMessageResponse;
1021 context: &Data<LemmyContext>,
1022 websocket_id: Option<ConnectionId>,
1023 ) -> Result<PrivateMessageResponse, LemmyError> {
1024 let data: &CreatePrivateMessage = &self;
1025 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
1027 let content_slurs_removed = remove_slurs(&data.content.to_owned());
1029 let private_message_form = PrivateMessageForm {
1030 content: content_slurs_removed.to_owned(),
1031 creator_id: user.id,
1032 recipient_id: data.recipient_id,
1041 let inserted_private_message = match blocking(context.pool(), move |conn| {
1042 PrivateMessage::create(conn, &private_message_form)
1046 Ok(private_message) => private_message,
1048 return Err(ApiError::err("couldnt_create_private_message").into());
1052 let inserted_private_message_id = inserted_private_message.id;
1053 let updated_private_message = match blocking(
1055 move |conn| -> Result<PrivateMessage, LemmyError> {
1056 let apub_id = generate_apub_endpoint(
1057 EndpointType::PrivateMessage,
1058 &inserted_private_message_id.to_string(),
1060 Ok(PrivateMessage::update_ap_id(
1062 inserted_private_message_id,
1069 Ok(private_message) => private_message,
1070 Err(_e) => return Err(ApiError::err("couldnt_create_private_message").into()),
1073 updated_private_message.send_create(&user, context).await?;
1075 // Send notifications to the recipient
1076 let recipient_id = data.recipient_id;
1077 let recipient_user =
1078 blocking(context.pool(), move |conn| User_::read(conn, recipient_id)).await??;
1079 if recipient_user.send_notifications_to_email {
1082 "Private Message from",
1084 &content_slurs_removed,
1088 let message = blocking(context.pool(), move |conn| {
1089 PrivateMessageView::read(conn, inserted_private_message.id)
1093 let res = PrivateMessageResponse {
1094 private_message_view: message,
1097 context.chat_server().do_send(SendUserRoomMessage {
1098 op: UserOperation::CreatePrivateMessage,
1099 response: res.clone(),
1108 #[async_trait::async_trait(?Send)]
1109 impl Perform for EditPrivateMessage {
1110 type Response = PrivateMessageResponse;
1114 context: &Data<LemmyContext>,
1115 websocket_id: Option<ConnectionId>,
1116 ) -> Result<PrivateMessageResponse, LemmyError> {
1117 let data: &EditPrivateMessage = &self;
1118 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
1120 // Checking permissions
1121 let private_message_id = data.private_message_id;
1122 let orig_private_message = blocking(context.pool(), move |conn| {
1123 PrivateMessage::read(conn, private_message_id)
1126 if user.id != orig_private_message.creator_id {
1127 return Err(ApiError::err("no_private_message_edit_allowed").into());
1131 let content_slurs_removed = remove_slurs(&data.content);
1132 let private_message_id = data.private_message_id;
1133 let updated_private_message = match blocking(context.pool(), move |conn| {
1134 PrivateMessage::update_content(conn, private_message_id, &content_slurs_removed)
1138 Ok(private_message) => private_message,
1139 Err(_e) => return Err(ApiError::err("couldnt_update_private_message").into()),
1142 // Send the apub update
1143 updated_private_message.send_update(&user, context).await?;
1145 let private_message_id = data.private_message_id;
1146 let message = blocking(context.pool(), move |conn| {
1147 PrivateMessageView::read(conn, private_message_id)
1150 let recipient_id = message.recipient.id;
1152 let res = PrivateMessageResponse {
1153 private_message_view: message,
1156 context.chat_server().do_send(SendUserRoomMessage {
1157 op: UserOperation::EditPrivateMessage,
1158 response: res.clone(),
1167 #[async_trait::async_trait(?Send)]
1168 impl Perform for DeletePrivateMessage {
1169 type Response = PrivateMessageResponse;
1173 context: &Data<LemmyContext>,
1174 websocket_id: Option<ConnectionId>,
1175 ) -> Result<PrivateMessageResponse, LemmyError> {
1176 let data: &DeletePrivateMessage = &self;
1177 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
1179 // Checking permissions
1180 let private_message_id = data.private_message_id;
1181 let orig_private_message = blocking(context.pool(), move |conn| {
1182 PrivateMessage::read(conn, private_message_id)
1185 if user.id != orig_private_message.creator_id {
1186 return Err(ApiError::err("no_private_message_edit_allowed").into());
1190 let private_message_id = data.private_message_id;
1191 let deleted = data.deleted;
1192 let updated_private_message = match blocking(context.pool(), move |conn| {
1193 PrivateMessage::update_deleted(conn, private_message_id, deleted)
1197 Ok(private_message) => private_message,
1198 Err(_e) => return Err(ApiError::err("couldnt_update_private_message").into()),
1201 // Send the apub update
1203 updated_private_message.send_delete(&user, context).await?;
1205 updated_private_message
1206 .send_undo_delete(&user, context)
1210 let private_message_id = data.private_message_id;
1211 let message = blocking(context.pool(), move |conn| {
1212 PrivateMessageView::read(conn, private_message_id)
1215 let recipient_id = message.recipient.id;
1217 let res = PrivateMessageResponse {
1218 private_message_view: message,
1221 context.chat_server().do_send(SendUserRoomMessage {
1222 op: UserOperation::DeletePrivateMessage,
1223 response: res.clone(),
1232 #[async_trait::async_trait(?Send)]
1233 impl Perform for MarkPrivateMessageAsRead {
1234 type Response = PrivateMessageResponse;
1238 context: &Data<LemmyContext>,
1239 websocket_id: Option<ConnectionId>,
1240 ) -> Result<PrivateMessageResponse, LemmyError> {
1241 let data: &MarkPrivateMessageAsRead = &self;
1242 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
1244 // Checking permissions
1245 let private_message_id = data.private_message_id;
1246 let orig_private_message = blocking(context.pool(), move |conn| {
1247 PrivateMessage::read(conn, private_message_id)
1250 if user.id != orig_private_message.recipient_id {
1251 return Err(ApiError::err("couldnt_update_private_message").into());
1255 let private_message_id = data.private_message_id;
1256 let read = data.read;
1257 match blocking(context.pool(), move |conn| {
1258 PrivateMessage::update_read(conn, private_message_id, read)
1262 Ok(private_message) => private_message,
1263 Err(_e) => return Err(ApiError::err("couldnt_update_private_message").into()),
1266 // No need to send an apub update
1268 let private_message_id = data.private_message_id;
1269 let message = blocking(context.pool(), move |conn| {
1270 PrivateMessageView::read(conn, private_message_id)
1273 let recipient_id = message.recipient.id;
1275 let res = PrivateMessageResponse {
1276 private_message_view: message,
1279 context.chat_server().do_send(SendUserRoomMessage {
1280 op: UserOperation::MarkPrivateMessageAsRead,
1281 response: res.clone(),
1290 #[async_trait::async_trait(?Send)]
1291 impl Perform for GetPrivateMessages {
1292 type Response = PrivateMessagesResponse;
1296 context: &Data<LemmyContext>,
1297 _websocket_id: Option<ConnectionId>,
1298 ) -> Result<PrivateMessagesResponse, LemmyError> {
1299 let data: &GetPrivateMessages = &self;
1300 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
1301 let user_id = user.id;
1303 let page = data.page;
1304 let limit = data.limit;
1305 let unread_only = data.unread_only;
1306 let messages = blocking(context.pool(), move |conn| {
1307 PrivateMessageQueryBuilder::create(&conn, user_id)
1310 .unread_only(unread_only)
1315 Ok(PrivateMessagesResponse {
1316 private_messages: messages,
1321 #[async_trait::async_trait(?Send)]
1322 impl Perform for GetReportCount {
1323 type Response = GetReportCountResponse;
1327 context: &Data<LemmyContext>,
1328 websocket_id: Option<ConnectionId>,
1329 ) -> Result<GetReportCountResponse, LemmyError> {
1330 let data: &GetReportCount = &self;
1331 let user = get_user_from_jwt(&data.auth, context.pool()).await?;
1333 let user_id = user.id;
1334 let community_id = data.community;
1336 collect_moderated_communities(user_id, community_id, context.pool()).await?;
1339 if community_ids.is_empty() {
1340 GetReportCountResponse {
1346 let ids = community_ids.clone();
1347 let comment_reports = blocking(context.pool(), move |conn| {
1348 CommentReportView::get_report_count(conn, &ids)
1352 let ids = community_ids.clone();
1353 let post_reports = blocking(context.pool(), move |conn| {
1354 PostReportView::get_report_count(conn, &ids)
1358 GetReportCountResponse {
1359 community: data.community,
1366 context.chat_server().do_send(SendUserRoomMessage {
1367 op: UserOperation::GetReportCount,
1368 response: res.clone(),
1369 recipient_id: user.id,