diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx
index 8d43e4cf..3c16e27f 100644
--- a/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx
+++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx
@@ -143,7 +143,10 @@ const Mariadb = (
-
+
diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx
index 409b9bd6..f179e706 100644
--- a/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx
+++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx
@@ -145,7 +145,10 @@ const Mongo = (
-
+
diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx
index 21f4036e..5f4fe119 100644
--- a/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx
+++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx
@@ -144,7 +144,10 @@ const MySql = (
-
+
diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx
index e0342ee4..4901f1e4 100644
--- a/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx
+++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx
@@ -145,7 +145,10 @@ const Postgresql = (
-
+
diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx
index f092ec06..2b560e58 100644
--- a/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx
+++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx
@@ -143,7 +143,10 @@ const Redis = (
-
+
diff --git a/apps/dokploy/server/api/routers/docker.ts b/apps/dokploy/server/api/routers/docker.ts
index 22720095..54fe00db 100644
--- a/apps/dokploy/server/api/routers/docker.ts
+++ b/apps/dokploy/server/api/routers/docker.ts
@@ -40,10 +40,15 @@ export const dockerRouter = createTRPCRouter({
.union([z.literal("stack"), z.literal("docker-compose")])
.optional(),
appName: z.string().min(1),
+ serverId: z.string().optional(),
}),
)
.query(async ({ input }) => {
- return await getContainersByAppNameMatch(input.appName, input.appType);
+ return await getContainersByAppNameMatch(
+ input.appName,
+ input.appType,
+ input.serverId,
+ );
}),
getContainersByAppLabel: protectedProcedure
diff --git a/apps/dokploy/server/api/services/docker.ts b/apps/dokploy/server/api/services/docker.ts
index 379bebe4..7263ed67 100644
--- a/apps/dokploy/server/api/services/docker.ts
+++ b/apps/dokploy/server/api/services/docker.ts
@@ -1,4 +1,9 @@
+import { readSSHKey } from "@/server/utils/filesystem/ssh";
import { execAsync } from "@/server/utils/process/execAsync";
+import { tail } from "lodash";
+import { stderr, stdout } from "node:process";
+import { Client } from "ssh2";
+import { findServerById } from "./server";
export const getContainers = async () => {
try {
@@ -69,25 +74,65 @@ export const getConfig = async (containerId: string) => {
export const getContainersByAppNameMatch = async (
appName: string,
appType?: "stack" | "docker-compose",
+ serverId?: string,
) => {
try {
+ let result: string[] = [];
const cmd =
"docker ps -a --format 'CONTAINER ID : {{.ID}} | Name: {{.Names}} | State: {{.State}}'";
- const { stdout, stderr } = await execAsync(
+ const command =
appType === "docker-compose"
? `${cmd} --filter='label=com.docker.compose.project=${appName}'`
- : `${cmd} | grep ${appName}`,
- );
+ : `${cmd} | grep ${appName}`;
+ if (serverId) {
+ const server = await findServerById(serverId);
- if (stderr) {
- return [];
+ if (!server.sshKeyId) return;
+ const keys = await readSSHKey(server.sshKeyId);
+ const client = new Client();
+ result = await new Promise((resolve, reject) => {
+ let output = "";
+ client
+ .on("ready", () => {
+ client.exec(command, (err, stream) => {
+ if (err) {
+ console.error("Execution error:", err);
+ reject(err);
+ return;
+ }
+ stream
+ .on("close", () => {
+ client.end();
+ resolve(output.trim().split("\n"));
+ })
+ .on("data", (data: string) => {
+ output += data.toString();
+ })
+ .stderr.on("data", (data) => {});
+ });
+ })
+ .connect({
+ host: server.ipAddress,
+ port: server.port,
+ username: server.username,
+ privateKey: keys.privateKey,
+ timeout: 99999,
+ });
+ });
+ } else {
+ const { stdout, stderr } = await execAsync(command);
+
+ if (stderr) {
+ return [];
+ }
+
+ if (!stdout) return [];
+
+ result = stdout.trim().split("\n");
}
- if (!stdout) return [];
-
- const lines = stdout.trim().split("\n");
- const containers = lines.map((line) => {
+ const containers = result.map((line) => {
const parts = line.split(" | ");
const containerId = parts[0]
? parts[0].replace("CONTAINER ID : ", "").trim()
diff --git a/apps/dokploy/server/wss/docker-container-logs.ts b/apps/dokploy/server/wss/docker-container-logs.ts
index 0c40ecef..e0c8fba6 100644
--- a/apps/dokploy/server/wss/docker-container-logs.ts
+++ b/apps/dokploy/server/wss/docker-container-logs.ts
@@ -3,6 +3,9 @@ import { spawn } from "node-pty";
import { WebSocketServer } from "ws";
import { validateWebSocketRequest } from "../auth/auth";
import { getShell } from "./utils";
+import { Client } from "ssh2";
+import { findServerById } from "../api/services/server";
+import { readSSHKey } from "../utils/filesystem/ssh";
export const setupDockerContainerLogsWebSocketServer = (
server: http.Server,
@@ -30,6 +33,7 @@ export const setupDockerContainerLogsWebSocketServer = (
const url = new URL(req.url || "", `http://${req.headers.host}`);
const containerId = url.searchParams.get("containerId");
const tail = url.searchParams.get("tail");
+ const serverId = url.searchParams.get("serverId");
const { user, session } = await validateWebSocketRequest(req);
if (!containerId) {
@@ -42,41 +46,88 @@ export const setupDockerContainerLogsWebSocketServer = (
return;
}
try {
- const shell = getShell();
- const ptyProcess = spawn(
- shell,
- ["-c", `docker container logs --tail ${tail} --follow ${containerId}`],
- {
- name: "xterm-256color",
- cwd: process.env.HOME,
- env: process.env,
- encoding: "utf8",
- cols: 80,
- rows: 30,
- },
- );
+ if (serverId) {
+ const server = await findServerById(serverId);
- ptyProcess.onData((data) => {
- ws.send(data);
- });
- ws.on("close", () => {
- ptyProcess.kill();
- });
- ws.on("message", (message) => {
- try {
- let command: string | Buffer[] | Buffer | ArrayBuffer;
- if (Buffer.isBuffer(message)) {
- command = message.toString("utf8");
- } else {
- command = message;
+ if (!server.sshKeyId) return;
+ const keys = await readSSHKey(server.sshKeyId);
+ const client = new Client();
+ new Promise((resolve, reject) => {
+ client
+ .on("ready", () => {
+ const command = `
+ bash -c "docker container logs --tail ${tail} --follow ${containerId}"
+ `;
+ client.exec(command, (err, stream) => {
+ if (err) {
+ console.error("Execution error:", err);
+ reject(err);
+ return;
+ }
+ stream
+ .on("close", () => {
+ console.log("Connection closed ✅");
+ client.end();
+ resolve();
+ })
+ .on("data", (data: string) => {
+ ws.send(data.toString());
+ // console.log(`OUTPUT: ${data.toString()}`);
+ })
+ .stderr.on("data", (data) => {
+ ws.send(data.toString());
+ // console.error(`STDERR: ${data.toString()}`);
+ });
+ });
+ })
+ .connect({
+ host: server.ipAddress,
+ port: server.port,
+ username: server.username,
+ privateKey: keys.privateKey,
+ timeout: 99999,
+ });
+ });
+ } else {
+ const shell = getShell();
+ const ptyProcess = spawn(
+ shell,
+ [
+ "-c",
+ `docker container logs --tail ${tail} --follow ${containerId}`,
+ ],
+ {
+ name: "xterm-256color",
+ cwd: process.env.HOME,
+ env: process.env,
+ encoding: "utf8",
+ cols: 80,
+ rows: 30,
+ },
+ );
+
+ ptyProcess.onData((data) => {
+ ws.send(data);
+ });
+ ws.on("close", () => {
+ ptyProcess.kill();
+ });
+ ws.on("message", (message) => {
+ try {
+ let command: string | Buffer[] | Buffer | ArrayBuffer;
+ if (Buffer.isBuffer(message)) {
+ command = message.toString("utf8");
+ } else {
+ command = message;
+ }
+ ptyProcess.write(command.toString());
+ } catch (error) {
+ // @ts-ignore
+ const errorMessage = error?.message as unknown as string;
+ ws.send(errorMessage);
}
- ptyProcess.write(command.toString());
- } catch (error) {
- // @ts-ignore
- const errorMessage = error?.message as unknown as string;
- ws.send(errorMessage);
- }
- });
+ });
+ }
} catch (error) {
// @ts-ignore
const errorMessage = error?.message as unknown as string;