Skip to content

Commit 8d88a34

Browse files
vadamkclaudeSiumauricio
authored
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) <noreply@anthropic.com> * 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) <noreply@anthropic.com> Co-authored-by: Mauricio Siu <siumauricio@icloud.com>
1 parent a50f958 commit 8d88a34

8 files changed

Lines changed: 74 additions & 4 deletions

File tree

apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/application/[applicationId].tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ const Service = (
9393
);
9494

9595
const { data: isCloud } = api.settings.isCloud.useQuery();
96+
const { data: serverIp } = api.settings.getIp.useQuery();
9697
const { data: auth } = api.user.get.useQuery();
9798
const { data: permissions } = api.user.getPermissions.useQuery();
9899

@@ -147,8 +148,9 @@ const Service = (
147148
<Badge
148149
className="cursor-pointer"
149150
onClick={() => {
150-
if (data?.server?.ipAddress) {
151-
copy(data.server.ipAddress);
151+
const ip = data?.server?.ipAddress || serverIp;
152+
if (ip) {
153+
copy(ip);
152154
toast.success("IP Address Copied!");
153155
}
154156
}}

apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/compose/[composeId].tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ const Service = (
8585
const { data: auth } = api.user.get.useQuery();
8686
const { data: permissions } = api.user.getPermissions.useQuery();
8787
const { data: isCloud } = api.settings.isCloud.useQuery();
88+
const { data: serverIp } = api.settings.getIp.useQuery();
8889
const { data: environments } = api.environment.byProjectId.useQuery({
8990
projectId: data?.environment?.projectId || "",
9091
});
@@ -134,8 +135,9 @@ const Service = (
134135
<Badge
135136
className="cursor-pointer"
136137
onClick={() => {
137-
if (data?.server?.ipAddress) {
138-
copy(data.server.ipAddress);
138+
const ip = data?.server?.ipAddress || serverIp;
139+
if (ip) {
140+
copy(ip);
139141
toast.success("IP Address Copied!");
140142
}
141143
}}

apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/libsql/[libsqlId].tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import copy from "copy-to-clipboard";
12
import { validateRequest } from "@dokploy/server/lib/auth";
23
import { createServerSideHelpers } from "@trpc/react-query/server";
34
import { HelpCircle, ServerOff } from "lucide-react";
@@ -10,6 +11,7 @@ import Link from "next/link";
1011
import { useRouter } from "next/router";
1112
import { type ReactElement, useState } from "react";
1213
import superjson from "superjson";
14+
import { toast } from "sonner";
1315
import { ShowEnvironment } from "@/components/dashboard/application/environment/show-environment";
1416
import { ShowDockerLogs } from "@/components/dashboard/application/logs/show";
1517
import { DeleteService } from "@/components/dashboard/compose/delete-service";
@@ -61,6 +63,7 @@ const Libsql = (
6163
const { data: auth } = api.user.get.useQuery();
6264

6365
const { data: isCloud } = api.settings.isCloud.useQuery();
66+
const { data: serverIp } = api.settings.getIp.useQuery();
6467

6568
return (
6669
<div className="pb-10">
@@ -99,6 +102,14 @@ const Libsql = (
99102
<div className="flex flex-col h-fit w-fit gap-2">
100103
<div className="flex flex-row h-fit w-fit gap-2">
101104
<Badge
105+
className="cursor-pointer"
106+
onClick={() => {
107+
const ip = data?.server?.ipAddress || serverIp;
108+
if (ip) {
109+
copy(ip);
110+
toast.success("IP Address Copied!");
111+
}
112+
}}
102113
variant={
103114
!data?.serverId
104115
? "default"

apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mariadb/[mariadbId].tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import copy from "copy-to-clipboard";
12
import { validateRequest } from "@dokploy/server/lib/auth";
23
import { createServerSideHelpers } from "@trpc/react-query/server";
34
import { HelpCircle, ServerOff } from "lucide-react";
@@ -10,6 +11,7 @@ import Link from "next/link";
1011
import { useRouter } from "next/router";
1112
import { type ReactElement, useState } from "react";
1213
import superjson from "superjson";
14+
import { toast } from "sonner";
1315
import { ShowEnvironment } from "@/components/dashboard/application/environment/show-environment";
1416
import { ShowDockerLogs } from "@/components/dashboard/application/logs/show";
1517
import { DeleteService } from "@/components/dashboard/compose/delete-service";
@@ -63,6 +65,7 @@ const Mariadb = (
6365
const { data: permissions } = api.user.getPermissions.useQuery();
6466

6567
const { data: isCloud } = api.settings.isCloud.useQuery();
68+
const { data: serverIp } = api.settings.getIp.useQuery();
6669

6770
const { data: environments } = api.environment.byProjectId.useQuery({
6871
projectId: data?.environment?.projectId || "",
@@ -111,6 +114,14 @@ const Mariadb = (
111114
<div className="flex flex-col h-fit w-fit gap-2">
112115
<div className="flex flex-row h-fit w-fit gap-2">
113116
<Badge
117+
className="cursor-pointer"
118+
onClick={() => {
119+
const ip = data?.server?.ipAddress || serverIp;
120+
if (ip) {
121+
copy(ip);
122+
toast.success("IP Address Copied!");
123+
}
124+
}}
114125
variant={
115126
!data?.serverId
116127
? "default"

apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mongo/[mongoId].tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import copy from "copy-to-clipboard";
12
import { validateRequest } from "@dokploy/server/lib/auth";
23
import { createServerSideHelpers } from "@trpc/react-query/server";
34
import { HelpCircle, ServerOff } from "lucide-react";
@@ -10,6 +11,7 @@ import Link from "next/link";
1011
import { useRouter } from "next/router";
1112
import { type ReactElement, useState } from "react";
1213
import superjson from "superjson";
14+
import { toast } from "sonner";
1315
import { ShowEnvironment } from "@/components/dashboard/application/environment/show-environment";
1416
import { ShowDockerLogs } from "@/components/dashboard/application/logs/show";
1517
import { DeleteService } from "@/components/dashboard/compose/delete-service";
@@ -63,6 +65,7 @@ const Mongo = (
6365
const { data: permissions } = api.user.getPermissions.useQuery();
6466

6567
const { data: isCloud } = api.settings.isCloud.useQuery();
68+
const { data: serverIp } = api.settings.getIp.useQuery();
6669
const { data: environments } = api.environment.byProjectId.useQuery({
6770
projectId: data?.environment?.projectId || "",
6871
});
@@ -110,6 +113,14 @@ const Mongo = (
110113
<div className="flex flex-col h-fit w-fit gap-2">
111114
<div className="flex flex-row h-fit w-fit gap-2">
112115
<Badge
116+
className="cursor-pointer"
117+
onClick={() => {
118+
const ip = data?.server?.ipAddress || serverIp;
119+
if (ip) {
120+
copy(ip);
121+
toast.success("IP Address Copied!");
122+
}
123+
}}
113124
variant={
114125
!data?.serverId
115126
? "default"

apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mysql/[mysqlId].tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { validateRequest } from "@dokploy/server/lib/auth";
22
import { createServerSideHelpers } from "@trpc/react-query/server";
3+
import copy from "copy-to-clipboard";
34
import { HelpCircle, ServerOff } from "lucide-react";
45
import type {
56
GetServerSidePropsContext,
@@ -10,6 +11,7 @@ import Link from "next/link";
1011
import { useRouter } from "next/router";
1112
import { type ReactElement, useState } from "react";
1213
import superjson from "superjson";
14+
import { toast } from "sonner";
1315
import { ShowEnvironment } from "@/components/dashboard/application/environment/show-environment";
1416
import { ShowDockerLogs } from "@/components/dashboard/application/logs/show";
1517
import { DeleteService } from "@/components/dashboard/compose/delete-service";
@@ -62,6 +64,7 @@ const MySql = (
6264
const { data: permissions } = api.user.getPermissions.useQuery();
6365

6466
const { data: isCloud } = api.settings.isCloud.useQuery();
67+
const { data: serverIp } = api.settings.getIp.useQuery();
6568
const { data: environments } = api.environment.byProjectId.useQuery({
6669
projectId: data?.environment?.projectId || "",
6770
});
@@ -110,6 +113,14 @@ const MySql = (
110113
<div className="flex flex-col h-fit w-fit gap-2">
111114
<div className="flex flex-row h-fit w-fit gap-2">
112115
<Badge
116+
className="cursor-pointer"
117+
onClick={() => {
118+
const ip = data?.server?.ipAddress || serverIp;
119+
if (ip) {
120+
copy(ip);
121+
toast.success("IP Address Copied!");
122+
}
123+
}}
113124
variant={
114125
!data?.serverId
115126
? "default"

apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/postgres/[postgresId].tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import copy from "copy-to-clipboard";
12
import { validateRequest } from "@dokploy/server/lib/auth";
23
import { createServerSideHelpers } from "@trpc/react-query/server";
34
import { HelpCircle, ServerOff } from "lucide-react";
@@ -10,6 +11,7 @@ import Link from "next/link";
1011
import { useRouter } from "next/router";
1112
import { type ReactElement, useState } from "react";
1213
import superjson from "superjson";
14+
import { toast } from "sonner";
1315
import { ShowEnvironment } from "@/components/dashboard/application/environment/show-environment";
1416
import { ShowDockerLogs } from "@/components/dashboard/application/logs/show";
1517
import { DeleteService } from "@/components/dashboard/compose/delete-service";
@@ -62,6 +64,7 @@ const Postgresql = (
6264
const { data: permissions } = api.user.getPermissions.useQuery();
6365

6466
const { data: isCloud } = api.settings.isCloud.useQuery();
67+
const { data: serverIp } = api.settings.getIp.useQuery();
6568
const { data: environments } = api.environment.byProjectId.useQuery({
6669
projectId: data?.environment?.projectId || "",
6770
});
@@ -109,6 +112,14 @@ const Postgresql = (
109112
<div className="flex flex-col h-fit w-fit gap-2">
110113
<div className="flex flex-row h-fit w-fit gap-2">
111114
<Badge
115+
className="cursor-pointer"
116+
onClick={() => {
117+
const ip = data?.server?.ipAddress || serverIp;
118+
if (ip) {
119+
copy(ip);
120+
toast.success("IP Address Copied!");
121+
}
122+
}}
112123
variant={
113124
!data?.serverId
114125
? "default"

apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/redis/[redisId].tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import copy from "copy-to-clipboard";
12
import { validateRequest } from "@dokploy/server/lib/auth";
23
import { createServerSideHelpers } from "@trpc/react-query/server";
34
import { HelpCircle, ServerOff } from "lucide-react";
@@ -10,6 +11,7 @@ import Link from "next/link";
1011
import { useRouter } from "next/router";
1112
import { type ReactElement, useState } from "react";
1213
import superjson from "superjson";
14+
import { toast } from "sonner";
1315
import { ShowEnvironment } from "@/components/dashboard/application/environment/show-environment";
1416
import { ShowDockerLogs } from "@/components/dashboard/application/logs/show";
1517
import { DeleteService } from "@/components/dashboard/compose/delete-service";
@@ -62,6 +64,7 @@ const Redis = (
6264
const { data: permissions } = api.user.getPermissions.useQuery();
6365

6466
const { data: isCloud } = api.settings.isCloud.useQuery();
67+
const { data: serverIp } = api.settings.getIp.useQuery();
6568
const { data: environments } = api.environment.byProjectId.useQuery({
6669
projectId: data?.environment?.projectId || "",
6770
});
@@ -109,6 +112,14 @@ const Redis = (
109112
<div className="flex flex-col h-fit w-fit gap-2">
110113
<div className="flex flex-row h-fit w-fit gap-2">
111114
<Badge
115+
className="cursor-pointer"
116+
onClick={() => {
117+
const ip = data?.server?.ipAddress || serverIp;
118+
if (ip) {
119+
copy(ip);
120+
toast.success("IP Address Copied!");
121+
}
122+
}}
112123
variant={
113124
!data?.serverId
114125
? "default"

0 commit comments

Comments
 (0)