diff --git a/server/src/api/mod.rs b/server/src/api/mod.rs index 8872cd213..5ffb57d89 100644 --- a/server/src/api/mod.rs +++ b/server/src/api/mod.rs @@ -56,6 +56,7 @@ pub enum UserOperation { SaveUserSettings, TransferCommunity, TransferSite, + DeleteAccount, } #[derive(Fail, Debug)] diff --git a/server/src/api/user.rs b/server/src/api/user.rs index 785f99c48..2fc0a92ca 100644 --- a/server/src/api/user.rs +++ b/server/src/api/user.rs @@ -103,6 +103,11 @@ pub struct GetReplies { auth: String, } +#[derive(Serialize, Deserialize)] +pub struct DeleteAccount { + auth: String, +} + impl Perform for Oper { fn perform(&self) -> Result { let data: &Login = &self.data; @@ -583,3 +588,67 @@ impl Perform for Oper { }) } } + +impl Perform for Oper { + fn perform(&self) -> Result { + let data: &DeleteAccount = &self.data; + let conn = establish_connection(); + + let claims = match Claims::decode(&data.auth) { + Ok(claims) => claims.claims, + Err(_e) => return Err(APIError::err(&self.op, "not_logged_in"))?, + }; + + let user_id = claims.id; + + // Comments + let comments = CommentView::list(&conn, &SortType::New, None, Some(user_id), None, None, false, None, Some(std::i64::MAX))?; + + for comment in &comments { + let comment_form = CommentForm { + content: "*Permananently Deleted*".to_string(), + parent_id: comment.to_owned().parent_id, + post_id: comment.to_owned().post_id, + creator_id: comment.to_owned().creator_id, + removed: None, + deleted: Some(true), + read: None, + updated: Some(naive_now()), + }; + + let _updated_comment = match Comment::update(&conn, comment.id, &comment_form) { + Ok(comment) => comment, + Err(_e) => return Err(APIError::err(&self.op, "couldnt_update_comment"))?, + }; + } + + // Posts + let posts = PostView::list(&conn, PostListingType::All, &SortType::New,None, Some(user_id), None, None, None, true, false, false, None, Some(std::i64::MAX))?; + + for post in &posts { + let post_form = PostForm { + name: "*Permananently Deleted*".to_string(), + url: Some("https://deleted.com".to_string()), + body: Some("*Permananently Deleted*".to_string()), + creator_id: post.to_owned().creator_id, + community_id: post.to_owned().community_id, + removed: None, + deleted: Some(true), + nsfw: post.to_owned().nsfw, + locked: None, + stickied: None, + updated: Some(naive_now()), + }; + + let _updated_post = match Post::update(&conn, post.id, &post_form) { + Ok(post) => post, + Err(_e) => return Err(APIError::err(&self.op, "couldnt_update_post"))?, + }; + } + + Ok(LoginResponse { + op: self.op.to_string(), + jwt: data.auth.to_owned(), + }) + } +} diff --git a/server/src/websocket/server.rs b/server/src/websocket/server.rs index 85de7eeab..a00cfc3b0 100644 --- a/server/src/websocket/server.rs +++ b/server/src/websocket/server.rs @@ -519,5 +519,10 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result { + let delete_account: DeleteAccount = serde_json::from_str(data)?; + let res = Oper::new(user_operation, delete_account).perform()?; + Ok(serde_json::to_string(&res)?) + } } } diff --git a/ui/src/components/user.tsx b/ui/src/components/user.tsx index 2a2a81240..c53a672a0 100644 --- a/ui/src/components/user.tsx +++ b/ui/src/components/user.tsx @@ -31,6 +31,8 @@ interface UserState { loading: boolean; userSettingsForm: UserSettingsForm; userSettingsLoading: boolean; + deleteAccountLoading: boolean; + deleteAccountShowConfirm: boolean; } export class User extends Component { @@ -65,6 +67,8 @@ export class User extends Component { auth: null, }, userSettingsLoading: null, + deleteAccountLoading: null, + deleteAccountShowConfirm: false, } constructor(props: any, context: any) { @@ -307,8 +311,17 @@ export class User extends Component {
- + + {this.state.deleteAccountShowConfirm && + <> + + + + + }
@@ -434,6 +447,20 @@ export class User extends Component { WebSocketService.Instance.saveUserSettings(i.state.userSettingsForm); } + handleDeleteAccountShowConfirmToggle(i: User, event: any) { + event.preventDefault(); + i.state.deleteAccountShowConfirm = !i.state.deleteAccountShowConfirm; + i.setState(i.state); + } + + handleDeleteAccount(i: User, event: any) { + event.preventDefault(); + i.state.deleteAccountLoading = true; + i.setState(i.state); + + WebSocketService.Instance.deleteAccount(); + } + parseMessage(msg: any) { console.log(msg); let op: UserOperation = msgOp(msg); @@ -505,6 +532,11 @@ export class User extends Component { this.setState(this.state); let res: LoginResponse = msg; UserService.Instance.login(res); + } else if (op == UserOperation.DeleteAccount) { + this.state.deleteAccountLoading = false; + this.state.deleteAccountShowConfirm = false; + this.setState(this.state); + this.context.router.history.push('/'); } } } diff --git a/ui/src/interfaces.ts b/ui/src/interfaces.ts index ab40d16a3..4bd013ce9 100644 --- a/ui/src/interfaces.ts +++ b/ui/src/interfaces.ts @@ -1,5 +1,5 @@ export enum UserOperation { - Login, Register, CreateCommunity, CreatePost, ListCommunities, ListCategories, GetPost, GetCommunity, CreateComment, EditComment, SaveComment, CreateCommentLike, GetPosts, CreatePostLike, EditPost, SavePost, EditCommunity, FollowCommunity, GetFollowedCommunities, GetUserDetails, GetReplies, GetModlog, BanFromCommunity, AddModToCommunity, CreateSite, EditSite, GetSite, AddAdmin, BanUser, Search, MarkAllAsRead, SaveUserSettings, TransferCommunity, TransferSite + Login, Register, CreateCommunity, CreatePost, ListCommunities, ListCategories, GetPost, GetCommunity, CreateComment, EditComment, SaveComment, CreateCommentLike, GetPosts, CreatePostLike, EditPost, SavePost, EditCommunity, FollowCommunity, GetFollowedCommunities, GetUserDetails, GetReplies, GetModlog, BanFromCommunity, AddModToCommunity, CreateSite, EditSite, GetSite, AddAdmin, BanUser, Search, MarkAllAsRead, SaveUserSettings, TransferCommunity, TransferSite, DeleteAccount } export enum CommentSortType { diff --git a/ui/src/services/WebSocketService.ts b/ui/src/services/WebSocketService.ts index f67dbf6d9..f8838d400 100644 --- a/ui/src/services/WebSocketService.ts +++ b/ui/src/services/WebSocketService.ts @@ -199,6 +199,12 @@ export class WebSocketService { this.subject.next(this.wsSendWrapper(UserOperation.SaveUserSettings, userSettingsForm)); } + public deleteAccount() { + let form = {}; + this.setAuth(form); + this.subject.next(this.wsSendWrapper(UserOperation.DeleteAccount, form)); + } + private wsSendWrapper(op: UserOperation, data: any) { let send = { op: UserOperation[op], data: data }; console.log(send); diff --git a/ui/src/translations/en.ts b/ui/src/translations/en.ts index 3d10cfa41..9c45c7155 100644 --- a/ui/src/translations/en.ts +++ b/ui/src/translations/en.ts @@ -55,6 +55,8 @@ export const en = { mark_as_unread: 'mark as unread', delete: 'delete', deleted: 'deleted', + delete_account: 'Delete Account', + delete_account_confirm: 'Warning: this will permanently delete all your data. Are you sure?', restore: 'restore', ban: 'ban', ban_from_site: 'ban from site',