diff --git a/apps/dokploy/components/dashboard/application/deployments/show-deployment.tsx b/apps/dokploy/components/dashboard/application/deployments/show-deployment.tsx index 380b22d9..c67d1ba6 100644 --- a/apps/dokploy/components/dashboard/application/deployments/show-deployment.tsx +++ b/apps/dokploy/components/dashboard/application/deployments/show-deployment.tsx @@ -1,3 +1,4 @@ +import { Badge } from "@/components/ui/badge"; import { Dialog, DialogContent, @@ -5,11 +6,10 @@ import { DialogHeader, DialogTitle, } from "@/components/ui/dialog"; +import { Loader2 } from "lucide-react"; import { useEffect, useRef, useState } from "react"; import { TerminalLine } from "../../docker/logs/terminal-line"; -import { LogLine, parseLogs } from "../../docker/logs/utils"; -import { Badge } from "@/components/ui/badge"; -import { Loader2 } from "lucide-react"; +import { type LogLine, parseLogs } from "../../docker/logs/utils"; interface Props { logPath: string | null; @@ -24,21 +24,20 @@ export const ShowDeployment = ({ logPath, open, onClose, serverId }: Props) => { const [autoScroll, setAutoScroll] = useState(true); const scrollRef = useRef(null); - const scrollToBottom = () => { if (autoScroll && scrollRef.current) { - scrollRef.current.scrollTop = scrollRef.current.scrollHeight; + scrollRef.current.scrollTop = scrollRef.current.scrollHeight; } - }; - + }; + const handleScroll = () => { if (!scrollRef.current) return; - + const { scrollTop, scrollHeight, clientHeight } = scrollRef.current; const isAtBottom = Math.abs(scrollHeight - scrollTop - clientHeight) < 10; setAutoScroll(isAtBottom); - }; - + }; + useEffect(() => { if (!open || !logPath) return; @@ -69,7 +68,6 @@ export const ShowDeployment = ({ logPath, open, onClose, serverId }: Props) => { }; }, [logPath, open]); - useEffect(() => { const logs = parseLogs(data); setFilteredLogs(logs); @@ -77,12 +75,11 @@ export const ShowDeployment = ({ logPath, open, onClose, serverId }: Props) => { useEffect(() => { scrollToBottom(); - - if (autoScroll && scrollRef.current) { - scrollRef.current.scrollTop = scrollRef.current.scrollHeight; - } - }, [filteredLogs, autoScroll]); + if (autoScroll && scrollRef.current) { + scrollRef.current.scrollTop = scrollRef.current.scrollHeight; + } + }, [filteredLogs, autoScroll]); return ( { Deployment - See all the details of this deployment | {filteredLogs.length} lines + See all the details of this deployment |{" "} + + {filteredLogs.length} lines + -
{ - filteredLogs.length > 0 ? filteredLogs.map((log: LogLine, index: number) => ( - - )) : - ( -
- -
- )} + > + {" "} + {filteredLogs.length > 0 ? ( + filteredLogs.map((log: LogLine, index: number) => ( + + )) + ) : ( +
+ +
+ )}
diff --git a/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-builds.tsx b/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-builds.tsx index bff6c929..154510c3 100644 --- a/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-builds.tsx +++ b/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-builds.tsx @@ -26,7 +26,9 @@ export const ShowPreviewBuilds = ({ deployments, serverId }: Props) => { return ( - + diff --git a/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-deployments.tsx b/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-deployments.tsx index 898d412a..c2d712c3 100644 --- a/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-deployments.tsx +++ b/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-deployments.tsx @@ -1,237 +1,237 @@ -import React from "react"; +import { DateTooltip } from "@/components/shared/date-tooltip"; +import { DialogAction } from "@/components/shared/dialog-action"; +import { StatusTooltip } from "@/components/shared/status-tooltip"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; -import { Separator } from "@/components/ui/separator"; import { - Clock, - GitBranch, - GitPullRequest, - Pencil, - RocketIcon, + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { Separator } from "@/components/ui/separator"; +import { api } from "@/utils/api"; +import { + Clock, + GitBranch, + GitPullRequest, + Pencil, + RocketIcon, } from "lucide-react"; import Link from "next/link"; -import { ShowModalLogs } from "../../settings/web-server/show-modal-logs"; -import { DialogAction } from "@/components/shared/dialog-action"; -import { api } from "@/utils/api"; -import { ShowPreviewBuilds } from "./show-preview-builds"; -import { DateTooltip } from "@/components/shared/date-tooltip"; +import React from "react"; import { toast } from "sonner"; -import { StatusTooltip } from "@/components/shared/status-tooltip"; +import { ShowModalLogs } from "../../settings/web-server/show-modal-logs"; import { AddPreviewDomain } from "./add-preview-domain"; -import { - Card, - CardContent, - CardDescription, - CardHeader, - CardTitle, -} from "@/components/ui/card"; +import { ShowPreviewBuilds } from "./show-preview-builds"; import { ShowPreviewSettings } from "./show-preview-settings"; interface Props { - applicationId: string; + applicationId: string; } export const ShowPreviewDeployments = ({ applicationId }: Props) => { - const { data } = api.application.one.useQuery({ applicationId }); + const { data } = api.application.one.useQuery({ applicationId }); - const { mutateAsync: deletePreviewDeployment, isLoading } = - api.previewDeployment.delete.useMutation(); + const { mutateAsync: deletePreviewDeployment, isLoading } = + api.previewDeployment.delete.useMutation(); - const { data: previewDeployments, refetch: refetchPreviewDeployments } = - api.previewDeployment.all.useQuery( - { applicationId }, - { - enabled: !!applicationId, - } - ); + const { data: previewDeployments, refetch: refetchPreviewDeployments } = + api.previewDeployment.all.useQuery( + { applicationId }, + { + enabled: !!applicationId, + }, + ); - const handleDeletePreviewDeployment = async (previewDeploymentId: string) => { - deletePreviewDeployment({ - previewDeploymentId: previewDeploymentId, - }) - .then(() => { - refetchPreviewDeployments(); - toast.success("Preview deployment deleted"); - }) - .catch((error) => { - toast.error(error.message); - }); - }; + const handleDeletePreviewDeployment = async (previewDeploymentId: string) => { + deletePreviewDeployment({ + previewDeploymentId: previewDeploymentId, + }) + .then(() => { + refetchPreviewDeployments(); + toast.success("Preview deployment deleted"); + }) + .catch((error) => { + toast.error(error.message); + }); + }; - return ( - - -
- Preview Deployments - See all the preview deployments -
- {data?.isPreviewDeploymentsActive && ( - - )} -
- - {data?.isPreviewDeploymentsActive ? ( - <> -
- - Preview deployments are a way to test your application before it - is deployed to production. It will create a new deployment for - each pull request you create. - -
- {!previewDeployments?.length ? ( -
- - - No preview deployments found - -
- ) : ( -
- {previewDeployments.map((previewDeployment) => ( -
-
- - {previewDeployment.pullRequestTitle} - - - - {previewDeployment.previewStatus - ?.replace("running", "Running") - .replace("done", "Done") - .replace("error", "Error") - .replace("idle", "Idle") || "Idle"} - -
+ return ( + + +
+ Preview Deployments + See all the preview deployments +
+ {data?.isPreviewDeploymentsActive && ( + + )} +
+ + {data?.isPreviewDeploymentsActive ? ( + <> +
+ + Preview deployments are a way to test your application before it + is deployed to production. It will create a new deployment for + each pull request you create. + +
+ {!previewDeployments?.length ? ( +
+ + + No preview deployments found + +
+ ) : ( +
+ {previewDeployments.map((previewDeployment) => ( +
+
+ + {previewDeployment.pullRequestTitle} + + + + {previewDeployment.previewStatus + ?.replace("running", "Running") + .replace("done", "Done") + .replace("error", "Error") + .replace("idle", "Idle") || "Idle"} + +
-
-
- - {previewDeployment.domain?.host} - +
+
+ + {previewDeployment.domain?.host} + - - - -
+ + + +
-
-
- - Branch: - - {previewDeployment.branch} - -
-
- - Deployed: - - - -
-
+
+
+ + Branch: + + {previewDeployment.branch} + +
+
+ + Deployed: + + + +
+
- + -
-

- Pull Request -

-
- - - {previewDeployment.pullRequestTitle} - -
-
-
+
+

+ Pull Request +

+
+ + + {previewDeployment.pullRequestTitle} + +
+
+
-
-
- - - +
+
+ + + - + - - handleDeletePreviewDeployment( - previewDeployment.previewDeploymentId - ) - } - > - - -
-
-
- ))} -
- )} - - ) : ( -
- - - Preview deployments are disabled for this application, please - enable it - - -
- )} - - - ); + + handleDeletePreviewDeployment( + previewDeployment.previewDeploymentId, + ) + } + > + + +
+
+
+ ))} +
+ )} + + ) : ( +
+ + + Preview deployments are disabled for this application, please + enable it + + +
+ )} +
+
+ ); }; diff --git a/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx b/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx index 6e56bbdd..0e4231eb 100644 --- a/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx +++ b/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx @@ -1,5 +1,3 @@ -import { api } from "@/utils/api"; -import { useEffect, useState } from "react"; import { Button } from "@/components/ui/button"; import { Dialog, @@ -20,12 +18,7 @@ import { FormMessage, } from "@/components/ui/form"; import { Input, NumberInput } from "@/components/ui/input"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { useForm } from "react-hook-form"; -import { z } from "zod"; import { Secrets } from "@/components/ui/secrets"; -import { toast } from "sonner"; -import { Switch } from "@/components/ui/switch"; import { Select, SelectContent, @@ -33,6 +26,13 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; +import { Switch } from "@/components/ui/switch"; +import { api } from "@/utils/api"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { z } from "zod"; const schema = z.object({ env: z.string(), diff --git a/apps/dokploy/components/dashboard/compose/deployments/show-deployment-compose.tsx b/apps/dokploy/components/dashboard/compose/deployments/show-deployment-compose.tsx index 4a45fb20..d683d4ab 100644 --- a/apps/dokploy/components/dashboard/compose/deployments/show-deployment-compose.tsx +++ b/apps/dokploy/components/dashboard/compose/deployments/show-deployment-compose.tsx @@ -1,3 +1,4 @@ +import { Badge } from "@/components/ui/badge"; import { Dialog, DialogContent, @@ -5,12 +6,10 @@ import { DialogHeader, DialogTitle, } from "@/components/ui/dialog"; +import { Loader2 } from "lucide-react"; import { useEffect, useRef, useState } from "react"; import { TerminalLine } from "../../docker/logs/terminal-line"; -import { LogLine, parseLogs } from "../../docker/logs/utils"; -import { Badge } from "@/components/ui/badge"; -import { Loader2 } from "lucide-react"; - +import { type LogLine, parseLogs } from "../../docker/logs/utils"; interface Props { logPath: string | null; @@ -32,19 +31,18 @@ export const ShowDeploymentCompose = ({ const scrollToBottom = () => { if (autoScroll && scrollRef.current) { - scrollRef.current.scrollTop = scrollRef.current.scrollHeight; + scrollRef.current.scrollTop = scrollRef.current.scrollHeight; } - }; - + }; + const handleScroll = () => { if (!scrollRef.current) return; - + const { scrollTop, scrollHeight, clientHeight } = scrollRef.current; const isAtBottom = Math.abs(scrollHeight - scrollTop - clientHeight) < 10; setAutoScroll(isAtBottom); - }; - - + }; + useEffect(() => { if (!open || !logPath) return; @@ -76,7 +74,6 @@ export const ShowDeploymentCompose = ({ }; }, [logPath, open]); - useEffect(() => { const logs = parseLogs(data); setFilteredLogs(logs); @@ -84,11 +81,11 @@ export const ShowDeploymentCompose = ({ useEffect(() => { scrollToBottom(); - + if (autoScroll && scrollRef.current) { - scrollRef.current.scrollTop = scrollRef.current.scrollHeight; + scrollRef.current.scrollTop = scrollRef.current.scrollHeight; } - }, [filteredLogs, autoScroll]); + }, [filteredLogs, autoScroll]); return ( Deployment - See all the details of this deployment | {filteredLogs.length} lines + See all the details of this deployment |{" "} + + {filteredLogs.length} lines +
-
- - - { - filteredLogs.length > 0 ? filteredLogs.map((log: LogLine, index: number) => ( - - )) : - ( + {filteredLogs.length > 0 ? ( + filteredLogs.map((log: LogLine, index: number) => ( + + )) + ) : (
- ) - } + )}
diff --git a/apps/dokploy/components/dashboard/compose/general/deploy-compose.tsx b/apps/dokploy/components/dashboard/compose/general/deploy-compose.tsx index c02a7802..25b59cc7 100644 --- a/apps/dokploy/components/dashboard/compose/general/deploy-compose.tsx +++ b/apps/dokploy/components/dashboard/compose/general/deploy-compose.tsx @@ -53,7 +53,7 @@ export const DeployCompose = ({ composeId }: Props) => { }) .then(async () => { router.push( - `/dashboard/project/${data?.project.projectId}/services/compose/${composeId}?tab=deployments` + `/dashboard/project/${data?.project.projectId}/services/compose/${composeId}?tab=deployments`, ); }) .catch(() => { diff --git a/apps/dokploy/components/dashboard/docker/terminal/docker-terminal-modal.tsx b/apps/dokploy/components/dashboard/docker/terminal/docker-terminal-modal.tsx index e7301b0a..90aa2b40 100644 --- a/apps/dokploy/components/dashboard/docker/terminal/docker-terminal-modal.tsx +++ b/apps/dokploy/components/dashboard/docker/terminal/docker-terminal-modal.tsx @@ -59,7 +59,7 @@ export const DockerTerminalModal = ({ {children} - event.preventDefault()} > diff --git a/apps/dokploy/components/dashboard/projects/show.tsx b/apps/dokploy/components/dashboard/projects/show.tsx index d05bbba2..d4d9ac55 100644 --- a/apps/dokploy/components/dashboard/projects/show.tsx +++ b/apps/dokploy/components/dashboard/projects/show.tsx @@ -1,35 +1,35 @@ import { DateTooltip } from "@/components/shared/date-tooltip"; import { - AlertDialog, - AlertDialogAction, - AlertDialogCancel, - AlertDialogContent, - AlertDialogDescription, - AlertDialogFooter, - AlertDialogHeader, - AlertDialogTitle, - AlertDialogTrigger, + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, + AlertDialogTrigger, } from "@/components/ui/alert-dialog"; import { Button } from "@/components/ui/button"; import { Card, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"; import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuGroup, - DropdownMenuItem, - DropdownMenuLabel, - DropdownMenuSeparator, - DropdownMenuTrigger, + DropdownMenu, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { api } from "@/utils/api"; import { - AlertTriangle, - BookIcon, - ExternalLink, - ExternalLinkIcon, - FolderInput, - MoreHorizontalIcon, - TrashIcon, + AlertTriangle, + BookIcon, + ExternalLink, + ExternalLinkIcon, + FolderInput, + MoreHorizontalIcon, + TrashIcon, } from "lucide-react"; import Link from "next/link"; import { Fragment } from "react"; @@ -38,257 +38,257 @@ import { ProjectEnviroment } from "./project-enviroment"; import { UpdateProject } from "./update"; export const ShowProjects = () => { - const utils = api.useUtils(); - const { data } = api.project.all.useQuery(); - const { data: auth } = api.auth.get.useQuery(); - const { data: user } = api.user.byAuthId.useQuery( - { - authId: auth?.id || "", - }, - { - enabled: !!auth?.id && auth?.rol === "user", - } - ); - const { mutateAsync } = api.project.remove.useMutation(); + const utils = api.useUtils(); + const { data } = api.project.all.useQuery(); + const { data: auth } = api.auth.get.useQuery(); + const { data: user } = api.user.byAuthId.useQuery( + { + authId: auth?.id || "", + }, + { + enabled: !!auth?.id && auth?.rol === "user", + }, + ); + const { mutateAsync } = api.project.remove.useMutation(); - return ( - <> - {data?.length === 0 && ( -
- - - No projects added yet. Click on Create project. - -
- )} -
- {data?.map((project) => { - const emptyServices = - project?.mariadb.length === 0 && - project?.mongo.length === 0 && - project?.mysql.length === 0 && - project?.postgres.length === 0 && - project?.redis.length === 0 && - project?.applications.length === 0 && - project?.compose.length === 0; + return ( + <> + {data?.length === 0 && ( +
+ + + No projects added yet. Click on Create project. + +
+ )} +
+ {data?.map((project) => { + const emptyServices = + project?.mariadb.length === 0 && + project?.mongo.length === 0 && + project?.mysql.length === 0 && + project?.postgres.length === 0 && + project?.redis.length === 0 && + project?.applications.length === 0 && + project?.compose.length === 0; - const totalServices = - project?.mariadb.length + - project?.mongo.length + - project?.mysql.length + - project?.postgres.length + - project?.redis.length + - project?.applications.length + - project?.compose.length; + const totalServices = + project?.mariadb.length + + project?.mongo.length + + project?.mysql.length + + project?.postgres.length + + project?.redis.length + + project?.applications.length + + project?.compose.length; - const flattedDomains = [ - ...project.applications.flatMap((a) => a.domains), - ...project.compose.flatMap((a) => a.domains), - ]; + const flattedDomains = [ + ...project.applications.flatMap((a) => a.domains), + ...project.compose.flatMap((a) => a.domains), + ]; - const renderDomainsDropdown = ( - item: typeof project.compose | typeof project.applications - ) => - item[0] ? ( - - - {"applicationId" in item[0] ? "Applications" : "Compose"} - - {item.map((a) => ( - - - - - {a.name} - - - {a.domains.map((domain) => ( - - - {domain.host} - - - - ))} - - - ))} - - ) : null; + const renderDomainsDropdown = ( + item: typeof project.compose | typeof project.applications, + ) => + item[0] ? ( + + + {"applicationId" in item[0] ? "Applications" : "Compose"} + + {item.map((a) => ( + + + + + {a.name} + + + {a.domains.map((domain) => ( + + + {domain.host} + + + + ))} + + + ))} + + ) : null; - return ( -
- - - {flattedDomains.length > 1 ? ( - - - - - e.stopPropagation()} - > - {renderDomainsDropdown(project.applications)} - {renderDomainsDropdown(project.compose)} - - - ) : flattedDomains[0] ? ( - - ) : null} + return ( +
+ + + {flattedDomains.length > 1 ? ( + + + + + e.stopPropagation()} + > + {renderDomainsDropdown(project.applications)} + {renderDomainsDropdown(project.compose)} + + + ) : flattedDomains[0] ? ( + + ) : null} - - - -
- - - {project.name} - -
+ + + +
+ + + {project.name} + +
- - {project.description} - -
-
- - - - - - - Actions - -
e.stopPropagation()}> - -
-
e.stopPropagation()}> - -
+ + {project.description} + + +
+ + + + + + + Actions + +
e.stopPropagation()}> + +
+
e.stopPropagation()}> + +
-
e.stopPropagation()}> - {(auth?.rol === "admin" || - user?.canDeleteProjects) && ( - - - e.preventDefault()} - > - - Delete - - - - - - Are you sure to delete this project? - - {!emptyServices ? ( -
- - - You have active services, please - delete them first - -
- ) : ( - - This action cannot be undone - - )} -
- - - Cancel - - { - await mutateAsync({ - projectId: project.projectId, - }) - .then(() => { - toast.success( - "Project delete succesfully" - ); - }) - .catch(() => { - toast.error( - "Error to delete this project" - ); - }) - .finally(() => { - utils.project.all.invalidate(); - }); - }} - > - Delete - - -
-
- )} -
-
-
-
- - - -
- - Created - - - {totalServices}{" "} - {totalServices === 1 ? "service" : "services"} - -
-
- - -
- ); - })} -
- - ); +
e.stopPropagation()}> + {(auth?.rol === "admin" || + user?.canDeleteProjects) && ( + + + e.preventDefault()} + > + + Delete + + + + + + Are you sure to delete this project? + + {!emptyServices ? ( +
+ + + You have active services, please + delete them first + +
+ ) : ( + + This action cannot be undone + + )} +
+ + + Cancel + + { + await mutateAsync({ + projectId: project.projectId, + }) + .then(() => { + toast.success( + "Project delete succesfully", + ); + }) + .catch(() => { + toast.error( + "Error to delete this project", + ); + }) + .finally(() => { + utils.project.all.invalidate(); + }); + }} + > + Delete + + +
+
+ )} +
+ + +
+ + + +
+ + Created + + + {totalServices}{" "} + {totalServices === 1 ? "service" : "services"} + +
+
+ + +
+ ); + })} +
+ + ); }; diff --git a/apps/dokploy/components/dashboard/search-command.tsx b/apps/dokploy/components/dashboard/search-command.tsx index 8afea672..4d3c75f9 100644 --- a/apps/dokploy/components/dashboard/search-command.tsx +++ b/apps/dokploy/components/dashboard/search-command.tsx @@ -1,189 +1,189 @@ "use client"; -import React from "react"; import { - Command, - CommandEmpty, - CommandList, - CommandGroup, - CommandInput, - CommandItem, - CommandDialog, - CommandSeparator, + MariadbIcon, + MongodbIcon, + MysqlIcon, + PostgresqlIcon, + RedisIcon, +} from "@/components/icons/data-tools-icons"; +import { Badge } from "@/components/ui/badge"; +import { + Command, + CommandDialog, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, + CommandSeparator, } from "@/components/ui/command"; -import { useRouter } from "next/router"; import { - extractServices, - type Services, + type Services, + extractServices, } from "@/pages/dashboard/project/[projectId]"; +import { api } from "@/utils/api"; import type { findProjectById } from "@dokploy/server/services/project"; import { BookIcon, CircuitBoard, GlobeIcon } from "lucide-react"; -import { - MariadbIcon, - MongodbIcon, - MysqlIcon, - PostgresqlIcon, - RedisIcon, -} from "@/components/icons/data-tools-icons"; -import { api } from "@/utils/api"; -import { Badge } from "@/components/ui/badge"; +import { useRouter } from "next/router"; +import React from "react"; import { StatusTooltip } from "../shared/status-tooltip"; type Project = Awaited>; export const SearchCommand = () => { - const router = useRouter(); - const [open, setOpen] = React.useState(false); - const [search, setSearch] = React.useState(""); + const router = useRouter(); + const [open, setOpen] = React.useState(false); + const [search, setSearch] = React.useState(""); - const { data } = api.project.all.useQuery(); - const { data: isCloud, isLoading } = api.settings.isCloud.useQuery(); + const { data } = api.project.all.useQuery(); + const { data: isCloud, isLoading } = api.settings.isCloud.useQuery(); - React.useEffect(() => { - const down = (e: KeyboardEvent) => { - if (e.key === "j" && (e.metaKey || e.ctrlKey)) { - e.preventDefault(); - setOpen((open) => !open); - } - }; + React.useEffect(() => { + const down = (e: KeyboardEvent) => { + if (e.key === "j" && (e.metaKey || e.ctrlKey)) { + e.preventDefault(); + setOpen((open) => !open); + } + }; - document.addEventListener("keydown", down); - return () => document.removeEventListener("keydown", down); - }, []); + document.addEventListener("keydown", down); + return () => document.removeEventListener("keydown", down); + }, []); - return ( -
- - - - - No projects added yet. Click on Create project. - - - - {data?.map((project) => ( - { - router.push(`/dashboard/project/${project.projectId}`); - setOpen(false); - }} - > - - {project.name} - - ))} - - - - - - {data?.map((project) => { - const applications: Services[] = extractServices(project); - return applications.map((application) => ( - { - router.push( - `/dashboard/project/${project.projectId}/services/${application.type}/${application.id}` - ); - setOpen(false); - }} - > - {application.type === "postgres" && ( - - )} - {application.type === "redis" && ( - - )} - {application.type === "mariadb" && ( - - )} - {application.type === "mongo" && ( - - )} - {application.type === "mysql" && ( - - )} - {application.type === "application" && ( - - )} - {application.type === "compose" && ( - - )} - - {project.name} / {application.name}{" "} -
{application.id}
-
-
- -
-
- )); - })} -
-
- - -
-
-
- ); + return ( +
+ + + + + No projects added yet. Click on Create project. + + + + {data?.map((project) => ( + { + router.push(`/dashboard/project/${project.projectId}`); + setOpen(false); + }} + > + + {project.name} + + ))} + + + + + + {data?.map((project) => { + const applications: Services[] = extractServices(project); + return applications.map((application) => ( + { + router.push( + `/dashboard/project/${project.projectId}/services/${application.type}/${application.id}`, + ); + setOpen(false); + }} + > + {application.type === "postgres" && ( + + )} + {application.type === "redis" && ( + + )} + {application.type === "mariadb" && ( + + )} + {application.type === "mongo" && ( + + )} + {application.type === "mysql" && ( + + )} + {application.type === "application" && ( + + )} + {application.type === "compose" && ( + + )} + + {project.name} / {application.name}{" "} +
{application.id}
+
+
+ +
+
+ )); + })} +
+
+ + +
+
+
+ ); }; diff --git a/apps/dokploy/components/dashboard/settings/notifications/delete-notification.tsx b/apps/dokploy/components/dashboard/settings/notifications/delete-notification.tsx index 4bb197b2..02c95d8b 100644 --- a/apps/dokploy/components/dashboard/settings/notifications/delete-notification.tsx +++ b/apps/dokploy/components/dashboard/settings/notifications/delete-notification.tsx @@ -24,12 +24,12 @@ export const DeleteNotification = ({ notificationId }: Props) => { return ( - diff --git a/apps/dokploy/components/dashboard/settings/notifications/show-notifications.tsx b/apps/dokploy/components/dashboard/settings/notifications/show-notifications.tsx index 2b5d02e0..8a45bbf3 100644 --- a/apps/dokploy/components/dashboard/settings/notifications/show-notifications.tsx +++ b/apps/dokploy/components/dashboard/settings/notifications/show-notifications.tsx @@ -40,58 +40,60 @@ export const ShowNotifications = () => { ) : (
-
- {data?.map((notification, index) => ( -
-
- {notification.notificationType === "slack" && ( -
- -
- )} - {notification.notificationType === "telegram" && ( -
- -
- )} - {notification.notificationType === "discord" && ( -
- -
- )} - {notification.notificationType === "email" && ( -
- -
- )} -
- - {notification.name} - - - {notification.notificationType?.[0]?.toUpperCase() + notification.notificationType?.slice(1)} notification - -
-
-
- - -
+
+ {data?.map((notification, index) => ( +
+
+ {notification.notificationType === "slack" && ( +
+ +
+ )} + {notification.notificationType === "telegram" && ( +
+ +
+ )} + {notification.notificationType === "discord" && ( +
+ +
+ )} + {notification.notificationType === "email" && ( +
+ +
+ )} +
+ + {notification.name} + + + {notification.notificationType?.[0]?.toUpperCase() + + notification.notificationType?.slice(1)}{" "} + notification + +
+
+
+ + +
+
+ ))} +
+ +
+
- ))}
- -
- -
-
)} diff --git a/apps/dokploy/components/dashboard/settings/notifications/update-notification.tsx b/apps/dokploy/components/dashboard/settings/notifications/update-notification.tsx index 5c594dc4..41306385 100644 --- a/apps/dokploy/components/dashboard/settings/notifications/update-notification.tsx +++ b/apps/dokploy/components/dashboard/settings/notifications/update-notification.tsx @@ -218,9 +218,11 @@ export const UpdateNotification = ({ notificationId }: Props) => { return ( - diff --git a/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx b/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx index 1141397f..191c1936 100644 --- a/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx +++ b/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx @@ -1,3 +1,4 @@ +import { AlertBlock } from "@/components/shared/alert-block"; import { Button } from "@/components/ui/button"; import { Card, @@ -26,7 +27,6 @@ import { toast } from "sonner"; import { z } from "zod"; import { Disable2FA } from "./disable-2fa"; import { Enable2FA } from "./enable-2fa"; -import { AlertBlock } from "@/components/shared/alert-block"; const profileSchema = z.object({ email: z.string(), diff --git a/apps/dokploy/components/dashboard/settings/profile/remove-self-account.tsx b/apps/dokploy/components/dashboard/settings/profile/remove-self-account.tsx index f4f4680b..3fc55452 100644 --- a/apps/dokploy/components/dashboard/settings/profile/remove-self-account.tsx +++ b/apps/dokploy/components/dashboard/settings/profile/remove-self-account.tsx @@ -1,3 +1,5 @@ +import { AlertBlock } from "@/components/shared/alert-block"; +import { DialogAction } from "@/components/shared/dialog-action"; import { Button } from "@/components/ui/button"; import { Card, @@ -18,13 +20,11 @@ import { Input } from "@/components/ui/input"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; import { useTranslation } from "next-i18next"; +import { useRouter } from "next/router"; import { useEffect } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; -import { DialogAction } from "@/components/shared/dialog-action"; -import { AlertBlock } from "@/components/shared/alert-block"; -import { useRouter } from "next/router"; const profileSchema = z.object({ password: z.string().min(1, { diff --git a/apps/dokploy/components/dashboard/settings/servers/actions/show-traefik-actions.tsx b/apps/dokploy/components/dashboard/settings/servers/actions/show-traefik-actions.tsx index 546069c5..72854f93 100644 --- a/apps/dokploy/components/dashboard/settings/servers/actions/show-traefik-actions.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/actions/show-traefik-actions.tsx @@ -25,8 +25,8 @@ import { toast } from "sonner"; import { cn } from "@/lib/utils"; import { useTranslation } from "next-i18next"; import { EditTraefikEnv } from "../../web-server/edit-traefik-env"; -import { ShowModalLogs } from "../../web-server/show-modal-logs"; import { ManageTraefikPorts } from "../../web-server/manage-traefik-ports"; +import { ShowModalLogs } from "../../web-server/show-modal-logs"; interface Props { serverId?: string; diff --git a/apps/dokploy/components/dashboard/settings/servers/edit-script.tsx b/apps/dokploy/components/dashboard/settings/servers/edit-script.tsx index faaecb1f..0a22220e 100644 --- a/apps/dokploy/components/dashboard/settings/servers/edit-script.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/edit-script.tsx @@ -108,7 +108,8 @@ export const EditScript = ({ serverId }: Props) => { - We recommend not modifying this script unless you know what you are doing. + We recommend not modifying this script unless you know what you are + doing.
diff --git a/apps/dokploy/components/dashboard/settings/servers/setup-server.tsx b/apps/dokploy/components/dashboard/settings/servers/setup-server.tsx index 7c181459..252ca16c 100644 --- a/apps/dokploy/components/dashboard/settings/servers/setup-server.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/setup-server.tsx @@ -34,8 +34,8 @@ import { toast } from "sonner"; import { ShowDeployment } from "../../application/deployments/show-deployment"; import { EditScript } from "./edit-script"; import { GPUSupport } from "./gpu-support"; -import { ValidateServer } from "./validate-server"; import { SecurityAudit } from "./security-audit"; +import { ValidateServer } from "./validate-server"; interface Props { serverId: string; diff --git a/apps/dokploy/components/dashboard/settings/servers/show-servers.tsx b/apps/dokploy/components/dashboard/settings/servers/show-servers.tsx index d476fb15..d45a3b77 100644 --- a/apps/dokploy/components/dashboard/settings/servers/show-servers.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/show-servers.tsx @@ -23,17 +23,17 @@ import { api } from "@/utils/api"; import { format } from "date-fns"; import { KeyIcon, MoreHorizontal, ServerIcon } from "lucide-react"; import Link from "next/link"; +import { useRouter } from "next/router"; import { toast } from "sonner"; import { TerminalModal } from "../web-server/terminal-modal"; import { ShowServerActions } from "./actions/show-server-actions"; import { AddServer } from "./add-server"; import { SetupServer } from "./setup-server"; import { ShowDockerContainersModal } from "./show-docker-containers-modal"; +import { ShowSwarmOverviewModal } from "./show-swarm-overview-modal"; import { ShowTraefikFileSystemModal } from "./show-traefik-file-system-modal"; import { UpdateServer } from "./update-server"; -import { useRouter } from "next/router"; import { WelcomeSuscription } from "./welcome-stripe/welcome-suscription"; -import { ShowSwarmOverviewModal } from "./show-swarm-overview-modal"; export const ShowServers = () => { const router = useRouter(); diff --git a/apps/dokploy/components/dashboard/settings/servers/welcome-stripe/create-ssh-key.tsx b/apps/dokploy/components/dashboard/settings/servers/welcome-stripe/create-ssh-key.tsx index 740f7960..37f8e017 100644 --- a/apps/dokploy/components/dashboard/settings/servers/welcome-stripe/create-ssh-key.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/welcome-stripe/create-ssh-key.tsx @@ -1,12 +1,12 @@ +import { CodeEditor } from "@/components/shared/code-editor"; import { Card, CardContent } from "@/components/ui/card"; import { api } from "@/utils/api"; -import { ExternalLinkIcon, Loader2 } from "lucide-react"; import copy from "copy-to-clipboard"; +import { ExternalLinkIcon, Loader2 } from "lucide-react"; import { CopyIcon } from "lucide-react"; +import Link from "next/link"; import { useEffect, useRef } from "react"; import { toast } from "sonner"; -import { CodeEditor } from "@/components/shared/code-editor"; -import Link from "next/link"; export const CreateSSHKey = () => { const { data, refetch } = api.sshKey.all.useQuery(); diff --git a/apps/dokploy/components/dashboard/settings/servers/welcome-stripe/setup.tsx b/apps/dokploy/components/dashboard/settings/servers/welcome-stripe/setup.tsx index 39179de8..be7a8e48 100644 --- a/apps/dokploy/components/dashboard/settings/servers/welcome-stripe/setup.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/welcome-stripe/setup.tsx @@ -5,26 +5,26 @@ import { StatusTooltip } from "@/components/shared/status-tooltip"; import { Button } from "@/components/ui/button"; import { Card, + CardContent, + CardDescription, CardHeader, CardTitle, - CardDescription, - CardContent, } from "@/components/ui/card"; -import { RocketIcon } from "lucide-react"; -import { toast } from "sonner"; -import { EditScript } from "../edit-script"; -import { api } from "@/utils/api"; -import { useState } from "react"; import { Label } from "@/components/ui/label"; import { Select, - SelectTrigger, - SelectValue, SelectContent, SelectGroup, SelectItem, SelectLabel, + SelectTrigger, + SelectValue, } from "@/components/ui/select"; +import { api } from "@/utils/api"; +import { RocketIcon } from "lucide-react"; +import { useState } from "react"; +import { toast } from "sonner"; +import { EditScript } from "../edit-script"; export const Setup = () => { const { data: servers } = api.server.all.useQuery(); diff --git a/apps/dokploy/components/dashboard/settings/servers/welcome-stripe/verify.tsx b/apps/dokploy/components/dashboard/settings/servers/welcome-stripe/verify.tsx index 3d6cfa7d..fe8c36c2 100644 --- a/apps/dokploy/components/dashboard/settings/servers/welcome-stripe/verify.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/welcome-stripe/verify.tsx @@ -1,27 +1,27 @@ import { Button } from "@/components/ui/button"; import { Card, + CardContent, + CardDescription, CardHeader, CardTitle, - CardDescription, - CardContent, } from "@/components/ui/card"; -import { Loader2, PcCase, RefreshCw } from "lucide-react"; -import { api } from "@/utils/api"; -import { useState } from "react"; import { Label } from "@/components/ui/label"; +import { api } from "@/utils/api"; +import { Loader2, PcCase, RefreshCw } from "lucide-react"; +import { useState } from "react"; +import { AlertBlock } from "@/components/shared/alert-block"; import { Select, - SelectTrigger, - SelectValue, SelectContent, SelectGroup, SelectItem, SelectLabel, + SelectTrigger, + SelectValue, } from "@/components/ui/select"; import { StatusRow } from "../gpu-support"; -import { AlertBlock } from "@/components/shared/alert-block"; export const Verify = () => { const { data: servers } = api.server.all.useQuery(); diff --git a/apps/dokploy/components/dashboard/settings/servers/welcome-stripe/welcome-suscription.tsx b/apps/dokploy/components/dashboard/settings/servers/welcome-stripe/welcome-suscription.tsx index e9de0523..bab93047 100644 --- a/apps/dokploy/components/dashboard/settings/servers/welcome-stripe/welcome-suscription.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/welcome-stripe/welcome-suscription.tsx @@ -1,3 +1,5 @@ +import { GithubIcon } from "@/components/icons/data-tools-icons"; +import { AlertBlock } from "@/components/shared/alert-block"; import { Button } from "@/components/ui/button"; import { Dialog, @@ -7,21 +9,19 @@ import { DialogHeader, DialogTitle, } from "@/components/ui/dialog"; +import { Separator } from "@/components/ui/separator"; +import { defineStepper } from "@stepperize/react"; import { BookIcon, Puzzle } from "lucide-react"; +import { Code2, Database, GitMerge, Globe, Plug, Users } from "lucide-react"; +import Link from "next/link"; import { useRouter } from "next/router"; import { useEffect, useState } from "react"; -import { defineStepper } from "@stepperize/react"; import React from "react"; -import { Separator } from "@/components/ui/separator"; -import { AlertBlock } from "@/components/shared/alert-block"; +import ConfettiExplosion from "react-confetti-explosion"; import { CreateServer } from "./create-server"; import { CreateSSHKey } from "./create-ssh-key"; import { Setup } from "./setup"; import { Verify } from "./verify"; -import { Database, Globe, GitMerge, Users, Code2, Plug } from "lucide-react"; -import ConfettiExplosion from "react-confetti-explosion"; -import Link from "next/link"; -import { GithubIcon } from "@/components/icons/data-tools-icons"; export const { useStepper, steps, Scoped } = defineStepper( { diff --git a/apps/dokploy/components/dashboard/settings/web-server/docker-terminal-modal.tsx b/apps/dokploy/components/dashboard/settings/web-server/docker-terminal-modal.tsx index f5b6b2a9..fa9f1a41 100644 --- a/apps/dokploy/components/dashboard/settings/web-server/docker-terminal-modal.tsx +++ b/apps/dokploy/components/dashboard/settings/web-server/docker-terminal-modal.tsx @@ -80,7 +80,7 @@ export const DockerTerminalModal = ({ children, appName, serverId }: Props) => { return ( {children} - event.preventDefault()} > diff --git a/apps/dokploy/components/dashboard/settings/web-server/manage-traefik-ports.tsx b/apps/dokploy/components/dashboard/settings/web-server/manage-traefik-ports.tsx index 965948ca..180b2fcb 100644 --- a/apps/dokploy/components/dashboard/settings/web-server/manage-traefik-ports.tsx +++ b/apps/dokploy/components/dashboard/settings/web-server/manage-traefik-ports.tsx @@ -6,6 +6,7 @@ import { DialogHeader, DialogTitle, } from "@/components/ui/dialog"; +import { Label } from "@/components/ui/label"; import { Select, SelectContent, @@ -13,7 +14,6 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; -import { Label } from "@/components/ui/label"; import { api } from "@/utils/api"; import { useTranslation } from "next-i18next"; import type React from "react"; diff --git a/apps/dokploy/pages/api/deploy/github.ts b/apps/dokploy/pages/api/deploy/github.ts index 0e0a6c82..45923762 100644 --- a/apps/dokploy/pages/api/deploy/github.ts +++ b/apps/dokploy/pages/api/deploy/github.ts @@ -3,19 +3,19 @@ import { applications, compose, github } from "@/server/db/schema"; import type { DeploymentJob } from "@/server/queues/queue-types"; import { myQueue } from "@/server/queues/queueSetup"; import { deploy } from "@/server/utils/deploy"; +import { generateRandomDomain } from "@/templates/utils"; import { - createPreviewDeployment, type Domain, + IS_CLOUD, + createPreviewDeployment, findPreviewDeploymentByApplicationId, findPreviewDeploymentsByPullRequestId, - IS_CLOUD, removePreviewDeployment, } from "@dokploy/server"; import { Webhooks } from "@octokit/webhooks"; import { and, eq } from "drizzle-orm"; import type { NextApiRequest, NextApiResponse } from "next"; import { extractCommitMessage, extractHash } from "./[refreshToken]"; -import { generateRandomDomain } from "@/templates/utils"; export default async function handler( req: NextApiRequest, diff --git a/apps/dokploy/public/locales/it/common.json b/apps/dokploy/public/locales/it/common.json index 9e26dfee..0967ef42 100644 --- a/apps/dokploy/public/locales/it/common.json +++ b/apps/dokploy/public/locales/it/common.json @@ -1 +1 @@ -{} \ No newline at end of file +{} diff --git a/apps/dokploy/public/locales/it/settings.json b/apps/dokploy/public/locales/it/settings.json index f4c2cc06..6280e44e 100644 --- a/apps/dokploy/public/locales/it/settings.json +++ b/apps/dokploy/public/locales/it/settings.json @@ -1,44 +1,44 @@ -{ - "settings.common.save": "Salva", - "settings.server.domain.title": "Dominio del server", - "settings.server.domain.description": "Aggiungi un dominio alla tua applicazione server.", - "settings.server.domain.form.domain": "Dominio", - "settings.server.domain.form.letsEncryptEmail": "Email di Let's Encrypt", - "settings.server.domain.form.certificate.label": "Certificato", - "settings.server.domain.form.certificate.placeholder": "Seleziona un certificato", - "settings.server.domain.form.certificateOptions.none": "Nessuno", - "settings.server.domain.form.certificateOptions.letsencrypt": "Let's Encrypt (Predefinito)", - - "settings.server.webServer.title": "Server Web", - "settings.server.webServer.description": "Ricarica o pulisci il server web.", - "settings.server.webServer.actions": "Azioni", - "settings.server.webServer.reload": "Ricarica", - "settings.server.webServer.watchLogs": "Guarda i log", - "settings.server.webServer.updateServerIp": "Aggiorna IP del server", - "settings.server.webServer.server.label": "Server", - "settings.server.webServer.traefik.label": "Traefik", - "settings.server.webServer.traefik.modifyEnv": "Modifica Env", - "settings.server.webServer.storage.label": "Spazio", - "settings.server.webServer.storage.cleanUnusedImages": "Pulisci immagini inutilizzate", - "settings.server.webServer.storage.cleanUnusedVolumes": "Pulisci volumi inutilizzati", - "settings.server.webServer.storage.cleanStoppedContainers": "Pulisci container fermati", - "settings.server.webServer.storage.cleanDockerBuilder": "Pulisci Docker Builder e sistema", - "settings.server.webServer.storage.cleanMonitoring": "Pulisci monitoraggio", - "settings.server.webServer.storage.cleanAll": "Pulisci tutto", - - "settings.profile.title": "Account", - "settings.profile.description": "Modifica i dettagli del tuo profilo qui.", - "settings.profile.email": "Email", - "settings.profile.password": "Password", - "settings.profile.avatar": "Avatar", - - "settings.appearance.title": "Aspetto", - "settings.appearance.description": "Personalizza il tema della tua dashboard.", - "settings.appearance.theme": "Tema", - "settings.appearance.themeDescription": "Seleziona un tema per la tua dashboard", - "settings.appearance.themes.light": "Chiaro", - "settings.appearance.themes.dark": "Scuro", - "settings.appearance.themes.system": "Sistema", - "settings.appearance.language": "Lingua", - "settings.appearance.languageDescription": "Seleziona una lingua per la tua dashboard" -} \ No newline at end of file +{ + "settings.common.save": "Salva", + "settings.server.domain.title": "Dominio del server", + "settings.server.domain.description": "Aggiungi un dominio alla tua applicazione server.", + "settings.server.domain.form.domain": "Dominio", + "settings.server.domain.form.letsEncryptEmail": "Email di Let's Encrypt", + "settings.server.domain.form.certificate.label": "Certificato", + "settings.server.domain.form.certificate.placeholder": "Seleziona un certificato", + "settings.server.domain.form.certificateOptions.none": "Nessuno", + "settings.server.domain.form.certificateOptions.letsencrypt": "Let's Encrypt (Predefinito)", + + "settings.server.webServer.title": "Server Web", + "settings.server.webServer.description": "Ricarica o pulisci il server web.", + "settings.server.webServer.actions": "Azioni", + "settings.server.webServer.reload": "Ricarica", + "settings.server.webServer.watchLogs": "Guarda i log", + "settings.server.webServer.updateServerIp": "Aggiorna IP del server", + "settings.server.webServer.server.label": "Server", + "settings.server.webServer.traefik.label": "Traefik", + "settings.server.webServer.traefik.modifyEnv": "Modifica Env", + "settings.server.webServer.storage.label": "Spazio", + "settings.server.webServer.storage.cleanUnusedImages": "Pulisci immagini inutilizzate", + "settings.server.webServer.storage.cleanUnusedVolumes": "Pulisci volumi inutilizzati", + "settings.server.webServer.storage.cleanStoppedContainers": "Pulisci container fermati", + "settings.server.webServer.storage.cleanDockerBuilder": "Pulisci Docker Builder e sistema", + "settings.server.webServer.storage.cleanMonitoring": "Pulisci monitoraggio", + "settings.server.webServer.storage.cleanAll": "Pulisci tutto", + + "settings.profile.title": "Account", + "settings.profile.description": "Modifica i dettagli del tuo profilo qui.", + "settings.profile.email": "Email", + "settings.profile.password": "Password", + "settings.profile.avatar": "Avatar", + + "settings.appearance.title": "Aspetto", + "settings.appearance.description": "Personalizza il tema della tua dashboard.", + "settings.appearance.theme": "Tema", + "settings.appearance.themeDescription": "Seleziona un tema per la tua dashboard", + "settings.appearance.themes.light": "Chiaro", + "settings.appearance.themes.dark": "Scuro", + "settings.appearance.themes.system": "Sistema", + "settings.appearance.language": "Lingua", + "settings.appearance.languageDescription": "Seleziona una lingua per la tua dashboard" +} diff --git a/apps/dokploy/templates/onedev/index.ts b/apps/dokploy/templates/onedev/index.ts index 5dad1728..8017c351 100644 --- a/apps/dokploy/templates/onedev/index.ts +++ b/apps/dokploy/templates/onedev/index.ts @@ -1,22 +1,22 @@ import { - type DomainSchema, - type Schema, - type Template, - generateRandomDomain, + type DomainSchema, + type Schema, + type Template, + generateRandomDomain, } from "../utils"; export function generate(schema: Schema): Template { - const randomDomain = generateRandomDomain(schema); + const randomDomain = generateRandomDomain(schema); - const domains: DomainSchema[] = [ - { - host: randomDomain, - port: 6610, - serviceName: "onedev", - }, - ]; + const domains: DomainSchema[] = [ + { + host: randomDomain, + port: 6610, + serviceName: "onedev", + }, + ]; - return { - domains, - }; + return { + domains, + }; } diff --git a/apps/dokploy/templates/unsend/index.ts b/apps/dokploy/templates/unsend/index.ts index a383b771..1c4c9c71 100644 --- a/apps/dokploy/templates/unsend/index.ts +++ b/apps/dokploy/templates/unsend/index.ts @@ -1,44 +1,44 @@ import { - generateHash, - generateRandomDomain, - generateBase64, - type Template, - type Schema, - type DomainSchema, + type DomainSchema, + type Schema, + type Template, + generateBase64, + generateHash, + generateRandomDomain, } from "../utils"; export function generate(schema: Schema): Template { - const mainDomain = generateRandomDomain(schema); - const secretBase = generateBase64(64); + const mainDomain = generateRandomDomain(schema); + const secretBase = generateBase64(64); - const domains: DomainSchema[] = [ - { - host: mainDomain, - port: 3000, - serviceName: "unsend", - }, - ]; + const domains: DomainSchema[] = [ + { + host: mainDomain, + port: 3000, + serviceName: "unsend", + }, + ]; - const envs = [ - "REDIS_URL=redis://unsend-redis-prod:6379", - "POSTGRES_USER=postgres", - "POSTGRES_PASSWORD=postgres", - "POSTGRES_DB=unsend", - "DATABASE_URL=postgresql://postgres:postgres@unsend-db-prod:5432/unsend", - "NEXTAUTH_URL=http://localhost:3000", - `NEXTAUTH_SECRET=${secretBase}`, - "GITHUB_ID='Fill'", - "GITHUB_SECRET='Fill'", - "AWS_DEFAULT_REGION=us-east-1", - "AWS_SECRET_KEY='Fill'", - "AWS_ACCESS_KEY='Fill'", - "DOCKER_OUTPUT=1", - "API_RATE_LIMIT=1", - "DISCORD_WEBHOOK_URL=", - ]; + const envs = [ + "REDIS_URL=redis://unsend-redis-prod:6379", + "POSTGRES_USER=postgres", + "POSTGRES_PASSWORD=postgres", + "POSTGRES_DB=unsend", + "DATABASE_URL=postgresql://postgres:postgres@unsend-db-prod:5432/unsend", + "NEXTAUTH_URL=http://localhost:3000", + `NEXTAUTH_SECRET=${secretBase}`, + "GITHUB_ID='Fill'", + "GITHUB_SECRET='Fill'", + "AWS_DEFAULT_REGION=us-east-1", + "AWS_SECRET_KEY='Fill'", + "AWS_ACCESS_KEY='Fill'", + "DOCKER_OUTPUT=1", + "API_RATE_LIMIT=1", + "DISCORD_WEBHOOK_URL=", + ]; - return { - envs, - domains, - }; + return { + envs, + domains, + }; } diff --git a/packages/server/src/db/schema/application.ts b/packages/server/src/db/schema/application.ts index 425f8d13..0f6aaed3 100644 --- a/packages/server/src/db/schema/application.ts +++ b/packages/server/src/db/schema/application.ts @@ -1,11 +1,11 @@ import { relations } from "drizzle-orm"; import { - boolean, - integer, - json, - pgEnum, - pgTable, - text, + boolean, + integer, + json, + pgEnum, + pgTable, + text, } from "drizzle-orm/pg-core"; import { createInsertSchema } from "drizzle-zod"; import { nanoid } from "nanoid"; @@ -28,493 +28,493 @@ import { sshKeys } from "./ssh-key"; import { generateAppName } from "./utils"; export const sourceType = pgEnum("sourceType", [ - "docker", - "git", - "github", - "gitlab", - "bitbucket", - "drop", + "docker", + "git", + "github", + "gitlab", + "bitbucket", + "drop", ]); export const buildType = pgEnum("buildType", [ - "dockerfile", - "heroku_buildpacks", - "paketo_buildpacks", - "nixpacks", - "static", + "dockerfile", + "heroku_buildpacks", + "paketo_buildpacks", + "nixpacks", + "static", ]); // TODO: refactor this types export interface HealthCheckSwarm { - Test?: string[] | undefined; - Interval?: number | undefined; - Timeout?: number | undefined; - StartPeriod?: number | undefined; - Retries?: number | undefined; + Test?: string[] | undefined; + Interval?: number | undefined; + Timeout?: number | undefined; + StartPeriod?: number | undefined; + Retries?: number | undefined; } export interface RestartPolicySwarm { - Condition?: string | undefined; - Delay?: number | undefined; - MaxAttempts?: number | undefined; - Window?: number | undefined; + Condition?: string | undefined; + Delay?: number | undefined; + MaxAttempts?: number | undefined; + Window?: number | undefined; } export interface PlacementSwarm { - Constraints?: string[] | undefined; - Preferences?: Array<{ Spread: { SpreadDescriptor: string } }> | undefined; - MaxReplicas?: number | undefined; - Platforms?: - | Array<{ - Architecture: string; - OS: string; - }> - | undefined; + Constraints?: string[] | undefined; + Preferences?: Array<{ Spread: { SpreadDescriptor: string } }> | undefined; + MaxReplicas?: number | undefined; + Platforms?: + | Array<{ + Architecture: string; + OS: string; + }> + | undefined; } export interface UpdateConfigSwarm { - Parallelism: number; - Delay?: number | undefined; - FailureAction?: string | undefined; - Monitor?: number | undefined; - MaxFailureRatio?: number | undefined; - Order: string; + Parallelism: number; + Delay?: number | undefined; + FailureAction?: string | undefined; + Monitor?: number | undefined; + MaxFailureRatio?: number | undefined; + Order: string; } export interface ServiceModeSwarm { - Replicated?: { Replicas?: number | undefined } | undefined; - Global?: {} | undefined; - ReplicatedJob?: - | { - MaxConcurrent?: number | undefined; - TotalCompletions?: number | undefined; - } - | undefined; - GlobalJob?: {} | undefined; + Replicated?: { Replicas?: number | undefined } | undefined; + Global?: {} | undefined; + ReplicatedJob?: + | { + MaxConcurrent?: number | undefined; + TotalCompletions?: number | undefined; + } + | undefined; + GlobalJob?: {} | undefined; } export interface NetworkSwarm { - Target?: string | undefined; - Aliases?: string[] | undefined; - DriverOpts?: { [key: string]: string } | undefined; + Target?: string | undefined; + Aliases?: string[] | undefined; + DriverOpts?: { [key: string]: string } | undefined; } export interface LabelsSwarm { - [name: string]: string; + [name: string]: string; } export const applications = pgTable("application", { - applicationId: text("applicationId") - .notNull() - .primaryKey() - .$defaultFn(() => nanoid()), - name: text("name").notNull(), - appName: text("appName") - .notNull() - .$defaultFn(() => generateAppName("app")) - .unique(), - description: text("description"), - env: text("env"), - previewEnv: text("previewEnv"), - previewBuildArgs: text("previewBuildArgs"), - previewWildcard: text("previewWildcard"), - previewPort: integer("previewPort").default(3000), - previewHttps: boolean("previewHttps").notNull().default(false), - previewPath: text("previewPath").default("/"), - previewCertificateType: certificateType("certificateType") - .notNull() - .default("none"), - previewLimit: integer("previewLimit").default(3), - isPreviewDeploymentsActive: boolean("isPreviewDeploymentsActive").default( - false - ), - buildArgs: text("buildArgs"), - memoryReservation: integer("memoryReservation"), - memoryLimit: integer("memoryLimit"), - cpuReservation: integer("cpuReservation"), - cpuLimit: integer("cpuLimit"), - title: text("title"), - enabled: boolean("enabled"), - subtitle: text("subtitle"), - command: text("command"), - refreshToken: text("refreshToken").$defaultFn(() => nanoid()), - sourceType: sourceType("sourceType").notNull().default("github"), - // Github - repository: text("repository"), - owner: text("owner"), - branch: text("branch"), - buildPath: text("buildPath").default("/"), - autoDeploy: boolean("autoDeploy").$defaultFn(() => true), - // Gitlab - gitlabProjectId: integer("gitlabProjectId"), - gitlabRepository: text("gitlabRepository"), - gitlabOwner: text("gitlabOwner"), - gitlabBranch: text("gitlabBranch"), - gitlabBuildPath: text("gitlabBuildPath").default("/"), - gitlabPathNamespace: text("gitlabPathNamespace"), - // Bitbucket - bitbucketRepository: text("bitbucketRepository"), - bitbucketOwner: text("bitbucketOwner"), - bitbucketBranch: text("bitbucketBranch"), - bitbucketBuildPath: text("bitbucketBuildPath").default("/"), - // Docker - username: text("username"), - password: text("password"), - dockerImage: text("dockerImage"), - registryUrl: text("registryUrl"), - // Git - customGitUrl: text("customGitUrl"), - customGitBranch: text("customGitBranch"), - customGitBuildPath: text("customGitBuildPath"), - customGitSSHKeyId: text("customGitSSHKeyId").references( - () => sshKeys.sshKeyId, - { - onDelete: "set null", - } - ), - dockerfile: text("dockerfile"), - dockerContextPath: text("dockerContextPath"), - dockerBuildStage: text("dockerBuildStage"), - // Drop - dropBuildPath: text("dropBuildPath"), - // Docker swarm json - healthCheckSwarm: json("healthCheckSwarm").$type(), - restartPolicySwarm: json("restartPolicySwarm").$type(), - placementSwarm: json("placementSwarm").$type(), - updateConfigSwarm: json("updateConfigSwarm").$type(), - rollbackConfigSwarm: json("rollbackConfigSwarm").$type(), - modeSwarm: json("modeSwarm").$type(), - labelsSwarm: json("labelsSwarm").$type(), - networkSwarm: json("networkSwarm").$type(), - // - replicas: integer("replicas").default(1).notNull(), - applicationStatus: applicationStatus("applicationStatus") - .notNull() - .default("idle"), - buildType: buildType("buildType").notNull().default("nixpacks"), - herokuVersion: text("herokuVersion").default("24"), - publishDirectory: text("publishDirectory"), - createdAt: text("createdAt") - .notNull() - .$defaultFn(() => new Date().toISOString()), - registryId: text("registryId").references(() => registry.registryId, { - onDelete: "set null", - }), - projectId: text("projectId") - .notNull() - .references(() => projects.projectId, { onDelete: "cascade" }), - githubId: text("githubId").references(() => github.githubId, { - onDelete: "set null", - }), - gitlabId: text("gitlabId").references(() => gitlab.gitlabId, { - onDelete: "set null", - }), - bitbucketId: text("bitbucketId").references(() => bitbucket.bitbucketId, { - onDelete: "set null", - }), - serverId: text("serverId").references(() => server.serverId, { - onDelete: "cascade", - }), + applicationId: text("applicationId") + .notNull() + .primaryKey() + .$defaultFn(() => nanoid()), + name: text("name").notNull(), + appName: text("appName") + .notNull() + .$defaultFn(() => generateAppName("app")) + .unique(), + description: text("description"), + env: text("env"), + previewEnv: text("previewEnv"), + previewBuildArgs: text("previewBuildArgs"), + previewWildcard: text("previewWildcard"), + previewPort: integer("previewPort").default(3000), + previewHttps: boolean("previewHttps").notNull().default(false), + previewPath: text("previewPath").default("/"), + previewCertificateType: certificateType("certificateType") + .notNull() + .default("none"), + previewLimit: integer("previewLimit").default(3), + isPreviewDeploymentsActive: boolean("isPreviewDeploymentsActive").default( + false, + ), + buildArgs: text("buildArgs"), + memoryReservation: integer("memoryReservation"), + memoryLimit: integer("memoryLimit"), + cpuReservation: integer("cpuReservation"), + cpuLimit: integer("cpuLimit"), + title: text("title"), + enabled: boolean("enabled"), + subtitle: text("subtitle"), + command: text("command"), + refreshToken: text("refreshToken").$defaultFn(() => nanoid()), + sourceType: sourceType("sourceType").notNull().default("github"), + // Github + repository: text("repository"), + owner: text("owner"), + branch: text("branch"), + buildPath: text("buildPath").default("/"), + autoDeploy: boolean("autoDeploy").$defaultFn(() => true), + // Gitlab + gitlabProjectId: integer("gitlabProjectId"), + gitlabRepository: text("gitlabRepository"), + gitlabOwner: text("gitlabOwner"), + gitlabBranch: text("gitlabBranch"), + gitlabBuildPath: text("gitlabBuildPath").default("/"), + gitlabPathNamespace: text("gitlabPathNamespace"), + // Bitbucket + bitbucketRepository: text("bitbucketRepository"), + bitbucketOwner: text("bitbucketOwner"), + bitbucketBranch: text("bitbucketBranch"), + bitbucketBuildPath: text("bitbucketBuildPath").default("/"), + // Docker + username: text("username"), + password: text("password"), + dockerImage: text("dockerImage"), + registryUrl: text("registryUrl"), + // Git + customGitUrl: text("customGitUrl"), + customGitBranch: text("customGitBranch"), + customGitBuildPath: text("customGitBuildPath"), + customGitSSHKeyId: text("customGitSSHKeyId").references( + () => sshKeys.sshKeyId, + { + onDelete: "set null", + }, + ), + dockerfile: text("dockerfile"), + dockerContextPath: text("dockerContextPath"), + dockerBuildStage: text("dockerBuildStage"), + // Drop + dropBuildPath: text("dropBuildPath"), + // Docker swarm json + healthCheckSwarm: json("healthCheckSwarm").$type(), + restartPolicySwarm: json("restartPolicySwarm").$type(), + placementSwarm: json("placementSwarm").$type(), + updateConfigSwarm: json("updateConfigSwarm").$type(), + rollbackConfigSwarm: json("rollbackConfigSwarm").$type(), + modeSwarm: json("modeSwarm").$type(), + labelsSwarm: json("labelsSwarm").$type(), + networkSwarm: json("networkSwarm").$type(), + // + replicas: integer("replicas").default(1).notNull(), + applicationStatus: applicationStatus("applicationStatus") + .notNull() + .default("idle"), + buildType: buildType("buildType").notNull().default("nixpacks"), + herokuVersion: text("herokuVersion").default("24"), + publishDirectory: text("publishDirectory"), + createdAt: text("createdAt") + .notNull() + .$defaultFn(() => new Date().toISOString()), + registryId: text("registryId").references(() => registry.registryId, { + onDelete: "set null", + }), + projectId: text("projectId") + .notNull() + .references(() => projects.projectId, { onDelete: "cascade" }), + githubId: text("githubId").references(() => github.githubId, { + onDelete: "set null", + }), + gitlabId: text("gitlabId").references(() => gitlab.gitlabId, { + onDelete: "set null", + }), + bitbucketId: text("bitbucketId").references(() => bitbucket.bitbucketId, { + onDelete: "set null", + }), + serverId: text("serverId").references(() => server.serverId, { + onDelete: "cascade", + }), }); export const applicationsRelations = relations( - applications, - ({ one, many }) => ({ - project: one(projects, { - fields: [applications.projectId], - references: [projects.projectId], - }), - deployments: many(deployments), - customGitSSHKey: one(sshKeys, { - fields: [applications.customGitSSHKeyId], - references: [sshKeys.sshKeyId], - }), - domains: many(domains), - mounts: many(mounts), - redirects: many(redirects), - security: many(security), - ports: many(ports), - registry: one(registry, { - fields: [applications.registryId], - references: [registry.registryId], - }), - github: one(github, { - fields: [applications.githubId], - references: [github.githubId], - }), - gitlab: one(gitlab, { - fields: [applications.gitlabId], - references: [gitlab.gitlabId], - }), - bitbucket: one(bitbucket, { - fields: [applications.bitbucketId], - references: [bitbucket.bitbucketId], - }), - server: one(server, { - fields: [applications.serverId], - references: [server.serverId], - }), - previewDeployments: many(previewDeployments), - }) + applications, + ({ one, many }) => ({ + project: one(projects, { + fields: [applications.projectId], + references: [projects.projectId], + }), + deployments: many(deployments), + customGitSSHKey: one(sshKeys, { + fields: [applications.customGitSSHKeyId], + references: [sshKeys.sshKeyId], + }), + domains: many(domains), + mounts: many(mounts), + redirects: many(redirects), + security: many(security), + ports: many(ports), + registry: one(registry, { + fields: [applications.registryId], + references: [registry.registryId], + }), + github: one(github, { + fields: [applications.githubId], + references: [github.githubId], + }), + gitlab: one(gitlab, { + fields: [applications.gitlabId], + references: [gitlab.gitlabId], + }), + bitbucket: one(bitbucket, { + fields: [applications.bitbucketId], + references: [bitbucket.bitbucketId], + }), + server: one(server, { + fields: [applications.serverId], + references: [server.serverId], + }), + previewDeployments: many(previewDeployments), + }), ); const HealthCheckSwarmSchema = z - .object({ - Test: z.array(z.string()).optional(), - Interval: z.number().optional(), - Timeout: z.number().optional(), - StartPeriod: z.number().optional(), - Retries: z.number().optional(), - }) - .strict(); + .object({ + Test: z.array(z.string()).optional(), + Interval: z.number().optional(), + Timeout: z.number().optional(), + StartPeriod: z.number().optional(), + Retries: z.number().optional(), + }) + .strict(); const RestartPolicySwarmSchema = z - .object({ - Condition: z.string().optional(), - Delay: z.number().optional(), - MaxAttempts: z.number().optional(), - Window: z.number().optional(), - }) - .strict(); + .object({ + Condition: z.string().optional(), + Delay: z.number().optional(), + MaxAttempts: z.number().optional(), + Window: z.number().optional(), + }) + .strict(); const PreferenceSchema = z - .object({ - Spread: z.object({ - SpreadDescriptor: z.string(), - }), - }) - .strict(); + .object({ + Spread: z.object({ + SpreadDescriptor: z.string(), + }), + }) + .strict(); const PlatformSchema = z - .object({ - Architecture: z.string(), - OS: z.string(), - }) - .strict(); + .object({ + Architecture: z.string(), + OS: z.string(), + }) + .strict(); const PlacementSwarmSchema = z - .object({ - Constraints: z.array(z.string()).optional(), - Preferences: z.array(PreferenceSchema).optional(), - MaxReplicas: z.number().optional(), - Platforms: z.array(PlatformSchema).optional(), - }) - .strict(); + .object({ + Constraints: z.array(z.string()).optional(), + Preferences: z.array(PreferenceSchema).optional(), + MaxReplicas: z.number().optional(), + Platforms: z.array(PlatformSchema).optional(), + }) + .strict(); const UpdateConfigSwarmSchema = z - .object({ - Parallelism: z.number(), - Delay: z.number().optional(), - FailureAction: z.string().optional(), - Monitor: z.number().optional(), - MaxFailureRatio: z.number().optional(), - Order: z.string(), - }) - .strict(); + .object({ + Parallelism: z.number(), + Delay: z.number().optional(), + FailureAction: z.string().optional(), + Monitor: z.number().optional(), + MaxFailureRatio: z.number().optional(), + Order: z.string(), + }) + .strict(); const ReplicatedSchema = z - .object({ - Replicas: z.number().optional(), - }) - .strict(); + .object({ + Replicas: z.number().optional(), + }) + .strict(); const ReplicatedJobSchema = z - .object({ - MaxConcurrent: z.number().optional(), - TotalCompletions: z.number().optional(), - }) - .strict(); + .object({ + MaxConcurrent: z.number().optional(), + TotalCompletions: z.number().optional(), + }) + .strict(); const ServiceModeSwarmSchema = z - .object({ - Replicated: ReplicatedSchema.optional(), - Global: z.object({}).optional(), - ReplicatedJob: ReplicatedJobSchema.optional(), - GlobalJob: z.object({}).optional(), - }) - .strict(); + .object({ + Replicated: ReplicatedSchema.optional(), + Global: z.object({}).optional(), + ReplicatedJob: ReplicatedJobSchema.optional(), + GlobalJob: z.object({}).optional(), + }) + .strict(); const NetworkSwarmSchema = z.array( - z - .object({ - Target: z.string().optional(), - Aliases: z.array(z.string()).optional(), - DriverOpts: z.object({}).optional(), - }) - .strict() + z + .object({ + Target: z.string().optional(), + Aliases: z.array(z.string()).optional(), + DriverOpts: z.object({}).optional(), + }) + .strict(), ); const LabelsSwarmSchema = z.record(z.string()); const createSchema = createInsertSchema(applications, { - appName: z.string(), - createdAt: z.string(), - applicationId: z.string(), - autoDeploy: z.boolean(), - env: z.string().optional(), - buildArgs: z.string().optional(), - name: z.string().min(1), - description: z.string().optional(), - memoryReservation: z.number().optional(), - memoryLimit: z.number().optional(), - cpuReservation: z.number().optional(), - cpuLimit: z.number().optional(), - title: z.string().optional(), - enabled: z.boolean().optional(), - subtitle: z.string().optional(), - dockerImage: z.string().optional(), - username: z.string().optional(), - isPreviewDeploymentsActive: z.boolean().optional(), - password: z.string().optional(), - registryUrl: z.string().optional(), - customGitSSHKeyId: z.string().optional(), - repository: z.string().optional(), - dockerfile: z.string().optional(), - branch: z.string().optional(), - customGitBranch: z.string().optional(), - customGitBuildPath: z.string().optional(), - customGitUrl: z.string().optional(), - buildPath: z.string().optional(), - projectId: z.string(), - sourceType: z.enum(["github", "docker", "git"]).optional(), - applicationStatus: z.enum(["idle", "running", "done", "error"]), - buildType: z.enum([ - "dockerfile", - "heroku_buildpacks", - "paketo_buildpacks", - "nixpacks", - "static", - ]), - herokuVersion: z.string().optional(), - publishDirectory: z.string().optional(), - owner: z.string(), - healthCheckSwarm: HealthCheckSwarmSchema.nullable(), - restartPolicySwarm: RestartPolicySwarmSchema.nullable(), - placementSwarm: PlacementSwarmSchema.nullable(), - updateConfigSwarm: UpdateConfigSwarmSchema.nullable(), - rollbackConfigSwarm: UpdateConfigSwarmSchema.nullable(), - modeSwarm: ServiceModeSwarmSchema.nullable(), - labelsSwarm: LabelsSwarmSchema.nullable(), - networkSwarm: NetworkSwarmSchema.nullable(), - previewPort: z.number().optional(), - previewEnv: z.string().optional(), - previewBuildArgs: z.string().optional(), - previewWildcard: z.string().optional(), - previewLimit: z.number().optional(), - previewHttps: z.boolean().optional(), - previewPath: z.string().optional(), - previewCertificateType: z.enum(["letsencrypt", "none"]).optional(), + appName: z.string(), + createdAt: z.string(), + applicationId: z.string(), + autoDeploy: z.boolean(), + env: z.string().optional(), + buildArgs: z.string().optional(), + name: z.string().min(1), + description: z.string().optional(), + memoryReservation: z.number().optional(), + memoryLimit: z.number().optional(), + cpuReservation: z.number().optional(), + cpuLimit: z.number().optional(), + title: z.string().optional(), + enabled: z.boolean().optional(), + subtitle: z.string().optional(), + dockerImage: z.string().optional(), + username: z.string().optional(), + isPreviewDeploymentsActive: z.boolean().optional(), + password: z.string().optional(), + registryUrl: z.string().optional(), + customGitSSHKeyId: z.string().optional(), + repository: z.string().optional(), + dockerfile: z.string().optional(), + branch: z.string().optional(), + customGitBranch: z.string().optional(), + customGitBuildPath: z.string().optional(), + customGitUrl: z.string().optional(), + buildPath: z.string().optional(), + projectId: z.string(), + sourceType: z.enum(["github", "docker", "git"]).optional(), + applicationStatus: z.enum(["idle", "running", "done", "error"]), + buildType: z.enum([ + "dockerfile", + "heroku_buildpacks", + "paketo_buildpacks", + "nixpacks", + "static", + ]), + herokuVersion: z.string().optional(), + publishDirectory: z.string().optional(), + owner: z.string(), + healthCheckSwarm: HealthCheckSwarmSchema.nullable(), + restartPolicySwarm: RestartPolicySwarmSchema.nullable(), + placementSwarm: PlacementSwarmSchema.nullable(), + updateConfigSwarm: UpdateConfigSwarmSchema.nullable(), + rollbackConfigSwarm: UpdateConfigSwarmSchema.nullable(), + modeSwarm: ServiceModeSwarmSchema.nullable(), + labelsSwarm: LabelsSwarmSchema.nullable(), + networkSwarm: NetworkSwarmSchema.nullable(), + previewPort: z.number().optional(), + previewEnv: z.string().optional(), + previewBuildArgs: z.string().optional(), + previewWildcard: z.string().optional(), + previewLimit: z.number().optional(), + previewHttps: z.boolean().optional(), + previewPath: z.string().optional(), + previewCertificateType: z.enum(["letsencrypt", "none"]).optional(), }); export const apiCreateApplication = createSchema.pick({ - name: true, - appName: true, - description: true, - projectId: true, - serverId: true, + name: true, + appName: true, + description: true, + projectId: true, + serverId: true, }); export const apiFindOneApplication = createSchema - .pick({ - applicationId: true, - }) - .required(); + .pick({ + applicationId: true, + }) + .required(); export const apiReloadApplication = createSchema - .pick({ - appName: true, - applicationId: true, - }) - .required(); + .pick({ + appName: true, + applicationId: true, + }) + .required(); export const apiSaveBuildType = createSchema - .pick({ - applicationId: true, - buildType: true, - dockerfile: true, - dockerContextPath: true, - dockerBuildStage: true, - herokuVersion: true, - }) - .required() - .merge(createSchema.pick({ publishDirectory: true })); + .pick({ + applicationId: true, + buildType: true, + dockerfile: true, + dockerContextPath: true, + dockerBuildStage: true, + herokuVersion: true, + }) + .required() + .merge(createSchema.pick({ publishDirectory: true })); export const apiSaveGithubProvider = createSchema - .pick({ - applicationId: true, - repository: true, - branch: true, - owner: true, - buildPath: true, - githubId: true, - }) - .required(); + .pick({ + applicationId: true, + repository: true, + branch: true, + owner: true, + buildPath: true, + githubId: true, + }) + .required(); export const apiSaveGitlabProvider = createSchema - .pick({ - applicationId: true, - gitlabBranch: true, - gitlabBuildPath: true, - gitlabOwner: true, - gitlabRepository: true, - gitlabId: true, - gitlabProjectId: true, - gitlabPathNamespace: true, - }) - .required(); + .pick({ + applicationId: true, + gitlabBranch: true, + gitlabBuildPath: true, + gitlabOwner: true, + gitlabRepository: true, + gitlabId: true, + gitlabProjectId: true, + gitlabPathNamespace: true, + }) + .required(); export const apiSaveBitbucketProvider = createSchema - .pick({ - bitbucketBranch: true, - bitbucketBuildPath: true, - bitbucketOwner: true, - bitbucketRepository: true, - bitbucketId: true, - applicationId: true, - }) - .required(); + .pick({ + bitbucketBranch: true, + bitbucketBuildPath: true, + bitbucketOwner: true, + bitbucketRepository: true, + bitbucketId: true, + applicationId: true, + }) + .required(); export const apiSaveDockerProvider = createSchema - .pick({ - dockerImage: true, - applicationId: true, - username: true, - password: true, - registryUrl: true, - }) - .required(); + .pick({ + dockerImage: true, + applicationId: true, + username: true, + password: true, + registryUrl: true, + }) + .required(); export const apiSaveGitProvider = createSchema - .pick({ - customGitBranch: true, - applicationId: true, - customGitBuildPath: true, - customGitUrl: true, - }) - .required() - .merge( - createSchema.pick({ - customGitSSHKeyId: true, - }) - ); + .pick({ + customGitBranch: true, + applicationId: true, + customGitBuildPath: true, + customGitUrl: true, + }) + .required() + .merge( + createSchema.pick({ + customGitSSHKeyId: true, + }), + ); export const apiSaveEnvironmentVariables = createSchema - .pick({ - applicationId: true, - env: true, - buildArgs: true, - }) - .required(); + .pick({ + applicationId: true, + env: true, + buildArgs: true, + }) + .required(); export const apiFindMonitoringStats = createSchema - .pick({ - appName: true, - }) - .required(); + .pick({ + appName: true, + }) + .required(); export const apiUpdateApplication = createSchema - .partial() - .extend({ - applicationId: z.string().min(1), - }) - .omit({ serverId: true }); + .partial() + .extend({ + applicationId: z.string().min(1), + }) + .omit({ serverId: true }); diff --git a/packages/server/src/db/schema/deployment.ts b/packages/server/src/db/schema/deployment.ts index f79b48ee..ccaf6466 100644 --- a/packages/server/src/db/schema/deployment.ts +++ b/packages/server/src/db/schema/deployment.ts @@ -11,8 +11,8 @@ import { nanoid } from "nanoid"; import { z } from "zod"; import { applications } from "./application"; import { compose } from "./compose"; -import { server } from "./server"; import { previewDeployments } from "./preview-deployments"; +import { server } from "./server"; export const deploymentStatus = pgEnum("deploymentStatus", [ "running", diff --git a/packages/server/src/db/schema/domain.ts b/packages/server/src/db/schema/domain.ts index f115ce66..5c34d455 100644 --- a/packages/server/src/db/schema/domain.ts +++ b/packages/server/src/db/schema/domain.ts @@ -14,8 +14,8 @@ import { z } from "zod"; import { domain } from "../validations/domain"; import { applications } from "./application"; import { compose } from "./compose"; -import { certificateType } from "./shared"; import { previewDeployments } from "./preview-deployments"; +import { certificateType } from "./shared"; export const domainType = pgEnum("domainType", [ "compose", diff --git a/packages/server/src/db/schema/index.ts b/packages/server/src/db/schema/index.ts index f07a1870..9c7a079c 100644 --- a/packages/server/src/db/schema/index.ts +++ b/packages/server/src/db/schema/index.ts @@ -29,4 +29,4 @@ export * from "./github"; export * from "./gitlab"; export * from "./server"; export * from "./utils"; -export * from "./preview-deployments"; \ No newline at end of file +export * from "./preview-deployments"; diff --git a/packages/server/src/db/schema/preview-deployments.ts b/packages/server/src/db/schema/preview-deployments.ts index 5d0671e8..3bdab2c2 100644 --- a/packages/server/src/db/schema/preview-deployments.ts +++ b/packages/server/src/db/schema/preview-deployments.ts @@ -1,13 +1,13 @@ import { relations } from "drizzle-orm"; import { pgTable, text } from "drizzle-orm/pg-core"; -import { nanoid } from "nanoid"; -import { applications } from "./application"; -import { domains } from "./domain"; -import { deployments } from "./deployment"; import { createInsertSchema } from "drizzle-zod"; +import { nanoid } from "nanoid"; import { z } from "zod"; -import { generateAppName } from "./utils"; +import { applications } from "./application"; +import { deployments } from "./deployment"; +import { domains } from "./domain"; import { applicationStatus } from "./shared"; +import { generateAppName } from "./utils"; export const previewDeployments = pgTable("preview_deployments", { previewDeploymentId: text("previewDeploymentId") diff --git a/packages/server/src/utils/backups/index.ts b/packages/server/src/utils/backups/index.ts index 797feb38..6fec7a31 100644 --- a/packages/server/src/utils/backups/index.ts +++ b/packages/server/src/utils/backups/index.ts @@ -7,12 +7,12 @@ import { cleanUpSystemPrune, cleanUpUnusedImages, } from "../docker/utils"; +import { sendDatabaseBackupNotifications } from "../notifications/database-backup"; +import { sendDockerCleanupNotifications } from "../notifications/docker-cleanup"; import { runMariadbBackup } from "./mariadb"; import { runMongoBackup } from "./mongo"; import { runMySqlBackup } from "./mysql"; import { runPostgresBackup } from "./postgres"; -import { sendDockerCleanupNotifications } from "../notifications/docker-cleanup"; -import { sendDatabaseBackupNotifications } from "../notifications/database-backup"; export const initCronJobs = async () => { console.log("Setting up cron jobs...."); diff --git a/packages/server/src/utils/builders/index.ts b/packages/server/src/utils/builders/index.ts index ce10413a..df85d98f 100644 --- a/packages/server/src/utils/builders/index.ts +++ b/packages/server/src/utils/builders/index.ts @@ -2,6 +2,7 @@ import { createWriteStream } from "node:fs"; import { join } from "node:path"; import type { InferResultType } from "@dokploy/server/types/with"; import type { CreateServiceOptions } from "dockerode"; +import { nanoid } from "nanoid"; import { uploadImage, uploadImageRemoteCommand } from "../cluster/upload"; import { calculateResources, @@ -17,7 +18,6 @@ import { buildHeroku, getHerokuCommand } from "./heroku"; import { buildNixpacks, getNixpacksCommand } from "./nixpacks"; import { buildPaketo, getPaketoCommand } from "./paketo"; import { buildStatic, getStaticCommand } from "./static"; -import { nanoid } from "nanoid"; // NIXPACKS codeDirectory = where is the path of the code directory // HEROKU codeDirectory = where is the path of the code directory diff --git a/packages/server/src/utils/docker/utils.ts b/packages/server/src/utils/docker/utils.ts index e8c9e6c2..1c4a38ab 100644 --- a/packages/server/src/utils/docker/utils.ts +++ b/packages/server/src/utils/docker/utils.ts @@ -15,528 +15,528 @@ import { spawnAsync } from "../process/spawnAsync"; import { getRemoteDocker } from "../servers/remote-docker"; interface RegistryAuth { - username: string; - password: string; - registryUrl: string; + username: string; + password: string; + registryUrl: string; } export const pullImage = async ( - dockerImage: string, - onData?: (data: any) => void, - authConfig?: Partial + dockerImage: string, + onData?: (data: any) => void, + authConfig?: Partial, ): Promise => { - try { - if (!dockerImage) { - throw new Error("Docker image not found"); - } + try { + if (!dockerImage) { + throw new Error("Docker image not found"); + } - if (authConfig?.username && authConfig?.password) { - await spawnAsync( - "docker", - [ - "login", - authConfig.registryUrl || "", - "-u", - authConfig.username, - "-p", - authConfig.password, - ], - onData - ); - } - await spawnAsync("docker", ["pull", dockerImage], onData); - } catch (error) { - throw error; - } + if (authConfig?.username && authConfig?.password) { + await spawnAsync( + "docker", + [ + "login", + authConfig.registryUrl || "", + "-u", + authConfig.username, + "-p", + authConfig.password, + ], + onData, + ); + } + await spawnAsync("docker", ["pull", dockerImage], onData); + } catch (error) { + throw error; + } }; export const pullRemoteImage = async ( - dockerImage: string, - serverId: string, - onData?: (data: any) => void, - authConfig?: Partial + dockerImage: string, + serverId: string, + onData?: (data: any) => void, + authConfig?: Partial, ): Promise => { - try { - if (!dockerImage) { - throw new Error("Docker image not found"); - } + try { + if (!dockerImage) { + throw new Error("Docker image not found"); + } - const remoteDocker = await getRemoteDocker(serverId); + const remoteDocker = await getRemoteDocker(serverId); - await new Promise((resolve, reject) => { - remoteDocker.pull( - dockerImage, - { authconfig: authConfig }, - (err, stream) => { - if (err) { - reject(err); - return; - } + await new Promise((resolve, reject) => { + remoteDocker.pull( + dockerImage, + { authconfig: authConfig }, + (err, stream) => { + if (err) { + reject(err); + return; + } - remoteDocker.modem.followProgress( - stream as Readable, - (err: Error | null, res) => { - if (!err) { - resolve(res); - } - if (err) { - reject(err); - } - }, - (event) => { - onData?.(event); - } - ); - } - ); - }); - } catch (error) { - throw error; - } + remoteDocker.modem.followProgress( + stream as Readable, + (err: Error | null, res) => { + if (!err) { + resolve(res); + } + if (err) { + reject(err); + } + }, + (event) => { + onData?.(event); + }, + ); + }, + ); + }); + } catch (error) { + throw error; + } }; export const containerExists = async (containerName: string) => { - const container = docker.getContainer(containerName); - try { - await container.inspect(); - return true; - } catch (error) { - return false; - } + const container = docker.getContainer(containerName); + try { + await container.inspect(); + return true; + } catch (error) { + return false; + } }; export const stopService = async (appName: string) => { - try { - await execAsync(`docker service scale ${appName}=0 `); - } catch (error) { - console.error(error); - return error; - } + try { + await execAsync(`docker service scale ${appName}=0 `); + } catch (error) { + console.error(error); + return error; + } }; export const stopServiceRemote = async (serverId: string, appName: string) => { - try { - await execAsyncRemote(serverId, `docker service scale ${appName}=0 `); - } catch (error) { - console.error(error); - return error; - } + try { + await execAsyncRemote(serverId, `docker service scale ${appName}=0 `); + } catch (error) { + console.error(error); + return error; + } }; export const getContainerByName = (name: string): Promise => { - const opts = { - limit: 1, - filters: { - name: [name], - }, - }; - return new Promise((resolve, reject) => { - docker.listContainers(opts, (err, containers) => { - if (err) { - reject(err); - } else if (containers?.length === 0) { - reject(new Error(`No container found with name: ${name}`)); - } else if (containers && containers?.length > 0 && containers[0]) { - resolve(containers[0]); - } - }); - }); + const opts = { + limit: 1, + filters: { + name: [name], + }, + }; + return new Promise((resolve, reject) => { + docker.listContainers(opts, (err, containers) => { + if (err) { + reject(err); + } else if (containers?.length === 0) { + reject(new Error(`No container found with name: ${name}`)); + } else if (containers && containers?.length > 0 && containers[0]) { + resolve(containers[0]); + } + }); + }); }; export const cleanUpUnusedImages = async (serverId?: string) => { - try { - if (serverId) { - await execAsyncRemote(serverId, "docker image prune --all --force"); - } else { - await execAsync("docker image prune --all --force"); - } - } catch (error) { - console.error(error); - throw error; - } + try { + if (serverId) { + await execAsyncRemote(serverId, "docker image prune --all --force"); + } else { + await execAsync("docker image prune --all --force"); + } + } catch (error) { + console.error(error); + throw error; + } }; export const cleanStoppedContainers = async (serverId?: string) => { - try { - if (serverId) { - await execAsyncRemote(serverId, "docker container prune --force"); - } else { - await execAsync("docker container prune --force"); - } - } catch (error) { - console.error(error); - throw error; - } + try { + if (serverId) { + await execAsyncRemote(serverId, "docker container prune --force"); + } else { + await execAsync("docker container prune --force"); + } + } catch (error) { + console.error(error); + throw error; + } }; export const cleanUpUnusedVolumes = async (serverId?: string) => { - try { - if (serverId) { - await execAsyncRemote(serverId, "docker volume prune --all --force"); - } else { - await execAsync("docker volume prune --all --force"); - } - } catch (error) { - console.error(error); - throw error; - } + try { + if (serverId) { + await execAsyncRemote(serverId, "docker volume prune --all --force"); + } else { + await execAsync("docker volume prune --all --force"); + } + } catch (error) { + console.error(error); + throw error; + } }; export const cleanUpInactiveContainers = async () => { - try { - const containers = await docker.listContainers({ all: true }); - const inactiveContainers = containers.filter( - (container) => container.State !== "running" - ); + try { + const containers = await docker.listContainers({ all: true }); + const inactiveContainers = containers.filter( + (container) => container.State !== "running", + ); - for (const container of inactiveContainers) { - await docker.getContainer(container.Id).remove({ force: true }); - console.log(`Cleaning up inactive container: ${container.Id}`); - } - } catch (error) { - console.error("Error cleaning up inactive containers:", error); - throw error; - } + for (const container of inactiveContainers) { + await docker.getContainer(container.Id).remove({ force: true }); + console.log(`Cleaning up inactive container: ${container.Id}`); + } + } catch (error) { + console.error("Error cleaning up inactive containers:", error); + throw error; + } }; export const cleanUpDockerBuilder = async (serverId?: string) => { - if (serverId) { - await execAsyncRemote(serverId, "docker builder prune --all --force"); - } else { - await execAsync("docker builder prune --all --force"); - } + if (serverId) { + await execAsyncRemote(serverId, "docker builder prune --all --force"); + } else { + await execAsync("docker builder prune --all --force"); + } }; export const cleanUpSystemPrune = async (serverId?: string) => { - if (serverId) { - await execAsyncRemote( - serverId, - "docker system prune --all --force --volumes" - ); - } else { - await execAsync("docker system prune --all --force --volumes"); - } + if (serverId) { + await execAsyncRemote( + serverId, + "docker system prune --all --force --volumes", + ); + } else { + await execAsync("docker system prune --all --force --volumes"); + } }; export const startService = async (appName: string) => { - try { - await execAsync(`docker service scale ${appName}=1 `); - } catch (error) { - console.error(error); - throw error; - } + try { + await execAsync(`docker service scale ${appName}=1 `); + } catch (error) { + console.error(error); + throw error; + } }; export const startServiceRemote = async (serverId: string, appName: string) => { - try { - await execAsyncRemote(serverId, `docker service scale ${appName}=1 `); - } catch (error) { - console.error(error); - throw error; - } + try { + await execAsyncRemote(serverId, `docker service scale ${appName}=1 `); + } catch (error) { + console.error(error); + throw error; + } }; export const removeService = async ( - appName: string, - serverId?: string | null, - deleteVolumes = false + appName: string, + serverId?: string | null, + deleteVolumes = false, ) => { - try { - let command: string; + try { + let command: string; - if (deleteVolumes) { - command = `docker service rm --force ${appName}`; - } else { - command = `docker service rm ${appName}`; - } + if (deleteVolumes) { + command = `docker service rm --force ${appName}`; + } else { + command = `docker service rm ${appName}`; + } - if (serverId) { - await execAsyncRemote(serverId, command); - } else { - await execAsync(command); - } - } catch (error) { - return error; - } + if (serverId) { + await execAsyncRemote(serverId, command); + } else { + await execAsync(command); + } + } catch (error) { + return error; + } }; export const prepareEnvironmentVariables = ( - serviceEnv: string | null, - projectEnv?: string | null + serviceEnv: string | null, + projectEnv?: string | null, ) => { - const projectVars = parse(projectEnv ?? ""); - const serviceVars = parse(serviceEnv ?? ""); + const projectVars = parse(projectEnv ?? ""); + const serviceVars = parse(serviceEnv ?? ""); - const resolvedVars = Object.entries(serviceVars).map(([key, value]) => { - let resolvedValue = value; - if (projectVars) { - resolvedValue = value.replace(/\$\{\{project\.(.*?)\}\}/g, (_, ref) => { - if (projectVars[ref] !== undefined) { - return projectVars[ref]; - } - throw new Error(`Invalid project environment variable: project.${ref}`); - }); - } - return `${key}=${resolvedValue}`; - }); + const resolvedVars = Object.entries(serviceVars).map(([key, value]) => { + let resolvedValue = value; + if (projectVars) { + resolvedValue = value.replace(/\$\{\{project\.(.*?)\}\}/g, (_, ref) => { + if (projectVars[ref] !== undefined) { + return projectVars[ref]; + } + throw new Error(`Invalid project environment variable: project.${ref}`); + }); + } + return `${key}=${resolvedValue}`; + }); - return resolvedVars; + return resolvedVars; }; export const prepareBuildArgs = (input: string | null) => { - const pairs = (input ?? "").split("\n"); + const pairs = (input ?? "").split("\n"); - const jsonObject: Record = {}; + const jsonObject: Record = {}; - for (const pair of pairs) { - const [key, value] = pair.split("="); - if (key && value) { - jsonObject[key] = value; - } - } + for (const pair of pairs) { + const [key, value] = pair.split("="); + if (key && value) { + jsonObject[key] = value; + } + } - return jsonObject; + return jsonObject; }; export const generateVolumeMounts = (mounts: ApplicationNested["mounts"]) => { - if (!mounts || mounts.length === 0) { - return []; - } + if (!mounts || mounts.length === 0) { + return []; + } - return mounts - .filter((mount) => mount.type === "volume") - .map((mount) => ({ - Type: "volume" as const, - Source: mount.volumeName || "", - Target: mount.mountPath, - })); + return mounts + .filter((mount) => mount.type === "volume") + .map((mount) => ({ + Type: "volume" as const, + Source: mount.volumeName || "", + Target: mount.mountPath, + })); }; type Resources = { - memoryLimit: number | null; - memoryReservation: number | null; - cpuLimit: number | null; - cpuReservation: number | null; + memoryLimit: number | null; + memoryReservation: number | null; + cpuLimit: number | null; + cpuReservation: number | null; }; export const calculateResources = ({ - memoryLimit, - memoryReservation, - cpuLimit, - cpuReservation, + memoryLimit, + memoryReservation, + cpuLimit, + cpuReservation, }: Resources): ResourceRequirements => { - return { - Limits: { - MemoryBytes: memoryLimit ?? undefined, - NanoCPUs: cpuLimit ?? undefined, - }, - Reservations: { - MemoryBytes: memoryReservation ?? undefined, - NanoCPUs: cpuReservation ?? undefined, - }, - }; + return { + Limits: { + MemoryBytes: memoryLimit ?? undefined, + NanoCPUs: cpuLimit ?? undefined, + }, + Reservations: { + MemoryBytes: memoryReservation ?? undefined, + NanoCPUs: cpuReservation ?? undefined, + }, + }; }; export const generateConfigContainer = (application: ApplicationNested) => { - const { - healthCheckSwarm, - restartPolicySwarm, - placementSwarm, - updateConfigSwarm, - rollbackConfigSwarm, - modeSwarm, - labelsSwarm, - replicas, - mounts, - networkSwarm, - } = application; + const { + healthCheckSwarm, + restartPolicySwarm, + placementSwarm, + updateConfigSwarm, + rollbackConfigSwarm, + modeSwarm, + labelsSwarm, + replicas, + mounts, + networkSwarm, + } = application; - const haveMounts = mounts.length > 0; + const haveMounts = mounts.length > 0; - return { - ...(healthCheckSwarm && { - HealthCheck: healthCheckSwarm, - }), - ...(restartPolicySwarm - ? { - RestartPolicy: restartPolicySwarm, - } - : {}), - ...(placementSwarm - ? { - Placement: placementSwarm, - } - : { - // if app have mounts keep manager as constraint - Placement: { - Constraints: haveMounts ? ["node.role==manager"] : [], - }, - }), - ...(labelsSwarm && { - Labels: labelsSwarm, - }), - ...(modeSwarm - ? { - Mode: modeSwarm, - } - : { - // use replicas value if no modeSwarm provided - Mode: { - Replicated: { - Replicas: replicas, - }, - }, - }), - ...(rollbackConfigSwarm && { - RollbackConfig: rollbackConfigSwarm, - }), - ...(updateConfigSwarm - ? { UpdateConfig: updateConfigSwarm } - : { - // default config if no updateConfigSwarm provided - UpdateConfig: { - Parallelism: 1, - Order: "start-first", - }, - }), - ...(networkSwarm - ? { - Networks: networkSwarm, - } - : { - Networks: [{ Target: "dokploy-network" }], - }), - }; + return { + ...(healthCheckSwarm && { + HealthCheck: healthCheckSwarm, + }), + ...(restartPolicySwarm + ? { + RestartPolicy: restartPolicySwarm, + } + : {}), + ...(placementSwarm + ? { + Placement: placementSwarm, + } + : { + // if app have mounts keep manager as constraint + Placement: { + Constraints: haveMounts ? ["node.role==manager"] : [], + }, + }), + ...(labelsSwarm && { + Labels: labelsSwarm, + }), + ...(modeSwarm + ? { + Mode: modeSwarm, + } + : { + // use replicas value if no modeSwarm provided + Mode: { + Replicated: { + Replicas: replicas, + }, + }, + }), + ...(rollbackConfigSwarm && { + RollbackConfig: rollbackConfigSwarm, + }), + ...(updateConfigSwarm + ? { UpdateConfig: updateConfigSwarm } + : { + // default config if no updateConfigSwarm provided + UpdateConfig: { + Parallelism: 1, + Order: "start-first", + }, + }), + ...(networkSwarm + ? { + Networks: networkSwarm, + } + : { + Networks: [{ Target: "dokploy-network" }], + }), + }; }; export const generateBindMounts = (mounts: ApplicationNested["mounts"]) => { - if (!mounts || mounts.length === 0) { - return []; - } + if (!mounts || mounts.length === 0) { + return []; + } - return mounts - .filter((mount) => mount.type === "bind") - .map((mount) => ({ - Type: "bind" as const, - Source: mount.hostPath || "", - Target: mount.mountPath, - })); + return mounts + .filter((mount) => mount.type === "bind") + .map((mount) => ({ + Type: "bind" as const, + Source: mount.hostPath || "", + Target: mount.mountPath, + })); }; export const generateFileMounts = ( - appName: string, - service: - | ApplicationNested - | MongoNested - | MariadbNested - | MysqlNested - | PostgresNested - | RedisNested + appName: string, + service: + | ApplicationNested + | MongoNested + | MariadbNested + | MysqlNested + | PostgresNested + | RedisNested, ) => { - const { mounts } = service; - const { APPLICATIONS_PATH } = paths(!!service.serverId); - if (!mounts || mounts.length === 0) { - return []; - } + const { mounts } = service; + const { APPLICATIONS_PATH } = paths(!!service.serverId); + if (!mounts || mounts.length === 0) { + return []; + } - return mounts - .filter((mount) => mount.type === "file") - .map((mount) => { - const fileName = mount.filePath; - const absoluteBasePath = path.resolve(APPLICATIONS_PATH); - const directory = path.join(absoluteBasePath, appName, "files"); - const sourcePath = path.join(directory, fileName || ""); - return { - Type: "bind" as const, - Source: sourcePath, - Target: mount.mountPath, - }; - }); + return mounts + .filter((mount) => mount.type === "file") + .map((mount) => { + const fileName = mount.filePath; + const absoluteBasePath = path.resolve(APPLICATIONS_PATH); + const directory = path.join(absoluteBasePath, appName, "files"); + const sourcePath = path.join(directory, fileName || ""); + return { + Type: "bind" as const, + Source: sourcePath, + Target: mount.mountPath, + }; + }); }; export const createFile = async ( - outputPath: string, - filePath: string, - content: string + outputPath: string, + filePath: string, + content: string, ) => { - try { - const fullPath = path.join(outputPath, filePath); - if (fullPath.endsWith(path.sep) || filePath.endsWith("/")) { - fs.mkdirSync(fullPath, { recursive: true }); - return; - } + try { + const fullPath = path.join(outputPath, filePath); + if (fullPath.endsWith(path.sep) || filePath.endsWith("/")) { + fs.mkdirSync(fullPath, { recursive: true }); + return; + } - const directory = path.dirname(fullPath); - fs.mkdirSync(directory, { recursive: true }); - fs.writeFileSync(fullPath, content || ""); - } catch (error) { - throw error; - } + const directory = path.dirname(fullPath); + fs.mkdirSync(directory, { recursive: true }); + fs.writeFileSync(fullPath, content || ""); + } catch (error) { + throw error; + } }; export const encodeBase64 = (content: string) => - Buffer.from(content, "utf-8").toString("base64"); + Buffer.from(content, "utf-8").toString("base64"); export const getCreateFileCommand = ( - outputPath: string, - filePath: string, - content: string + outputPath: string, + filePath: string, + content: string, ) => { - const fullPath = path.join(outputPath, filePath); - if (fullPath.endsWith(path.sep) || filePath.endsWith("/")) { - return `mkdir -p ${fullPath};`; - } + const fullPath = path.join(outputPath, filePath); + if (fullPath.endsWith(path.sep) || filePath.endsWith("/")) { + return `mkdir -p ${fullPath};`; + } - const directory = path.dirname(fullPath); - const encodedContent = encodeBase64(content); - return ` + const directory = path.dirname(fullPath); + const encodedContent = encodeBase64(content); + return ` mkdir -p ${directory}; echo "${encodedContent}" | base64 -d > "${fullPath}"; `; }; export const getServiceContainer = async (appName: string) => { - try { - const filter = { - status: ["running"], - label: [`com.docker.swarm.service.name=${appName}`], - }; + try { + const filter = { + status: ["running"], + label: [`com.docker.swarm.service.name=${appName}`], + }; - const containers = await docker.listContainers({ - filters: JSON.stringify(filter), - }); + const containers = await docker.listContainers({ + filters: JSON.stringify(filter), + }); - if (containers.length === 0 || !containers[0]) { - throw new Error(`No container found with name: ${appName}`); - } + if (containers.length === 0 || !containers[0]) { + throw new Error(`No container found with name: ${appName}`); + } - const container = containers[0]; + const container = containers[0]; - return container; - } catch (error) { - throw error; - } + return container; + } catch (error) { + throw error; + } }; export const getRemoteServiceContainer = async ( - serverId: string, - appName: string + serverId: string, + appName: string, ) => { - try { - const filter = { - status: ["running"], - label: [`com.docker.swarm.service.name=${appName}`], - }; - const remoteDocker = await getRemoteDocker(serverId); - const containers = await remoteDocker.listContainers({ - filters: JSON.stringify(filter), - }); + try { + const filter = { + status: ["running"], + label: [`com.docker.swarm.service.name=${appName}`], + }; + const remoteDocker = await getRemoteDocker(serverId); + const containers = await remoteDocker.listContainers({ + filters: JSON.stringify(filter), + }); - if (containers.length === 0 || !containers[0]) { - throw new Error(`No container found with name: ${appName}`); - } + if (containers.length === 0 || !containers[0]) { + throw new Error(`No container found with name: ${appName}`); + } - const container = containers[0]; + const container = containers[0]; - return container; - } catch (error) { - throw error; - } + return container; + } catch (error) { + throw error; + } };