diff --git a/package.json b/package.json index 6c9cf17b..fe7a87f0 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "@emotion/react": "^11.1.4", "@emotion/styled": "^11.0.0", "@hapi/boom": "^9.1.2", - "@prisma/client": "^2.19.0", + "@prisma/client": "^2.30", "@sendgrid/mail": "^7.4.2", "@sentry/node": "^6.3.1", "@sentry/tracing": "^6.3.1", @@ -75,7 +75,7 @@ "babel-plugin-transform-typescript-metadata": "^0.3.1", "prettier": "^2.2.1", "pretty-quick": "^3.1.0", - "prisma": "^2.19.0", + "prisma": "^2.30", "typegraphql-prisma": "^0.9.4", "typescript": "^4.1.3" } diff --git a/pages/api/user.ts b/pages/api/user.ts index d8a831fb..49047062 100644 --- a/pages/api/user.ts +++ b/pages/api/user.ts @@ -27,6 +27,18 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) notificationEmail }) + res.json({ + message: 'success' + }) + } else if (req.method === 'DELETE') { + const user = await authService.authGuard() + + if (!user) { + return + } + + await userService.delete(user.uid) + res.json({ message: 'success' }) diff --git a/pages/user.tsx b/pages/user.tsx index 3e70901e..c0a64cc5 100644 --- a/pages/user.tsx +++ b/pages/user.tsx @@ -1,6 +1,7 @@ import { User } from "@prisma/client" -import { Box, Button, Checkbox, Container, FormControl, FormLabel, Heading, HStack, Input, InputGroup, InputRightAddon, Switch, useToast, VStack } from "@chakra-ui/react" +import { AlertDialog, AlertDialogBody, AlertDialogContent, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, Text, Box, Button, Checkbox, Container, FormControl, FormLabel, Heading, HStack, Input, InputGroup, InputRightAddon, Switch, useToast, VStack } from "@chakra-ui/react" import React from "react" +import { signOut } from "next-auth/client" import { useMutation } from "react-query" import { Footer } from "../components/Footer" import { Navbar } from "../components/Navbar" @@ -30,6 +31,11 @@ const updateUserSettings = async (params: { return res.data } +const deleteAccount = async () => { + const res = await apiClient.delete(`/user`) + return res.data +} + function UserPage(props: { session: UserSession, defaultUserInfo: DefaultUserInfo @@ -37,6 +43,42 @@ function UserPage(props: { const updateNotificationEmailMutation = useMutation(updateUserSettings) const updatePreferenceMutation = useMutation(updateUserSettings) + const deleteAccountMutation = useMutation(deleteAccount, { + onSuccess() { + toast({ + title: 'Deleted', + status: 'success', + position: 'top' + }) + signOut() + }, + onError() { + toast({ + title: 'Something went wrong', + status: 'error', + position: 'top' + }) + setIsOpenDeleteAccountModal(false) + } + }) + + const [isDisableDeleteAccount, setIsDisableDeleteAccount] = React.useState(true) + + const [confirmUsername, setConfirmUsername] = React.useState("") + const onChangeConfirmUsername = (event) => { + setConfirmUsername(event.target.value) + if (event.target.value === props.session.user.name) { + setIsDisableDeleteAccount(false) + } else { + setIsDisableDeleteAccount(true) + } + } + + const [isOpenDeleteAccountModal, setIsOpenDeleteAccountModal] = React.useState(false) + const cancelDeleteAccountRef = React.useRef() + const onCloseDeleteAccountModal = () => { + setIsOpenDeleteAccountModal(false) + } const toast = useToast() @@ -59,6 +101,7 @@ function UserPage(props: { notificationEmail: value }, { onSuccess() { + signOut() toast({ title: 'Updated', status: 'success', @@ -94,6 +137,42 @@ function UserPage(props: { }) } + const DeleteAccountDialog = ( + + + + + Delete Account + + + + + Are you sure? + + + This action cannot be undone. This will permanently delete your account, projects and comments. + + + Please type {props.session.user.name} to confirm. + + + + + + + + + + + + ) + return ( <> @@ -141,6 +220,14 @@ function UserPage(props: { + + + Danger Zone + + + {DeleteAccountDialog} + + diff --git a/prisma/pgsql/schema.prisma b/prisma/pgsql/schema.prisma index dc81bcc5..0bdd45a8 100644 --- a/prisma/pgsql/schema.prisma +++ b/prisma/pgsql/schema.prisma @@ -8,6 +8,7 @@ datasource db { generator client { provider = "prisma-client-js" + previewFeatures = ["referentialActions"] } // next-auth BEGIN @@ -84,7 +85,7 @@ model Project { deletedAt DateTime? @map(name: "deleted_at") ownerId String - owner User @relation(fields: [ownerId], references: [id]) + owner User @relation(fields: [ownerId], references: [id], onDelete: Cascade) pages Page[] @@ -110,7 +111,7 @@ model Page { updatedAt DateTime @default(now()) @map(name: "updated_at") projectId String - project Project @relation(fields: [projectId], references: [id]) + project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) comments Comment[] @@ -123,7 +124,7 @@ model Comment { id String @id @default(uuid()) pageId String - page Page @relation(fields: [pageId], references: [id]) + page Page @relation(fields: [pageId], references: [id], onDelete: Cascade) createdAt DateTime @default(now()) @map(name: "created_at") updatedAt DateTime @default(now()) @map(name: "updated_at") diff --git a/prisma/sqlite/schema.prisma b/prisma/sqlite/schema.prisma index 2c56f2e8..875a4b6f 100644 --- a/prisma/sqlite/schema.prisma +++ b/prisma/sqlite/schema.prisma @@ -8,6 +8,7 @@ datasource db { generator client { provider = "prisma-client-js" + previewFeatures = ["referentialActions"] } // next-auth BEGIN @@ -84,7 +85,7 @@ model Project { deletedAt DateTime? @map(name: "deleted_at") ownerId String - owner User @relation(fields: [ownerId], references: [id]) + owner User @relation(fields: [ownerId], references: [id], onDelete: Cascade) pages Page[] @@ -110,7 +111,7 @@ model Page { updatedAt DateTime @default(now()) @map(name: "updated_at") projectId String - project Project @relation(fields: [projectId], references: [id]) + project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) comments Comment[] @@ -123,7 +124,7 @@ model Comment { id String @id @default(uuid()) pageId String - page Page @relation(fields: [pageId], references: [id]) + page Page @relation(fields: [pageId], references: [id], onDelete: Cascade) createdAt DateTime @default(now()) @map(name: "created_at") updatedAt DateTime @default(now()) @map(name: "updated_at") diff --git a/service/user.service.ts b/service/user.service.ts index 7b73e4c8..5c778a5d 100644 --- a/service/user.service.ts +++ b/service/user.service.ts @@ -1,3 +1,4 @@ +import { Page, Comment } from ".prisma/client"; import { nanoid } from "nanoid"; import { RequestScopeService } from "."; import { prisma } from "../utils.server"; @@ -17,4 +18,12 @@ export class UserService extends RequestScopeService { } }) } + + async delete(userId: string) { + await prisma.user.delete({ + where: { + id: userId + } + }) + } } \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 4d843849..7032fd85 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1025,12 +1025,12 @@ resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.4.4.tgz#11d5db19bd178936ec89cd84519c4de439574398" integrity sha512-1oO6+dN5kdIA3sKPZhRGJTfGVP4SWV6KqlMOwry4J3HfyD68sl/3KmG7DeYUzvN+RbhXDnv/D8vNNB8168tAMg== -"@prisma/client@^2.19.0": - version "2.24.1" - resolved "https://registry.yarnpkg.com/@prisma/client/-/client-2.24.1.tgz#c4f26fb4d768dd52dd20a17e626f10e69cc0b85c" - integrity sha512-vllhf36g3oI98GF1Q5IPmnR5MYzBPeCcl/Xiz6EAi4DMOxE069o9ka5BAqYbUG2USx8JuKw09QdMnDrp3Kyn8g== +"@prisma/client@^2.30": + version "2.30.3" + resolved "https://registry.yarnpkg.com/@prisma/client/-/client-2.30.3.tgz#49c1015e2cec26a44b20c62eb2fd738cb0bb043b" + integrity sha512-Ey2miZ+Hne12We3rA8XrlPoAF0iuKEhw5IK2nropaelSt0Ju3b2qSz9Qt50a/1Mx3+7yRSu/iSXt8y9TUMl/Yw== dependencies: - "@prisma/engines-version" "2.24.1-2.18095475d5ee64536e2f93995e48ad800737a9e4" + "@prisma/engines-version" "2.30.1-2.b8c35d44de987a9691890b3ddf3e2e7effb9bf20" "@prisma/debug@2.14.0": version "2.14.0" @@ -1058,20 +1058,20 @@ terminal-link "^2.1.1" undici "2.2.1" -"@prisma/engines-version@2.24.1-2.18095475d5ee64536e2f93995e48ad800737a9e4": - version "2.24.1-2.18095475d5ee64536e2f93995e48ad800737a9e4" - resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-2.24.1-2.18095475d5ee64536e2f93995e48ad800737a9e4.tgz#2c5813ef98bcbe659b18b521f002f5c8aabbaae2" - integrity sha512-60Do+ByVfHnhJ2id5h/lXOZnDQNIf5pz3enkKWOmyr744Z2IxkBu65jRckFfMN5cPtmXDre/Ay/GKm0aoeLwrw== +"@prisma/engines-version@2.30.1-2.b8c35d44de987a9691890b3ddf3e2e7effb9bf20": + version "2.30.1-2.b8c35d44de987a9691890b3ddf3e2e7effb9bf20" + resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-2.30.1-2.b8c35d44de987a9691890b3ddf3e2e7effb9bf20.tgz#d5ef55c92beeba56e52bba12b703af0bfd30530d" + integrity sha512-/iDRgaoSQC77WN2oDsOM8dn61fykm6tnZUAClY+6p+XJbOEgZ9gy4CKuKTBgrjSGDVjtQ/S2KGcYd3Ring8xaw== "@prisma/engines@2.14.0-28.5d491261d382a2a5ffdc71de17072b0e409f1cc1": version "2.14.0-28.5d491261d382a2a5ffdc71de17072b0e409f1cc1" resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-2.14.0-28.5d491261d382a2a5ffdc71de17072b0e409f1cc1.tgz#dee16f1c648cd291540937ad5d69062526368946" integrity sha512-t5/Mq0VtL7khYQtbap8myr7RwyIvmruCFxsph6oTi8QZaklmuxSp7FAC2DWBz8r/KoZFngMpmliKfDrWN7uwzQ== -"@prisma/engines@2.24.1-2.18095475d5ee64536e2f93995e48ad800737a9e4": - version "2.24.1-2.18095475d5ee64536e2f93995e48ad800737a9e4" - resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-2.24.1-2.18095475d5ee64536e2f93995e48ad800737a9e4.tgz#7e542d510f0c03f41b73edbb17254f5a0b272a4d" - integrity sha512-29/xO9kqeQka+wN5Ev10l5L4XQXNVXdPToJs1M29VZ2imQsNsL4rtz26m3qGM54IoGWwwfTVdvuVRxKnDl2rig== +"@prisma/engines@2.30.1-2.b8c35d44de987a9691890b3ddf3e2e7effb9bf20": + version "2.30.1-2.b8c35d44de987a9691890b3ddf3e2e7effb9bf20" + resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-2.30.1-2.b8c35d44de987a9691890b3ddf3e2e7effb9bf20.tgz#2df768aa7c9f84acaa1f35c970417822233a9fb1" + integrity sha512-WPnA/IUrxDihrRhdP6+8KAVSwsc0zsh8ioPYsLJjOhzVhwpRbuFH2tJDRIAbc+qFh+BbTIZbeyBYt8fpNXaYQQ== "@prisma/fetch-engine@2.14.0": version "2.14.0" @@ -4421,12 +4421,12 @@ prettysize@^2.0.0: resolved "https://registry.yarnpkg.com/prettysize/-/prettysize-2.0.0.tgz#902c02480d865d9cc0813011c9feb4fa02ce6996" integrity sha512-VVtxR7sOh0VsG8o06Ttq5TrI1aiZKmC+ClSn4eBPaNf4SHr5lzbYW+kYGX3HocBL/MfpVrRfFZ9V3vCbLaiplg== -prisma@^2.19.0: - version "2.24.1" - resolved "https://registry.yarnpkg.com/prisma/-/prisma-2.24.1.tgz#f8f4cb8baf407a71800256160277f69603bd43a3" - integrity sha512-L+ykMpttbWzpTNsy+PPynnEX/mS1s5zs49euXBrMjxXh1M6/f9MYlTNAj+iP90O9ZSaURSpNa+1jzatPghqUcQ== +prisma@^2.30: + version "2.30.3" + resolved "https://registry.yarnpkg.com/prisma/-/prisma-2.30.3.tgz#e4a770e1f52151e72c1c5be0aa2e75222a0135c4" + integrity sha512-48qYba2BIyUmXuosBZs0g3kYGrxKvo4VkSHYOuLlDdDirmKyvoY2hCYMUYHSx3f++8ovfgs+MX5KmNlP+iAZrQ== dependencies: - "@prisma/engines" "2.24.1-2.18095475d5ee64536e2f93995e48ad800737a9e4" + "@prisma/engines" "2.30.1-2.b8c35d44de987a9691890b3ddf3e2e7effb9bf20" process-nextick-args@~2.0.0: version "2.0.1"