diff --git a/apps/dokploy/__test__/drop/drop.test.test.ts b/apps/dokploy/__test__/drop/drop.test.test.ts index f5b68c1df..74c803655 100644 --- a/apps/dokploy/__test__/drop/drop.test.test.ts +++ b/apps/dokploy/__test__/drop/drop.test.test.ts @@ -27,6 +27,7 @@ if (typeof window === "undefined") { const baseApp: ApplicationNested = { applicationId: "", herokuVersion: "", + cleanCache: false, watchPaths: [], applicationStatus: "done", appName: "", diff --git a/apps/dokploy/__test__/traefik/traefik.test.ts b/apps/dokploy/__test__/traefik/traefik.test.ts index a64103ff3..74b0e265b 100644 --- a/apps/dokploy/__test__/traefik/traefik.test.ts +++ b/apps/dokploy/__test__/traefik/traefik.test.ts @@ -7,6 +7,7 @@ import { expect, test } from "vitest"; const baseApp: ApplicationNested = { applicationId: "", herokuVersion: "", + cleanCache: false, applicationStatus: "done", appName: "", autoDeploy: true, diff --git a/apps/dokploy/components/dashboard/application/general/show.tsx b/apps/dokploy/components/dashboard/application/general/show.tsx index 2f0662805..01b38bd3f 100644 --- a/apps/dokploy/components/dashboard/application/general/show.tsx +++ b/apps/dokploy/components/dashboard/application/general/show.tsx @@ -10,14 +10,14 @@ import { TooltipProvider, TooltipTrigger, } from "@/components/ui/tooltip"; -import * as TooltipPrimitive from "@radix-ui/react-tooltip"; import { api } from "@/utils/api"; +import * as TooltipPrimitive from "@radix-ui/react-tooltip"; import { Ban, CheckCircle2, Hammer, - HelpCircle, RefreshCcw, + Rocket, Terminal, } from "lucide-react"; import { useRouter } from "next/router"; @@ -55,7 +55,7 @@ export const ShowGeneralApplication = ({ applicationId }: Props) => { Deploy Settings - + { }); }} > - + + + + + + +

+ Downloads the source code and performs a complete build +

+
+
+
{ }); }} > - + + + + + + +

Reload the application without rebuilding it

+
+
+
{ }); }} > - + + + + + + +

+ Only rebuilds the application without downloading new code +

+
+
+
{data?.applicationStatus === "idle" ? ( @@ -177,27 +188,26 @@ export const ShowGeneralApplication = ({ applicationId }: Props) => { }); }} > - + + + + + + +

+ Start the application (requires a previous successful + build) +

+
+
+
) : ( { }); }} > - + + + + + + +

Stop the currently running application

+
+
+
)}
@@ -241,15 +250,18 @@ export const ShowGeneralApplication = ({ applicationId }: Props) => { appName={data?.appName || ""} serverId={data?.serverId || ""} > -
Autodeploy { await update({ @@ -264,14 +276,14 @@ export const ShowGeneralApplication = ({ applicationId }: Props) => { toast.error("Error updating Auto Deploy"); }); }} - className="flex flex-row gap-2 items-center" + className="flex flex-row gap-2 items-center data-[state=checked]:bg-primary" />
Clean Cache { await update({ @@ -286,7 +298,7 @@ export const ShowGeneralApplication = ({ applicationId }: Props) => { toast.error("Error updating Clean Cache"); }); }} - className="flex flex-row gap-2 items-center" + className="flex flex-row gap-2 items-center data-[state=checked]:bg-primary" />
diff --git a/apps/dokploy/components/dashboard/compose/general/actions.tsx b/apps/dokploy/components/dashboard/compose/general/actions.tsx index f77619204..fc5daec78 100644 --- a/apps/dokploy/components/dashboard/compose/general/actions.tsx +++ b/apps/dokploy/components/dashboard/compose/general/actions.tsx @@ -7,9 +7,9 @@ import { TooltipProvider, TooltipTrigger, } from "@/components/ui/tooltip"; -import * as TooltipPrimitive from "@radix-ui/react-tooltip"; import { api } from "@/utils/api"; -import { Ban, CheckCircle2, Hammer, HelpCircle, Terminal } from "lucide-react"; +import * as TooltipPrimitive from "@radix-ui/react-tooltip"; +import { Ban, CheckCircle2, RefreshCcw, Rocket, Terminal } from "lucide-react"; import { useRouter } from "next/router"; import { toast } from "sonner"; import { DockerTerminalModal } from "../../settings/web-server/docker-terminal-modal"; @@ -34,7 +34,7 @@ export const ComposeActions = ({ composeId }: Props) => { api.compose.stop.useMutation(); return (
- + { }); }} > - + + + + + + +

Downloads the source code and performs a complete build

+
+
+
{ await redeploy({ composeId: composeId, }) .then(() => { - toast.success("Compose rebuilt successfully"); + toast.success("Compose reloaded successfully"); refetch(); }) .catch(() => { - toast.error("Error rebuilding compose"); + toast.error("Error reloading compose"); }); }} > - + + + + + + +

Reload the compose without rebuilding it

+
+
+
{data?.composeType === "docker-compose" && data?.composeStatus === "idle" ? ( @@ -128,26 +127,25 @@ export const ComposeActions = ({ composeId }: Props) => { }); }} > - + + + + + + +

+ Start the compose (requires a previous successful build) +

+
+
+
) : ( { }); }} > - + + + + + + +

Stop the currently running compose

