diff --git a/src/app/settings/actions.ts b/src/app/settings/actions.ts new file mode 100644 index 0000000..a012a23 --- /dev/null +++ b/src/app/settings/actions.ts @@ -0,0 +1,35 @@ +'use server'; + +import { env } from '@/config/env'; +import { siteConfig } from '@/config/site'; +import { fileStorage } from '@/infra'; +import { auth } from '@/lib/auth'; +import { authAction } from '@/lib/safe-action'; +import { findManyByUserId as findManyAssetsByUserId } from '@/models/asset'; +import { deleteOne as deleteOneUser } from '@/models/user'; +import { cookies } from 'next/headers'; +import { redirect } from 'next/navigation'; +import { z } from 'zod'; + +const schema = z.object({}); + +export const deleteAccount = authAction(schema, async ({}, { user }) => { + // Don't need to invalidate the session, since the session table has a + // cascade delete on the user id. This will automatically delete the + // session when the user is deleted. + const sessionCookie = auth.createBlankSessionCookie(); + cookies().set( + sessionCookie.name, + sessionCookie.value, + sessionCookie.attributes, + ); + const assets = await findManyAssetsByUserId(user.id); + await Promise.all([ + fileStorage.removeObjects( + env.S3_BUCKET_NAME, + assets.map((asset) => asset.name), + ), + deleteOneUser(user.id), + ]); + return redirect(siteConfig.paths.auth.signIn); +}); diff --git a/src/app/settings/page.tsx b/src/app/settings/page.tsx index 6c4cd4c..830b6fb 100644 --- a/src/app/settings/page.tsx +++ b/src/app/settings/page.tsx @@ -6,13 +6,26 @@ import { CardHeader, CardTitle, } from '@/components/ui/card'; +import { DialogFooter, DialogHeader } from '@/components/ui/dialog'; +import { + Dialog, + DialogClose, + DialogContent, + DialogDescription, + DialogTitle, + DialogTrigger, +} from '@/components/ui/dialog'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { siteConfig } from '@/config/site'; +import { umami } from '@/lib/analytics'; import { getUserSession } from '@/models/user'; +import { Trash2, X } from 'lucide-react'; import Link from 'next/link'; import { redirect } from 'next/navigation'; +import { deleteAccount } from './actions'; + const ProfilePage = async () => { const { user } = await getUserSession(); @@ -39,6 +52,47 @@ const ProfilePage = async () => { + + + + + + + + Confirm account deletion + + + This irreversible action will delete your + account and all associated data. + + + + + + +
+ +
+
+
+