14 captcha_espeak_wav_base64,
16 server::{CaptchaItem, CheckCaptcha, JoinUserRoom, SendAllMessage, SendUserRoomMessage},
24 use captcha::{gen, Difficulty};
31 diesel_option_overwrite,
34 password_reset_request::*,
38 private_message_view::*,
52 generate_actor_keypair,
53 generate_random_string,
63 use serde::{Deserialize, Serialize};
64 use std::str::FromStr;
66 #[derive(Serialize, Deserialize, Debug)]
68 username_or_email: String,
72 #[derive(Serialize, Deserialize)]
75 pub email: Option<String>,
77 pub password_verify: String,
80 pub captcha_uuid: Option<String>,
81 pub captcha_answer: Option<String>,
84 #[derive(Serialize, Deserialize)]
85 pub struct GetCaptcha {}
87 #[derive(Serialize, Deserialize)]
88 pub struct GetCaptchaResponse {
89 ok: Option<CaptchaResponse>,
92 #[derive(Serialize, Deserialize)]
93 pub struct CaptchaResponse {
94 png: String, // A Base64 encoded png
95 wav: Option<String>, // A Base64 encoded wav audio
99 #[derive(Serialize, Deserialize)]
100 pub struct SaveUserSettings {
103 default_sort_type: i16,
104 default_listing_type: i16,
106 avatar: Option<String>,
107 banner: Option<String>,
108 preferred_username: Option<String>,
109 email: Option<String>,
111 matrix_user_id: Option<String>,
112 new_password: Option<String>,
113 new_password_verify: Option<String>,
114 old_password: Option<String>,
116 send_notifications_to_email: bool,
120 #[derive(Serialize, Deserialize)]
121 pub struct LoginResponse {
125 #[derive(Serialize, Deserialize)]
126 pub struct GetUserDetails {
127 user_id: Option<i32>,
128 username: Option<String>,
132 community_id: Option<i32>,
134 auth: Option<String>,
137 #[derive(Serialize, Deserialize)]
138 pub struct GetUserDetailsResponse {
140 follows: Vec<CommunityFollowerView>,
141 moderates: Vec<CommunityModeratorView>,
142 comments: Vec<CommentView>,
143 posts: Vec<PostView>,
146 #[derive(Serialize, Deserialize)]
147 pub struct GetRepliesResponse {
148 replies: Vec<ReplyView>,
151 #[derive(Serialize, Deserialize)]
152 pub struct GetUserMentionsResponse {
153 mentions: Vec<UserMentionView>,
156 #[derive(Serialize, Deserialize)]
157 pub struct MarkAllAsRead {
161 #[derive(Serialize, Deserialize)]
162 pub struct AddAdmin {
168 #[derive(Serialize, Deserialize, Clone)]
169 pub struct AddAdminResponse {
170 admins: Vec<UserView>,
173 #[derive(Serialize, Deserialize)]
177 reason: Option<String>,
178 expires: Option<i64>,
182 #[derive(Serialize, Deserialize, Clone)]
183 pub struct BanUserResponse {
188 #[derive(Serialize, Deserialize)]
189 pub struct GetReplies {
197 #[derive(Serialize, Deserialize)]
198 pub struct GetUserMentions {
206 #[derive(Serialize, Deserialize)]
207 pub struct MarkUserMentionAsRead {
208 user_mention_id: i32,
213 #[derive(Serialize, Deserialize, Clone)]
214 pub struct UserMentionResponse {
215 mention: UserMentionView,
218 #[derive(Serialize, Deserialize)]
219 pub struct DeleteAccount {
224 #[derive(Serialize, Deserialize)]
225 pub struct PasswordReset {
229 #[derive(Serialize, Deserialize, Clone)]
230 pub struct PasswordResetResponse {}
232 #[derive(Serialize, Deserialize)]
233 pub struct PasswordChange {
236 password_verify: String,
239 #[derive(Serialize, Deserialize)]
240 pub struct CreatePrivateMessage {
242 pub recipient_id: i32,
246 #[derive(Serialize, Deserialize)]
247 pub struct EditPrivateMessage {
253 #[derive(Serialize, Deserialize)]
254 pub struct DeletePrivateMessage {
260 #[derive(Serialize, Deserialize)]
261 pub struct MarkPrivateMessageAsRead {
267 #[derive(Serialize, Deserialize)]
268 pub struct GetPrivateMessages {
275 #[derive(Serialize, Deserialize, Clone)]
276 pub struct PrivateMessagesResponse {
277 messages: Vec<PrivateMessageView>,
280 #[derive(Serialize, Deserialize, Clone)]
281 pub struct PrivateMessageResponse {
282 pub message: PrivateMessageView,
285 #[derive(Serialize, Deserialize, Debug)]
286 pub struct UserJoin {
290 #[derive(Serialize, Deserialize, Clone)]
291 pub struct UserJoinResponse {
295 #[async_trait::async_trait(?Send)]
296 impl Perform for Oper<Login> {
297 type Response = LoginResponse;
302 _websocket_info: Option<WebsocketInfo>,
303 ) -> Result<LoginResponse, LemmyError> {
304 let data: &Login = &self.data;
306 // Fetch that username / email
307 let username_or_email = data.username_or_email.clone();
308 let user = match blocking(pool, move |conn| {
309 User_::find_by_email_or_username(conn, &username_or_email)
314 Err(_e) => return Err(APIError::err("couldnt_find_that_username_or_email").into()),
317 // Verify the password
318 let valid: bool = verify(&data.password, &user.password_encrypted).unwrap_or(false);
320 return Err(APIError::err("password_incorrect").into());
325 jwt: Claims::jwt(user, Settings::get().hostname),
330 #[async_trait::async_trait(?Send)]
331 impl Perform for Oper<Register> {
332 type Response = LoginResponse;
337 websocket_info: Option<WebsocketInfo>,
338 ) -> Result<LoginResponse, LemmyError> {
339 let data: &Register = &self.data;
341 // Make sure site has open registration
342 if let Ok(site) = blocking(pool, move |conn| SiteView::read(conn)).await? {
343 let site: SiteView = site;
344 if !site.open_registration {
345 return Err(APIError::err("registration_closed").into());
349 // Make sure passwords match
350 if data.password != data.password_verify {
351 return Err(APIError::err("passwords_dont_match").into());
354 // If its not the admin, check the captcha
355 if !data.admin && Settings::get().captcha.enabled {
356 match websocket_info {
364 .unwrap_or_else(|| "".to_string()),
368 .unwrap_or_else(|| "".to_string()),
372 return Err(APIError::err("captcha_incorrect").into());
375 None => return Err(APIError::err("captcha_incorrect").into()),
379 check_slurs(&data.username)?;
381 // Make sure there are no admins
382 let any_admins = blocking(pool, move |conn| {
383 UserView::admins(conn).map(|a| a.is_empty())
386 if data.admin && !any_admins {
387 return Err(APIError::err("admin_already_created").into());
390 let user_keypair = generate_actor_keypair()?;
391 if !is_valid_username(&data.username) {
392 return Err(APIError::err("invalid_username").into());
395 // Register the new user
396 let user_form = UserForm {
397 name: data.username.to_owned(),
398 email: data.email.to_owned(),
399 matrix_user_id: None,
402 password_encrypted: data.password.to_owned(),
403 preferred_username: None,
407 show_nsfw: data.show_nsfw,
408 theme: "darkly".into(),
409 default_sort_type: SortType::Active as i16,
410 default_listing_type: ListingType::Subscribed as i16,
411 lang: "browser".into(),
413 send_notifications_to_email: false,
414 actor_id: make_apub_endpoint(EndpointType::User, &data.username).to_string(),
417 private_key: Some(user_keypair.private_key),
418 public_key: Some(user_keypair.public_key),
419 last_refreshed_at: None,
423 let inserted_user = match blocking(pool, move |conn| User_::register(conn, &user_form)).await? {
426 let err_type = if e.to_string()
427 == "duplicate key value violates unique constraint \"user__email_key\""
429 "email_already_exists"
431 "user_already_exists"
434 return Err(APIError::err(err_type).into());
438 let main_community_keypair = generate_actor_keypair()?;
440 // Create the main community if it doesn't exist
441 let main_community = match blocking(pool, move |conn| Community::read(conn, 2)).await? {
444 let default_community_name = "main";
445 let community_form = CommunityForm {
446 name: default_community_name.to_string(),
447 title: "The Default Community".to_string(),
448 description: Some("The Default Community".to_string()),
451 creator_id: inserted_user.id,
455 actor_id: make_apub_endpoint(EndpointType::Community, default_community_name).to_string(),
457 private_key: Some(main_community_keypair.private_key),
458 public_key: Some(main_community_keypair.public_key),
459 last_refreshed_at: None,
464 blocking(pool, move |conn| Community::create(conn, &community_form)).await??
468 // Sign them up for main community no matter what
469 let community_follower_form = CommunityFollowerForm {
470 community_id: main_community.id,
471 user_id: inserted_user.id,
474 let follow = move |conn: &'_ _| CommunityFollower::follow(conn, &community_follower_form);
475 if blocking(pool, follow).await?.is_err() {
476 return Err(APIError::err("community_follower_already_exists").into());
479 // If its an admin, add them as a mod and follower to main
481 let community_moderator_form = CommunityModeratorForm {
482 community_id: main_community.id,
483 user_id: inserted_user.id,
486 let join = move |conn: &'_ _| CommunityModerator::join(conn, &community_moderator_form);
487 if blocking(pool, join).await?.is_err() {
488 return Err(APIError::err("community_moderator_already_exists").into());
494 jwt: Claims::jwt(inserted_user, Settings::get().hostname),
499 #[async_trait::async_trait(?Send)]
500 impl Perform for Oper<GetCaptcha> {
501 type Response = GetCaptchaResponse;
506 websocket_info: Option<WebsocketInfo>,
507 ) -> Result<Self::Response, LemmyError> {
508 let captcha_settings = Settings::get().captcha;
510 if !captcha_settings.enabled {
511 return Ok(GetCaptchaResponse { ok: None });
514 let captcha = match captcha_settings.difficulty.as_str() {
515 "easy" => gen(Difficulty::Easy),
516 "medium" => gen(Difficulty::Medium),
517 "hard" => gen(Difficulty::Hard),
518 _ => gen(Difficulty::Medium),
521 let answer = captcha.chars_as_string();
523 let png_byte_array = captcha.as_png().expect("failed to generate captcha");
525 let png = base64::encode(png_byte_array);
527 let uuid = uuid::Uuid::new_v4().to_string();
529 let wav = captcha_espeak_wav_base64(&answer).ok();
531 let captcha_item = CaptchaItem {
533 uuid: uuid.to_owned(),
534 expires: naive_now() + Duration::minutes(10), // expires in 10 minutes
537 if let Some(ws) = websocket_info {
538 ws.chatserver.do_send(captcha_item);
541 Ok(GetCaptchaResponse {
542 ok: Some(CaptchaResponse { png, uuid, wav }),
547 #[async_trait::async_trait(?Send)]
548 impl Perform for Oper<SaveUserSettings> {
549 type Response = LoginResponse;
554 _websocket_info: Option<WebsocketInfo>,
555 ) -> Result<LoginResponse, LemmyError> {
556 let data: &SaveUserSettings = &self.data;
557 let user = get_user_from_jwt(&data.auth, pool).await?;
559 let user_id = user.id;
560 let read_user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
562 let email = match &data.email {
563 Some(email) => Some(email.to_owned()),
564 None => read_user.email,
567 let bio = match &data.bio {
569 if bio.chars().count() <= 300 {
572 return Err(APIError::err("bio_length_overflow").into());
575 None => read_user.bio,
578 let avatar = diesel_option_overwrite(&data.avatar);
579 let banner = diesel_option_overwrite(&data.banner);
581 // The DB constraint should stop too many characters
582 let preferred_username = match &data.preferred_username {
583 Some(preferred_username) => Some(preferred_username.to_owned()),
584 None => read_user.preferred_username,
587 let password_encrypted = match &data.new_password {
588 Some(new_password) => {
589 match &data.new_password_verify {
590 Some(new_password_verify) => {
591 // Make sure passwords match
592 if new_password != new_password_verify {
593 return Err(APIError::err("passwords_dont_match").into());
596 // Check the old password
597 match &data.old_password {
598 Some(old_password) => {
600 verify(old_password, &read_user.password_encrypted).unwrap_or(false);
602 return Err(APIError::err("password_incorrect").into());
604 let new_password = new_password.to_owned();
605 let user = blocking(pool, move |conn| {
606 User_::update_password(conn, user_id, &new_password)
609 user.password_encrypted
611 None => return Err(APIError::err("password_incorrect").into()),
614 None => return Err(APIError::err("passwords_dont_match").into()),
617 None => read_user.password_encrypted,
620 let user_form = UserForm {
621 name: read_user.name,
623 matrix_user_id: data.matrix_user_id.to_owned(),
628 updated: Some(naive_now()),
629 admin: read_user.admin,
630 banned: read_user.banned,
631 show_nsfw: data.show_nsfw,
632 theme: data.theme.to_owned(),
633 default_sort_type: data.default_sort_type,
634 default_listing_type: data.default_listing_type,
635 lang: data.lang.to_owned(),
636 show_avatars: data.show_avatars,
637 send_notifications_to_email: data.send_notifications_to_email,
638 actor_id: read_user.actor_id,
640 local: read_user.local,
641 private_key: read_user.private_key,
642 public_key: read_user.public_key,
643 last_refreshed_at: None,
646 let res = blocking(pool, move |conn| User_::update(conn, user_id, &user_form)).await?;
647 let updated_user: User_ = match res {
650 let err_type = if e.to_string()
651 == "duplicate key value violates unique constraint \"user__email_key\""
653 "email_already_exists"
655 "user_already_exists"
658 return Err(APIError::err(err_type).into());
664 jwt: Claims::jwt(updated_user, Settings::get().hostname),
669 #[async_trait::async_trait(?Send)]
670 impl Perform for Oper<GetUserDetails> {
671 type Response = GetUserDetailsResponse;
676 _websocket_info: Option<WebsocketInfo>,
677 ) -> Result<GetUserDetailsResponse, LemmyError> {
678 let data: &GetUserDetails = &self.data;
679 let user = get_user_from_jwt_opt(&data.auth, pool).await?;
681 let show_nsfw = match &user {
682 Some(user) => user.show_nsfw,
686 let sort = SortType::from_str(&data.sort)?;
691 .unwrap_or_else(|| "admin".to_string());
692 let user_details_id = match data.user_id {
695 let user = blocking(pool, move |conn| User_::read_from_name(conn, &username)).await?;
698 Err(_e) => return Err(APIError::err("couldnt_find_that_username_or_email").into()),
703 let mut user_view = blocking(pool, move |conn| UserView::read(conn, user_details_id)).await??;
705 let page = data.page;
706 let limit = data.limit;
707 let saved_only = data.saved_only;
708 let community_id = data.community_id;
709 let user_id = user.map(|u| u.id);
710 let (posts, comments) = blocking(pool, move |conn| {
711 let mut posts_query = PostQueryBuilder::create(conn)
713 .show_nsfw(show_nsfw)
714 .saved_only(saved_only)
715 .for_community_id(community_id)
720 let mut comments_query = CommentQueryBuilder::create(conn)
722 .saved_only(saved_only)
727 // If its saved only, you don't care what creator it was
728 // Or, if its not saved, then you only want it for that specific creator
730 posts_query = posts_query.for_creator_id(user_details_id);
731 comments_query = comments_query.for_creator_id(user_details_id);
734 let posts = posts_query.list()?;
735 let comments = comments_query.list()?;
737 Ok((posts, comments)) as Result<_, LemmyError>
741 let follows = blocking(pool, move |conn| {
742 CommunityFollowerView::for_user(conn, user_details_id)
745 let moderates = blocking(pool, move |conn| {
746 CommunityModeratorView::for_user(conn, user_details_id)
750 // If its not the same user, remove the email, and settings
751 // TODO an if let chain would be better here, but can't figure it out
752 // TODO separate out settings into its own thing
753 if user_id.is_none() || user_details_id != user_id.unwrap_or(0) {
754 user_view.email = None;
758 Ok(GetUserDetailsResponse {
768 #[async_trait::async_trait(?Send)]
769 impl Perform for Oper<AddAdmin> {
770 type Response = AddAdminResponse;
775 websocket_info: Option<WebsocketInfo>,
776 ) -> Result<AddAdminResponse, LemmyError> {
777 let data: &AddAdmin = &self.data;
778 let user = get_user_from_jwt(&data.auth, pool).await?;
780 // Make sure user is an admin
781 is_admin(pool, user.id).await?;
783 let added = data.added;
784 let added_user_id = data.user_id;
785 let add_admin = move |conn: &'_ _| User_::add_admin(conn, added_user_id, added);
786 if blocking(pool, add_admin).await?.is_err() {
787 return Err(APIError::err("couldnt_update_user").into());
791 let form = ModAddForm {
792 mod_user_id: user.id,
793 other_user_id: data.user_id,
794 removed: Some(!data.added),
797 blocking(pool, move |conn| ModAdd::create(conn, &form)).await??;
799 let site_creator_id =
800 blocking(pool, move |conn| Site::read(conn, 1).map(|s| s.creator_id)).await??;
802 let mut admins = blocking(pool, move |conn| UserView::admins(conn)).await??;
803 let creator_index = admins.iter().position(|r| r.id == site_creator_id).unwrap();
804 let creator_user = admins.remove(creator_index);
805 admins.insert(0, creator_user);
807 let res = AddAdminResponse { admins };
809 if let Some(ws) = websocket_info {
810 ws.chatserver.do_send(SendAllMessage {
811 op: UserOperation::AddAdmin,
812 response: res.clone(),
821 #[async_trait::async_trait(?Send)]
822 impl Perform for Oper<BanUser> {
823 type Response = BanUserResponse;
828 websocket_info: Option<WebsocketInfo>,
829 ) -> Result<BanUserResponse, LemmyError> {
830 let data: &BanUser = &self.data;
831 let user = get_user_from_jwt(&data.auth, pool).await?;
833 // Make sure user is an admin
834 is_admin(pool, user.id).await?;
837 let banned_user_id = data.user_id;
838 let ban_user = move |conn: &'_ _| User_::ban_user(conn, banned_user_id, ban);
839 if blocking(pool, ban_user).await?.is_err() {
840 return Err(APIError::err("couldnt_update_user").into());
844 let expires = match data.expires {
845 Some(time) => Some(naive_from_unix(time)),
849 let form = ModBanForm {
850 mod_user_id: user.id,
851 other_user_id: data.user_id,
852 reason: data.reason.to_owned(),
853 banned: Some(data.ban),
857 blocking(pool, move |conn| ModBan::create(conn, &form)).await??;
859 let user_id = data.user_id;
860 let user_view = blocking(pool, move |conn| UserView::read(conn, user_id)).await??;
862 let res = BanUserResponse {
867 if let Some(ws) = websocket_info {
868 ws.chatserver.do_send(SendAllMessage {
869 op: UserOperation::BanUser,
870 response: res.clone(),
879 #[async_trait::async_trait(?Send)]
880 impl Perform for Oper<GetReplies> {
881 type Response = GetRepliesResponse;
886 _websocket_info: Option<WebsocketInfo>,
887 ) -> Result<GetRepliesResponse, LemmyError> {
888 let data: &GetReplies = &self.data;
889 let user = get_user_from_jwt(&data.auth, pool).await?;
891 let sort = SortType::from_str(&data.sort)?;
893 let page = data.page;
894 let limit = data.limit;
895 let unread_only = data.unread_only;
896 let user_id = user.id;
897 let replies = blocking(pool, move |conn| {
898 ReplyQueryBuilder::create(conn, user_id)
900 .unread_only(unread_only)
907 Ok(GetRepliesResponse { replies })
911 #[async_trait::async_trait(?Send)]
912 impl Perform for Oper<GetUserMentions> {
913 type Response = GetUserMentionsResponse;
918 _websocket_info: Option<WebsocketInfo>,
919 ) -> Result<GetUserMentionsResponse, LemmyError> {
920 let data: &GetUserMentions = &self.data;
921 let user = get_user_from_jwt(&data.auth, pool).await?;
923 let sort = SortType::from_str(&data.sort)?;
925 let page = data.page;
926 let limit = data.limit;
927 let unread_only = data.unread_only;
928 let user_id = user.id;
929 let mentions = blocking(pool, move |conn| {
930 UserMentionQueryBuilder::create(conn, user_id)
932 .unread_only(unread_only)
939 Ok(GetUserMentionsResponse { mentions })
943 #[async_trait::async_trait(?Send)]
944 impl Perform for Oper<MarkUserMentionAsRead> {
945 type Response = UserMentionResponse;
950 _websocket_info: Option<WebsocketInfo>,
951 ) -> Result<UserMentionResponse, LemmyError> {
952 let data: &MarkUserMentionAsRead = &self.data;
953 let user = get_user_from_jwt(&data.auth, pool).await?;
955 let user_mention_id = data.user_mention_id;
956 let read_user_mention =
957 blocking(pool, move |conn| UserMention::read(conn, user_mention_id)).await??;
959 if user.id != read_user_mention.recipient_id {
960 return Err(APIError::err("couldnt_update_comment").into());
963 let user_mention_id = read_user_mention.id;
964 let read = data.read;
965 let update_mention = move |conn: &'_ _| UserMention::update_read(conn, user_mention_id, read);
966 if blocking(pool, update_mention).await?.is_err() {
967 return Err(APIError::err("couldnt_update_comment").into());
970 let user_mention_id = read_user_mention.id;
971 let user_id = user.id;
972 let user_mention_view = blocking(pool, move |conn| {
973 UserMentionView::read(conn, user_mention_id, user_id)
977 Ok(UserMentionResponse {
978 mention: user_mention_view,
983 #[async_trait::async_trait(?Send)]
984 impl Perform for Oper<MarkAllAsRead> {
985 type Response = GetRepliesResponse;
990 _websocket_info: Option<WebsocketInfo>,
991 ) -> Result<GetRepliesResponse, LemmyError> {
992 let data: &MarkAllAsRead = &self.data;
993 let user = get_user_from_jwt(&data.auth, pool).await?;
995 let user_id = user.id;
996 let replies = blocking(pool, move |conn| {
997 ReplyQueryBuilder::create(conn, user_id)
1005 // TODO: this should probably be a bulk operation
1006 // Not easy to do as a bulk operation,
1007 // because recipient_id isn't in the comment table
1008 for reply in &replies {
1009 let reply_id = reply.id;
1010 let mark_as_read = move |conn: &'_ _| Comment::update_read(conn, reply_id, true);
1011 if blocking(pool, mark_as_read).await?.is_err() {
1012 return Err(APIError::err("couldnt_update_comment").into());
1016 // Mark all user mentions as read
1017 let update_user_mentions = move |conn: &'_ _| UserMention::mark_all_as_read(conn, user_id);
1018 if blocking(pool, update_user_mentions).await?.is_err() {
1019 return Err(APIError::err("couldnt_update_comment").into());
1022 // Mark all private_messages as read
1023 let update_pm = move |conn: &'_ _| PrivateMessage::mark_all_as_read(conn, user_id);
1024 if blocking(pool, update_pm).await?.is_err() {
1025 return Err(APIError::err("couldnt_update_private_message").into());
1028 Ok(GetRepliesResponse { replies: vec![] })
1032 #[async_trait::async_trait(?Send)]
1033 impl Perform for Oper<DeleteAccount> {
1034 type Response = LoginResponse;
1039 _websocket_info: Option<WebsocketInfo>,
1040 ) -> Result<LoginResponse, LemmyError> {
1041 let data: &DeleteAccount = &self.data;
1042 let user = get_user_from_jwt(&data.auth, pool).await?;
1044 // Verify the password
1045 let valid: bool = verify(&data.password, &user.password_encrypted).unwrap_or(false);
1047 return Err(APIError::err("password_incorrect").into());
1051 let user_id = user.id;
1052 let comments = blocking(pool, move |conn| {
1053 CommentQueryBuilder::create(conn)
1054 .for_creator_id(user_id)
1055 .limit(std::i64::MAX)
1060 // TODO: this should probably be a bulk operation
1061 for comment in &comments {
1062 let comment_id = comment.id;
1063 let permadelete = move |conn: &'_ _| Comment::permadelete(conn, comment_id);
1064 if blocking(pool, permadelete).await?.is_err() {
1065 return Err(APIError::err("couldnt_update_comment").into());
1070 let posts = blocking(pool, move |conn| {
1071 PostQueryBuilder::create(conn)
1072 .sort(&SortType::New)
1073 .for_creator_id(user_id)
1074 .limit(std::i64::MAX)
1079 // TODO: this should probably be a bulk operation
1080 for post in &posts {
1081 let post_id = post.id;
1082 let permadelete = move |conn: &'_ _| Post::permadelete(conn, post_id);
1083 if blocking(pool, permadelete).await?.is_err() {
1084 return Err(APIError::err("couldnt_update_post").into());
1089 jwt: data.auth.to_owned(),
1094 #[async_trait::async_trait(?Send)]
1095 impl Perform for Oper<PasswordReset> {
1096 type Response = PasswordResetResponse;
1101 _websocket_info: Option<WebsocketInfo>,
1102 ) -> Result<PasswordResetResponse, LemmyError> {
1103 let data: &PasswordReset = &self.data;
1106 let email = data.email.clone();
1107 let user = match blocking(pool, move |conn| User_::find_by_email(conn, &email)).await? {
1109 Err(_e) => return Err(APIError::err("couldnt_find_that_username_or_email").into()),
1112 // Generate a random token
1113 let token = generate_random_string();
1116 let token2 = token.clone();
1117 let user_id = user.id;
1118 blocking(pool, move |conn| {
1119 PasswordResetRequest::create_token(conn, user_id, &token2)
1123 // Email the pure token to the user.
1124 // TODO no i18n support here.
1125 let user_email = &user.email.expect("email");
1126 let subject = &format!("Password reset for {}", user.name);
1127 let hostname = &format!("https://{}", Settings::get().hostname); //TODO add https for now.
1128 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);
1129 match send_email(subject, user_email, &user.name, html) {
1131 Err(_e) => return Err(APIError::err(&_e).into()),
1134 Ok(PasswordResetResponse {})
1138 #[async_trait::async_trait(?Send)]
1139 impl Perform for Oper<PasswordChange> {
1140 type Response = LoginResponse;
1145 _websocket_info: Option<WebsocketInfo>,
1146 ) -> Result<LoginResponse, LemmyError> {
1147 let data: &PasswordChange = &self.data;
1149 // Fetch the user_id from the token
1150 let token = data.token.clone();
1151 let user_id = blocking(pool, move |conn| {
1152 PasswordResetRequest::read_from_token(conn, &token).map(|p| p.user_id)
1156 // Make sure passwords match
1157 if data.password != data.password_verify {
1158 return Err(APIError::err("passwords_dont_match").into());
1161 // Update the user with the new password
1162 let password = data.password.clone();
1163 let updated_user = match blocking(pool, move |conn| {
1164 User_::update_password(conn, user_id, &password)
1169 Err(_e) => return Err(APIError::err("couldnt_update_user").into()),
1174 jwt: Claims::jwt(updated_user, Settings::get().hostname),
1179 #[async_trait::async_trait(?Send)]
1180 impl Perform for Oper<CreatePrivateMessage> {
1181 type Response = PrivateMessageResponse;
1186 websocket_info: Option<WebsocketInfo>,
1187 ) -> Result<PrivateMessageResponse, LemmyError> {
1188 let data: &CreatePrivateMessage = &self.data;
1189 let user = get_user_from_jwt(&data.auth, pool).await?;
1191 let hostname = &format!("https://{}", Settings::get().hostname);
1193 let content_slurs_removed = remove_slurs(&data.content.to_owned());
1195 let private_message_form = PrivateMessageForm {
1196 content: content_slurs_removed.to_owned(),
1197 creator_id: user.id,
1198 recipient_id: data.recipient_id,
1202 ap_id: "http://fake.com".into(),
1207 let inserted_private_message = match blocking(pool, move |conn| {
1208 PrivateMessage::create(conn, &private_message_form)
1212 Ok(private_message) => private_message,
1214 return Err(APIError::err("couldnt_create_private_message").into());
1218 let inserted_private_message_id = inserted_private_message.id;
1219 let updated_private_message = match blocking(pool, move |conn| {
1220 let apub_id = make_apub_endpoint(
1221 EndpointType::PrivateMessage,
1222 &inserted_private_message_id.to_string(),
1225 PrivateMessage::update_ap_id(&conn, inserted_private_message_id, apub_id)
1229 Ok(private_message) => private_message,
1230 Err(_e) => return Err(APIError::err("couldnt_create_private_message").into()),
1233 updated_private_message
1234 .send_create(&user, &self.client, pool)
1237 // Send notifications to the recipient
1238 let recipient_id = data.recipient_id;
1239 let recipient_user = blocking(pool, move |conn| User_::read(conn, recipient_id)).await??;
1240 if recipient_user.send_notifications_to_email {
1241 if let Some(email) = recipient_user.email {
1242 let subject = &format!(
1243 "{} - Private Message from {}",
1244 Settings::get().hostname,
1247 let html = &format!(
1248 "<h1>Private Message</h1><br><div>{} - {}</div><br><a href={}/inbox>inbox</a>",
1249 user.name, &content_slurs_removed, hostname
1251 match send_email(subject, &email, &recipient_user.name, html) {
1253 Err(e) => error!("{}", e),
1258 let message = blocking(pool, move |conn| {
1259 PrivateMessageView::read(conn, inserted_private_message.id)
1263 let res = PrivateMessageResponse { message };
1265 if let Some(ws) = websocket_info {
1266 ws.chatserver.do_send(SendUserRoomMessage {
1267 op: UserOperation::CreatePrivateMessage,
1268 response: res.clone(),
1269 recipient_id: recipient_user.id,
1278 #[async_trait::async_trait(?Send)]
1279 impl Perform for Oper<EditPrivateMessage> {
1280 type Response = PrivateMessageResponse;
1285 websocket_info: Option<WebsocketInfo>,
1286 ) -> Result<PrivateMessageResponse, LemmyError> {
1287 let data: &EditPrivateMessage = &self.data;
1288 let user = get_user_from_jwt(&data.auth, pool).await?;
1290 // Checking permissions
1291 let edit_id = data.edit_id;
1292 let orig_private_message =
1293 blocking(pool, move |conn| PrivateMessage::read(conn, edit_id)).await??;
1294 if user.id != orig_private_message.creator_id {
1295 return Err(APIError::err("no_private_message_edit_allowed").into());
1299 let content_slurs_removed = remove_slurs(&data.content);
1300 let edit_id = data.edit_id;
1301 let updated_private_message = match blocking(pool, move |conn| {
1302 PrivateMessage::update_content(conn, edit_id, &content_slurs_removed)
1306 Ok(private_message) => private_message,
1307 Err(_e) => return Err(APIError::err("couldnt_update_private_message").into()),
1310 // Send the apub update
1311 updated_private_message
1312 .send_update(&user, &self.client, pool)
1315 let edit_id = data.edit_id;
1316 let message = blocking(pool, move |conn| PrivateMessageView::read(conn, edit_id)).await??;
1317 let recipient_id = message.recipient_id;
1319 let res = PrivateMessageResponse { message };
1321 if let Some(ws) = websocket_info {
1322 ws.chatserver.do_send(SendUserRoomMessage {
1323 op: UserOperation::EditPrivateMessage,
1324 response: res.clone(),
1334 #[async_trait::async_trait(?Send)]
1335 impl Perform for Oper<DeletePrivateMessage> {
1336 type Response = PrivateMessageResponse;
1341 websocket_info: Option<WebsocketInfo>,
1342 ) -> Result<PrivateMessageResponse, LemmyError> {
1343 let data: &DeletePrivateMessage = &self.data;
1344 let user = get_user_from_jwt(&data.auth, pool).await?;
1346 // Checking permissions
1347 let edit_id = data.edit_id;
1348 let orig_private_message =
1349 blocking(pool, move |conn| PrivateMessage::read(conn, edit_id)).await??;
1350 if user.id != orig_private_message.creator_id {
1351 return Err(APIError::err("no_private_message_edit_allowed").into());
1355 let edit_id = data.edit_id;
1356 let deleted = data.deleted;
1357 let updated_private_message = match blocking(pool, move |conn| {
1358 PrivateMessage::update_deleted(conn, edit_id, deleted)
1362 Ok(private_message) => private_message,
1363 Err(_e) => return Err(APIError::err("couldnt_update_private_message").into()),
1366 // Send the apub update
1368 updated_private_message
1369 .send_delete(&user, &self.client, pool)
1372 updated_private_message
1373 .send_undo_delete(&user, &self.client, pool)
1377 let edit_id = data.edit_id;
1378 let message = blocking(pool, move |conn| PrivateMessageView::read(conn, edit_id)).await??;
1379 let recipient_id = message.recipient_id;
1381 let res = PrivateMessageResponse { message };
1383 if let Some(ws) = websocket_info {
1384 ws.chatserver.do_send(SendUserRoomMessage {
1385 op: UserOperation::DeletePrivateMessage,
1386 response: res.clone(),
1396 #[async_trait::async_trait(?Send)]
1397 impl Perform for Oper<MarkPrivateMessageAsRead> {
1398 type Response = PrivateMessageResponse;
1403 websocket_info: Option<WebsocketInfo>,
1404 ) -> Result<PrivateMessageResponse, LemmyError> {
1405 let data: &MarkPrivateMessageAsRead = &self.data;
1406 let user = get_user_from_jwt(&data.auth, pool).await?;
1408 // Checking permissions
1409 let edit_id = data.edit_id;
1410 let orig_private_message =
1411 blocking(pool, move |conn| PrivateMessage::read(conn, edit_id)).await??;
1412 if user.id != orig_private_message.recipient_id {
1413 return Err(APIError::err("couldnt_update_private_message").into());
1417 let edit_id = data.edit_id;
1418 let read = data.read;
1419 match blocking(pool, move |conn| {
1420 PrivateMessage::update_read(conn, edit_id, read)
1424 Ok(private_message) => private_message,
1425 Err(_e) => return Err(APIError::err("couldnt_update_private_message").into()),
1428 // No need to send an apub update
1430 let edit_id = data.edit_id;
1431 let message = blocking(pool, move |conn| PrivateMessageView::read(conn, edit_id)).await??;
1432 let recipient_id = message.recipient_id;
1434 let res = PrivateMessageResponse { message };
1436 if let Some(ws) = websocket_info {
1437 ws.chatserver.do_send(SendUserRoomMessage {
1438 op: UserOperation::MarkPrivateMessageAsRead,
1439 response: res.clone(),
1449 #[async_trait::async_trait(?Send)]
1450 impl Perform for Oper<GetPrivateMessages> {
1451 type Response = PrivateMessagesResponse;
1456 _websocket_info: Option<WebsocketInfo>,
1457 ) -> Result<PrivateMessagesResponse, LemmyError> {
1458 let data: &GetPrivateMessages = &self.data;
1459 let user = get_user_from_jwt(&data.auth, pool).await?;
1460 let user_id = user.id;
1462 let page = data.page;
1463 let limit = data.limit;
1464 let unread_only = data.unread_only;
1465 let messages = blocking(pool, move |conn| {
1466 PrivateMessageQueryBuilder::create(&conn, user_id)
1469 .unread_only(unread_only)
1474 Ok(PrivateMessagesResponse { messages })
1478 #[async_trait::async_trait(?Send)]
1479 impl Perform for Oper<UserJoin> {
1480 type Response = UserJoinResponse;
1485 websocket_info: Option<WebsocketInfo>,
1486 ) -> Result<UserJoinResponse, LemmyError> {
1487 let data: &UserJoin = &self.data;
1488 let user = get_user_from_jwt(&data.auth, pool).await?;
1490 if let Some(ws) = websocket_info {
1491 if let Some(id) = ws.id {
1492 ws.chatserver.do_send(JoinUserRoom {
1499 Ok(UserJoinResponse { user_id: user.id })