From d4fdf881cd946f3fae5bc17aaf66520adfa62515 Mon Sep 17 00:00:00 2001 From: 190km Date: Fri, 29 Nov 2024 02:06:25 +0100 Subject: [PATCH] feat: validating app name before delete --- .../application/delete-application.tsx | 160 +++++++++++++----- .../dashboard/compose/delete-compose.tsx | 158 ++++++++++++----- .../dashboard/mariadb/delete-mariadb.tsx | 160 +++++++++++++----- .../dashboard/mongo/delete-mongo.tsx | 157 ++++++++++++----- .../dashboard/mysql/delete-mysql.tsx | 156 ++++++++++++----- .../dashboard/postgres/delete-postgres.tsx | 159 ++++++++++++----- .../dashboard/redis/delete-redis.tsx | 156 ++++++++++++----- 7 files changed, 825 insertions(+), 281 deletions(-) diff --git a/apps/dokploy/components/dashboard/application/delete-application.tsx b/apps/dokploy/components/dashboard/application/delete-application.tsx index aa6fd9025..93173d637 100644 --- a/apps/dokploy/components/dashboard/application/delete-application.tsx +++ b/apps/dokploy/components/dashboard/application/delete-application.tsx @@ -1,63 +1,143 @@ -import { - AlertDialog, - AlertDialogAction, - AlertDialogCancel, - AlertDialogContent, - AlertDialogDescription, - AlertDialogFooter, - AlertDialogHeader, - AlertDialogTitle, - AlertDialogTrigger, -} from "@/components/ui/alert-dialog"; import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; import { api } from "@/utils/api"; +import { zodResolver } from "@hookform/resolvers/zod"; import { TrashIcon } from "lucide-react"; import { useRouter } from "next/router"; +import { useState } from "react"; +import { useForm } from "react-hook-form"; import { toast } from "sonner"; +import { z } from "zod"; + +const deleteApplicationSchema = z.object({ + projectName: z.string().min(1, { + message: "Application name is required", + }), +}); + +type DeleteApplication = z.infer; interface Props { applicationId: string; } export const DeleteApplication = ({ applicationId }: Props) => { + const [isOpen, setIsOpen] = useState(false); const { mutateAsync, isLoading } = api.application.delete.useMutation(); + const { data } = api.application.one.useQuery( + { applicationId }, + { enabled: !!applicationId }, + ); const { push } = useRouter(); + const form = useForm({ + defaultValues: { + projectName: "", + }, + resolver: zodResolver(deleteApplicationSchema), + }); + + const onSubmit = async (formData: DeleteApplication) => { + const expectedName = `${data?.name}/${data?.appName}`; + if (formData.projectName === expectedName) { + await mutateAsync({ + applicationId, + }) + .then((data) => { + push(`/dashboard/project/${data?.projectId}`); + toast.success("Application deleted successfully"); + setIsOpen(false); + }) + .catch(() => { + toast.error("Error deleting the application"); + }); + } else { + form.setError("projectName", { + message: "Project name does not match", + }); + } + }; + return ( - - + + - - - - Are you absolutely sure? - + + + + Are you absolutely sure? + This action cannot be undone. This will permanently delete the - application - - - - Cancel - { - await mutateAsync({ - applicationId, - }) - .then((data) => { - push(`/dashboard/project/${data?.projectId}`); - - toast.success("Application delete succesfully"); - }) - .catch(() => { - toast.error("Error to delete Application"); - }); + application. If you are sure please enter the application name to + delete this application. + + +
+
+ + ( + + + To confirm, type "{data?.name}/{data?.appName}" in the box + below + + + + + + + )} + /> + + +
+ + + + + + ); }; diff --git a/apps/dokploy/components/dashboard/compose/delete-compose.tsx b/apps/dokploy/components/dashboard/compose/delete-compose.tsx index 43af14c31..07f42448c 100644 --- a/apps/dokploy/components/dashboard/compose/delete-compose.tsx +++ b/apps/dokploy/components/dashboard/compose/delete-compose.tsx @@ -1,63 +1,141 @@ -import { - AlertDialog, - AlertDialogAction, - AlertDialogCancel, - AlertDialogContent, - AlertDialogDescription, - AlertDialogFooter, - AlertDialogHeader, - AlertDialogTitle, - AlertDialogTrigger, -} from "@/components/ui/alert-dialog"; import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; import { api } from "@/utils/api"; +import { zodResolver } from "@hookform/resolvers/zod"; import { TrashIcon } from "lucide-react"; import { useRouter } from "next/router"; +import { useState } from "react"; +import { useForm } from "react-hook-form"; import { toast } from "sonner"; +import { z } from "zod"; + +const deleteComposeSchema = z.object({ + projectName: z.string().min(1, { + message: "Compose name is required", + }), +}); + +type DeleteCompose = z.infer; interface Props { composeId: string; } export const DeleteCompose = ({ composeId }: Props) => { + const [isOpen, setIsOpen] = useState(false); const { mutateAsync, isLoading } = api.compose.delete.useMutation(); + const { data } = api.compose.one.useQuery( + { composeId }, + { enabled: !!composeId }, + ); const { push } = useRouter(); + const form = useForm({ + defaultValues: { + projectName: "", + }, + resolver: zodResolver(deleteComposeSchema), + }); + + const onSubmit = async (formData: DeleteCompose) => { + const expectedName = `${data?.name}/${data?.appName}`; + if (formData.projectName === expectedName) { + await mutateAsync({ composeId }) + .then((result) => { + push(`/dashboard/project/${result?.projectId}`); + toast.success("Compose deleted successfully"); + setIsOpen(false); + }) + .catch(() => { + toast.error("Error deleting the compose"); + }); + } else { + form.setError("projectName", { + message: `Project name must match "${expectedName}"`, + }); + } + }; + return ( - - + + - - - - Are you absolutely sure? - + + + + Are you absolutely sure? + This action cannot be undone. This will permanently delete the - compose and all its services. - - - - Cancel - { - await mutateAsync({ - composeId, - }) - .then((data) => { - push(`/dashboard/project/${data?.projectId}`); - - toast.success("Compose delete succesfully"); - }) - .catch(() => { - toast.error("Error to delete the compose"); - }); + compose. If you are sure please enter the compose name to delete + this compose. + + +
+
+ + ( + + + To confirm, type "{data?.name}/{data?.appName}" in the box + below + {" "} + + + + + + )} + /> + + +
+ + + + + + ); }; diff --git a/apps/dokploy/components/dashboard/mariadb/delete-mariadb.tsx b/apps/dokploy/components/dashboard/mariadb/delete-mariadb.tsx index 0687a2bdf..26a6215f4 100644 --- a/apps/dokploy/components/dashboard/mariadb/delete-mariadb.tsx +++ b/apps/dokploy/components/dashboard/mariadb/delete-mariadb.tsx @@ -1,62 +1,140 @@ -import { - AlertDialog, - AlertDialogAction, - AlertDialogCancel, - AlertDialogContent, - AlertDialogDescription, - AlertDialogFooter, - AlertDialogHeader, - AlertDialogTitle, - AlertDialogTrigger, -} from "@/components/ui/alert-dialog"; import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; import { api } from "@/utils/api"; +import { zodResolver } from "@hookform/resolvers/zod"; import { TrashIcon } from "lucide-react"; import { useRouter } from "next/router"; +import { useState } from "react"; +import { useForm } from "react-hook-form"; import { toast } from "sonner"; +import { z } from "zod"; + +const deleteMariadbSchema = z.object({ + projectName: z.string().min(1, { + message: "Database name is required", + }), +}); + +type DeleteMariadb = z.infer; interface Props { mariadbId: string; } - export const DeleteMariadb = ({ mariadbId }: Props) => { + const [isOpen, setIsOpen] = useState(false); const { mutateAsync, isLoading } = api.mariadb.remove.useMutation(); + const { data } = api.mariadb.one.useQuery( + { mariadbId }, + { enabled: !!mariadbId }, + ); const { push } = useRouter(); + const form = useForm({ + defaultValues: { + projectName: "", + }, + resolver: zodResolver(deleteMariadbSchema), + }); + + const onSubmit = async (formData: DeleteMariadb) => { + const expectedName = `${data?.name}/${data?.appName}`; + if (formData.projectName === expectedName) { + await mutateAsync({ mariadbId }) + .then((data) => { + push(`/dashboard/project/${data?.projectId}`); + toast.success("Database deleted successfully"); + setIsOpen(false); + }) + .catch(() => { + toast.error("Error deleting the database"); + }); + } else { + form.setError("projectName", { + message: "Database name does not match", + }); + } + }; + return ( - - + + - - - - Are you absolutely sure? - + + + + Are you absolutely sure? + This action cannot be undone. This will permanently delete the - database - - - - Cancel - { - await mutateAsync({ - mariadbId, - }) - .then((data) => { - push(`/dashboard/project/${data?.projectId}`); - toast.success("Database delete succesfully"); - }) - .catch(() => { - toast.error("Error to delete the database"); - }); + database. If you are sure please enter the database name to delete + this database. + + +
+
+ + ( + + + To confirm, type "{data?.name}/{data?.appName}" in the box + below + + + + + + + )} + /> + + +
+ + + + + + ); }; diff --git a/apps/dokploy/components/dashboard/mongo/delete-mongo.tsx b/apps/dokploy/components/dashboard/mongo/delete-mongo.tsx index 35e1eaa0b..86c2e2425 100644 --- a/apps/dokploy/components/dashboard/mongo/delete-mongo.tsx +++ b/apps/dokploy/components/dashboard/mongo/delete-mongo.tsx @@ -1,62 +1,139 @@ -import { - AlertDialog, - AlertDialogAction, - AlertDialogCancel, - AlertDialogContent, - AlertDialogDescription, - AlertDialogFooter, - AlertDialogHeader, - AlertDialogTitle, - AlertDialogTrigger, -} from "@/components/ui/alert-dialog"; import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; import { api } from "@/utils/api"; +import { zodResolver } from "@hookform/resolvers/zod"; import { TrashIcon } from "lucide-react"; import { useRouter } from "next/router"; +import { useState } from "react"; +import { useForm } from "react-hook-form"; import { toast } from "sonner"; +import { z } from "zod"; + +const deleteMongoSchema = z.object({ + projectName: z.string().min(1, { + message: "Database name is required", + }), +}); + +type DeleteMongo = z.infer; interface Props { mongoId: string; } +// commen + export const DeleteMongo = ({ mongoId }: Props) => { + const [isOpen, setIsOpen] = useState(false); const { mutateAsync, isLoading } = api.mongo.remove.useMutation(); + const { data } = api.mongo.one.useQuery({ mongoId }, { enabled: !!mongoId }); const { push } = useRouter(); + const form = useForm({ + defaultValues: { + projectName: "", + }, + resolver: zodResolver(deleteMongoSchema), + }); + + const onSubmit = async (formData: DeleteMongo) => { + const expectedName = `${data?.name}/${data?.appName}`; + if (formData.projectName === expectedName) { + await mutateAsync({ mongoId }) + .then((data) => { + push(`/dashboard/project/${data?.projectId}`); + toast.success("Database deleted successfully"); + setIsOpen(false); + }) + .catch(() => { + toast.error("Error deleting the database"); + }); + } else { + form.setError("projectName", { + message: "Database name does not match", + }); + } + }; return ( - - + + - - - - Are you absolutely sure? - + + + + Are you absolutely sure? + This action cannot be undone. This will permanently delete the - database - - - - Cancel - { - await mutateAsync({ - mongoId, - }) - .then((data) => { - push(`/dashboard/project/${data?.projectId}`); - toast.success("Database delete succesfully"); - }) - .catch(() => { - toast.error("Error to delete the database"); - }); + database. If you are sure please enter the database name to delete + this database. + + +
+
+ + ( + + + To confirm, type "{data?.name}/{data?.appName}" in the box + below + + + + + + + )} + /> + + +
+ + + + + + ); }; diff --git a/apps/dokploy/components/dashboard/mysql/delete-mysql.tsx b/apps/dokploy/components/dashboard/mysql/delete-mysql.tsx index 373c68fdb..a887adc5a 100644 --- a/apps/dokploy/components/dashboard/mysql/delete-mysql.tsx +++ b/apps/dokploy/components/dashboard/mysql/delete-mysql.tsx @@ -1,62 +1,138 @@ -import { - AlertDialog, - AlertDialogAction, - AlertDialogCancel, - AlertDialogContent, - AlertDialogDescription, - AlertDialogFooter, - AlertDialogHeader, - AlertDialogTitle, - AlertDialogTrigger, -} from "@/components/ui/alert-dialog"; import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; import { api } from "@/utils/api"; +import { zodResolver } from "@hookform/resolvers/zod"; import { TrashIcon } from "lucide-react"; import { useRouter } from "next/router"; +import { useState } from "react"; +import { useForm } from "react-hook-form"; import { toast } from "sonner"; +import { z } from "zod"; + +const deleteMysqlSchema = z.object({ + projectName: z.string().min(1, { + message: "Database name is required", + }), +}); + +type DeleteMysql = z.infer; interface Props { mysqlId: string; } export const DeleteMysql = ({ mysqlId }: Props) => { + const [isOpen, setIsOpen] = useState(false); const { mutateAsync, isLoading } = api.mysql.remove.useMutation(); + const { data } = api.mysql.one.useQuery({ mysqlId }, { enabled: !!mysqlId }); const { push } = useRouter(); + const form = useForm({ + defaultValues: { + projectName: "", + }, + resolver: zodResolver(deleteMysqlSchema), + }); + + const onSubmit = async (formData: DeleteMysql) => { + const expectedName = `${data?.name}/${data?.appName}`; + if (formData.projectName === expectedName) { + await mutateAsync({ mysqlId }) + .then((data) => { + push(`/dashboard/project/${data?.projectId}`); + toast.success("Database deleted successfully"); + setIsOpen(false); + }) + .catch(() => { + toast.error("Error deleting the database"); + }); + } else { + form.setError("projectName", { + message: "Database name does not match", + }); + } + }; + return ( - - + + - - - - Are you absolutely sure? - + + + + Are you absolutely sure? + This action cannot be undone. This will permanently delete the - database - - - - Cancel - { - await mutateAsync({ - mysqlId, - }) - .then((data) => { - push(`/dashboard/project/${data?.projectId}`); - toast.success("Database delete succesfully"); - }) - .catch(() => { - toast.error("Error to delete the database"); - }); + database. If you are sure please enter the database name to delete + this database. + + +
+
+ + ( + + + To confirm, type "{data?.name}/{data?.appName}" in the box + below + + + + + + + )} + /> + + +
+ + + + + + ); }; diff --git a/apps/dokploy/components/dashboard/postgres/delete-postgres.tsx b/apps/dokploy/components/dashboard/postgres/delete-postgres.tsx index ce64c44b0..1767831a5 100644 --- a/apps/dokploy/components/dashboard/postgres/delete-postgres.tsx +++ b/apps/dokploy/components/dashboard/postgres/delete-postgres.tsx @@ -1,62 +1,141 @@ -import { - AlertDialog, - AlertDialogAction, - AlertDialogCancel, - AlertDialogContent, - AlertDialogDescription, - AlertDialogFooter, - AlertDialogHeader, - AlertDialogTitle, - AlertDialogTrigger, -} from "@/components/ui/alert-dialog"; import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; import { api } from "@/utils/api"; +import { zodResolver } from "@hookform/resolvers/zod"; import { TrashIcon } from "lucide-react"; import { useRouter } from "next/router"; +import { useState } from "react"; +import { useForm } from "react-hook-form"; import { toast } from "sonner"; +import { z } from "zod"; + +const deletePostgresSchema = z.object({ + projectName: z.string().min(1, { + message: "Database name is required", + }), +}); + +type DeletePostgres = z.infer; interface Props { postgresId: string; } export const DeletePostgres = ({ postgresId }: Props) => { + const [isOpen, setIsOpen] = useState(false); const { mutateAsync, isLoading } = api.postgres.remove.useMutation(); + const { data } = api.postgres.one.useQuery( + { postgresId }, + { enabled: !!postgresId }, + ); const { push } = useRouter(); + const form = useForm({ + defaultValues: { + projectName: "", + }, + resolver: zodResolver(deletePostgresSchema), + }); + + const onSubmit = async (formData: DeletePostgres) => { + const expectedName = `${data?.name}/${data?.appName}`; + if (formData.projectName === expectedName) { + await mutateAsync({ postgresId }) + .then((data) => { + push(`/dashboard/project/${data?.projectId}`); + toast.success("Database deleted successfully"); + setIsOpen(false); + }) + .catch(() => { + toast.error("Error deleting the database"); + }); + } else { + form.setError("projectName", { + message: "Database name does not match", + }); + } + }; + return ( - - + + - - - - Are you absolutely sure? - + + + + Are you absolutely sure? + This action cannot be undone. This will permanently delete the - database - - - - Cancel - { - await mutateAsync({ - postgresId, - }) - .then((data) => { - push(`/dashboard/project/${data?.projectId}`); - toast.success("Database delete succesfully"); - }) - .catch(() => { - toast.error("Error to delete the database"); - }); + database. If you are sure please enter the database name to delete + this database. + + +
+
+ + ( + + + To confirm, type "{data?.name}/{data?.appName}" in the box + below + + + + + + + )} + /> + + +
+ + + + + + ); }; diff --git a/apps/dokploy/components/dashboard/redis/delete-redis.tsx b/apps/dokploy/components/dashboard/redis/delete-redis.tsx index 62db598b4..af3a084c7 100644 --- a/apps/dokploy/components/dashboard/redis/delete-redis.tsx +++ b/apps/dokploy/components/dashboard/redis/delete-redis.tsx @@ -1,62 +1,138 @@ -import { - AlertDialog, - AlertDialogAction, - AlertDialogCancel, - AlertDialogContent, - AlertDialogDescription, - AlertDialogFooter, - AlertDialogHeader, - AlertDialogTitle, - AlertDialogTrigger, -} from "@/components/ui/alert-dialog"; import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; import { api } from "@/utils/api"; +import { zodResolver } from "@hookform/resolvers/zod"; import { TrashIcon } from "lucide-react"; import { useRouter } from "next/router"; +import { useState } from "react"; +import { useForm } from "react-hook-form"; import { toast } from "sonner"; +import { z } from "zod"; + +const deleteRedisSchema = z.object({ + projectName: z.string().min(1, { + message: "Database name is required", + }), +}); + +type DeleteRedis = z.infer; interface Props { redisId: string; } export const DeleteRedis = ({ redisId }: Props) => { + const [isOpen, setIsOpen] = useState(false); const { mutateAsync, isLoading } = api.redis.remove.useMutation(); + const { data } = api.redis.one.useQuery({ redisId }, { enabled: !!redisId }); const { push } = useRouter(); + const form = useForm({ + defaultValues: { + projectName: "", + }, + resolver: zodResolver(deleteRedisSchema), + }); + + const onSubmit = async (formData: DeleteRedis) => { + const expectedName = `${data?.name}/${data?.appName}`; + if (formData.projectName === expectedName) { + await mutateAsync({ redisId }) + .then((data) => { + push(`/dashboard/project/${data?.projectId}`); + toast.success("Database deleted successfully"); + setIsOpen(false); + }) + .catch(() => { + toast.error("Error deleting the database"); + }); + } else { + form.setError("projectName", { + message: "Database name does not match", + }); + } + }; + return ( - - + + - - - - Are you absolutely sure? - + + + + Are you absolutely sure? + This action cannot be undone. This will permanently delete the - database - - - - Cancel - { - await mutateAsync({ - redisId, - }) - .then((data) => { - push(`/dashboard/project/${data?.projectId}`); - toast.success("Database delete succesfully"); - }) - .catch(() => { - toast.error("Error to delete the database"); - }); + database. If you are sure please enter the database name to delete + this database. + + +
+
+ + ( + + + To confirm, type "{data?.name}/{data?.appName}" in the box + below + + + + + + + )} + /> + + +
+ + + + + + ); };