-
Notifications
You must be signed in to change notification settings - Fork 112
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: delete account, remove cron job, misc
- Loading branch information
1 parent
d324d97
commit a638b9e
Showing
15 changed files
with
326 additions
and
73 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
"use server"; | ||
|
||
import { createAdminClient } from "@/lib/supabase/admin"; | ||
import { createClient } from "@/lib/supabase/server"; | ||
import { cookies } from "next/headers"; | ||
import { z } from "zod"; | ||
import { redirect } from "next/navigation"; | ||
|
||
type FormState = { | ||
message: string; | ||
status: number; | ||
}; | ||
|
||
export async function deleteAccount(prevState: FormState, formData: FormData) { | ||
const cookieStore = cookies(); | ||
const supabase = createClient(cookieStore); | ||
const supabaseAdmin = createAdminClient(); | ||
|
||
// get user object | ||
const { | ||
data: { user }, | ||
error: userError, | ||
} = await supabase.auth.getUser(); | ||
if (userError) | ||
return { | ||
message: `Unable to get user object: ${userError.message}`, | ||
status: 400, | ||
}; | ||
if (!user) return { message: `Unable to get user object`, status: 400 }; | ||
|
||
// delete user input storage items | ||
const { error: storageInputError } = await supabaseAdmin.storage | ||
.from("input") | ||
.remove([`${user.id}`]); | ||
if (storageInputError) | ||
return { | ||
message: `Unable to delete user input images: ${storageInputError.message}`, | ||
status: 400, | ||
}; | ||
|
||
// delete user output storage items | ||
const { error: storageOutputError } = await supabaseAdmin.storage | ||
.from("output") | ||
.remove([`${user.id}`]); | ||
if (storageOutputError) | ||
return { | ||
message: `Unable to delete user output images: ${storageOutputError.message}`, | ||
status: 400, | ||
}; | ||
|
||
// delete user data | ||
const { error } = await supabase.from("data").delete().eq("user_id", user.id); | ||
|
||
// delete user | ||
const { data: deleteData, error: deleteError } = | ||
await supabaseAdmin.auth.admin.deleteUser(user.id); | ||
if (error) | ||
return { | ||
message: `Unable to delete user: ${deleteError?.message}`, | ||
status: 400, | ||
}; | ||
|
||
return { | ||
message: `Successfully deleted account for ${user.email}. Sign out or refresh the page`, | ||
status: 200, | ||
}; | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
"use client"; | ||
|
||
import { | ||
Dialog, | ||
DialogContent, | ||
DialogDescription, | ||
DialogHeader, | ||
DialogTitle, | ||
} from "@/components/ui/dialog"; | ||
import { | ||
Drawer, | ||
DrawerClose, | ||
DrawerContent, | ||
DrawerDescription, | ||
DrawerFooter, | ||
DrawerHeader, | ||
DrawerTitle, | ||
} from "@/components/ui/drawer"; | ||
import { useMediaQuery } from "@/lib/hooks/use-media-query"; | ||
import { Button } from "@/components/ui/button"; | ||
import { create } from "zustand"; | ||
import Image from "next/image"; | ||
import { Separator } from "@/components/ui/separator"; | ||
import { useEffect } from "react"; | ||
import { LoadingDots } from "@/components/shared/icons"; | ||
import { deleteAccount } from "@/app/actions/deleteAccount"; | ||
import { Input } from "@/components/ui/input"; | ||
import { Label } from "@/components/ui/label"; | ||
import { useFormState, useFormStatus } from "react-dom"; | ||
|
||
type DeleteDialogStore = { | ||
open: boolean; | ||
setOpen: (isOpen: boolean) => void; | ||
}; | ||
|
||
export const useDeleteAccountDialog = create<DeleteDialogStore>((set) => ({ | ||
open: false, | ||
setOpen: (open) => set(() => ({ open: open })), | ||
})); | ||
|
||
export function DeleteAccountDialog() { | ||
const [open, setOpen] = useDeleteAccountDialog((s) => [s.open, s.setOpen]); | ||
const isDesktop = useMediaQuery("(min-width: 768px)"); | ||
|
||
if (isDesktop) { | ||
return ( | ||
<Dialog open={open} onOpenChange={setOpen} modal={true}> | ||
<DialogContent className="gap-0 overflow-hidden p-0 md:rounded-2xl"> | ||
<DialogHeader className="items-center justify-center space-y-3 px-16 py-8"> | ||
<a href="https://precedent.dev"> | ||
<Image | ||
src="/logo.png" | ||
alt="Logo" | ||
className="h-10 w-10 rounded-full" | ||
width={20} | ||
height={20} | ||
/> | ||
</a> | ||
<DialogTitle className="font-display text-2xl font-bold leading-normal tracking-normal"> | ||
Delete Account | ||
</DialogTitle> | ||
<DialogDescription className="text-center"> | ||
This account will be deleted along with all your uploaded images | ||
and AI generated images/gifs. | ||
</DialogDescription> | ||
</DialogHeader> | ||
|
||
<Separator /> | ||
|
||
{/* Buttons */} | ||
<div className="flex flex-col space-y-4 bg-muted px-16 py-8"> | ||
<DeleteAccountForm /> | ||
</div> | ||
</DialogContent> | ||
</Dialog> | ||
); | ||
} | ||
|
||
return ( | ||
<Drawer open={open} onOpenChange={setOpen}> | ||
<DrawerContent className="rounded-t-2xl"> | ||
<DrawerHeader className="flex flex-col items-center justify-center space-y-3 px-4 py-8"> | ||
<a href="https://precedent.dev"> | ||
<Image | ||
src="/logo.png" | ||
alt="Logo" | ||
className="h-10 w-10 rounded-full" | ||
width={20} | ||
height={20} | ||
/> | ||
</a> | ||
<DrawerTitle className="font-display text-2xl font-bold leading-normal tracking-normal"> | ||
Delete Account | ||
</DrawerTitle> | ||
<DrawerDescription className="text-center"> | ||
This account will be deleted along with all your uploaded images and | ||
AI generated images/gifs. | ||
</DrawerDescription> | ||
</DrawerHeader> | ||
|
||
<Separator /> | ||
|
||
{/* Buttons */} | ||
<div className="flex flex-col space-y-4 bg-muted px-4 py-8"> | ||
<DeleteAccountForm /> | ||
</div> | ||
|
||
<DrawerFooter className="bg-muted"> | ||
<DrawerClose asChild> | ||
<Button variant="outline">Cancel</Button> | ||
</DrawerClose> | ||
</DrawerFooter> | ||
</DrawerContent> | ||
</Drawer> | ||
); | ||
} | ||
|
||
function DeleteAccountForm() { | ||
const [state, deleteAccountFormAction] = useFormState(deleteAccount, { | ||
message: "", | ||
status: 0, | ||
}); | ||
|
||
useEffect(() => { | ||
if (state.message.includes("Successfully deleted account for")) { | ||
window.location.reload(); | ||
} | ||
}, [state]); | ||
|
||
return ( | ||
<form action={deleteAccountFormAction} className="flex flex-col space-y-4"> | ||
<div className="grid w-full items-center gap-1.5"> | ||
<Label htmlFor="deleteConfirmation"> | ||
To verify, type <b>delete my account</b> below: | ||
</Label> | ||
<Input | ||
type="text" | ||
id="deleteConfirmation" | ||
required | ||
pattern="delete my account" | ||
className="w-full" | ||
/> | ||
{state?.message && <p className="text-destructive">{state.message}</p>} | ||
</div> | ||
<DeleteAccountButton /> | ||
</form> | ||
); | ||
} | ||
|
||
function DeleteAccountButton() { | ||
const { pending } = useFormStatus(); | ||
return ( | ||
<Button | ||
type="submit" | ||
variant="destructive" | ||
className="w-full focus-visible:ring-0 focus-visible:ring-transparent focus-visible:ring-offset-0" | ||
disabled={pending} | ||
> | ||
{pending ? ( | ||
<LoadingDots color="#808080" /> | ||
) : ( | ||
<> | ||
<p>Delete Account</p> | ||
</> | ||
)} | ||
</Button> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.