+
+
+
)}
@@ -191,15 +188,18 @@ export const ComposeActions = ({ composeId }: Props) => { appName={data?.appName || ""} serverId={data?.serverId || ""} > -
Autodeploy { await update({ @@ -214,7 +214,7 @@ export const ComposeActions = ({ composeId }: Props) => { toast.error("Error updating Auto Deploy"); }); }} - className="flex flex-row gap-2 items-center" + className="flex flex-row gap-2 items-center data-[state=checked]:bg-primary" />
diff --git a/apps/dokploy/components/dashboard/database/backups/restore-backup.tsx b/apps/dokploy/components/dashboard/database/backups/restore-backup.tsx new file mode 100644 index 000000000..c761fc701 --- /dev/null +++ b/apps/dokploy/components/dashboard/database/backups/restore-backup.tsx @@ -0,0 +1,367 @@ +import { Button } from "@/components/ui/button"; +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, +} from "@/components/ui/command"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover"; +import { ScrollArea } from "@/components/ui/scroll-area"; +import { cn } from "@/lib/utils"; +import { api } from "@/utils/api"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { CheckIcon, ChevronsUpDown, Copy, RotateCcw } from "lucide-react"; +import { useState } from "react"; +import { useForm } from "react-hook-form"; +import { z } from "zod"; +import type { ServiceType } from "../../application/advanced/show-resources"; +import { debounce } from "lodash"; +import { Input } from "@/components/ui/input"; +import { type LogLine, parseLogs } from "../../docker/logs/utils"; +import { DrawerLogs } from "@/components/shared/drawer-logs"; +import { Badge } from "@/components/ui/badge"; +import copy from "copy-to-clipboard"; +import { toast } from "sonner"; + +interface Props { + databaseId: string; + databaseType: Exclude; +} + +const RestoreBackupSchema = z.object({ + destinationId: z + .string({ + required_error: "Please select a destination", + }) + .min(1, { + message: "Destination is required", + }), + backupFile: z + .string({ + required_error: "Please select a backup file", + }) + .min(1, { + message: "Backup file is required", + }), + databaseName: z + .string({ + required_error: "Please enter a database name", + }) + .min(1, { + message: "Database name is required", + }), +}); + +type RestoreBackup = z.infer; + +export const RestoreBackup = ({ databaseId, databaseType }: Props) => { + const [isOpen, setIsOpen] = useState(false); + const [search, setSearch] = useState(""); + + const { data: destinations = [] } = api.destination.all.useQuery(); + + const form = useForm({ + defaultValues: { + destinationId: "", + backupFile: "", + databaseName: "", + }, + resolver: zodResolver(RestoreBackupSchema), + }); + + const destionationId = form.watch("destinationId"); + + const debouncedSetSearch = debounce((value: string) => { + setSearch(value); + }, 300); + + const { data: files = [], isLoading } = api.backup.listBackupFiles.useQuery( + { + destinationId: destionationId, + search, + }, + { + enabled: isOpen && !!destionationId, + }, + ); + + const [isDrawerOpen, setIsDrawerOpen] = useState(false); + const [filteredLogs, setFilteredLogs] = useState([]); + const [isDeploying, setIsDeploying] = useState(false); + + // const { mutateAsync: restore, isLoading: isRestoring } = + // api.backup.restoreBackup.useMutation(); + + api.backup.restoreBackupWithLogs.useSubscription( + { + databaseId, + databaseType, + databaseName: form.watch("databaseName"), + backupFile: form.watch("backupFile"), + destinationId: form.watch("destinationId"), + }, + { + enabled: isDeploying, + onData(log) { + if (!isDrawerOpen) { + setIsDrawerOpen(true); + } + + if (log === "Restore completed successfully!") { + setIsDeploying(false); + } + const parsedLogs = parseLogs(log); + setFilteredLogs((prev) => [...prev, ...parsedLogs]); + }, + onError(error) { + console.error("Restore logs error:", error); + setIsDeploying(false); + }, + }, + ); + + const onSubmit = async (_data: RestoreBackup) => { + setIsDeploying(true); + }; + + return ( + + + + + + + + + Restore Backup + + + Select a destination and search for backup files + + + +
+ + ( + + Destination + + + + + + + + + + No destinations found. + + + {destinations.map((destination) => ( + { + form.setValue( + "destinationId", + destination.destinationId, + ); + }} + > + {destination.name} + + + ))} + + + + + + + + )} + /> + + ( + + + Search Backup Files + {field.value && ( + + {field.value} + { + e.stopPropagation(); + e.preventDefault(); + copy(field.value); + toast.success("Backup file copied to clipboard"); + }} + /> + + )} + + + + + + + + + + + {isLoading ? ( +
+ Loading backup files... +
+ ) : files.length === 0 && search ? ( +
+ No backup files found for "{search}" +
+ ) : files.length === 0 ? ( +
+ No backup files available +
+ ) : ( + + + {files.map((file) => ( + { + form.setValue("backupFile", file); + }} + > + {file} + + + ))} + + + )} +
+
+
+ +
+ )} + /> + ( + + Database Name + + + + + + )} + /> + + + + + + + { + setIsDrawerOpen(false); + setFilteredLogs([]); + setIsDeploying(false); + // refetch(); + }} + filteredLogs={filteredLogs} + /> +
+
+ ); +}; diff --git a/apps/dokploy/components/dashboard/database/backups/show-backups.tsx b/apps/dokploy/components/dashboard/database/backups/show-backups.tsx index b46065834..402cce955 100644 --- a/apps/dokploy/components/dashboard/database/backups/show-backups.tsx +++ b/apps/dokploy/components/dashboard/database/backups/show-backups.tsx @@ -20,6 +20,7 @@ import { toast } from "sonner"; import type { ServiceType } from "../../application/advanced/show-resources"; import { AddBackup } from "./add-backup"; import { UpdateBackup } from "./update-backup"; +import { RestoreBackup } from "./restore-backup"; import { useState } from "react"; interface Props { @@ -27,7 +28,9 @@ interface Props { type: Exclude; } export const ShowBackups = ({ id, type }: Props) => { - const [activeManualBackup, setActiveManualBackup] = useState(); + const [activeManualBackup, setActiveManualBackup] = useState< + string | undefined + >(); const queryMap = { postgres: () => api.postgres.one.useQuery({ postgresId: id }, { enabled: !!id }), @@ -69,7 +72,10 @@ export const ShowBackups = ({ id, type }: Props) => { {postgres && postgres?.backups?.length > 0 && ( - +
+ + +
)} @@ -96,11 +102,14 @@ export const ShowBackups = ({ id, type }: Props) => { No backups configured - +
+ + +
) : (
@@ -142,7 +151,7 @@ export const ShowBackups = ({ id, type }: Props) => {
Keep Latest - {backup.keepLatestCount || 'All'} + {backup.keepLatestCount || "All"}
@@ -153,7 +162,10 @@ export const ShowBackups = ({ id, type }: Props) => { - - - -
- - - - ); +
+ +
+ + + + + + + ); }; diff --git a/apps/dokploy/components/dashboard/mariadb/general/show-general-mariadb.tsx b/apps/dokploy/components/dashboard/mariadb/general/show-general-mariadb.tsx index f4489ca2d..c222b2565 100644 --- a/apps/dokploy/components/dashboard/mariadb/general/show-general-mariadb.tsx +++ b/apps/dokploy/components/dashboard/mariadb/general/show-general-mariadb.tsx @@ -8,242 +8,245 @@ import { TooltipProvider, TooltipTrigger, } from "@/components/ui/tooltip"; -import * as TooltipPrimitive from "@radix-ui/react-tooltip"; import { api } from "@/utils/api"; -import { - Ban, - CheckCircle2, - HelpCircle, - RefreshCcw, - Terminal, -} from "lucide-react"; +import * as TooltipPrimitive from "@radix-ui/react-tooltip"; +import { Ban, CheckCircle2, RefreshCcw, Rocket, Terminal } from "lucide-react"; import { useState } from "react"; import { toast } from "sonner"; import { type LogLine, parseLogs } from "../../docker/logs/utils"; import { DockerTerminalModal } from "../../settings/web-server/docker-terminal-modal"; interface Props { - mariadbId: string; + mariadbId: string; } export const ShowGeneralMariadb = ({ mariadbId }: Props) => { - const { data, refetch } = api.mariadb.one.useQuery( - { - mariadbId, - }, - { enabled: !!mariadbId }, - ); + const { data, refetch } = api.mariadb.one.useQuery( + { + mariadbId, + }, + { enabled: !!mariadbId } + ); - const { mutateAsync: reload, isLoading: isReloading } = - api.mariadb.reload.useMutation(); + const { mutateAsync: reload, isLoading: isReloading } = + api.mariadb.reload.useMutation(); - const { mutateAsync: start, isLoading: isStarting } = - api.mariadb.start.useMutation(); + const { mutateAsync: start, isLoading: isStarting } = + api.mariadb.start.useMutation(); - const { mutateAsync: stop, isLoading: isStopping } = - api.mariadb.stop.useMutation(); + const { mutateAsync: stop, isLoading: isStopping } = + api.mariadb.stop.useMutation(); - const [isDrawerOpen, setIsDrawerOpen] = useState(false); - const [filteredLogs, setFilteredLogs] = useState([]); - const [isDeploying, setIsDeploying] = useState(false); - api.mariadb.deployWithLogs.useSubscription( - { - mariadbId: mariadbId, - }, - { - enabled: isDeploying, - onData(log) { - if (!isDrawerOpen) { - setIsDrawerOpen(true); - } + const [isDrawerOpen, setIsDrawerOpen] = useState(false); + const [filteredLogs, setFilteredLogs] = useState([]); + const [isDeploying, setIsDeploying] = useState(false); + api.mariadb.deployWithLogs.useSubscription( + { + mariadbId: mariadbId, + }, + { + enabled: isDeploying, + onData(log) { + if (!isDrawerOpen) { + setIsDrawerOpen(true); + } - if (log === "Deployment completed successfully!") { - setIsDeploying(false); - } - const parsedLogs = parseLogs(log); - setFilteredLogs((prev) => [...prev, ...parsedLogs]); - }, - onError(error) { - console.error("Deployment logs error:", error); - setIsDeploying(false); - }, - }, - ); + if (log === "Deployment completed successfully!") { + setIsDeploying(false); + } + const parsedLogs = parseLogs(log); + setFilteredLogs((prev) => [...prev, ...parsedLogs]); + }, + onError(error) { + console.error("Deployment logs error:", error); + setIsDeploying(false); + }, + } + ); - return ( - <> -
- - - Deploy Settings - - - - { - setIsDeploying(true); - await new Promise((resolve) => setTimeout(resolve, 1000)); - refetch(); - }} - > - - - { - await reload({ - mariadbId: mariadbId, - appName: data?.appName || "", - }) - .then(() => { - toast.success("Mariadb reloaded successfully"); - refetch(); - }) - .catch(() => { - toast.error("Error reloading Mariadb"); - }); - }} - > - - - {data?.applicationStatus === "idle" ? ( - { - await start({ - mariadbId: mariadbId, - }) - .then(() => { - toast.success("Mariadb started successfully"); - refetch(); - }) - .catch(() => { - toast.error("Error starting Mariadb"); - }); - }} - > - - - ) : ( - { - await stop({ - mariadbId: mariadbId, - }) - .then(() => { - toast.success("Mariadb stopped successfully"); - refetch(); - }) - .catch(() => { - toast.error("Error stopping Mariadb"); - }); - }} - > - - - )} - - - - - - - { - setIsDrawerOpen(false); - setFilteredLogs([]); - setIsDeploying(false); - refetch(); - }} - filteredLogs={filteredLogs} - /> -
- - ); + return ( + <> +
+ + + Deploy Settings + + + + { + setIsDeploying(true); + await new Promise((resolve) => setTimeout(resolve, 1000)); + refetch(); + }} + > + + + + + + +

