From 2974a8183e61afaca33d63fd61dbbd7214071559 Mon Sep 17 00:00:00 2001 From: nb5p Date: Sun, 16 Mar 2025 15:37:28 +0800 Subject: [PATCH 01/13] fix(server-setup): resolve Alpine Linux compatibility issues with setup scripts Resolves #1482 --- packages/server/src/setup/server-setup.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/setup/server-setup.ts b/packages/server/src/setup/server-setup.ts index 677ce74ac..aaacdb33c 100644 --- a/packages/server/src/setup/server-setup.ts +++ b/packages/server/src/setup/server-setup.ts @@ -361,7 +361,7 @@ const installUtilities = () => ` alpine) sed -i '/^#.*\/community/s/^#//' /etc/apk/repositories apk update >/dev/null - apk add curl wget git jq openssl >/dev/null + apk add curl wget git jq openssl sudo unzip tar >/dev/null ;; ubuntu | debian | raspbian) DEBIAN_FRONTEND=noninteractive apt-get update -y >/dev/null From 04fd77c3a9a3a4d1cbbcac6a7ff93b1494db7e2d Mon Sep 17 00:00:00 2001 From: Ensar Kurt Date: Mon, 17 Mar 2025 23:42:09 +0300 Subject: [PATCH 02/13] replicas input cannot be zero and empty --- .../application/advanced/cluster/show-cluster-settings.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/dokploy/components/dashboard/application/advanced/cluster/show-cluster-settings.tsx b/apps/dokploy/components/dashboard/application/advanced/cluster/show-cluster-settings.tsx index 1eadf8bab..57f851c9e 100644 --- a/apps/dokploy/components/dashboard/application/advanced/cluster/show-cluster-settings.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/cluster/show-cluster-settings.tsx @@ -40,7 +40,7 @@ interface Props { } const AddRedirectchema = z.object({ - replicas: z.number(), + replicas: z.number().min(1, "Replicas must be at least 1"), registryId: z.string(), }); @@ -130,9 +130,11 @@ export const ShowClusterSettings = ({ applicationId }: Props) => { placeholder="1" {...field} onChange={(e) => { - field.onChange(Number(e.target.value)); + const value = e.target.value; + field.onChange(value === "" ? 0 : Number(value)); }} type="number" + value={field.value || ""} /> From fe57333f846c488e48eb9477d35cbee4e00307aa Mon Sep 17 00:00:00 2001 From: Ensar Kurt Date: Mon, 17 Mar 2025 23:47:54 +0300 Subject: [PATCH 03/13] manage port inputs, default zero fix --- .../settings/web-server/manage-traefik-ports.tsx | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) 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 92ef9f128..d20b7c91a 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 @@ -159,9 +159,11 @@ export const ManageTraefikPorts = ({ children, serverId }: Props) => { - field.onChange(Number(e.target.value)) - } + onChange={(e) => { + const value = e.target.value; + field.onChange(value === "" ? undefined : Number(value)); + }} + value={field.value || ""} className="w-full dark:bg-black" placeholder="e.g. 8080" /> @@ -185,9 +187,11 @@ export const ManageTraefikPorts = ({ children, serverId }: Props) => { - field.onChange(Number(e.target.value)) - } + onChange={(e) => { + const value = e.target.value; + field.onChange(value === "" ? undefined : Number(value)); + }} + value={field.value || ""} className="w-full dark:bg-black" placeholder="e.g. 80" /> From 7cb184dc979c39272421040a6ac4298742b758be Mon Sep 17 00:00:00 2001 From: Ensar Kurt Date: Mon, 17 Mar 2025 23:48:17 +0300 Subject: [PATCH 04/13] email notification port, last digit staying error fix --- .../settings/notifications/handle-notifications.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/dokploy/components/dashboard/settings/notifications/handle-notifications.tsx b/apps/dokploy/components/dashboard/settings/notifications/handle-notifications.tsx index 7a3e286e3..e04765298 100644 --- a/apps/dokploy/components/dashboard/settings/notifications/handle-notifications.tsx +++ b/apps/dokploy/components/dashboard/settings/notifications/handle-notifications.tsx @@ -663,13 +663,16 @@ export const HandleNotifications = ({ notificationId }: Props) => { {...field} onChange={(e) => { const value = e.target.value; - if (value) { + if (value === "") { + field.onChange(undefined); + } else { const port = Number.parseInt(value); if (port > 0 && port < 65536) { field.onChange(port); } } }} + value={field.value || ""} type="number" /> From 3cdf4c426c01842a9f034b17518ec55a6de61fe9 Mon Sep 17 00:00:00 2001 From: Ensar Kurt Date: Tue, 18 Mar 2025 00:05:59 +0300 Subject: [PATCH 05/13] revert commit from #1513 --- apps/dokploy/components/ui/input.tsx | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/apps/dokploy/components/ui/input.tsx b/apps/dokploy/components/ui/input.tsx index 7339d21a2..18b713af5 100644 --- a/apps/dokploy/components/ui/input.tsx +++ b/apps/dokploy/components/ui/input.tsx @@ -39,7 +39,7 @@ const NumberInput = React.forwardRef( className={cn("text-left", className)} ref={ref} {...props} - value={props.value === undefined || props.value === "" ? "" : String(props.value)} + value={props.value === undefined ? undefined : String(props.value)} onChange={(e) => { const value = e.target.value; if (value === "") { @@ -60,21 +60,6 @@ const NumberInput = React.forwardRef( } } }} - onBlur={(e) => { - // If input is empty, make 0 when focus is lost - if (e.target.value === "") { - const syntheticEvent = { - ...e, - target: { - ...e.target, - value: "0", - }, - }; - props.onChange?.( - syntheticEvent as unknown as React.ChangeEvent, - ); - } - }} /> ); }, From c80a31e8c4868cd205df009f6bfd1e36befdca0e Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Mon, 17 Mar 2025 23:16:29 -0600 Subject: [PATCH 06/13] refactor: improve code formatting and structure in ShowGeneralMongo component - Standardized indentation and formatting for better readability. - Enhanced tooltip integration within button elements for improved user experience. - Maintained functionality for deploying, reloading, starting, and stopping MongoDB instances while ensuring consistent code style. --- .../mongo/general/show-general-mongo.tsx | 464 +++++++++--------- 1 file changed, 237 insertions(+), 227 deletions(-) diff --git a/apps/dokploy/components/dashboard/mongo/general/show-general-mongo.tsx b/apps/dokploy/components/dashboard/mongo/general/show-general-mongo.tsx index dfbf501eb..fdc28adc3 100644 --- a/apps/dokploy/components/dashboard/mongo/general/show-general-mongo.tsx +++ b/apps/dokploy/components/dashboard/mongo/general/show-general-mongo.tsx @@ -3,10 +3,10 @@ import { DrawerLogs } from "@/components/shared/drawer-logs"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { - Tooltip, - TooltipContent, - TooltipProvider, - TooltipTrigger, + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, } from "@/components/ui/tooltip"; import { api } from "@/utils/api"; import * as TooltipPrimitive from "@radix-ui/react-tooltip"; @@ -16,236 +16,246 @@ import { toast } from "sonner"; import { type LogLine, parseLogs } from "../../docker/logs/utils"; import { DockerTerminalModal } from "../../settings/web-server/docker-terminal-modal"; interface Props { - mongoId: string; + mongoId: string; } export const ShowGeneralMongo = ({ mongoId }: Props) => { - const { data, refetch } = api.mongo.one.useQuery( - { - mongoId, - }, - { enabled: !!mongoId } - ); + const { data, refetch } = api.mongo.one.useQuery( + { + mongoId, + }, + { enabled: !!mongoId }, + ); - const { mutateAsync: reload, isLoading: isReloading } = - api.mongo.reload.useMutation(); + const { mutateAsync: reload, isLoading: isReloading } = + api.mongo.reload.useMutation(); - const { mutateAsync: start, isLoading: isStarting } = - api.mongo.start.useMutation(); + const { mutateAsync: start, isLoading: isStarting } = + api.mongo.start.useMutation(); - const { mutateAsync: stop, isLoading: isStopping } = - api.mongo.stop.useMutation(); + const { mutateAsync: stop, isLoading: isStopping } = + api.mongo.stop.useMutation(); - const [isDrawerOpen, setIsDrawerOpen] = useState(false); - const [filteredLogs, setFilteredLogs] = useState([]); - const [isDeploying, setIsDeploying] = useState(false); - api.mongo.deployWithLogs.useSubscription( - { - mongoId: mongoId, - }, - { - enabled: isDeploying, - onData(log) { - if (!isDrawerOpen) { - setIsDrawerOpen(true); - } + const [isDrawerOpen, setIsDrawerOpen] = useState(false); + const [filteredLogs, setFilteredLogs] = useState([]); + const [isDeploying, setIsDeploying] = useState(false); + api.mongo.deployWithLogs.useSubscription( + { + mongoId: mongoId, + }, + { + enabled: isDeploying, + onData(log) { + if (!isDrawerOpen) { + setIsDrawerOpen(true); + } - if (log === "Deployment completed successfully!") { - setIsDeploying(false); - } + if (log === "Deployment completed successfully!") { + setIsDeploying(false); + } - const parsedLogs = parseLogs(log); - setFilteredLogs((prev) => [...prev, ...parsedLogs]); - }, - onError(error) { - console.error("Deployment logs error:", error); - setIsDeploying(false); - }, - } - ); - return ( - <> -
- - - Deploy Settings - - - - { - setIsDeploying(true); - await new Promise((resolve) => setTimeout(resolve, 1000)); - refetch(); - }} - > - - - - - - -

