diff --git a/apps/dokploy/components/dashboard/settings/servers/setup-server.tsx b/apps/dokploy/components/dashboard/settings/servers/setup-server.tsx
index 7274869a..06c9a296 100644
--- a/apps/dokploy/components/dashboard/settings/servers/setup-server.tsx
+++ b/apps/dokploy/components/dashboard/settings/servers/setup-server.tsx
@@ -46,16 +46,15 @@ export const SetupServer = ({ serverId }: Props) => {
{ serverId },
{
enabled: !!serverId,
- refetchInterval: 1000,
},
);
- const { mutateAsync } = api.server.setup.useMutation({
- // onMutate: async (variables) => {
- // console.log("Running....");
- // utils.deployment.allByServer.invalidate({ serverId: variables.serverId });
- // // refetch();
- // },
+ const { mutateAsync, isLoading } = api.server.setup.useMutation({
+ onMutate: async (variables) => {
+ console.log("Running....");
+ refetch();
+ // refetch();
+ },
});
return (
@@ -108,7 +107,7 @@ export const SetupServer = ({ serverId }: Props) => {
});
}}
>
-
+
diff --git a/apps/dokploy/server/api/routers/compose.ts b/apps/dokploy/server/api/routers/compose.ts
index 591904bc..6cc95edc 100644
--- a/apps/dokploy/server/api/routers/compose.ts
+++ b/apps/dokploy/server/api/routers/compose.ts
@@ -19,7 +19,11 @@ import {
randomizeComposeFile,
randomizeSpecificationFile,
} from "@/server/utils/docker/compose";
-import { addDomainToCompose, cloneCompose } from "@/server/utils/docker/domain";
+import {
+ addDomainToCompose,
+ cloneCompose,
+ cloneComposeRemote,
+} from "@/server/utils/docker/domain";
import { removeComposeDirectory } from "@/server/utils/filesystem/directory";
import { templates } from "@/templates/templates";
import type { TemplatesKeys } from "@/templates/types/templates-data.type";
@@ -131,7 +135,11 @@ export const composeRouter = createTRPCRouter({
.mutation(async ({ input }) => {
try {
const compose = await findComposeById(input.composeId);
- await cloneCompose(compose);
+ if (compose.serverId) {
+ await cloneComposeRemote(compose);
+ } else {
+ await cloneCompose(compose);
+ }
return compose.sourceType;
} catch (err) {
throw new TRPCError({
diff --git a/apps/dokploy/server/api/services/compose.ts b/apps/dokploy/server/api/services/compose.ts
index 73f36e10..3fce36f1 100644
--- a/apps/dokploy/server/api/services/compose.ts
+++ b/apps/dokploy/server/api/services/compose.ts
@@ -97,6 +97,7 @@ export const createComposeByTemplate = async (
.insert(compose)
.values({
...input,
+ serverId: "y91z1__c4SJbBe1TwQuaN",
})
.returning()
.then((value) => value[0]);
@@ -241,16 +242,33 @@ export const deployCompose = async ({
command += getCreateComposeFileCommand(compose);
}
- Promise.resolve()
- .then(() => {
- return execAsyncRemote(compose.serverId, command);
- })
- .then(() => {
- return getBuildComposeCommand(compose, deployment.logPath);
- })
- .then(() => {
- console.log(" ---- done ----");
- });
+ // Promise.resolve()
+ // .then(() => {
+ // return execAsyncRemote(compose.serverId, command);
+ // })
+ // .then(() => {
+ // return getBuildComposeCommand(compose, deployment.logPath);
+ // })
+ // .catch((err) => {
+ // throw err;
+ // })
+ // .then(() => {
+ // console.log(" ---- done ----");
+ // });
+ async function* sequentialSteps() {
+ yield execAsyncRemote(compose.serverId, command);
+ yield getBuildComposeCommand(compose, deployment.logPath);
+ }
+
+ const steps = sequentialSteps();
+ for await (const step of steps) {
+ if (step.stderr) {
+ console.log(step.stderr);
+ }
+ step;
+ }
+
+ console.log(" ---- done ----");
} else {
if (compose.sourceType === "github") {
await cloneGithubRepository(compose, deployment.logPath, true);
diff --git a/apps/dokploy/server/api/services/docker.ts b/apps/dokploy/server/api/services/docker.ts
index 1554846a..fea9a7e2 100644
--- a/apps/dokploy/server/api/services/docker.ts
+++ b/apps/dokploy/server/api/services/docker.ts
@@ -1,9 +1,4 @@
-import { readSSHKey } from "@/server/utils/filesystem/ssh";
import { execAsync, execAsyncRemote } 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 {
diff --git a/apps/dokploy/server/utils/builders/compose.ts b/apps/dokploy/server/utils/builders/compose.ts
index ad70b880..773f26a6 100644
--- a/apps/dokploy/server/utils/builders/compose.ts
+++ b/apps/dokploy/server/utils/builders/compose.ts
@@ -12,7 +12,7 @@ import {
writeDomainsToCompose,
writeDomainsToComposeRemote,
} from "../docker/domain";
-import { prepareEnvironmentVariables } from "../docker/utils";
+import { encodeBase64, prepareEnvironmentVariables } from "../docker/utils";
import { spawnAsync } from "../process/spawnAsync";
import { execAsyncRemote } from "../process/execAsync";
@@ -106,9 +106,7 @@ docker ${command.split(" ").join(" ")} >> ${logPath} 2>&1;
echo "Docker Compose Deployed: ✅" >> ${logPath};
`;
- await execAsyncRemote(compose.serverId, bashCommand);
-
- return bashCommand;
+ return await execAsyncRemote(compose.serverId, bashCommand);
};
const sanitizeCommand = (command: string) => {
@@ -174,6 +172,7 @@ export const getCreateEnvFileCommand = (compose: ComposeNested) => {
join(COMPOSE_PATH, appName, "code", "docker-compose.yml");
const envFilePath = join(dirname(composeFilePath), ".env");
+
let envContent = env || "";
if (!envContent.includes("DOCKER_CONFIG")) {
envContent += "\nDOCKER_CONFIG=/root/.docker/config.json";
@@ -184,8 +183,10 @@ export const getCreateEnvFileCommand = (compose: ComposeNested) => {
}
const envFileContent = prepareEnvironmentVariables(envContent).join("\n");
+
+ const encodedContent = encodeBase64(envFileContent);
return `
-mkdir -p ${envFilePath};
-echo "${envFileContent}" > ${envFilePath};
+touch ${envFilePath};
+echo "${encodedContent}" | base64 -d > "${envFilePath}";
`;
};
diff --git a/apps/dokploy/server/utils/docker/domain.ts b/apps/dokploy/server/utils/docker/domain.ts
index 7d10b255..538b83cf 100644
--- a/apps/dokploy/server/utils/docker/domain.ts
+++ b/apps/dokploy/server/utils/docker/domain.ts
@@ -32,6 +32,7 @@ import type {
PropertiesNetworks,
} from "./types";
import { execAsyncRemote } from "../process/execAsync";
+import { encodeBase64 } from "./utils";
export const cloneCompose = async (compose: Compose) => {
if (compose.sourceType === "github") {
@@ -144,13 +145,14 @@ export const writeDomainsToComposeRemote = async (
try {
if (compose.serverId) {
const composeString = dump(composeConverted, { lineWidth: 1000 });
- return `printf '%s' '${composeString.replace(/'/g, "'\\''")}' > ${path}`;
+ const encodedContent = encodeBase64(composeString);
+ return `echo "${encodedContent}" | base64 -d > "${path}";`;
}
} catch (error) {
throw error;
}
};
-
+// (node:59875) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 SIGTERM listeners added to [process]. Use emitter.setMaxListeners() to increase limit
export const addDomainToCompose = async (
compose: Compose,
domains: Domain[],
diff --git a/apps/dokploy/server/utils/docker/utils.ts b/apps/dokploy/server/utils/docker/utils.ts
index 146b010c..f2ffb777 100644
--- a/apps/dokploy/server/utils/docker/utils.ts
+++ b/apps/dokploy/server/utils/docker/utils.ts
@@ -410,6 +410,8 @@ export const createFile = async (
throw error;
}
};
+export const encodeBase64 = (content: string) =>
+ Buffer.from(content, "utf-8").toString("base64");
export const getCreateFileCommand = (
outputPath: string,
@@ -422,10 +424,10 @@ export const getCreateFileCommand = (
}
const directory = path.dirname(fullPath);
-
+ const encodedContent = encodeBase64(content);
return `
mkdir -p ${directory};
- echo "${content}" > ${fullPath};
+ echo "${encodedContent}" | base64 -d > "${fullPath}";
`;
};
diff --git a/apps/dokploy/server/utils/process/execAsync.ts b/apps/dokploy/server/utils/process/execAsync.ts
index eb9dfa29..5b72ec62 100644
--- a/apps/dokploy/server/utils/process/execAsync.ts
+++ b/apps/dokploy/server/utils/process/execAsync.ts
@@ -1,39 +1,81 @@
import { exec } from "node:child_process";
import util from "node:util";
-import { connectSSH } from "../servers/connection";
+import { findServerById } from "@/server/api/services/server";
+import { readSSHKey } from "../filesystem/ssh";
+import { Client } from "ssh2";
export const execAsync = util.promisify(exec);
export const execAsyncRemote = async (
- serverId: string,
+ serverId: string | null,
command: string,
): Promise<{ stdout: string; stderr: string }> => {
- const client = await connectSSH(serverId);
+ if (!serverId) return { stdout: "", stderr: "" };
+ const server = await findServerById(serverId);
+ if (!server.sshKeyId) throw new Error("No SSH key available for this server");
+ const keys = await readSSHKey(server.sshKeyId);
+
+ const conn = new Client();
+ let stdout = "";
+ let stderr = "";
return new Promise((resolve, reject) => {
- client.exec(command, (err, stream) => {
- if (err) {
- client.end();
- return reject(err);
- }
-
- let stdout = "";
- let stderr = "";
-
- stream
- .on("data", (data: string) => {
- stdout += data.toString();
- })
- .on("close", (code, signal) => {
- client.end();
- if (code === 0) {
- resolve({ stdout, stderr });
- } else {
- reject(new Error(`Command exited with code ${code}`));
- }
- })
- .stderr.on("data", (data) => {
- stderr += data.toString();
+ conn
+ .once("ready", () => {
+ console.log("Client :: ready");
+ conn.exec(command, (err, stream) => {
+ if (err) throw err;
+ stream
+ .on("close", (code, signal) => {
+ console.log(
+ `Stream :: close :: code: ${code}, signal: ${signal}`,
+ );
+ conn.end();
+ if (code === 0) {
+ resolve({ stdout, stderr });
+ } else {
+ reject(new Error(`Command exited with code ${code}`));
+ }
+ })
+ .on("data", (data: string) => {
+ stdout += data.toString();
+ })
+ .stderr.on("data", (data) => {
+ stderr += data.toString();
+ });
});
- });
+ })
+ .connect({
+ host: server.ipAddress,
+ port: server.port,
+ username: server.username,
+ privateKey: keys.privateKey,
+ timeout: 99999,
+ });
+
+ // client.exec(command, (err, stream) => {
+ // if (err) {
+ // client.end();
+ // return reject(err);
+ // }
+
+ // let stdout = "";
+ // let stderr = "";
+
+ // stream
+ // .on("data", (data: string) => {
+ // stdout += data.toString();
+ // })
+ // .on("close", (code, signal) => {
+ // client.end();
+ // if (code === 0) {
+ // resolve({ stdout, stderr });
+ // } else {
+ // reject(new Error(`Command exited with code ${code}`));
+ // }
+ // })
+ // .stderr.on("data", (data) => {
+ // stderr += data.toString();
+ // });
+ // });
});
};
diff --git a/apps/dokploy/server/utils/providers/raw.ts b/apps/dokploy/server/utils/providers/raw.ts
index ea4253e7..ea559675 100644
--- a/apps/dokploy/server/utils/providers/raw.ts
+++ b/apps/dokploy/server/utils/providers/raw.ts
@@ -5,6 +5,7 @@ import type { Compose } from "@/server/api/services/compose";
import { COMPOSE_PATH } from "@/server/constants";
import { recreateDirectory } from "../filesystem/directory";
import { execAsyncRemote } from "../process/execAsync";
+import { encodeBase64 } from "../docker/utils";
export const createComposeFile = async (compose: Compose, logPath: string) => {
const { appName, composeFile } = compose;
@@ -32,13 +33,13 @@ export const getCreateComposeFileCommand = (compose: Compose) => {
const { appName, composeFile } = compose;
const outputPath = join(COMPOSE_PATH, appName, "code");
const filePath = join(outputPath, "docker-compose.yml");
- const command = [];
- command.push(`rm -rf ${outputPath};`);
- command.push(`mkdir -p ${outputPath};`);
- command.push(
- `printf '%s' '${composeFile.replace(/'/g, "'\\''")}' > ${filePath};`,
- );
- return command.join("\n");
+ const encodedContent = encodeBase64(composeFile);
+ const bashCommand = `
+ rm -rf ${outputPath};
+ mkdir -p ${outputPath};
+ echo "${encodedContent}" | base64 -d > "${filePath}";
+ `;
+ return bashCommand;
};
export const createComposeFileRaw = async (compose: Compose) => {
@@ -59,9 +60,10 @@ export const createComposeFileRawRemote = async (compose: Compose) => {
const filePath = join(outputPath, "docker-compose.yml");
try {
+ const encodedContent = encodeBase64(composeFile);
const command = `
mkdir -p ${outputPath};
- echo "${composeFile}" > ${filePath};
+ echo "${encodedContent}" | base64 -d > "${filePath}";
`;
await execAsyncRemote(serverId, command);
} catch (error) {
diff --git a/apps/dokploy/server/utils/servers/connection.ts b/apps/dokploy/server/utils/servers/connection.ts
index 0402006f..846c2455 100644
--- a/apps/dokploy/server/utils/servers/connection.ts
+++ b/apps/dokploy/server/utils/servers/connection.ts
@@ -1,24 +1,3 @@
import { findServerById } from "@/server/api/services/server";
import { Client } from "ssh2";
import { readSSHKey } from "../filesystem/ssh";
-
-export const connectSSH = async (serverId: string) => {
- const server = await findServerById(serverId);
- if (!server.sshKeyId) throw new Error("No SSH key available for this server");
-
- const keys = await readSSHKey(server.sshKeyId);
- const client = new Client();
-
- return new Promise((resolve, reject) => {
- client
- .once("ready", () => resolve(client))
- .on("error", reject)
- .connect({
- host: server.ipAddress,
- port: server.port,
- username: server.username,
- privateKey: keys.privateKey,
- timeout: 99999,
- });
- });
-};
diff --git a/apps/dokploy/server/utils/servers/setup-server.ts b/apps/dokploy/server/utils/servers/setup-server.ts
index bcb97d00..b2b6a570 100644
--- a/apps/dokploy/server/utils/servers/setup-server.ts
+++ b/apps/dokploy/server/utils/servers/setup-server.ts
@@ -67,7 +67,7 @@ const connectToServer = async (serverId: string, logPath: string) => {
const keys = await readSSHKey(server.sshKeyId);
return new Promise((resolve, reject) => {
client
- .on("ready", () => {
+ .once("ready", () => {
console.log("Client :: ready");
const bashCommand = `
diff --git a/apps/dokploy/server/wss/docker-container-logs.ts b/apps/dokploy/server/wss/docker-container-logs.ts
index e0c8fba6..39876bf2 100644
--- a/apps/dokploy/server/wss/docker-container-logs.ts
+++ b/apps/dokploy/server/wss/docker-container-logs.ts
@@ -54,7 +54,7 @@ export const setupDockerContainerLogsWebSocketServer = (
const client = new Client();
new Promise((resolve, reject) => {
client
- .on("ready", () => {
+ .once("ready", () => {
const command = `
bash -c "docker container logs --tail ${tail} --follow ${containerId}"
`;