Downloads and sets up the MariaDB database

+
+
+
+
+ { + await reload({ + mariadbId: mariadbId, + appName: data?.appName || "", + }) + .then(() => { + toast.success("Mariadb reloaded successfully"); + refetch(); + }) + .catch(() => { + toast.error("Error reloading Mariadb"); + }); + }} + > + + + + + + +

Restart the MariaDB service without rebuilding

+
+
+
+
+ {data?.applicationStatus === "idle" ? ( + { + await start({ + mariadbId: mariadbId, + }) + .then(() => { + toast.success("Mariadb started successfully"); + refetch(); + }) + .catch(() => { + toast.error("Error starting Mariadb"); + }); + }} + > + + + + + + +

+ Start the MariaDB database (requires a previous + successful setup) +

+
+
+
+
+ ) : ( + { + await stop({ + mariadbId: mariadbId, + }) + .then(() => { + toast.success("Mariadb stopped successfully"); + refetch(); + }) + .catch(() => { + toast.error("Error stopping Mariadb"); + }); + }} + > + + + + + + +

Stop the currently running MariaDB database

+
+
+
+
+ )} +
+ + + + + + + +

Open a terminal to the MariaDB container

+
+
+
+
+
+
+ { + setIsDrawerOpen(false); + setFilteredLogs([]); + setIsDeploying(false); + refetch(); + }} + filteredLogs={filteredLogs} + /> +
+ + ); }; diff --git a/apps/dokploy/components/dashboard/mongo/general/show-external-mongo-credentials.tsx b/apps/dokploy/components/dashboard/mongo/general/show-external-mongo-credentials.tsx index b845004fa..b5ed9f863 100644 --- a/apps/dokploy/components/dashboard/mongo/general/show-external-mongo-credentials.tsx +++ b/apps/dokploy/components/dashboard/mongo/general/show-external-mongo-credentials.tsx @@ -1,6 +1,6 @@ +import { AlertBlock } from "@/components/shared/alert-block"; import { ToggleVisibilityInput } from "@/components/shared/toggle-visibility-input"; import { Button } from "@/components/ui/button"; -import { AlertBlock } from "@/components/shared/alert-block"; import { Card, CardContent, @@ -20,152 +20,151 @@ import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; +import Link from "next/link"; import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; -import Link from "next/link"; const DockerProviderSchema = z.object({ - externalPort: z.preprocess((a) => { - if (a !== null) { - const parsed = Number.parseInt(z.string().parse(a), 10); - return Number.isNaN(parsed) ? null : parsed; - } - return null; - }, z - .number() - .gte(0, "Range must be 0 - 65535") - .lte(65535, "Range must be 0 - 65535") - .nullable()), + externalPort: z.preprocess((a) => { + if (a !== null) { + const parsed = Number.parseInt(z.string().parse(a), 10); + return Number.isNaN(parsed) ? null : parsed; + } + return null; + }, z.number().gte(0, "Range must be 0 - 65535").lte(65535, "Range must be 0 - 65535").nullable()), }); type DockerProvider = z.infer; interface Props { - mongoId: string; + mongoId: string; } export const ShowExternalMongoCredentials = ({ mongoId }: Props) => { - const { data: ip } = api.settings.getIp.useQuery(); - const { data, refetch } = api.mongo.one.useQuery({ mongoId }); - const { mutateAsync, isLoading } = api.mongo.saveExternalPort.useMutation(); - const [connectionUrl, setConnectionUrl] = useState(""); - const getIp = data?.server?.ipAddress || ip; - const form = useForm({ - defaultValues: {}, - resolver: zodResolver(DockerProviderSchema), - }); + const { data: ip } = api.settings.getIp.useQuery(); + const { data, refetch } = api.mongo.one.useQuery({ mongoId }); + const { mutateAsync, isLoading } = api.mongo.saveExternalPort.useMutation(); + const [connectionUrl, setConnectionUrl] = useState(""); + const getIp = data?.server?.ipAddress || ip; + const form = useForm({ + defaultValues: {}, + resolver: zodResolver(DockerProviderSchema), + }); - useEffect(() => { - if (data?.externalPort) { - form.reset({ - externalPort: data.externalPort, - }); - } - }, [form.reset, data, form]); + useEffect(() => { + if (data?.externalPort) { + form.reset({ + externalPort: data.externalPort, + }); + } + }, [form.reset, data, form]); - const onSubmit = async (values: DockerProvider) => { - await mutateAsync({ - externalPort: values.externalPort, - mongoId, - }) - .then(async () => { - toast.success("External Port updated"); - await refetch(); - }) - .catch(() => { - toast.error("Error saving the external port"); - }); - }; + const onSubmit = async (values: DockerProvider) => { + await mutateAsync({ + externalPort: values.externalPort, + mongoId, + }) + .then(async () => { + toast.success("External Port updated"); + await refetch(); + }) + .catch(() => { + toast.error("Error saving the external port"); + }); + }; - useEffect(() => { - const buildConnectionUrl = () => { - const port = form.watch("externalPort") || data?.externalPort; + useEffect(() => { + const buildConnectionUrl = () => { + const port = form.watch("externalPort") || data?.externalPort; - return `mongodb://${data?.databaseUser}:${data?.databasePassword}@${getIp}:${port}`; - }; + return `mongodb://${data?.databaseUser}:${data?.databasePassword}@${getIp}:${port}`; + }; - setConnectionUrl(buildConnectionUrl()); - }, [ - data?.appName, - data?.externalPort, - data?.databasePassword, - form, - data?.databaseUser, - getIp, - ]); + setConnectionUrl(buildConnectionUrl()); + }, [ + data?.appName, + data?.externalPort, + data?.databasePassword, + form, + data?.databaseUser, + getIp, + ]); - return ( - <> -
- - - External Credentials - - In order to make the database reachable trought internet is - required to set a port, make sure the port is not used by another - application or database - - - - {!getIp && ( - - You need to set an IP address in your{" "} - - {data?.serverId - ? "Remote Servers -> Server -> Edit Server -> Update IP Address" - : "Web Server -> Server -> Update Server IP"} - {" "} - to fix the database url connection. - - )} -
- -
-
- { - return ( - - External Port (Internet) - - - - - - ); - }} - /> -
-
- {!!data?.externalPort && ( -
-
- - -
-
- )} + return ( + <> +
+ + + External Credentials + + In order to make the database reachable trought internet is + required to set a port, make sure the port is not used by another + application or database + + + + {!getIp && ( + + You need to set an IP address in your{" "} + + {data?.serverId + ? "Remote Servers -> Server -> Edit Server -> Update IP Address" + : "Web Server -> Server -> Update Server IP"} + {" "} + to fix the database url connection. + + )} + + +
+
+ { + return ( + + External Port (Internet) + + + + + + ); + }} + /> +
+
+ {!!data?.externalPort && ( +
+
+ + +
+
+ )} -
- -
- - -
-
-
- - ); +
+ +
+ + +
+
+
+ + ); }; diff --git a/apps/dokploy/components/dashboard/mongo/general/show-general-mongo.tsx b/apps/dokploy/components/dashboard/mongo/general/show-general-mongo.tsx index 644d1a266..dfbf501eb 100644 --- a/apps/dokploy/components/dashboard/mongo/general/show-general-mongo.tsx +++ b/apps/dokploy/components/dashboard/mongo/general/show-general-mongo.tsx @@ -3,246 +3,249 @@ import { DrawerLogs } from "@/components/shared/drawer-logs"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { - Tooltip, - TooltipContent, - TooltipProvider, - TooltipTrigger, + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, } from "@/components/ui/tooltip"; -import * as TooltipPrimitive from "@radix-ui/react-tooltip"; import { api } from "@/utils/api"; -import { - Ban, - CheckCircle2, - HelpCircle, - RefreshCcw, - Terminal, -} from "lucide-react"; +import * as TooltipPrimitive from "@radix-ui/react-tooltip"; +import { Ban, CheckCircle2, RefreshCcw, Rocket, Terminal } from "lucide-react"; import { useState } from "react"; import { toast } from "sonner"; import { type LogLine, parseLogs } from "../../docker/logs/utils"; import { DockerTerminalModal } from "../../settings/web-server/docker-terminal-modal"; interface Props { - mongoId: string; + mongoId: string; } export const ShowGeneralMongo = ({ mongoId }: Props) => { - const { data, refetch } = api.mongo.one.useQuery( - { - mongoId, - }, - { enabled: !!mongoId }, - ); + const { data, refetch } = api.mongo.one.useQuery( + { + mongoId, + }, + { enabled: !!mongoId } + ); - const { mutateAsync: reload, isLoading: isReloading } = - api.mongo.reload.useMutation(); + const { mutateAsync: reload, isLoading: isReloading } = + api.mongo.reload.useMutation(); - const { mutateAsync: start, isLoading: isStarting } = - api.mongo.start.useMutation(); + const { mutateAsync: start, isLoading: isStarting } = + api.mongo.start.useMutation(); - const { mutateAsync: stop, isLoading: isStopping } = - api.mongo.stop.useMutation(); + const { mutateAsync: stop, isLoading: isStopping } = + api.mongo.stop.useMutation(); - const [isDrawerOpen, setIsDrawerOpen] = useState(false); - const [filteredLogs, setFilteredLogs] = useState([]); - const [isDeploying, setIsDeploying] = useState(false); - api.mongo.deployWithLogs.useSubscription( - { - mongoId: mongoId, - }, - { - enabled: isDeploying, - onData(log) { - if (!isDrawerOpen) { - setIsDrawerOpen(true); - } + const [isDrawerOpen, setIsDrawerOpen] = useState(false); + const [filteredLogs, setFilteredLogs] = useState([]); + const [isDeploying, setIsDeploying] = useState(false); + api.mongo.deployWithLogs.useSubscription( + { + mongoId: mongoId, + }, + { + enabled: isDeploying, + onData(log) { + if (!isDrawerOpen) { + setIsDrawerOpen(true); + } - if (log === "Deployment completed successfully!") { - setIsDeploying(false); - } + if (log === "Deployment completed successfully!") { + setIsDeploying(false); + } - const parsedLogs = parseLogs(log); - setFilteredLogs((prev) => [...prev, ...parsedLogs]); - }, - onError(error) { - console.error("Deployment logs error:", error); - setIsDeploying(false); - }, - }, - ); - return ( - <> -
- - - Deploy Settings - - - - { - setIsDeploying(true); - await new Promise((resolve) => setTimeout(resolve, 1000)); - refetch(); - }} - > - - - { - await reload({ - mongoId: mongoId, - appName: data?.appName || "", - }) - .then(() => { - toast.success("Mongo reloaded successfully"); - refetch(); - }) - .catch(() => { - toast.error("Error reloading Mongo"); - }); - }} - > - - - {data?.applicationStatus === "idle" ? ( - { - await start({ - mongoId: mongoId, - }) - .then(() => { - toast.success("Mongo started successfully"); - refetch(); - }) - .catch(() => { - toast.error("Error starting Mongo"); - }); - }} - > - - - ) : ( - { - await stop({ - mongoId: mongoId, - }) - .then(() => { - toast.success("Mongo stopped successfully"); - refetch(); - }) - .catch(() => { - toast.error("Error stopping Mongo"); - }); - }} - > - - - )} - - - - - - - { - setIsDrawerOpen(false); - setFilteredLogs([]); - setIsDeploying(false); - refetch(); - }} - filteredLogs={filteredLogs} - /> -
- - ); + const parsedLogs = parseLogs(log); + setFilteredLogs((prev) => [...prev, ...parsedLogs]); + }, + onError(error) { + console.error("Deployment logs error:", error); + setIsDeploying(false); + }, + } + ); + return ( + <> +
+ + + Deploy Settings + + + + { + setIsDeploying(true); + await new Promise((resolve) => setTimeout(resolve, 1000)); + refetch(); + }} + > + + + + + + +