Downloads and sets up the MongoDB database

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

Restart the MongoDB service without rebuilding

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

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

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

Stop the currently running MongoDB database

-
-
-
-
- )} -
- - - - - - - -

Open a terminal to the MongoDB container

-
-
-
-
-
-
- { - setIsDrawerOpen(false); - setFilteredLogs([]); - setIsDeploying(false); - refetch(); - }} - filteredLogs={filteredLogs} - /> -
- - ); + const parsedLogs = parseLogs(log); + setFilteredLogs((prev) => [...prev, ...parsedLogs]); + }, + onError(error) { + console.error("Deployment logs error:", error); + setIsDeploying(false); + }, + }, + ); + return ( + <> +
+ + + Deploy Settings + + + + { + setIsDeploying(true); + await new Promise((resolve) => setTimeout(resolve, 1000)); + refetch(); + }} + > + + + { + await reload({ + mongoId: mongoId, + appName: data?.appName || "", + }) + .then(() => { + toast.success("Mongo reloaded successfully"); + refetch(); + }) + .catch(() => { + toast.error("Error reloading Mongo"); + }); + }} + > + + + {data?.applicationStatus === "idle" ? ( + { + await start({ + mongoId: mongoId, + }) + .then(() => { + toast.success("Mongo started successfully"); + refetch(); + }) + .catch(() => { + toast.error("Error starting Mongo"); + }); + }} + > + + + ) : ( + { + await stop({ + mongoId: mongoId, + }) + .then(() => { + toast.success("Mongo stopped successfully"); + refetch(); + }) + .catch(() => { + toast.error("Error stopping Mongo"); + }); + }} + > + + + )} + + + + + + + { + setIsDrawerOpen(false); + setFilteredLogs([]); + setIsDeploying(false); + refetch(); + }} + filteredLogs={filteredLogs} + /> +
+ + ); }; From 0722182650fee325d670cecf36729263226694f5 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Mon, 17 Mar 2025 23:59:39 -0600 Subject: [PATCH 07/13] feat(auth): implement user creation validation and IP update logic - Added validation for user creation to check for existing admin presence and validate x-dokploy-token. - Integrated public IP retrieval for user updates when not in cloud environment. - Enhanced error handling with APIError for better feedback during user creation process. --- packages/server/src/lib/auth.ts | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/packages/server/src/lib/auth.ts b/packages/server/src/lib/auth.ts index 9043f2030..6695756c5 100644 --- a/packages/server/src/lib/auth.ts +++ b/packages/server/src/lib/auth.ts @@ -8,6 +8,10 @@ import { db } from "../db"; import * as schema from "../db/schema"; import { sendEmail } from "../verification/send-verification-email"; import { IS_CLOUD } from "../constants"; +import { getPublicIpWithFallback } from "../wss/utils"; +import { updateUser } from "../services/user"; +import { getUserByToken } from "../services/admin"; +import { APIError } from "better-auth/api"; const { handler, api } = betterAuth({ database: drizzleAdapter(db, { @@ -88,11 +92,40 @@ const { handler, api } = betterAuth({ databaseHooks: { user: { create: { + before: async (_user, context) => { + if (!IS_CLOUD) { + const xDokployToken = + context?.request?.headers?.get("x-dokploy-token"); + if (xDokployToken) { + const user = await getUserByToken(xDokployToken); + if (!user) { + throw new APIError("BAD_REQUEST", { + message: "User not found", + }); + } + } else { + const isAdminPresent = await db.query.member.findFirst({ + where: eq(schema.member.role, "owner"), + }); + if (isAdminPresent) { + throw new APIError("BAD_REQUEST", { + message: "Admin is already created", + }); + } + } + } + }, after: async (user) => { const isAdminPresent = await db.query.member.findFirst({ where: eq(schema.member.role, "owner"), }); + if (!IS_CLOUD) { + await updateUser(user.id, { + serverIp: await getPublicIpWithFallback(), + }); + } + if (IS_CLOUD || !isAdminPresent) { await db.transaction(async (tx) => { const organization = await tx From 6a388fe3706c66ed89b8f2427545a529eac8149d Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Tue, 18 Mar 2025 00:13:55 -0600 Subject: [PATCH 08/13] feat(domain): add validation for traefik.me domain IP address requirement - Implemented a check to ensure an IP address is set for traefik.me domains in the AddDomain and AddDomainCompose components. - Integrated a new API query to determine if traefik.me domains can be generated based on the server's IP address. - Added user feedback through alert messages when the IP address is not configured. --- .../application/domains/add-domain.tsx | 23 +++++++++++++++++++ .../dashboard/compose/domains/add-domain.tsx | 21 +++++++++++++++++ apps/dokploy/server/api/routers/domain.ts | 15 ++++++++++++ 3 files changed, 59 insertions(+) diff --git a/apps/dokploy/components/dashboard/application/domains/add-domain.tsx b/apps/dokploy/components/dashboard/application/domains/add-domain.tsx index f91218cee..8b1fa7e1c 100644 --- a/apps/dokploy/components/dashboard/application/domains/add-domain.tsx +++ b/apps/dokploy/components/dashboard/application/domains/add-domain.tsx @@ -42,6 +42,7 @@ import { domain } from "@/server/db/validations/domain"; import { zodResolver } from "@hookform/resolvers/zod"; import { Dices } from "lucide-react"; import type z from "zod"; +import Link from "next/link"; type Domain = z.infer; @@ -83,6 +84,13 @@ export const AddDomain = ({ const { mutateAsync: generateDomain, isLoading: isLoadingGenerate } = api.domain.generateDomain.useMutation(); + const { data: canGenerateTraefikMeDomains } = + api.domain.canGenerateTraefikMeDomains.useQuery({ + serverId: application?.serverId || "", + }); + + console.log("canGenerateTraefikMeDomains", canGenerateTraefikMeDomains); + const form = useForm({ resolver: zodResolver(domain), defaultValues: { @@ -186,6 +194,21 @@ export const AddDomain = ({ name="host" render={({ field }) => ( + {!canGenerateTraefikMeDomains && + field.value.includes("traefik.me") && ( + + You need to set an IP address in your{" "} + + {application?.serverId + ? "Remote Servers -> Server -> Edit Server -> Update IP Address" + : "Web Server -> Server -> Update Server IP"} + {" "} + to make your traefik.me domain work. + + )} Host
diff --git a/apps/dokploy/components/dashboard/compose/domains/add-domain.tsx b/apps/dokploy/components/dashboard/compose/domains/add-domain.tsx index 9b412c83a..975ce1ffe 100644 --- a/apps/dokploy/components/dashboard/compose/domains/add-domain.tsx +++ b/apps/dokploy/components/dashboard/compose/domains/add-domain.tsx @@ -42,6 +42,7 @@ import { domainCompose } from "@/server/db/validations/domain"; import { zodResolver } from "@hookform/resolvers/zod"; import { DatabaseZap, Dices, RefreshCw } from "lucide-react"; import type z from "zod"; +import Link from "next/link"; type Domain = z.infer; @@ -102,6 +103,11 @@ export const AddDomainCompose = ({ ? api.domain.update.useMutation() : api.domain.create.useMutation(); + const { data: canGenerateTraefikMeDomains } = + api.domain.canGenerateTraefikMeDomains.useQuery({ + serverId: compose?.serverId || "", + }); + const form = useForm({ resolver: zodResolver(domainCompose), defaultValues: { @@ -313,6 +319,21 @@ export const AddDomainCompose = ({ name="host" render={({ field }) => ( + {!canGenerateTraefikMeDomains && + field.value.includes("traefik.me") && ( + + You need to set an IP address in your{" "} + + {compose?.serverId + ? "Remote Servers -> Server -> Edit Server -> Update IP Address" + : "Web Server -> Server -> Update Server IP"} + {" "} + to make your traefik.me domain work. + + )} Host
diff --git a/apps/dokploy/server/api/routers/domain.ts b/apps/dokploy/server/api/routers/domain.ts index aac2a016f..9e81bee16 100644 --- a/apps/dokploy/server/api/routers/domain.ts +++ b/apps/dokploy/server/api/routers/domain.ts @@ -13,7 +13,9 @@ import { findDomainById, findDomainsByApplicationId, findDomainsByComposeId, + findOrganizationById, findPreviewDeploymentById, + findServerById, generateTraefikMeDomain, manageDomain, removeDomain, @@ -94,6 +96,19 @@ export const domainRouter = createTRPCRouter({ input.serverId, ); }), + canGenerateTraefikMeDomains: protectedProcedure + .input(z.object({ serverId: z.string() })) + .query(async ({ input, ctx }) => { + const organization = await findOrganizationById( + ctx.session.activeOrganizationId, + ); + + if (input.serverId) { + const server = await findServerById(input.serverId); + return server.ipAddress; + } + return organization?.owner.serverIp; + }), update: protectedProcedure .input(apiUpdateDomain) From 4fa5e10789cf7e3f73feb300b965dd9055f3a0d2 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Tue, 18 Mar 2025 00:18:39 -0600 Subject: [PATCH 09/13] chore(package): bump version to v0.20.6 --- apps/dokploy/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/package.json b/apps/dokploy/package.json index 84d248975..cf46d3acc 100644 --- a/apps/dokploy/package.json +++ b/apps/dokploy/package.json @@ -1,6 +1,6 @@ { "name": "dokploy", - "version": "v0.20.5", + "version": "v0.20.6", "private": true, "license": "Apache-2.0", "type": "module", From ea6cfc9d29ed270a113729890d7a6bb8d2d68bc5 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Tue, 18 Mar 2025 00:47:50 -0600 Subject: [PATCH 10/13] feat(backup): enhance RestoreBackup component and API to include serverId - Added serverId prop to RestoreBackup component for better context during backup restoration. - Updated ShowBackups component to pass serverId from the Postgres object. - Modified backup API to handle serverId, allowing remote execution of backup commands when specified. - Improved file display in RestoreBackup for better user experience. --- .../database/backups/restore-backup.tsx | 12 ++++++++++-- .../dashboard/database/backups/show-backups.tsx | 12 ++++++++++-- apps/dokploy/server/api/routers/backup.ts | 17 +++++++++++++++-- 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/apps/dokploy/components/dashboard/database/backups/restore-backup.tsx b/apps/dokploy/components/dashboard/database/backups/restore-backup.tsx index c761fc701..5dcd77327 100644 --- a/apps/dokploy/components/dashboard/database/backups/restore-backup.tsx +++ b/apps/dokploy/components/dashboard/database/backups/restore-backup.tsx @@ -48,6 +48,7 @@ import { toast } from "sonner"; interface Props { databaseId: string; databaseType: Exclude; + serverId: string | null; } const RestoreBackupSchema = z.object({ @@ -76,7 +77,11 @@ const RestoreBackupSchema = z.object({ type RestoreBackup = z.infer; -export const RestoreBackup = ({ databaseId, databaseType }: Props) => { +export const RestoreBackup = ({ + databaseId, + databaseType, + serverId, +}: Props) => { const [isOpen, setIsOpen] = useState(false); const [search, setSearch] = useState(""); @@ -101,6 +106,7 @@ export const RestoreBackup = ({ databaseId, databaseType }: Props) => { { destinationId: destionationId, search, + serverId: serverId ?? "", }, { enabled: isOpen && !!destionationId, @@ -304,7 +310,9 @@ export const RestoreBackup = ({ databaseId, databaseType }: Props) => { form.setValue("backupFile", file); }} > - {file} +
+ {file} +
{ {postgres && postgres?.backups?.length > 0 && (
- +
)} @@ -108,7 +112,11 @@ export const ShowBackups = ({ id, type }: Props) => { databaseType={type} refetch={refetch} /> - +
) : ( diff --git a/apps/dokploy/server/api/routers/backup.ts b/apps/dokploy/server/api/routers/backup.ts index 8e585b7c9..9ed8c6f93 100644 --- a/apps/dokploy/server/api/routers/backup.ts +++ b/apps/dokploy/server/api/routers/backup.ts @@ -31,7 +31,10 @@ import { import { TRPCError } from "@trpc/server"; import { z } from "zod"; -import { execAsync } from "@dokploy/server/utils/process/execAsync"; +import { + execAsync, + execAsyncRemote, +} from "@dokploy/server/utils/process/execAsync"; import { getS3Credentials } from "@dokploy/server/utils/backups/utils"; import { findDestinationById } from "@dokploy/server/services/destination"; import { @@ -229,6 +232,7 @@ export const backupRouter = createTRPCRouter({ z.object({ destinationId: z.string(), search: z.string(), + serverId: z.string().optional(), }), ) .query(async ({ input }) => { @@ -250,7 +254,16 @@ export const backupRouter = createTRPCRouter({ const searchPath = baseDir ? `${bucketPath}/${baseDir}` : bucketPath; const listCommand = `rclone lsf ${rcloneFlags.join(" ")} "${searchPath}" | head -n 100`; - const { stdout } = await execAsync(listCommand); + let stdout = ""; + + if (input.serverId) { + const result = await execAsyncRemote(listCommand, input.serverId); + stdout = result.stdout; + } else { + const result = await execAsync(listCommand); + stdout = result.stdout; + } + const files = stdout.split("\n").filter(Boolean); const results = baseDir From 891dc840f5631e4288aab246e8c4a503ba55a94d Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Tue, 18 Mar 2025 21:11:50 -0600 Subject: [PATCH 11/13] feat(cluster): enhance node management UI with loading indicators and improved tab content - Added loading indicators in AddManager and AddWorker components to enhance user experience during data fetching. - Updated AddNode component to include overflow handling for tab content. - Renamed "Show Nodes" to "Show Swarm Nodes" in ShowNodesModal for clarity. --- .../settings/cluster/nodes/add-node.tsx | 4 +- .../cluster/nodes/manager/add-manager.tsx | 101 ++++++++++-------- .../cluster/nodes/show-nodes-modal.tsx | 2 +- .../cluster/nodes/workers/add-worker.tsx | 97 +++++++++-------- 4 files changed, 108 insertions(+), 96 deletions(-) diff --git a/apps/dokploy/components/dashboard/settings/cluster/nodes/add-node.tsx b/apps/dokploy/components/dashboard/settings/cluster/nodes/add-node.tsx index eaa9851fc..a59681ba0 100644 --- a/apps/dokploy/components/dashboard/settings/cluster/nodes/add-node.tsx +++ b/apps/dokploy/components/dashboard/settings/cluster/nodes/add-node.tsx @@ -56,10 +56,10 @@ export const AddNode = ({ serverId }: Props) => { Worker Manager - + - + diff --git a/apps/dokploy/components/dashboard/settings/cluster/nodes/manager/add-manager.tsx b/apps/dokploy/components/dashboard/settings/cluster/nodes/manager/add-manager.tsx index 055c3f1cc..6cb8f257c 100644 --- a/apps/dokploy/components/dashboard/settings/cluster/nodes/manager/add-manager.tsx +++ b/apps/dokploy/components/dashboard/settings/cluster/nodes/manager/add-manager.tsx @@ -6,7 +6,7 @@ import { } from "@/components/ui/dialog"; import { api } from "@/utils/api"; import copy from "copy-to-clipboard"; -import { CopyIcon } from "lucide-react"; +import { CopyIcon, Loader2 } from "lucide-react"; import { toast } from "sonner"; interface Props { @@ -14,56 +14,63 @@ interface Props { } export const AddManager = ({ serverId }: Props) => { - const { data } = api.cluster.addManager.useQuery({ serverId }); + const { data, isLoading } = api.cluster.addManager.useQuery({ serverId }); return ( <> -
- - - Add a new manager - Add a new manager - -
- 1. Go to your new server and run the following command - - curl https://get.docker.com | sh -s -- --version {data?.version} - - -
+ + + Add a new manager + Add a new manager + + {isLoading ? ( + + ) : ( + <> +
+ + 1. Go to your new server and run the following command + + + curl https://get.docker.com | sh -s -- --version {data?.version} + + +
-
- - 2. Run the following command to add the node(manager) to your - cluster - - - {data?.command} - - -
-
-
+
+ + 2. Run the following command to add the node(manager) to your + cluster + + + + {data?.command} + + +
+ + )} + ); }; diff --git a/apps/dokploy/components/dashboard/settings/cluster/nodes/show-nodes-modal.tsx b/apps/dokploy/components/dashboard/settings/cluster/nodes/show-nodes-modal.tsx index 32339e668..82e6e1f9a 100644 --- a/apps/dokploy/components/dashboard/settings/cluster/nodes/show-nodes-modal.tsx +++ b/apps/dokploy/components/dashboard/settings/cluster/nodes/show-nodes-modal.tsx @@ -17,7 +17,7 @@ export const ShowNodesModal = ({ serverId }: Props) => { className="w-full cursor-pointer " onSelect={(e) => e.preventDefault()} > - Show Nodes + Show Swarm Nodes diff --git a/apps/dokploy/components/dashboard/settings/cluster/nodes/workers/add-worker.tsx b/apps/dokploy/components/dashboard/settings/cluster/nodes/workers/add-worker.tsx index 05f9838e7..3dec559e2 100644 --- a/apps/dokploy/components/dashboard/settings/cluster/nodes/workers/add-worker.tsx +++ b/apps/dokploy/components/dashboard/settings/cluster/nodes/workers/add-worker.tsx @@ -6,7 +6,7 @@ import { } from "@/components/ui/dialog"; import { api } from "@/utils/api"; import copy from "copy-to-clipboard"; -import { CopyIcon } from "lucide-react"; +import { CopyIcon, Loader2 } from "lucide-react"; import { toast } from "sonner"; interface Props { @@ -14,54 +14,59 @@ interface Props { } export const AddWorker = ({ serverId }: Props) => { - const { data } = api.cluster.addWorker.useQuery({ serverId }); + const { data, isLoading } = api.cluster.addWorker.useQuery({ serverId }); return ( -
- - - Add a new worker - Add a new worker - -
- 1. Go to your new server and run the following command - - curl https://get.docker.com | sh -s -- --version {data?.version} - - -
+ + + Add a new worker + Add a new worker + + {isLoading ? ( + + ) : ( + <> +
+ 1. Go to your new server and run the following command + + curl https://get.docker.com | sh -s -- --version {data?.version} + + +
-
- - 2. Run the following command to add the node(worker) to your cluster - +
+ + 2. Run the following command to add the node(worker) to your + cluster + - - {data?.command} - - -
- -
+ + {data?.command} + + +
+ + )} + ); }; From 7123b9b109df2388ce36dda76f23e3c6425370dd Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Tue, 18 Mar 2025 21:17:11 -0600 Subject: [PATCH 12/13] feat(cluster): add error handling in AddManager and AddWorker components - Integrated error handling in AddManager and AddWorker components to display error messages using AlertBlock when data fetching fails. - Updated API query hooks to include error and isError states for improved user feedback during data operations. --- .../settings/cluster/nodes/manager/add-manager.tsx | 6 +++++- .../dashboard/settings/cluster/nodes/workers/add-worker.tsx | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/apps/dokploy/components/dashboard/settings/cluster/nodes/manager/add-manager.tsx b/apps/dokploy/components/dashboard/settings/cluster/nodes/manager/add-manager.tsx index 6cb8f257c..bb2064d4e 100644 --- a/apps/dokploy/components/dashboard/settings/cluster/nodes/manager/add-manager.tsx +++ b/apps/dokploy/components/dashboard/settings/cluster/nodes/manager/add-manager.tsx @@ -1,3 +1,4 @@ +import { AlertBlock } from "@/components/shared/alert-block"; import { CardContent } from "@/components/ui/card"; import { DialogDescription, @@ -14,7 +15,9 @@ interface Props { } export const AddManager = ({ serverId }: Props) => { - const { data, isLoading } = api.cluster.addManager.useQuery({ serverId }); + const { data, isLoading, error, isError } = api.cluster.addManager.useQuery({ + serverId, + }); return ( <> @@ -23,6 +26,7 @@ export const AddManager = ({ serverId }: Props) => { Add a new manager Add a new manager + {isError && {error?.message}} {isLoading ? ( ) : ( diff --git a/apps/dokploy/components/dashboard/settings/cluster/nodes/workers/add-worker.tsx b/apps/dokploy/components/dashboard/settings/cluster/nodes/workers/add-worker.tsx index 3dec559e2..2623081bb 100644 --- a/apps/dokploy/components/dashboard/settings/cluster/nodes/workers/add-worker.tsx +++ b/apps/dokploy/components/dashboard/settings/cluster/nodes/workers/add-worker.tsx @@ -1,3 +1,4 @@ +import { AlertBlock } from "@/components/shared/alert-block"; import { CardContent } from "@/components/ui/card"; import { DialogDescription, @@ -14,7 +15,9 @@ interface Props { } export const AddWorker = ({ serverId }: Props) => { - const { data, isLoading } = api.cluster.addWorker.useQuery({ serverId }); + const { data, isLoading, error, isError } = api.cluster.addWorker.useQuery({ + serverId, + }); return ( @@ -22,6 +25,7 @@ export const AddWorker = ({ serverId }: Props) => { Add a new worker Add a new worker + {isError && {error?.message}} {isLoading ? ( ) : ( From 6e28196b0e1b7066f2b994f0da727db6652cd782 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Tue, 18 Mar 2025 21:36:39 -0600 Subject: [PATCH 13/13] chore(package): bump version to v0.20.7 --- apps/dokploy/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/package.json b/apps/dokploy/package.json index cf46d3acc..1d7ad1311 100644 --- a/apps/dokploy/package.json +++ b/apps/dokploy/package.json @@ -1,6 +1,6 @@ { "name": "dokploy", - "version": "v0.20.6", + "version": "v0.20.7", "private": true, "license": "Apache-2.0", "type": "module",