From 8d88a34a6459eb99674ab7d238695b0efd6d9b44 Mon Sep 17 00:00:00 2001 From: Volodymyr Kravchuk Date: Wed, 13 May 2026 10:03:29 +0300 Subject: [PATCH] fix: copy Dokploy server IP when clicking server badge (#4390) * fix: copy Dokploy server IP when clicking server badge When a service runs on the local Dokploy server (no remote server), clicking the server badge did nothing because `data.server` is null. Now falls back to the server IP from settings so the badge always copies an IP address. Co-Authored-By: Claude Opus 4.6 (1M context) * feat(copy-ip): implement IP address copying functionality across database service components - Added the ability to copy the server IP address to the clipboard when clicking the server badge in various database service components (Libsql, MariaDB, MongoDB, MySQL, PostgreSQL, Redis). - Integrated the `copy-to-clipboard` library and `sonner` for user feedback upon successful copy action. - Ensured fallback to the server IP from settings when the service data is not available, enhancing user experience and functionality. --------- Co-authored-by: Claude Opus 4.6 (1M context) Co-authored-by: Mauricio Siu --- .../services/application/[applicationId].tsx | 6 ++++-- .../[environmentId]/services/compose/[composeId].tsx | 6 ++++-- .../[environmentId]/services/libsql/[libsqlId].tsx | 11 +++++++++++ .../[environmentId]/services/mariadb/[mariadbId].tsx | 11 +++++++++++ .../[environmentId]/services/mongo/[mongoId].tsx | 11 +++++++++++ .../[environmentId]/services/mysql/[mysqlId].tsx | 11 +++++++++++ .../services/postgres/[postgresId].tsx | 11 +++++++++++ .../[environmentId]/services/redis/[redisId].tsx | 11 +++++++++++ 8 files changed, 74 insertions(+), 4 deletions(-) diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/application/[applicationId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/application/[applicationId].tsx index 4c34e82ea..998c2870a 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/application/[applicationId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/application/[applicationId].tsx @@ -93,6 +93,7 @@ const Service = ( ); const { data: isCloud } = api.settings.isCloud.useQuery(); + const { data: serverIp } = api.settings.getIp.useQuery(); const { data: auth } = api.user.get.useQuery(); const { data: permissions } = api.user.getPermissions.useQuery(); @@ -147,8 +148,9 @@ const Service = ( { - if (data?.server?.ipAddress) { - copy(data.server.ipAddress); + const ip = data?.server?.ipAddress || serverIp; + if (ip) { + copy(ip); toast.success("IP Address Copied!"); } }} diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/compose/[composeId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/compose/[composeId].tsx index ecf502125..550be21cb 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/compose/[composeId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/compose/[composeId].tsx @@ -85,6 +85,7 @@ const Service = ( const { data: auth } = api.user.get.useQuery(); const { data: permissions } = api.user.getPermissions.useQuery(); const { data: isCloud } = api.settings.isCloud.useQuery(); + const { data: serverIp } = api.settings.getIp.useQuery(); const { data: environments } = api.environment.byProjectId.useQuery({ projectId: data?.environment?.projectId || "", }); @@ -134,8 +135,9 @@ const Service = ( { - if (data?.server?.ipAddress) { - copy(data.server.ipAddress); + const ip = data?.server?.ipAddress || serverIp; + if (ip) { + copy(ip); toast.success("IP Address Copied!"); } }} diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/libsql/[libsqlId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/libsql/[libsqlId].tsx index 3048f8fda..b679890a5 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/libsql/[libsqlId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/libsql/[libsqlId].tsx @@ -1,3 +1,4 @@ +import copy from "copy-to-clipboard"; import { validateRequest } from "@dokploy/server/lib/auth"; import { createServerSideHelpers } from "@trpc/react-query/server"; import { HelpCircle, ServerOff } from "lucide-react"; @@ -10,6 +11,7 @@ import Link from "next/link"; import { useRouter } from "next/router"; import { type ReactElement, useState } from "react"; import superjson from "superjson"; +import { toast } from "sonner"; import { ShowEnvironment } from "@/components/dashboard/application/environment/show-environment"; import { ShowDockerLogs } from "@/components/dashboard/application/logs/show"; import { DeleteService } from "@/components/dashboard/compose/delete-service"; @@ -61,6 +63,7 @@ const Libsql = ( const { data: auth } = api.user.get.useQuery(); const { data: isCloud } = api.settings.isCloud.useQuery(); + const { data: serverIp } = api.settings.getIp.useQuery(); return (
@@ -99,6 +102,14 @@ const Libsql = (
{ + const ip = data?.server?.ipAddress || serverIp; + if (ip) { + copy(ip); + toast.success("IP Address Copied!"); + } + }} variant={ !data?.serverId ? "default" diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mariadb/[mariadbId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mariadb/[mariadbId].tsx index 9c9fe7a22..dfb097edb 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mariadb/[mariadbId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mariadb/[mariadbId].tsx @@ -1,3 +1,4 @@ +import copy from "copy-to-clipboard"; import { validateRequest } from "@dokploy/server/lib/auth"; import { createServerSideHelpers } from "@trpc/react-query/server"; import { HelpCircle, ServerOff } from "lucide-react"; @@ -10,6 +11,7 @@ import Link from "next/link"; import { useRouter } from "next/router"; import { type ReactElement, useState } from "react"; import superjson from "superjson"; +import { toast } from "sonner"; import { ShowEnvironment } from "@/components/dashboard/application/environment/show-environment"; import { ShowDockerLogs } from "@/components/dashboard/application/logs/show"; import { DeleteService } from "@/components/dashboard/compose/delete-service"; @@ -63,6 +65,7 @@ const Mariadb = ( const { data: permissions } = api.user.getPermissions.useQuery(); const { data: isCloud } = api.settings.isCloud.useQuery(); + const { data: serverIp } = api.settings.getIp.useQuery(); const { data: environments } = api.environment.byProjectId.useQuery({ projectId: data?.environment?.projectId || "", @@ -111,6 +114,14 @@ const Mariadb = (
{ + const ip = data?.server?.ipAddress || serverIp; + if (ip) { + copy(ip); + toast.success("IP Address Copied!"); + } + }} variant={ !data?.serverId ? "default" diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mongo/[mongoId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mongo/[mongoId].tsx index c1b7b38df..03e409bb4 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mongo/[mongoId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mongo/[mongoId].tsx @@ -1,3 +1,4 @@ +import copy from "copy-to-clipboard"; import { validateRequest } from "@dokploy/server/lib/auth"; import { createServerSideHelpers } from "@trpc/react-query/server"; import { HelpCircle, ServerOff } from "lucide-react"; @@ -10,6 +11,7 @@ import Link from "next/link"; import { useRouter } from "next/router"; import { type ReactElement, useState } from "react"; import superjson from "superjson"; +import { toast } from "sonner"; import { ShowEnvironment } from "@/components/dashboard/application/environment/show-environment"; import { ShowDockerLogs } from "@/components/dashboard/application/logs/show"; import { DeleteService } from "@/components/dashboard/compose/delete-service"; @@ -63,6 +65,7 @@ const Mongo = ( const { data: permissions } = api.user.getPermissions.useQuery(); const { data: isCloud } = api.settings.isCloud.useQuery(); + const { data: serverIp } = api.settings.getIp.useQuery(); const { data: environments } = api.environment.byProjectId.useQuery({ projectId: data?.environment?.projectId || "", }); @@ -110,6 +113,14 @@ const Mongo = (
{ + const ip = data?.server?.ipAddress || serverIp; + if (ip) { + copy(ip); + toast.success("IP Address Copied!"); + } + }} variant={ !data?.serverId ? "default" diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mysql/[mysqlId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mysql/[mysqlId].tsx index 26af96e04..e0d68cd12 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mysql/[mysqlId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mysql/[mysqlId].tsx @@ -1,5 +1,6 @@ import { validateRequest } from "@dokploy/server/lib/auth"; import { createServerSideHelpers } from "@trpc/react-query/server"; +import copy from "copy-to-clipboard"; import { HelpCircle, ServerOff } from "lucide-react"; import type { GetServerSidePropsContext, @@ -10,6 +11,7 @@ import Link from "next/link"; import { useRouter } from "next/router"; import { type ReactElement, useState } from "react"; import superjson from "superjson"; +import { toast } from "sonner"; import { ShowEnvironment } from "@/components/dashboard/application/environment/show-environment"; import { ShowDockerLogs } from "@/components/dashboard/application/logs/show"; import { DeleteService } from "@/components/dashboard/compose/delete-service"; @@ -62,6 +64,7 @@ const MySql = ( const { data: permissions } = api.user.getPermissions.useQuery(); const { data: isCloud } = api.settings.isCloud.useQuery(); + const { data: serverIp } = api.settings.getIp.useQuery(); const { data: environments } = api.environment.byProjectId.useQuery({ projectId: data?.environment?.projectId || "", }); @@ -110,6 +113,14 @@ const MySql = (
{ + const ip = data?.server?.ipAddress || serverIp; + if (ip) { + copy(ip); + toast.success("IP Address Copied!"); + } + }} variant={ !data?.serverId ? "default" diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/postgres/[postgresId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/postgres/[postgresId].tsx index 611842dea..be310f054 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/postgres/[postgresId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/postgres/[postgresId].tsx @@ -1,3 +1,4 @@ +import copy from "copy-to-clipboard"; import { validateRequest } from "@dokploy/server/lib/auth"; import { createServerSideHelpers } from "@trpc/react-query/server"; import { HelpCircle, ServerOff } from "lucide-react"; @@ -10,6 +11,7 @@ import Link from "next/link"; import { useRouter } from "next/router"; import { type ReactElement, useState } from "react"; import superjson from "superjson"; +import { toast } from "sonner"; import { ShowEnvironment } from "@/components/dashboard/application/environment/show-environment"; import { ShowDockerLogs } from "@/components/dashboard/application/logs/show"; import { DeleteService } from "@/components/dashboard/compose/delete-service"; @@ -62,6 +64,7 @@ const Postgresql = ( const { data: permissions } = api.user.getPermissions.useQuery(); const { data: isCloud } = api.settings.isCloud.useQuery(); + const { data: serverIp } = api.settings.getIp.useQuery(); const { data: environments } = api.environment.byProjectId.useQuery({ projectId: data?.environment?.projectId || "", }); @@ -109,6 +112,14 @@ const Postgresql = (
{ + const ip = data?.server?.ipAddress || serverIp; + if (ip) { + copy(ip); + toast.success("IP Address Copied!"); + } + }} variant={ !data?.serverId ? "default" diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/redis/[redisId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/redis/[redisId].tsx index ef71a8a39..3db482ef9 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/redis/[redisId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/redis/[redisId].tsx @@ -1,3 +1,4 @@ +import copy from "copy-to-clipboard"; import { validateRequest } from "@dokploy/server/lib/auth"; import { createServerSideHelpers } from "@trpc/react-query/server"; import { HelpCircle, ServerOff } from "lucide-react"; @@ -10,6 +11,7 @@ import Link from "next/link"; import { useRouter } from "next/router"; import { type ReactElement, useState } from "react"; import superjson from "superjson"; +import { toast } from "sonner"; import { ShowEnvironment } from "@/components/dashboard/application/environment/show-environment"; import { ShowDockerLogs } from "@/components/dashboard/application/logs/show"; import { DeleteService } from "@/components/dashboard/compose/delete-service"; @@ -62,6 +64,7 @@ const Redis = ( const { data: permissions } = api.user.getPermissions.useQuery(); const { data: isCloud } = api.settings.isCloud.useQuery(); + const { data: serverIp } = api.settings.getIp.useQuery(); const { data: environments } = api.environment.byProjectId.useQuery({ projectId: data?.environment?.projectId || "", }); @@ -109,6 +112,14 @@ const Redis = (
{ + const ip = data?.server?.ipAddress || serverIp; + if (ip) { + copy(ip); + toast.success("IP Address Copied!"); + } + }} variant={ !data?.serverId ? "default"