Downloads and sets up the MongoDB database

+
+
+
+
+ { + await reload({ + mongoId: mongoId, + appName: data?.appName || "", + }) + .then(() => { + toast.success("Mongo reloaded successfully"); + refetch(); + }) + .catch(() => { + toast.error("Error reloading Mongo"); + }); + }} + > + + + + + + +

Restart the MongoDB service without rebuilding

+
+
+
+
+ {data?.applicationStatus === "idle" ? ( + { + await start({ + mongoId: mongoId, + }) + .then(() => { + toast.success("Mongo started successfully"); + refetch(); + }) + .catch(() => { + toast.error("Error starting Mongo"); + }); + }} + > + + + + + + +

+ Start the MongoDB database (requires a previous + successful setup) +

+
+
+
+
+ ) : ( + { + await stop({ + mongoId: mongoId, + }) + .then(() => { + toast.success("Mongo stopped successfully"); + refetch(); + }) + .catch(() => { + toast.error("Error stopping Mongo"); + }); + }} + > + + + + + + +

Stop the currently running MongoDB database

+
+
+
+
+ )} +
+ + + + + + + +

Open a terminal to the MongoDB container

+
+
+
+
+
+
+ { + setIsDrawerOpen(false); + setFilteredLogs([]); + setIsDeploying(false); + refetch(); + }} + filteredLogs={filteredLogs} + /> +
+ + ); }; diff --git a/apps/dokploy/components/dashboard/mysql/general/show-external-mysql-credentials.tsx b/apps/dokploy/components/dashboard/mysql/general/show-external-mysql-credentials.tsx index 12b3eb065..2c8ed5f5b 100644 --- a/apps/dokploy/components/dashboard/mysql/general/show-external-mysql-credentials.tsx +++ b/apps/dokploy/components/dashboard/mysql/general/show-external-mysql-credentials.tsx @@ -1,6 +1,6 @@ +import { AlertBlock } from "@/components/shared/alert-block"; import { ToggleVisibilityInput } from "@/components/shared/toggle-visibility-input"; import { Button } from "@/components/ui/button"; -import { AlertBlock } from "@/components/shared/alert-block"; import { Card, CardContent, @@ -20,152 +20,151 @@ import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; +import Link from "next/link"; import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; -import Link from "next/link"; const DockerProviderSchema = z.object({ - externalPort: z.preprocess((a) => { - if (a !== null) { - const parsed = Number.parseInt(z.string().parse(a), 10); - return Number.isNaN(parsed) ? null : parsed; - } - return null; - }, z - .number() - .gte(0, "Range must be 0 - 65535") - .lte(65535, "Range must be 0 - 65535") - .nullable()), + externalPort: z.preprocess((a) => { + if (a !== null) { + const parsed = Number.parseInt(z.string().parse(a), 10); + return Number.isNaN(parsed) ? null : parsed; + } + return null; + }, z.number().gte(0, "Range must be 0 - 65535").lte(65535, "Range must be 0 - 65535").nullable()), }); type DockerProvider = z.infer; interface Props { - mysqlId: string; + mysqlId: string; } export const ShowExternalMysqlCredentials = ({ mysqlId }: Props) => { - const { data: ip } = api.settings.getIp.useQuery(); - const { data, refetch } = api.mysql.one.useQuery({ mysqlId }); - const { mutateAsync, isLoading } = api.mysql.saveExternalPort.useMutation(); - const [connectionUrl, setConnectionUrl] = useState(""); - const getIp = data?.server?.ipAddress || ip; - const form = useForm({ - defaultValues: {}, - resolver: zodResolver(DockerProviderSchema), - }); + const { data: ip } = api.settings.getIp.useQuery(); + const { data, refetch } = api.mysql.one.useQuery({ mysqlId }); + const { mutateAsync, isLoading } = api.mysql.saveExternalPort.useMutation(); + const [connectionUrl, setConnectionUrl] = useState(""); + const getIp = data?.server?.ipAddress || ip; + const form = useForm({ + defaultValues: {}, + resolver: zodResolver(DockerProviderSchema), + }); - useEffect(() => { - if (data?.externalPort) { - form.reset({ - externalPort: data.externalPort, - }); - } - }, [form.reset, data, form]); + useEffect(() => { + if (data?.externalPort) { + form.reset({ + externalPort: data.externalPort, + }); + } + }, [form.reset, data, form]); - const onSubmit = async (values: DockerProvider) => { - await mutateAsync({ - externalPort: values.externalPort, - mysqlId, - }) - .then(async () => { - toast.success("External Port updated"); - await refetch(); - }) - .catch(() => { - toast.error("Error saving the external port"); - }); - }; + const onSubmit = async (values: DockerProvider) => { + await mutateAsync({ + externalPort: values.externalPort, + mysqlId, + }) + .then(async () => { + toast.success("External Port updated"); + await refetch(); + }) + .catch(() => { + toast.error("Error saving the external port"); + }); + }; - useEffect(() => { - const buildConnectionUrl = () => { - const port = form.watch("externalPort") || data?.externalPort; + useEffect(() => { + const buildConnectionUrl = () => { + const port = form.watch("externalPort") || data?.externalPort; - return `mysql://${data?.databaseUser}:${data?.databasePassword}@${getIp}:${port}/${data?.databaseName}`; - }; + return `mysql://${data?.databaseUser}:${data?.databasePassword}@${getIp}:${port}/${data?.databaseName}`; + }; - setConnectionUrl(buildConnectionUrl()); - }, [ - data?.appName, - data?.externalPort, - data?.databasePassword, - data?.databaseName, - data?.databaseUser, - form, - getIp, - ]); - return ( - <> -
- - - External Credentials - - In order to make the database reachable trought internet is - required to set a port, make sure the port is not used by another - application or database - - - - {!getIp && ( - - You need to set an IP address in your{" "} - - {data?.serverId - ? "Remote Servers -> Server -> Edit Server -> Update IP Address" - : "Web Server -> Server -> Update Server IP"} - {" "} - to fix the database url connection. - - )} -
- -
-
- { - return ( - - External Port (Internet) - - - - - - ); - }} - /> -
-
- {!!data?.externalPort && ( -
-
- - -
-
- )} + setConnectionUrl(buildConnectionUrl()); + }, [ + data?.appName, + data?.externalPort, + data?.databasePassword, + data?.databaseName, + data?.databaseUser, + form, + getIp, + ]); + return ( + <> +
+ + + External Credentials + + In order to make the database reachable trought internet is + required to set a port, make sure the port is not used by another + application or database + + + + {!getIp && ( + + You need to set an IP address in your{" "} + + {data?.serverId + ? "Remote Servers -> Server -> Edit Server -> Update IP Address" + : "Web Server -> Server -> Update Server IP"} + {" "} + to fix the database url connection. + + )} + + +
+
+ { + return ( + + External Port (Internet) + + + + + + ); + }} + /> +
+
+ {!!data?.externalPort && ( +
+
+ + +
+
+ )} -
- -
- - -
-
-
- - ); +
+ +
+ + +
+
+
+ + ); }; diff --git a/apps/dokploy/components/dashboard/mysql/general/show-general-mysql.tsx b/apps/dokploy/components/dashboard/mysql/general/show-general-mysql.tsx index 3c8ae3ea9..7002ff783 100644 --- a/apps/dokploy/components/dashboard/mysql/general/show-general-mysql.tsx +++ b/apps/dokploy/components/dashboard/mysql/general/show-general-mysql.tsx @@ -8,239 +8,242 @@ import { TooltipProvider, TooltipTrigger, } from "@/components/ui/tooltip"; -import * as TooltipPrimitive from "@radix-ui/react-tooltip"; import { api } from "@/utils/api"; -import { - Ban, - CheckCircle2, - HelpCircle, - RefreshCcw, - Terminal, -} from "lucide-react"; +import * as TooltipPrimitive from "@radix-ui/react-tooltip"; +import { Ban, CheckCircle2, RefreshCcw, Rocket, Terminal } from "lucide-react"; import { useState } from "react"; import { toast } from "sonner"; import { type LogLine, parseLogs } from "../../docker/logs/utils"; import { DockerTerminalModal } from "../../settings/web-server/docker-terminal-modal"; interface Props { - mysqlId: string; + mysqlId: string; } export const ShowGeneralMysql = ({ mysqlId }: Props) => { - const { data, refetch } = api.mysql.one.useQuery( - { - mysqlId, - }, - { enabled: !!mysqlId }, - ); + const { data, refetch } = api.mysql.one.useQuery( + { + mysqlId, + }, + { enabled: !!mysqlId } + ); - const { mutateAsync: reload, isLoading: isReloading } = - api.mysql.reload.useMutation(); - const { mutateAsync: start, isLoading: isStarting } = - api.mysql.start.useMutation(); + const { mutateAsync: reload, isLoading: isReloading } = + api.mysql.reload.useMutation(); + const { mutateAsync: start, isLoading: isStarting } = + api.mysql.start.useMutation(); - const { mutateAsync: stop, isLoading: isStopping } = - api.mysql.stop.useMutation(); + const { mutateAsync: stop, isLoading: isStopping } = + api.mysql.stop.useMutation(); - const [isDrawerOpen, setIsDrawerOpen] = useState(false); - const [filteredLogs, setFilteredLogs] = useState([]); - const [isDeploying, setIsDeploying] = useState(false); - api.mysql.deployWithLogs.useSubscription( - { - mysqlId: mysqlId, - }, - { - enabled: isDeploying, - onData(log) { - if (!isDrawerOpen) { - setIsDrawerOpen(true); - } + const [isDrawerOpen, setIsDrawerOpen] = useState(false); + const [filteredLogs, setFilteredLogs] = useState([]); + const [isDeploying, setIsDeploying] = useState(false); + api.mysql.deployWithLogs.useSubscription( + { + mysqlId: mysqlId, + }, + { + enabled: isDeploying, + onData(log) { + if (!isDrawerOpen) { + setIsDrawerOpen(true); + } - if (log === "Deployment completed successfully!") { - setIsDeploying(false); - } - const parsedLogs = parseLogs(log); - setFilteredLogs((prev) => [...prev, ...parsedLogs]); - }, - onError(error) { - console.error("Deployment logs error:", error); - setIsDeploying(false); - }, - }, - ); - return ( - <> -
- - - Deploy Settings - - - - { - setIsDeploying(true); - await new Promise((resolve) => setTimeout(resolve, 1000)); - refetch(); - }} - > - - - { - await reload({ - mysqlId: mysqlId, - appName: data?.appName || "", - }) - .then(() => { - toast.success("Mysql reloaded successfully"); - refetch(); - }) - .catch(() => { - toast.error("Error reloading Mysql"); - }); - }} - > - - - {data?.applicationStatus === "idle" ? ( - { - await start({ - mysqlId: mysqlId, - }) - .then(() => { - toast.success("Mysql started successfully"); - refetch(); - }) - .catch(() => { - toast.error("Error starting Mysql"); - }); - }} - > - - - ) : ( - { - await stop({ - mysqlId: mysqlId, - }) - .then(() => { - toast.success("Mysql stopped successfully"); - refetch(); - }) - .catch(() => { - toast.error("Error stopping Mysql"); - }); - }} - > - - - )} - - - - - - - { - setIsDrawerOpen(false); - setFilteredLogs([]); - setIsDeploying(false); - refetch(); - }} - filteredLogs={filteredLogs} - /> -
- - ); + if (log === "Deployment completed successfully!") { + setIsDeploying(false); + } + const parsedLogs = parseLogs(log); + setFilteredLogs((prev) => [...prev, ...parsedLogs]); + }, + onError(error) { + console.error("Deployment logs error:", error); + setIsDeploying(false); + }, + } + ); + return ( + <> +
+ + + Deploy Settings + + + + { + setIsDeploying(true); + await new Promise((resolve) => setTimeout(resolve, 1000)); + refetch(); + }} + > + + + + + + +

