-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: upstream sync #109
fix: upstream sync #109
Changes from all commits
f652ca9
a3e5608
7762b1d
9e433af
014c09b
d598677
4d93ed6
4c09867
bc98907
3075281
7ef7715
6daaa3a
cab875f
c680cfc
fddd860
f98567e
7226d5a
9cf72e1
df3ba11
5bef2fb
187a988
6ea6dda
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
'use client'; | ||
|
||
import { signOut } from 'next-auth/react'; | ||
|
||
import type { User } from '@documenso/prisma/client'; | ||
import { TRPCClientError } from '@documenso/trpc/client'; | ||
import { trpc } from '@documenso/trpc/react'; | ||
import { Alert, AlertDescription, AlertTitle } from '@documenso/ui/primitives/alert'; | ||
import { Button } from '@documenso/ui/primitives/button'; | ||
import { | ||
Dialog, | ||
DialogContent, | ||
DialogDescription, | ||
DialogFooter, | ||
DialogHeader, | ||
DialogTitle, | ||
DialogTrigger, | ||
} from '@documenso/ui/primitives/dialog'; | ||
import { useToast } from '@documenso/ui/primitives/use-toast'; | ||
|
||
export type DeleteAccountDialogProps = { | ||
className?: string; | ||
user: User; | ||
}; | ||
|
||
export const DeleteAccountDialog = ({ className, user }: DeleteAccountDialogProps) => { | ||
const { toast } = useToast(); | ||
|
||
const hasTwoFactorAuthentication = user.twoFactorEnabled; | ||
|
||
const { mutateAsync: deleteAccount, isLoading: isDeletingAccount } = | ||
trpc.profile.deleteAccount.useMutation(); | ||
|
||
const onDeleteAccount = async () => { | ||
try { | ||
await deleteAccount(); | ||
|
||
toast({ | ||
title: 'Account deleted', | ||
description: 'Your account has been deleted successfully.', | ||
duration: 5000, | ||
}); | ||
|
||
return await signOut({ callbackUrl: '/' }); | ||
} catch (err) { | ||
if (err instanceof TRPCClientError && err.data?.code === 'BAD_REQUEST') { | ||
toast({ | ||
title: 'An error occurred', | ||
description: err.message, | ||
variant: 'destructive', | ||
}); | ||
} else { | ||
toast({ | ||
title: 'An unknown error occurred', | ||
variant: 'destructive', | ||
description: | ||
err.message ?? | ||
'We encountered an unknown error while attempting to delete your account. Please try again later.', | ||
}); | ||
} | ||
} | ||
}; | ||
|
||
return ( | ||
<div className={className}> | ||
<Alert | ||
className="flex flex-col items-center justify-between gap-4 p-6 md:flex-row " | ||
variant="neutral" | ||
> | ||
<div> | ||
<AlertTitle>Delete Account</AlertTitle> | ||
<AlertDescription className="mr-2"> | ||
Delete your account and all its contents, including completed documents. This action is | ||
irreversible and will cancel your subscription, so proceed with caution. | ||
</AlertDescription> | ||
</div> | ||
|
||
<div className="flex-shrink-0"> | ||
<Dialog> | ||
<DialogTrigger asChild> | ||
<Button variant="destructive">Delete Account</Button> | ||
</DialogTrigger> | ||
<DialogContent> | ||
<DialogHeader className="space-y-4"> | ||
<DialogTitle>Delete Account</DialogTitle> | ||
|
||
<Alert variant="destructive"> | ||
<AlertDescription className="selection:bg-red-100"> | ||
This action is not reversible. Please be certain. | ||
</AlertDescription> | ||
</Alert> | ||
|
||
{hasTwoFactorAuthentication && ( | ||
<Alert variant="destructive"> | ||
<AlertDescription className="selection:bg-red-100"> | ||
Disable Two Factor Authentication before deleting your account. | ||
</AlertDescription> | ||
</Alert> | ||
)} | ||
|
||
<DialogDescription> | ||
Documenso will delete <span className="font-semibold">all of your documents</span> | ||
, along with all of your completed documents, signatures, and all other resources | ||
belonging to your Account. | ||
</DialogDescription> | ||
</DialogHeader> | ||
|
||
<DialogFooter> | ||
<Button | ||
onClick={onDeleteAccount} | ||
loading={isDeletingAccount} | ||
variant="destructive" | ||
disabled={hasTwoFactorAuthentication} | ||
> | ||
{isDeletingAccount ? 'Deleting account...' : 'Delete Account'} | ||
</Button> | ||
</DialogFooter> | ||
</DialogContent> | ||
</Dialog> | ||
</div> | ||
</Alert> | ||
</div> | ||
); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { prisma } from '@documenso/prisma'; | ||
|
||
export const deletedAccountServiceAccount = async () => { | ||
const serviceAccount = await prisma.user.findFirst({ | ||
where: { | ||
email: '[email protected]', | ||
}, | ||
}); | ||
|
||
if (!serviceAccount) { | ||
throw new Error( | ||
'Deleted account service account not found, have you ran the appropriate migrations?', | ||
); | ||
} | ||
|
||
return serviceAccount; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
-- Create [email protected] | ||
DO $$ | ||
BEGIN | ||
IF NOT EXISTS (SELECT 1 FROM "public"."User" WHERE "email" = '[email protected]') THEN | ||
INSERT INTO | ||
"public"."User" ( | ||
"email", | ||
"emailVerified", | ||
"password", | ||
"createdAt", | ||
"updatedAt", | ||
"lastSignedIn", | ||
"roles", | ||
"identityProvider", | ||
"twoFactorEnabled" | ||
) | ||
VALUES | ||
( | ||
'[email protected]', | ||
NOW(), | ||
NULL, | ||
NOW(), | ||
NOW(), | ||
NOW(), | ||
ARRAY['USER'::TEXT]::"public"."Role" [], | ||
CAST('GOOGLE'::TEXT AS "public"."IdentityProvider"), | ||
FALSE | ||
); | ||
END IF; | ||
END $$ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
import { TRPCError } from '@trpc/server'; | ||
|
||
import { deleteUser } from '@documenso/lib/server-only/user/delete-user'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tip Codebase Verification The
Given these observations, it's recommended to review the Analysis chainThe import of Scripts ExecutedThe following scripts were executed for the analysis: Script: #!/bin/bash
# Verify the implementation of deleteUser function to ensure comprehensive data deletion
echo "Manual verification required for the deleteUser function implementation."
Length of output: 154 Script: #!/bin/bash
# Attempt to locate and display the contents of the deleteUser function implementation
fd "delete-user\.(js|ts)" --exec cat {}
Length of output: 1014 |
||
import { findUserSecurityAuditLogs } from '@documenso/lib/server-only/user/find-user-security-audit-logs'; | ||
import { forgotPassword } from '@documenso/lib/server-only/user/forgot-password'; | ||
import { getUserById } from '@documenso/lib/server-only/user/get-user-by-id'; | ||
|
@@ -155,4 +156,23 @@ export const profileRouter = router({ | |
}); | ||
} | ||
}), | ||
|
||
deleteAccount: authenticatedProcedure.mutation(async ({ ctx }) => { | ||
try { | ||
const user = ctx.user; | ||
|
||
return await deleteUser(user); | ||
} catch (err) { | ||
let message = 'We were unable to delete your account. Please try again.'; | ||
|
||
if (err instanceof Error) { | ||
message = err.message; | ||
} | ||
|
||
throw new TRPCError({ | ||
code: 'BAD_REQUEST', | ||
message, | ||
}); | ||
} | ||
}), | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider externalizing the email address
'[email protected]'
to an environment variable for easier configuration and to avoid hardcoding values in the codebase. Additionally, enhancing the error message to include steps for running migrations could improve developer experience.And for the error message: