-
+ {data?.composeType === "docker-compose" ? (
+
+ ) : (
+
+ )}
diff --git a/apps/dokploy/server/api/routers/docker.ts b/apps/dokploy/server/api/routers/docker.ts
index cb6b2712e..f6972e16b 100644
--- a/apps/dokploy/server/api/routers/docker.ts
+++ b/apps/dokploy/server/api/routers/docker.ts
@@ -4,6 +4,8 @@ import {
getContainers,
getContainersByAppLabel,
getContainersByAppNameMatch,
+ getServiceContainersByAppName,
+ getStackContainersByAppName,
} from "@dokploy/server";
import { z } from "zod";
import { createTRPCRouter, protectedProcedure } from "../trpc";
@@ -68,4 +70,26 @@ export const dockerRouter = createTRPCRouter({
.query(async ({ input }) => {
return await getContainersByAppLabel(input.appName, input.serverId);
}),
+
+ getStackContainersByAppName: protectedProcedure
+ .input(
+ z.object({
+ appName: z.string().min(1),
+ serverId: z.string().optional(),
+ }),
+ )
+ .query(async ({ input }) => {
+ return await getStackContainersByAppName(input.appName, input.serverId);
+ }),
+
+ getServiceContainersByAppName: protectedProcedure
+ .input(
+ z.object({
+ appName: z.string().min(1),
+ serverId: z.string().optional(),
+ }),
+ )
+ .query(async ({ input }) => {
+ return await getServiceContainersByAppName(input.appName, input.serverId);
+ }),
});
diff --git a/apps/dokploy/server/wss/docker-container-logs.ts b/apps/dokploy/server/wss/docker-container-logs.ts
index 0c1e66a4d..c1049ee0c 100644
--- a/apps/dokploy/server/wss/docker-container-logs.ts
+++ b/apps/dokploy/server/wss/docker-container-logs.ts
@@ -34,6 +34,7 @@ export const setupDockerContainerLogsWebSocketServer = (
const search = url.searchParams.get("search");
const since = url.searchParams.get("since");
const serverId = url.searchParams.get("serverId");
+ const runType = url.searchParams.get("runType");
const { user, session } = await validateWebSocketRequest(req);
if (!containerId) {
@@ -53,7 +54,7 @@ export const setupDockerContainerLogsWebSocketServer = (
const client = new Client();
client
.once("ready", () => {
- const baseCommand = `docker container logs --timestamps --tail ${tail} ${
+ const baseCommand = `docker ${runType==="swarm"?"service":"container"} logs --timestamps --tail ${tail} ${
since === "all" ? "" : `--since ${since}`
} --follow ${containerId}`;
const escapedSearch = search ? search.replace(/'/g, "'\\''") : "";
@@ -97,7 +98,7 @@ export const setupDockerContainerLogsWebSocketServer = (
});
} else {
const shell = getShell();
- const baseCommand = `docker container logs --timestamps --tail ${tail} ${
+ const baseCommand = `docker ${runType==="swarm"?"service":"container"} logs --timestamps --tail ${tail} ${
since === "all" ? "" : `--since ${since}`
} --follow ${containerId}`;
const command = search
diff --git a/packages/server/src/services/docker.ts b/packages/server/src/services/docker.ts
index 6ac613542..7b55b5e9e 100644
--- a/packages/server/src/services/docker.ts
+++ b/packages/server/src/services/docker.ts
@@ -157,6 +157,124 @@ export const getContainersByAppNameMatch = async (
return [];
};
+export const getStackContainersByAppName = async (
+ appName: string,
+ serverId?: string,
+) => {
+ try {
+ let result: string[] = [];
+
+ const command = `docker stack ps ${appName} --format 'CONTAINER ID : {{.ID}} | Name: {{.Name}} | State: {{.DesiredState}} | Node: {{.Node}}'`;
+ if (serverId) {
+ const { stdout, stderr } = await execAsyncRemote(serverId, command);
+
+ if (stderr) {
+ return [];
+ }
+
+ if (!stdout) return [];
+ result = stdout.trim().split("\n");
+ } else {
+ const { stdout, stderr } = await execAsync(command);
+
+ if (stderr) {
+ return [];
+ }
+
+ if (!stdout) return [];
+
+ result = stdout.trim().split("\n");
+ }
+
+ const containers = result.map((line) => {
+ const parts = line.split(" | ");
+ const containerId = parts[0]
+ ? parts[0].replace("CONTAINER ID : ", "").trim()
+ : "No container id";
+ const name = parts[1]
+ ? parts[1].replace("Name: ", "").trim()
+ : "No container name";
+
+ const state = parts[2]
+ ? parts[2].replace("State: ", "").trim()
+ : "No state";
+ const node = parts[3]
+ ? parts[3].replace("Node: ", "").trim()
+ : "No specific node";
+ return {
+ containerId,
+ name,
+ state,
+ node,
+ };
+ });
+
+ return containers || [];
+ } catch (error) {}
+
+ return [];
+};
+
+export const getServiceContainersByAppName = async (
+ appName: string,
+ serverId?: string,
+) => {
+ try {
+ let result: string[] = [];
+
+ const command = `docker service ps ${appName} --format 'CONTAINER ID : {{.ID}} | Name: {{.Name}} | State: {{.DesiredState}} | Node: {{.Node}}'`;
+
+ if (serverId) {
+ const { stdout, stderr } = await execAsyncRemote(serverId, command);
+
+ if (stderr) {
+ return [];
+ }
+
+ if (!stdout) return [];
+ result = stdout.trim().split("\n");
+ } else {
+ const { stdout, stderr } = await execAsync(command);
+
+ if (stderr) {
+ return [];
+ }
+
+ if (!stdout) return [];
+
+ result = stdout.trim().split("\n");
+ }
+
+ const containers = result.map((line) => {
+ const parts = line.split(" | ");
+ const containerId = parts[0]
+ ? parts[0].replace("CONTAINER ID : ", "").trim()
+ : "No container id";
+ const name = parts[1]
+ ? parts[1].replace("Name: ", "").trim()
+ : "No container name";
+
+ const state = parts[2]
+ ? parts[2].replace("State: ", "").trim()
+ : "No state";
+
+ const node = parts[3]
+ ? parts[3].replace("Node: ", "").trim()
+ : "No specific node";
+ return {
+ containerId,
+ name,
+ state,
+ node,
+ };
+ });
+
+ return containers || [];
+ } catch (error) {}
+
+ return [];
+};
+
export const getContainersByAppLabel = async (
appName: string,
serverId?: string,