Downloads and sets up the MySQL database

+
+
+
+
+ { + await reload({ + mysqlId: mysqlId, + appName: data?.appName || "", + }) + .then(() => { + toast.success("Mysql reloaded successfully"); + refetch(); + }) + .catch(() => { + toast.error("Error reloading Mysql"); + }); + }} + > + + + + + + +

Restart the MySQL service without rebuilding

+
+
+
+
+ {data?.applicationStatus === "idle" ? ( + { + await start({ + mysqlId: mysqlId, + }) + .then(() => { + toast.success("Mysql started successfully"); + refetch(); + }) + .catch(() => { + toast.error("Error starting Mysql"); + }); + }} + > + + + + + + +

+ Start the MySQL database (requires a previous + successful setup) +

+
+
+
+
+ ) : ( + { + await stop({ + mysqlId: mysqlId, + }) + .then(() => { + toast.success("Mysql stopped successfully"); + refetch(); + }) + .catch(() => { + toast.error("Error stopping Mysql"); + }); + }} + > + + + + + + +

Stop the currently running MySQL database

+
+
+
+
+ )} +
+ + + + + + + +

Open a terminal to the MySQL container

+
+
+
+
+
+
+ { + setIsDrawerOpen(false); + setFilteredLogs([]); + setIsDeploying(false); + refetch(); + }} + filteredLogs={filteredLogs} + /> +
+ + ); }; diff --git a/apps/dokploy/components/dashboard/postgres/general/show-external-postgres-credentials.tsx b/apps/dokploy/components/dashboard/postgres/general/show-external-postgres-credentials.tsx index 7643be2d7..0c87a7bcd 100644 --- a/apps/dokploy/components/dashboard/postgres/general/show-external-postgres-credentials.tsx +++ b/apps/dokploy/components/dashboard/postgres/general/show-external-postgres-credentials.tsx @@ -1,6 +1,6 @@ +import { AlertBlock } from "@/components/shared/alert-block"; import { ToggleVisibilityInput } from "@/components/shared/toggle-visibility-input"; import { Button } from "@/components/ui/button"; -import { AlertBlock } from "@/components/shared/alert-block"; import { Card, CardContent, @@ -20,154 +20,153 @@ import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; +import Link from "next/link"; import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; -import Link from "next/link"; const DockerProviderSchema = z.object({ - externalPort: z.preprocess((a) => { - if (a !== null) { - const parsed = Number.parseInt(z.string().parse(a), 10); - return Number.isNaN(parsed) ? null : parsed; - } - return null; - }, z - .number() - .gte(0, "Range must be 0 - 65535") - .lte(65535, "Range must be 0 - 65535") - .nullable()), + externalPort: z.preprocess((a) => { + if (a !== null) { + const parsed = Number.parseInt(z.string().parse(a), 10); + return Number.isNaN(parsed) ? null : parsed; + } + return null; + }, z.number().gte(0, "Range must be 0 - 65535").lte(65535, "Range must be 0 - 65535").nullable()), }); type DockerProvider = z.infer; interface Props { - postgresId: string; + postgresId: string; } export const ShowExternalPostgresCredentials = ({ postgresId }: Props) => { - const { data: ip } = api.settings.getIp.useQuery(); - const { data, refetch } = api.postgres.one.useQuery({ postgresId }); - const { mutateAsync, isLoading } = - api.postgres.saveExternalPort.useMutation(); - const getIp = data?.server?.ipAddress || ip; - const [connectionUrl, setConnectionUrl] = useState(""); + const { data: ip } = api.settings.getIp.useQuery(); + const { data, refetch } = api.postgres.one.useQuery({ postgresId }); + const { mutateAsync, isLoading } = + api.postgres.saveExternalPort.useMutation(); + const getIp = data?.server?.ipAddress || ip; + const [connectionUrl, setConnectionUrl] = useState(""); - const form = useForm({ - defaultValues: {}, - resolver: zodResolver(DockerProviderSchema), - }); + const form = useForm({ + defaultValues: {}, + resolver: zodResolver(DockerProviderSchema), + }); - useEffect(() => { - if (data?.externalPort) { - form.reset({ - externalPort: data.externalPort, - }); - } - }, [form.reset, data, form]); + useEffect(() => { + if (data?.externalPort) { + form.reset({ + externalPort: data.externalPort, + }); + } + }, [form.reset, data, form]); - const onSubmit = async (values: DockerProvider) => { - await mutateAsync({ - externalPort: values.externalPort, - postgresId, - }) - .then(async () => { - toast.success("External Port updated"); - await refetch(); - }) - .catch(() => { - toast.error("Error saving the external port"); - }); - }; + const onSubmit = async (values: DockerProvider) => { + await mutateAsync({ + externalPort: values.externalPort, + postgresId, + }) + .then(async () => { + toast.success("External Port updated"); + await refetch(); + }) + .catch(() => { + toast.error("Error saving the external port"); + }); + }; - useEffect(() => { - const buildConnectionUrl = () => { - const port = form.watch("externalPort") || data?.externalPort; + useEffect(() => { + const buildConnectionUrl = () => { + const port = form.watch("externalPort") || data?.externalPort; - return `postgresql://${data?.databaseUser}:${data?.databasePassword}@${getIp}:${port}/${data?.databaseName}`; - }; + return `postgresql://${data?.databaseUser}:${data?.databasePassword}@${getIp}:${port}/${data?.databaseName}`; + }; - setConnectionUrl(buildConnectionUrl()); - }, [ - data?.appName, - data?.externalPort, - data?.databasePassword, - form, - data?.databaseName, - getIp, - ]); + setConnectionUrl(buildConnectionUrl()); + }, [ + data?.appName, + data?.externalPort, + data?.databasePassword, + form, + data?.databaseName, + getIp, + ]); - return ( - <> -
- - - External Credentials - - In order to make the database reachable trought internet is - required to set a port, make sure the port is not used by another - application or database - - - - {!getIp && ( - - You need to set an IP address in your{" "} - - {data?.serverId - ? "Remote Servers -> Server -> Edit Server -> Update IP Address" - : "Web Server -> Server -> Update Server IP"} - {" "} - to fix the database url connection. - - )} -
- -
-
- { - return ( - - External Port (Internet) - - - - - - ); - }} - /> -
-
- {!!data?.externalPort && ( -
-
- - -
-
- )} + return ( + <> +
+ + + External Credentials + + In order to make the database reachable trought internet is + required to set a port, make sure the port is not used by another + application or database + + + + {!getIp && ( + + You need to set an IP address in your{" "} + + {data?.serverId + ? "Remote Servers -> Server -> Edit Server -> Update IP Address" + : "Web Server -> Server -> Update Server IP"} + {" "} + to fix the database url connection. + + )} + + +
+
+ { + return ( + + External Port (Internet) + + + + + + ); + }} + /> +
+
+ {!!data?.externalPort && ( +
+
+ + +
+
+ )} -
- -
- - -
-
-
- - ); +
+ +
+ + +
+
+
+ + ); }; diff --git a/apps/dokploy/components/dashboard/postgres/general/show-general-postgres.tsx b/apps/dokploy/components/dashboard/postgres/general/show-general-postgres.tsx index a22155451..999bc8504 100644 --- a/apps/dokploy/components/dashboard/postgres/general/show-general-postgres.tsx +++ b/apps/dokploy/components/dashboard/postgres/general/show-general-postgres.tsx @@ -8,15 +8,9 @@ import { TooltipProvider, TooltipTrigger, } from "@/components/ui/tooltip"; -import * as TooltipPrimitive from "@radix-ui/react-tooltip"; import { api } from "@/utils/api"; -import { - Ban, - CheckCircle2, - HelpCircle, - RefreshCcw, - Terminal, -} from "lucide-react"; +import * as TooltipPrimitive from "@radix-ui/react-tooltip"; +import { Ban, CheckCircle2, RefreshCcw, Rocket, Terminal } from "lucide-react"; import { useState } from "react"; import { toast } from "sonner"; import { type LogLine, parseLogs } from "../../docker/logs/utils"; @@ -78,7 +72,7 @@ export const ShowGeneralPostgres = ({ postgresId }: Props) => { Deploy Settings - + { refetch(); }} > - + + + + + + +

