Merge pull request #3027 from Dokploy/1925-remote-server-visibility-per-applicationservice

feat: enhance environment service to include server details
This commit is contained in:
Mauricio Siu
2025-11-15 23:55:11 -06:00
committed by GitHub
2 changed files with 107 additions and 11 deletions

View File

@@ -1,4 +1,4 @@
import type { findProjectById } from "@dokploy/server";
import type { findEnvironmentById } from "@dokploy/server";
import { validateRequest } from "@dokploy/server/lib/auth";
import { createServerSideHelpers } from "@trpc/react-query/server";
import {
@@ -102,6 +102,7 @@ import { api } from "@/utils/api";
export type Services = {
appName: string;
serverId?: string | null;
serverName?: string | null;
name: string;
type:
| "mariadb"
@@ -118,8 +119,7 @@ export type Services = {
lastDeployDate?: Date | null;
};
type Project = Awaited<ReturnType<typeof findProjectById>>;
type Environment = Project["environments"][0];
type Environment = Awaited<ReturnType<typeof findEnvironmentById>>;
export const extractServicesFromEnvironment = (
environment: Environment | undefined,
@@ -154,6 +154,7 @@ export const extractServicesFromEnvironment = (
status: item.applicationStatus,
description: item.description,
serverId: item.serverId,
serverName: item?.server?.name || null,
lastDeployDate,
};
}) || [];
@@ -168,6 +169,7 @@ export const extractServicesFromEnvironment = (
status: item.applicationStatus,
description: item.description,
serverId: item.serverId,
serverName: item?.server?.name || null,
})) || [];
const postgres: Services[] =
@@ -180,6 +182,7 @@ export const extractServicesFromEnvironment = (
status: item.applicationStatus,
description: item.description,
serverId: item.serverId,
serverName: item?.server?.name || null,
})) || [];
const mongo: Services[] =
@@ -192,6 +195,7 @@ export const extractServicesFromEnvironment = (
status: item.applicationStatus,
description: item.description,
serverId: item.serverId,
serverName: item?.server?.name || null,
})) || [];
const redis: Services[] =
@@ -204,6 +208,7 @@ export const extractServicesFromEnvironment = (
status: item.applicationStatus,
description: item.description,
serverId: item.serverId,
serverName: item?.server?.name || null,
})) || [];
const mysql: Services[] =
@@ -216,6 +221,7 @@ export const extractServicesFromEnvironment = (
status: item.applicationStatus,
description: item.description,
serverId: item.serverId,
serverName: item?.server?.name || null,
})) || [];
const compose: Services[] =
@@ -244,6 +250,7 @@ export const extractServicesFromEnvironment = (
status: item.composeStatus,
description: item.description,
serverId: item.serverId,
serverName: item?.server?.name || null,
lastDeployDate,
};
}) || [];
@@ -392,6 +399,7 @@ const EnvironmentPage = (
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
const [isBulkDeleteDialogOpen, setIsBulkDeleteDialogOpen] = useState(false);
const [deleteVolumes, setDeleteVolumes] = useState(false);
const [selectedServerId, setSelectedServerId] = useState<string>("all");
const handleSelectAll = () => {
if (selectedServices.length === filteredServices.length) {
@@ -781,6 +789,27 @@ const EnvironmentPage = (
setIsBulkActionLoading(false);
};
// Get unique servers from services
const availableServers = useMemo(() => {
if (!applications) return [];
const servers = new Map<string, { serverId: string; serverName: string }>();
applications.forEach((service) => {
if (service.serverId && service.serverName) {
servers.set(service.serverId, {
serverId: service.serverId,
serverName: service.serverName,
});
}
});
return Array.from(servers.values());
}, [applications]);
// Check if there are services without a server (Dokploy server)
const hasServicesWithoutServer = useMemo(() => {
if (!applications) return false;
return applications.some((service) => !service.serverId);
}, [applications]);
const filteredServices = useMemo(() => {
if (!applications) return [];
const filtered = applications.filter(
@@ -789,10 +818,14 @@ const EnvironmentPage = (
service.description
?.toLowerCase()
.includes(searchQuery.toLowerCase())) &&
(selectedTypes.length === 0 || selectedTypes.includes(service.type)),
(selectedTypes.length === 0 || selectedTypes.includes(service.type)) &&
(selectedServerId === "" ||
selectedServerId === "all" ||
(selectedServerId === "dokploy-server" && !service.serverId) ||
service.serverId === selectedServerId),
);
return sortServices(filtered);
}, [applications, searchQuery, selectedTypes, sortBy]);
}, [applications, searchQuery, selectedTypes, selectedServerId, sortBy]);
const selectedServicesWithRunningStatus = useMemo(() => {
return filteredServices.filter(
@@ -1366,6 +1399,39 @@ const EnvironmentPage = (
</Command>
</PopoverContent>
</Popover>
{(availableServers.length > 0 ||
hasServicesWithoutServer) && (
<Select
value={selectedServerId || "all"}
onValueChange={setSelectedServerId}
>
<SelectTrigger className="lg:w-[200px]">
<SelectValue placeholder="Filter by server..." />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">All servers</SelectItem>
{hasServicesWithoutServer && (
<SelectItem value="dokploy-server">
<div className="flex items-center gap-2">
<ServerIcon className="size-4" />
<span>Dokploy server</span>
</div>
</SelectItem>
)}
{availableServers.map((server) => (
<SelectItem
key={server.serverId}
value={server.serverId}
>
<div className="flex items-center gap-2">
<ServerIcon className="size-4" />
<span>{server.serverName}</span>
</div>
</SelectItem>
))}
</SelectContent>
</Select>
)}
</div>
</div>
@@ -1471,7 +1537,15 @@ const EnvironmentPage = (
</CardTitle>
</CardHeader>
<CardFooter className="mt-auto">
<div className="space-y-1 text-sm">
<div className="space-y-1 text-sm w-full">
{service.serverName && (
<div className="flex items-center gap-1.5 text-xs text-muted-foreground mb-1">
<ServerIcon className="size-3" />
<span className="truncate">
{service.serverName}
</span>
</div>
)}
<DateTooltip date={service.createdAt}>
Created
</DateTooltip>

View File

@@ -37,16 +37,38 @@ export const findEnvironmentById = async (environmentId: string) => {
applications: {
with: {
deployments: true,
server: true,
},
},
mariadb: {
with: {
server: true,
},
},
mongo: {
with: {
server: true,
},
},
mysql: {
with: {
server: true,
},
},
postgres: {
with: {
server: true,
},
},
redis: {
with: {
server: true,
},
},
mariadb: true,
mongo: true,
mysql: true,
postgres: true,
redis: true,
compose: {
with: {
deployments: true,
server: true,
},
},
project: true,