Downloads and sets up the PostgreSQL database

+
+
+
{ }); }} > - + + + + + + +

Reload the PostgreSQL without rebuilding it

+
+
+
{data?.applicationStatus === "idle" ? ( { }); }} > - + + + + + + +

+ Start the PostgreSQL database (requires a previous + successful setup) +

+
+
+
) : ( { }); }} > - + + + + + + +

Stop the currently running PostgreSQL database

+
+
+
)}
@@ -226,8 +217,11 @@ export const ShowGeneralPostgres = ({ postgresId }: Props) => { appName={data?.appName || ""} serverId={data?.serverId || ""} > - diff --git a/apps/dokploy/components/dashboard/postgres/general/show-internal-postgres-credentials.tsx b/apps/dokploy/components/dashboard/postgres/general/show-internal-postgres-credentials.tsx index 545150f87..cff00a998 100644 --- a/apps/dokploy/components/dashboard/postgres/general/show-internal-postgres-credentials.tsx +++ b/apps/dokploy/components/dashboard/postgres/general/show-internal-postgres-credentials.tsx @@ -5,58 +5,58 @@ import { Label } from "@/components/ui/label"; import { api } from "@/utils/api"; interface Props { - postgresId: string; + postgresId: string; } export const ShowInternalPostgresCredentials = ({ postgresId }: Props) => { - const { data } = api.postgres.one.useQuery({ postgresId }); - return ( - <> -
- - - Internal Credentials - - -
-
- - -
-
- - -
-
- -
- -
-
-
- - -
+ const { data } = api.postgres.one.useQuery({ postgresId }); + return ( + <> +
+ + + Internal Credentials + + +
+
+ + +
+
+ + +
+
+ +
+ +
+
+
+ + +
-
- - -
+
+ + +
-
- - -
-
-
-
-
- - ); +
+ + +
+
+
+
+
+ + ); }; // ReplyError: MISCONF Redis is configured to save RDB snapshots, but it's currently unable to persist to disk. Commands that may modify the data set are disabled, because this instance is configured to report errors during writes if RDB snapshotting fails (stop-w diff --git a/apps/dokploy/components/dashboard/postgres/update-postgres.tsx b/apps/dokploy/components/dashboard/postgres/update-postgres.tsx index 2b8804a57..33ed7a60e 100644 --- a/apps/dokploy/components/dashboard/postgres/update-postgres.tsx +++ b/apps/dokploy/components/dashboard/postgres/update-postgres.tsx @@ -21,145 +21,146 @@ import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; -import { PenBoxIcon } from "lucide-react"; +import { PenBox } from "lucide-react"; import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; const updatePostgresSchema = z.object({ - name: z.string().min(1, { - message: "Name is required", - }), - description: z.string().optional(), + name: z.string().min(1, { + message: "Name is required", + }), + description: z.string().optional(), }); type UpdatePostgres = z.infer; interface Props { - postgresId: string; + postgresId: string; } export const UpdatePostgres = ({ postgresId }: Props) => { - const [isOpen, setIsOpen] = useState(false); - const utils = api.useUtils(); - const { mutateAsync, error, isError, isLoading } = - api.postgres.update.useMutation(); - const { data } = api.postgres.one.useQuery( - { - postgresId, - }, - { - enabled: !!postgresId, - }, - ); - const form = useForm({ - defaultValues: { - description: data?.description ?? "", - name: data?.name ?? "", - }, - resolver: zodResolver(updatePostgresSchema), - }); - useEffect(() => { - if (data) { - form.reset({ - description: data.description ?? "", - name: data.name, - }); - } - }, [data, form, form.reset]); + const [isOpen, setIsOpen] = useState(false); + const utils = api.useUtils(); + const { mutateAsync, error, isError, isLoading } = + api.postgres.update.useMutation(); + const { data } = api.postgres.one.useQuery( + { + postgresId, + }, + { + enabled: !!postgresId, + } + ); + const form = useForm({ + defaultValues: { + description: data?.description ?? "", + name: data?.name ?? "", + }, + resolver: zodResolver(updatePostgresSchema), + }); + useEffect(() => { + if (data) { + form.reset({ + description: data.description ?? "", + name: data.name, + }); + } + }, [data, form, form.reset]); - const onSubmit = async (formData: UpdatePostgres) => { - await mutateAsync({ - name: formData.name, - postgresId: postgresId, - description: formData.description || "", - }) - .then(() => { - toast.success("Postgres updated successfully"); - utils.postgres.one.invalidate({ - postgresId: postgresId, - }); - setIsOpen(false); - }) - .catch(() => { - toast.error("Error updating Postgres"); - }) - .finally(() => {}); - }; + const onSubmit = async (formData: UpdatePostgres) => { + await mutateAsync({ + name: formData.name, + postgresId: postgresId, + description: formData.description || "", + }) + .then(() => { + toast.success("Postgres updated successfully"); + utils.postgres.one.invalidate({ + postgresId: postgresId, + }); + setIsOpen(false); + }) + .catch(() => { + toast.error("Error updating Postgres"); + }) + .finally(() => {}); + }; - return ( - - - - - - - Modify Postgres - Update the Postgres data - - {isError && {error?.message}} + return ( + + + + + + + Modify Postgres + Update the Postgres data + + {isError && {error?.message}} -
-
-
- - ( - - Name - - - +
+
+ + + ( + + Name + + + - - - )} - /> - ( - - Description - -