From 7369b54f322c324d04a5296a64338d61e26432ee Mon Sep 17 00:00:00 2001
From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com>
Date: Fri, 31 Jan 2025 01:20:52 -0600
Subject: [PATCH] refactor: update
---
.../services/compose/[composeId].tsx | 2 -
packages/server/src/utils/builders/compose.ts | 295 +++----
packages/server/src/utils/docker/utils.ts | 814 +++++++++---------
3 files changed, 556 insertions(+), 555 deletions(-)
diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx
index f9ece2e7..67c9c7b1 100644
--- a/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx
+++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx
@@ -221,9 +221,7 @@ const Service = (
)}
>
General
- {/* {data?.composeType === "docker-compose" && ( */}
Environment
- {/* )} */}
{!data?.serverId && (
Monitoring
)}
diff --git a/packages/server/src/utils/builders/compose.ts b/packages/server/src/utils/builders/compose.ts
index 03da7858..af560728 100644
--- a/packages/server/src/utils/builders/compose.ts
+++ b/packages/server/src/utils/builders/compose.ts
@@ -1,105 +1,105 @@
import {
- createWriteStream,
- existsSync,
- mkdirSync,
- readFileSync,
- writeFileSync,
+ createWriteStream,
+ existsSync,
+ mkdirSync,
+ readFileSync,
+ writeFileSync,
} from "node:fs";
import { dirname, join } from "node:path";
import { paths } from "@dokploy/server/constants";
import type { InferResultType } from "@dokploy/server/types/with";
import boxen from "boxen";
import {
- writeDomainsToCompose,
- writeDomainsToComposeRemote,
+ writeDomainsToCompose,
+ writeDomainsToComposeRemote,
} from "../docker/domain";
import {
- encodeBase64,
- getEnviromentVariablesObject,
- prepareEnvironmentVariables,
+ encodeBase64,
+ getEnviromentVariablesObject,
+ prepareEnvironmentVariables,
} from "../docker/utils";
import { execAsync, execAsyncRemote } from "../process/execAsync";
import { spawnAsync } from "../process/spawnAsync";
export type ComposeNested = InferResultType<
- "compose",
- { project: true; mounts: true; domains: true }
+ "compose",
+ { project: true; mounts: true; domains: true }
>;
export const buildCompose = async (compose: ComposeNested, logPath: string) => {
- const writeStream = createWriteStream(logPath, { flags: "a" });
- const { sourceType, appName, mounts, composeType, domains } = compose;
- try {
- const { COMPOSE_PATH } = paths();
- const command = createCommand(compose);
- await writeDomainsToCompose(compose, domains);
- createEnvFile(compose);
+ const writeStream = createWriteStream(logPath, { flags: "a" });
+ const { sourceType, appName, mounts, composeType, domains } = compose;
+ try {
+ const { COMPOSE_PATH } = paths();
+ const command = createCommand(compose);
+ await writeDomainsToCompose(compose, domains);
+ createEnvFile(compose);
- const logContent = `
+ const logContent = `
App Name: ${appName}
Build Compose 🐳
Detected: ${mounts.length} mounts 📂
Command: docker ${command}
Source Type: docker ${sourceType} ✅
Compose Type: ${composeType} ✅`;
- const logBox = boxen(logContent, {
- padding: {
- left: 1,
- right: 1,
- bottom: 1,
- },
- width: 80,
- borderStyle: "double",
- });
- writeStream.write(`\n${logBox}\n`);
- const projectPath = join(COMPOSE_PATH, compose.appName, "code");
+ const logBox = boxen(logContent, {
+ padding: {
+ left: 1,
+ right: 1,
+ bottom: 1,
+ },
+ width: 80,
+ borderStyle: "double",
+ });
+ writeStream.write(`\n${logBox}\n`);
+ const projectPath = join(COMPOSE_PATH, compose.appName, "code");
- await spawnAsync(
- "docker",
- [...command.split(" ")],
- (data) => {
- if (writeStream.writable) {
- writeStream.write(data.toString());
- }
- },
- {
- cwd: projectPath,
- env: {
- NODE_ENV: process.env.NODE_ENV,
- PATH: process.env.PATH,
- ...(composeType === "stack" && {
- ...getEnviromentVariablesObject(compose.env, compose.project.env),
- }),
- },
- }
- );
+ await spawnAsync(
+ "docker",
+ [...command.split(" ")],
+ (data) => {
+ if (writeStream.writable) {
+ writeStream.write(data.toString());
+ }
+ },
+ {
+ cwd: projectPath,
+ env: {
+ NODE_ENV: process.env.NODE_ENV,
+ PATH: process.env.PATH,
+ ...(composeType === "stack" && {
+ ...getEnviromentVariablesObject(compose.env, compose.project.env),
+ }),
+ },
+ },
+ );
- writeStream.write("Docker Compose Deployed: ✅");
- } catch (error) {
- writeStream.write(`Error ❌ ${(error as Error).message}`);
- throw error;
- } finally {
- writeStream.end();
- }
+ writeStream.write("Docker Compose Deployed: ✅");
+ } catch (error) {
+ writeStream.write(`Error ❌ ${(error as Error).message}`);
+ throw error;
+ } finally {
+ writeStream.end();
+ }
};
export const getBuildComposeCommand = async (
- compose: ComposeNested,
- logPath: string
+ compose: ComposeNested,
+ logPath: string,
) => {
- const { COMPOSE_PATH } = paths(true);
- const { sourceType, appName, mounts, composeType, domains, composePath } =
- compose;
- const command = createCommand(compose);
- const envCommand = getCreateEnvFileCommand(compose);
- const projectPath = join(COMPOSE_PATH, compose.appName, "code");
- const exportEnvCommand = getExportEnvCommand(compose);
+ const { COMPOSE_PATH } = paths(true);
+ const { sourceType, appName, mounts, composeType, domains, composePath } =
+ compose;
+ const command = createCommand(compose);
+ const envCommand = getCreateEnvFileCommand(compose);
+ const projectPath = join(COMPOSE_PATH, compose.appName, "code");
+ const exportEnvCommand = getExportEnvCommand(compose);
- const newCompose = await writeDomainsToComposeRemote(
- compose,
- domains,
- logPath
- );
- const logContent = `
+ const newCompose = await writeDomainsToComposeRemote(
+ compose,
+ domains,
+ logPath,
+ );
+ const logContent = `
App Name: ${appName}
Build Compose 🐳
Detected: ${mounts.length} mounts 📂
@@ -107,17 +107,17 @@ Command: docker ${command}
Source Type: docker ${sourceType} ✅
Compose Type: ${composeType} ✅`;
- const logBox = boxen(logContent, {
- padding: {
- left: 1,
- right: 1,
- bottom: 1,
- },
- width: 80,
- borderStyle: "double",
- });
+ const logBox = boxen(logContent, {
+ padding: {
+ left: 1,
+ right: 1,
+ bottom: 1,
+ },
+ width: 80,
+ borderStyle: "double",
+ });
- const bashCommand = `
+ const bashCommand = `
set -e
{
echo "${logBox}" >> "${logPath}"
@@ -139,102 +139,105 @@ Compose Type: ${composeType} ✅`;
}
`;
- return await execAsyncRemote(compose.serverId, bashCommand);
+ return await execAsyncRemote(compose.serverId, bashCommand);
};
const sanitizeCommand = (command: string) => {
- const sanitizedCommand = command.trim();
+ const sanitizedCommand = command.trim();
- const parts = sanitizedCommand.split(/\s+/);
+ const parts = sanitizedCommand.split(/\s+/);
- const restCommand = parts.map((arg) => arg.replace(/^"(.*)"$/, "$1"));
+ const restCommand = parts.map((arg) => arg.replace(/^"(.*)"$/, "$1"));
- return restCommand.join(" ");
+ return restCommand.join(" ");
};
export const createCommand = (compose: ComposeNested) => {
- const { composeType, appName, sourceType } = compose;
+ const { composeType, appName, sourceType } = compose;
- if (compose.command) {
- return `${sanitizeCommand(compose.command)}`;
- }
+ if (compose.command) {
+ return `${sanitizeCommand(compose.command)}`;
+ }
- const path =
- sourceType === "raw" ? "docker-compose.yml" : compose.composePath;
- let command = "";
+ const path =
+ sourceType === "raw" ? "docker-compose.yml" : compose.composePath;
+ let command = "";
- if (composeType === "stack") {
- command = `stack deploy -c ${path} ${appName} --prune`;
- }
+ if (composeType === "stack") {
+ command = `stack deploy -c ${path} ${appName} --prune`;
+ }
- return command;
+ return command;
};
const createEnvFile = (compose: ComposeNested) => {
- const { COMPOSE_PATH } = paths();
- const { env, composePath, appName } = compose;
- const composeFilePath =
- join(COMPOSE_PATH, appName, "code", composePath) ||
- join(COMPOSE_PATH, appName, "code", "docker-compose.yml");
+ const { COMPOSE_PATH } = paths();
+ const { env, composePath, appName } = compose;
+ const composeFilePath =
+ join(COMPOSE_PATH, appName, "code", composePath) ||
+ 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";
- }
+ const envFilePath = join(dirname(composeFilePath), ".env");
+ let envContent = env || "";
+ if (!envContent.includes("DOCKER_CONFIG")) {
+ envContent += "\nDOCKER_CONFIG=/root/.docker/config.json";
+ }
- if (compose.randomize) {
- envContent += `\nCOMPOSE_PREFIX=${compose.suffix}`;
- }
+ if (compose.randomize) {
+ envContent += `\nCOMPOSE_PREFIX=${compose.suffix}`;
+ }
- const envFileContent = prepareEnvironmentVariables(
- envContent,
- compose.project.env
- ).join("\n");
+ const envFileContent = prepareEnvironmentVariables(
+ envContent,
+ compose.project.env,
+ ).join("\n");
- if (!existsSync(dirname(envFilePath))) {
- mkdirSync(dirname(envFilePath), { recursive: true });
- }
- writeFileSync(envFilePath, envFileContent);
+ if (!existsSync(dirname(envFilePath))) {
+ mkdirSync(dirname(envFilePath), { recursive: true });
+ }
+ writeFileSync(envFilePath, envFileContent);
};
export const getCreateEnvFileCommand = (compose: ComposeNested) => {
- const { COMPOSE_PATH } = paths(true);
- const { env, composePath, appName } = compose;
- const composeFilePath =
- join(COMPOSE_PATH, appName, "code", composePath) ||
- join(COMPOSE_PATH, appName, "code", "docker-compose.yml");
+ const { COMPOSE_PATH } = paths(true);
+ const { env, composePath, appName } = compose;
+ const composeFilePath =
+ join(COMPOSE_PATH, appName, "code", composePath) ||
+ join(COMPOSE_PATH, appName, "code", "docker-compose.yml");
- const envFilePath = join(dirname(composeFilePath), ".env");
+ const envFilePath = join(dirname(composeFilePath), ".env");
- let envContent = env || "";
- if (!envContent.includes("DOCKER_CONFIG")) {
- envContent += "\nDOCKER_CONFIG=/root/.docker/config.json";
- }
+ let envContent = env || "";
+ if (!envContent.includes("DOCKER_CONFIG")) {
+ envContent += "\nDOCKER_CONFIG=/root/.docker/config.json";
+ }
- if (compose.randomize) {
- envContent += `\nCOMPOSE_PREFIX=${compose.suffix}`;
- }
+ if (compose.randomize) {
+ envContent += `\nCOMPOSE_PREFIX=${compose.suffix}`;
+ }
- const envFileContent = prepareEnvironmentVariables(
- envContent,
- compose.project.env
- ).join("\n");
+ const envFileContent = prepareEnvironmentVariables(
+ envContent,
+ compose.project.env,
+ ).join("\n");
- const encodedContent = encodeBase64(envFileContent);
- return `
+ const encodedContent = encodeBase64(envFileContent);
+ return `
touch ${envFilePath};
echo "${encodedContent}" | base64 -d > "${envFilePath}";
`;
};
const getExportEnvCommand = (compose: ComposeNested) => {
- if (compose.composeType !== "stack") return "";
-
- const envVars = getEnviromentVariablesObject(compose.env, compose.project.env);
- const exports = Object.entries(envVars)
- .map(([key, value]) => `export ${key}=${JSON.stringify(value)}`)
- .join("\n");
-
- return exports ? `\n# Export environment variables\n${exports}\n` : "";
+ if (compose.composeType !== "stack") return "";
+
+ const envVars = getEnviromentVariablesObject(
+ compose.env,
+ compose.project.env,
+ );
+ const exports = Object.entries(envVars)
+ .map(([key, value]) => `export ${key}=${JSON.stringify(value)}`)
+ .join("\n");
+
+ return exports ? `\n# Export environment variables\n${exports}\n` : "";
};
diff --git a/packages/server/src/utils/docker/utils.ts b/packages/server/src/utils/docker/utils.ts
index 96b2e08b..062e0722 100644
--- a/packages/server/src/utils/docker/utils.ts
+++ b/packages/server/src/utils/docker/utils.ts
@@ -15,529 +15,529 @@ import { spawnAsync } from "../process/spawnAsync";
import { getRemoteDocker } from "../servers/remote-docker";
interface RegistryAuth {
- username: string;
- password: string;
- registryUrl: string;
+ username: string;
+ password: string;
+ registryUrl: string;
}
export const pullImage = async (
- dockerImage: string,
- onData?: (data: any) => void,
- authConfig?: Partial
+ dockerImage: string,
+ onData?: (data: any) => void,
+ authConfig?: Partial,
): Promise => {
- try {
- if (!dockerImage) {
- throw new Error("Docker image not found");
- }
+ try {
+ if (!dockerImage) {
+ throw new Error("Docker image not found");
+ }
- if (authConfig?.username && authConfig?.password) {
- await spawnAsync(
- "docker",
- [
- "login",
- authConfig.registryUrl || "",
- "-u",
- authConfig.username,
- "-p",
- authConfig.password,
- ],
- onData
- );
- }
- await spawnAsync("docker", ["pull", dockerImage], onData);
- } catch (error) {
- throw error;
- }
+ if (authConfig?.username && authConfig?.password) {
+ await spawnAsync(
+ "docker",
+ [
+ "login",
+ authConfig.registryUrl || "",
+ "-u",
+ authConfig.username,
+ "-p",
+ authConfig.password,
+ ],
+ onData,
+ );
+ }
+ await spawnAsync("docker", ["pull", dockerImage], onData);
+ } catch (error) {
+ throw error;
+ }
};
export const pullRemoteImage = async (
- dockerImage: string,
- serverId: string,
- onData?: (data: any) => void,
- authConfig?: Partial
+ dockerImage: string,
+ serverId: string,
+ onData?: (data: any) => void,
+ authConfig?: Partial,
): Promise => {
- try {
- if (!dockerImage) {
- throw new Error("Docker image not found");
- }
+ try {
+ if (!dockerImage) {
+ throw new Error("Docker image not found");
+ }
- const remoteDocker = await getRemoteDocker(serverId);
+ const remoteDocker = await getRemoteDocker(serverId);
- await new Promise((resolve, reject) => {
- remoteDocker.pull(
- dockerImage,
- { authconfig: authConfig },
- (err, stream) => {
- if (err) {
- reject(err);
- return;
- }
+ await new Promise((resolve, reject) => {
+ remoteDocker.pull(
+ dockerImage,
+ { authconfig: authConfig },
+ (err, stream) => {
+ if (err) {
+ reject(err);
+ return;
+ }
- remoteDocker.modem.followProgress(
- stream as Readable,
- (err: Error | null, res) => {
- if (!err) {
- resolve(res);
- }
- if (err) {
- reject(err);
- }
- },
- (event) => {
- onData?.(event);
- }
- );
- }
- );
- });
- } catch (error) {
- throw error;
- }
+ remoteDocker.modem.followProgress(
+ stream as Readable,
+ (err: Error | null, res) => {
+ if (!err) {
+ resolve(res);
+ }
+ if (err) {
+ reject(err);
+ }
+ },
+ (event) => {
+ onData?.(event);
+ },
+ );
+ },
+ );
+ });
+ } catch (error) {
+ throw error;
+ }
};
export const containerExists = async (containerName: string) => {
- const container = docker.getContainer(containerName);
- try {
- await container.inspect();
- return true;
- } catch (error) {
- return false;
- }
+ const container = docker.getContainer(containerName);
+ try {
+ await container.inspect();
+ return true;
+ } catch (error) {
+ return false;
+ }
};
export const stopService = async (appName: string) => {
- try {
- await execAsync(`docker service scale ${appName}=0 `);
- } catch (error) {
- console.error(error);
- return error;
- }
+ try {
+ await execAsync(`docker service scale ${appName}=0 `);
+ } catch (error) {
+ console.error(error);
+ return error;
+ }
};
export const stopServiceRemote = async (serverId: string, appName: string) => {
- try {
- await execAsyncRemote(serverId, `docker service scale ${appName}=0 `);
- } catch (error) {
- console.error(error);
- return error;
- }
+ try {
+ await execAsyncRemote(serverId, `docker service scale ${appName}=0 `);
+ } catch (error) {
+ console.error(error);
+ return error;
+ }
};
export const getContainerByName = (name: string): Promise => {
- const opts = {
- limit: 1,
- filters: {
- name: [name],
- },
- };
- return new Promise((resolve, reject) => {
- docker.listContainers(opts, (err, containers) => {
- if (err) {
- reject(err);
- } else if (containers?.length === 0) {
- reject(new Error(`No container found with name: ${name}`));
- } else if (containers && containers?.length > 0 && containers[0]) {
- resolve(containers[0]);
- }
- });
- });
+ const opts = {
+ limit: 1,
+ filters: {
+ name: [name],
+ },
+ };
+ return new Promise((resolve, reject) => {
+ docker.listContainers(opts, (err, containers) => {
+ if (err) {
+ reject(err);
+ } else if (containers?.length === 0) {
+ reject(new Error(`No container found with name: ${name}`));
+ } else if (containers && containers?.length > 0 && containers[0]) {
+ resolve(containers[0]);
+ }
+ });
+ });
};
export const cleanUpUnusedImages = async (serverId?: string) => {
- try {
- const command = "docker image prune --force";
- if (serverId) {
- await execAsyncRemote(serverId, command);
- } else {
- await execAsync(command);
- }
- } catch (error) {
- console.error(error);
- throw error;
- }
+ try {
+ const command = "docker image prune --force";
+ if (serverId) {
+ await execAsyncRemote(serverId, command);
+ } else {
+ await execAsync(command);
+ }
+ } catch (error) {
+ console.error(error);
+ throw error;
+ }
};
export const cleanStoppedContainers = async (serverId?: string) => {
- try {
- const command = "docker container prune --force";
- if (serverId) {
- await execAsyncRemote(serverId, command);
- } else {
- await execAsync(command);
- }
- } catch (error) {
- console.error(error);
- throw error;
- }
+ try {
+ const command = "docker container prune --force";
+ if (serverId) {
+ await execAsyncRemote(serverId, command);
+ } else {
+ await execAsync(command);
+ }
+ } catch (error) {
+ console.error(error);
+ throw error;
+ }
};
export const cleanUpUnusedVolumes = async (serverId?: string) => {
- try {
- const command = "docker volume prune --force";
- if (serverId) {
- await execAsyncRemote(serverId, command);
- } else {
- await execAsync(command);
- }
- } catch (error) {
- console.error(error);
- throw error;
- }
+ try {
+ const command = "docker volume prune --force";
+ if (serverId) {
+ await execAsyncRemote(serverId, command);
+ } else {
+ await execAsync(command);
+ }
+ } catch (error) {
+ console.error(error);
+ throw error;
+ }
};
export const cleanUpInactiveContainers = async () => {
- try {
- const containers = await docker.listContainers({ all: true });
- const inactiveContainers = containers.filter(
- (container) => container.State !== "running"
- );
+ try {
+ const containers = await docker.listContainers({ all: true });
+ const inactiveContainers = containers.filter(
+ (container) => container.State !== "running",
+ );
- for (const container of inactiveContainers) {
- await docker.getContainer(container.Id).remove({ force: true });
- console.log(`Cleaning up inactive container: ${container.Id}`);
- }
- } catch (error) {
- console.error("Error cleaning up inactive containers:", error);
- throw error;
- }
+ for (const container of inactiveContainers) {
+ await docker.getContainer(container.Id).remove({ force: true });
+ console.log(`Cleaning up inactive container: ${container.Id}`);
+ }
+ } catch (error) {
+ console.error("Error cleaning up inactive containers:", error);
+ throw error;
+ }
};
export const cleanUpDockerBuilder = async (serverId?: string) => {
- const command = "docker builder prune --all --force";
- if (serverId) {
- await execAsyncRemote(serverId, command);
- } else {
- await execAsync(command);
- }
+ const command = "docker builder prune --all --force";
+ if (serverId) {
+ await execAsyncRemote(serverId, command);
+ } else {
+ await execAsync(command);
+ }
};
export const cleanUpSystemPrune = async (serverId?: string) => {
- const command = "docker system prune --all --force --volumes";
- if (serverId) {
- await execAsyncRemote(serverId, command);
- } else {
- await execAsync(command);
- }
+ const command = "docker system prune --all --force --volumes";
+ if (serverId) {
+ await execAsyncRemote(serverId, command);
+ } else {
+ await execAsync(command);
+ }
};
export const startService = async (appName: string) => {
- try {
- await execAsync(`docker service scale ${appName}=1 `);
- } catch (error) {
- console.error(error);
- throw error;
- }
+ try {
+ await execAsync(`docker service scale ${appName}=1 `);
+ } catch (error) {
+ console.error(error);
+ throw error;
+ }
};
export const startServiceRemote = async (serverId: string, appName: string) => {
- try {
- await execAsyncRemote(serverId, `docker service scale ${appName}=1 `);
- } catch (error) {
- console.error(error);
- throw error;
- }
+ try {
+ await execAsyncRemote(serverId, `docker service scale ${appName}=1 `);
+ } catch (error) {
+ console.error(error);
+ throw error;
+ }
};
export const removeService = async (
- appName: string,
- serverId?: string | null,
- deleteVolumes = false
+ appName: string,
+ serverId?: string | null,
+ deleteVolumes = false,
) => {
- try {
- const command = `docker service rm ${appName}`;
+ try {
+ const command = `docker service rm ${appName}`;
- if (serverId) {
- await execAsyncRemote(serverId, command);
- } else {
- await execAsync(command);
- }
- } catch (error) {
- return error;
- }
+ if (serverId) {
+ await execAsyncRemote(serverId, command);
+ } else {
+ await execAsync(command);
+ }
+ } catch (error) {
+ return error;
+ }
};
export const prepareEnvironmentVariables = (
- serviceEnv: string | null,
- projectEnv?: string | null
+ serviceEnv: string | null,
+ projectEnv?: string | null,
) => {
- const projectVars = parse(projectEnv ?? "");
- const serviceVars = parse(serviceEnv ?? "");
+ const projectVars = parse(projectEnv ?? "");
+ const serviceVars = parse(serviceEnv ?? "");
- const resolvedVars = Object.entries(serviceVars).map(([key, value]) => {
- let resolvedValue = value;
- if (projectVars) {
- resolvedValue = value.replace(/\$\{\{project\.(.*?)\}\}/g, (_, ref) => {
- if (projectVars[ref] !== undefined) {
- return projectVars[ref];
- }
- throw new Error(`Invalid project environment variable: project.${ref}`);
- });
- }
- return `${key}=${resolvedValue}`;
- });
+ const resolvedVars = Object.entries(serviceVars).map(([key, value]) => {
+ let resolvedValue = value;
+ if (projectVars) {
+ resolvedValue = value.replace(/\$\{\{project\.(.*?)\}\}/g, (_, ref) => {
+ if (projectVars[ref] !== undefined) {
+ return projectVars[ref];
+ }
+ throw new Error(`Invalid project environment variable: project.${ref}`);
+ });
+ }
+ return `${key}=${resolvedValue}`;
+ });
- return resolvedVars;
+ return resolvedVars;
};
export const getEnviromentVariablesObject = (
- input: string | null,
- projectEnv?: string | null
+ input: string | null,
+ projectEnv?: string | null,
) => {
- const envs = prepareEnvironmentVariables(input, projectEnv);
+ const envs = prepareEnvironmentVariables(input, projectEnv);
- const jsonObject: Record = {};
+ const jsonObject: Record = {};
- for (const pair of envs) {
- const [key, value] = pair.split("=");
- if (key && value) {
- jsonObject[key] = value;
- }
- }
+ for (const pair of envs) {
+ const [key, value] = pair.split("=");
+ if (key && value) {
+ jsonObject[key] = value;
+ }
+ }
- return jsonObject;
+ return jsonObject;
};
export const generateVolumeMounts = (mounts: ApplicationNested["mounts"]) => {
- if (!mounts || mounts.length === 0) {
- return [];
- }
+ if (!mounts || mounts.length === 0) {
+ return [];
+ }
- return mounts
- .filter((mount) => mount.type === "volume")
- .map((mount) => ({
- Type: "volume" as const,
- Source: mount.volumeName || "",
- Target: mount.mountPath,
- }));
+ return mounts
+ .filter((mount) => mount.type === "volume")
+ .map((mount) => ({
+ Type: "volume" as const,
+ Source: mount.volumeName || "",
+ Target: mount.mountPath,
+ }));
};
type Resources = {
- memoryLimit: string | null;
- memoryReservation: string | null;
- cpuLimit: string | null;
- cpuReservation: string | null;
+ memoryLimit: string | null;
+ memoryReservation: string | null;
+ cpuLimit: string | null;
+ cpuReservation: string | null;
};
export const calculateResources = ({
- memoryLimit,
- memoryReservation,
- cpuLimit,
- cpuReservation,
+ memoryLimit,
+ memoryReservation,
+ cpuLimit,
+ cpuReservation,
}: Resources): ResourceRequirements => {
- return {
- Limits: {
- MemoryBytes: memoryLimit ? Number.parseInt(memoryLimit) : undefined,
- NanoCPUs: cpuLimit ? Number.parseInt(cpuLimit) : undefined,
- },
- Reservations: {
- MemoryBytes: memoryReservation
- ? Number.parseInt(memoryReservation)
- : undefined,
- NanoCPUs: cpuReservation ? Number.parseInt(cpuReservation) : undefined,
- },
- };
+ return {
+ Limits: {
+ MemoryBytes: memoryLimit ? Number.parseInt(memoryLimit) : undefined,
+ NanoCPUs: cpuLimit ? Number.parseInt(cpuLimit) : undefined,
+ },
+ Reservations: {
+ MemoryBytes: memoryReservation
+ ? Number.parseInt(memoryReservation)
+ : undefined,
+ NanoCPUs: cpuReservation ? Number.parseInt(cpuReservation) : undefined,
+ },
+ };
};
export const generateConfigContainer = (application: ApplicationNested) => {
- const {
- healthCheckSwarm,
- restartPolicySwarm,
- placementSwarm,
- updateConfigSwarm,
- rollbackConfigSwarm,
- modeSwarm,
- labelsSwarm,
- replicas,
- mounts,
- networkSwarm,
- } = application;
+ const {
+ healthCheckSwarm,
+ restartPolicySwarm,
+ placementSwarm,
+ updateConfigSwarm,
+ rollbackConfigSwarm,
+ modeSwarm,
+ labelsSwarm,
+ replicas,
+ mounts,
+ networkSwarm,
+ } = application;
- const haveMounts = mounts.length > 0;
+ const haveMounts = mounts.length > 0;
- return {
- ...(healthCheckSwarm && {
- HealthCheck: healthCheckSwarm,
- }),
- ...(restartPolicySwarm
- ? {
- RestartPolicy: restartPolicySwarm,
- }
- : {}),
- ...(placementSwarm
- ? {
- Placement: placementSwarm,
- }
- : {
- // if app have mounts keep manager as constraint
- Placement: {
- Constraints: haveMounts ? ["node.role==manager"] : [],
- },
- }),
- ...(labelsSwarm && {
- Labels: labelsSwarm,
- }),
- ...(modeSwarm
- ? {
- Mode: modeSwarm,
- }
- : {
- // use replicas value if no modeSwarm provided
- Mode: {
- Replicated: {
- Replicas: replicas,
- },
- },
- }),
- ...(rollbackConfigSwarm && {
- RollbackConfig: rollbackConfigSwarm,
- }),
- ...(updateConfigSwarm
- ? { UpdateConfig: updateConfigSwarm }
- : {
- // default config if no updateConfigSwarm provided
- UpdateConfig: {
- Parallelism: 1,
- Order: "start-first",
- },
- }),
- ...(networkSwarm
- ? {
- Networks: networkSwarm,
- }
- : {
- Networks: [{ Target: "dokploy-network" }],
- }),
- };
+ return {
+ ...(healthCheckSwarm && {
+ HealthCheck: healthCheckSwarm,
+ }),
+ ...(restartPolicySwarm
+ ? {
+ RestartPolicy: restartPolicySwarm,
+ }
+ : {}),
+ ...(placementSwarm
+ ? {
+ Placement: placementSwarm,
+ }
+ : {
+ // if app have mounts keep manager as constraint
+ Placement: {
+ Constraints: haveMounts ? ["node.role==manager"] : [],
+ },
+ }),
+ ...(labelsSwarm && {
+ Labels: labelsSwarm,
+ }),
+ ...(modeSwarm
+ ? {
+ Mode: modeSwarm,
+ }
+ : {
+ // use replicas value if no modeSwarm provided
+ Mode: {
+ Replicated: {
+ Replicas: replicas,
+ },
+ },
+ }),
+ ...(rollbackConfigSwarm && {
+ RollbackConfig: rollbackConfigSwarm,
+ }),
+ ...(updateConfigSwarm
+ ? { UpdateConfig: updateConfigSwarm }
+ : {
+ // default config if no updateConfigSwarm provided
+ UpdateConfig: {
+ Parallelism: 1,
+ Order: "start-first",
+ },
+ }),
+ ...(networkSwarm
+ ? {
+ Networks: networkSwarm,
+ }
+ : {
+ Networks: [{ Target: "dokploy-network" }],
+ }),
+ };
};
export const generateBindMounts = (mounts: ApplicationNested["mounts"]) => {
- if (!mounts || mounts.length === 0) {
- return [];
- }
+ if (!mounts || mounts.length === 0) {
+ return [];
+ }
- return mounts
- .filter((mount) => mount.type === "bind")
- .map((mount) => ({
- Type: "bind" as const,
- Source: mount.hostPath || "",
- Target: mount.mountPath,
- }));
+ return mounts
+ .filter((mount) => mount.type === "bind")
+ .map((mount) => ({
+ Type: "bind" as const,
+ Source: mount.hostPath || "",
+ Target: mount.mountPath,
+ }));
};
export const generateFileMounts = (
- appName: string,
- service:
- | ApplicationNested
- | MongoNested
- | MariadbNested
- | MysqlNested
- | PostgresNested
- | RedisNested
+ appName: string,
+ service:
+ | ApplicationNested
+ | MongoNested
+ | MariadbNested
+ | MysqlNested
+ | PostgresNested
+ | RedisNested,
) => {
- const { mounts } = service;
- const { APPLICATIONS_PATH } = paths(!!service.serverId);
- if (!mounts || mounts.length === 0) {
- return [];
- }
+ const { mounts } = service;
+ const { APPLICATIONS_PATH } = paths(!!service.serverId);
+ if (!mounts || mounts.length === 0) {
+ return [];
+ }
- return mounts
- .filter((mount) => mount.type === "file")
- .map((mount) => {
- const fileName = mount.filePath;
- const absoluteBasePath = path.resolve(APPLICATIONS_PATH);
- const directory = path.join(absoluteBasePath, appName, "files");
- const sourcePath = path.join(directory, fileName || "");
- return {
- Type: "bind" as const,
- Source: sourcePath,
- Target: mount.mountPath,
- };
- });
+ return mounts
+ .filter((mount) => mount.type === "file")
+ .map((mount) => {
+ const fileName = mount.filePath;
+ const absoluteBasePath = path.resolve(APPLICATIONS_PATH);
+ const directory = path.join(absoluteBasePath, appName, "files");
+ const sourcePath = path.join(directory, fileName || "");
+ return {
+ Type: "bind" as const,
+ Source: sourcePath,
+ Target: mount.mountPath,
+ };
+ });
};
export const createFile = async (
- outputPath: string,
- filePath: string,
- content: string
+ outputPath: string,
+ filePath: string,
+ content: string,
) => {
- try {
- const fullPath = path.join(outputPath, filePath);
- if (fullPath.endsWith(path.sep) || filePath.endsWith("/")) {
- fs.mkdirSync(fullPath, { recursive: true });
- return;
- }
+ try {
+ const fullPath = path.join(outputPath, filePath);
+ if (fullPath.endsWith(path.sep) || filePath.endsWith("/")) {
+ fs.mkdirSync(fullPath, { recursive: true });
+ return;
+ }
- const directory = path.dirname(fullPath);
- fs.mkdirSync(directory, { recursive: true });
- fs.writeFileSync(fullPath, content || "");
- } catch (error) {
- throw error;
- }
+ const directory = path.dirname(fullPath);
+ fs.mkdirSync(directory, { recursive: true });
+ fs.writeFileSync(fullPath, content || "");
+ } catch (error) {
+ throw error;
+ }
};
export const encodeBase64 = (content: string) =>
- Buffer.from(content, "utf-8").toString("base64");
+ Buffer.from(content, "utf-8").toString("base64");
export const getCreateFileCommand = (
- outputPath: string,
- filePath: string,
- content: string
+ outputPath: string,
+ filePath: string,
+ content: string,
) => {
- const fullPath = path.join(outputPath, filePath);
- if (fullPath.endsWith(path.sep) || filePath.endsWith("/")) {
- return `mkdir -p ${fullPath};`;
- }
+ const fullPath = path.join(outputPath, filePath);
+ if (fullPath.endsWith(path.sep) || filePath.endsWith("/")) {
+ return `mkdir -p ${fullPath};`;
+ }
- const directory = path.dirname(fullPath);
- const encodedContent = encodeBase64(content);
- return `
+ const directory = path.dirname(fullPath);
+ const encodedContent = encodeBase64(content);
+ return `
mkdir -p ${directory};
echo "${encodedContent}" | base64 -d > "${fullPath}";
`;
};
export const getServiceContainer = async (appName: string) => {
- try {
- const filter = {
- status: ["running"],
- label: [`com.docker.swarm.service.name=${appName}`],
- };
+ try {
+ const filter = {
+ status: ["running"],
+ label: [`com.docker.swarm.service.name=${appName}`],
+ };
- const containers = await docker.listContainers({
- filters: JSON.stringify(filter),
- });
+ const containers = await docker.listContainers({
+ filters: JSON.stringify(filter),
+ });
- if (containers.length === 0 || !containers[0]) {
- throw new Error(`No container found with name: ${appName}`);
- }
+ if (containers.length === 0 || !containers[0]) {
+ throw new Error(`No container found with name: ${appName}`);
+ }
- const container = containers[0];
+ const container = containers[0];
- return container;
- } catch (error) {
- throw error;
- }
+ return container;
+ } catch (error) {
+ throw error;
+ }
};
export const getRemoteServiceContainer = async (
- serverId: string,
- appName: string
+ serverId: string,
+ appName: string,
) => {
- try {
- const filter = {
- status: ["running"],
- label: [`com.docker.swarm.service.name=${appName}`],
- };
- const remoteDocker = await getRemoteDocker(serverId);
- const containers = await remoteDocker.listContainers({
- filters: JSON.stringify(filter),
- });
+ try {
+ const filter = {
+ status: ["running"],
+ label: [`com.docker.swarm.service.name=${appName}`],
+ };
+ const remoteDocker = await getRemoteDocker(serverId);
+ const containers = await remoteDocker.listContainers({
+ filters: JSON.stringify(filter),
+ });
- if (containers.length === 0 || !containers[0]) {
- throw new Error(`No container found with name: ${appName}`);
- }
+ if (containers.length === 0 || !containers[0]) {
+ throw new Error(`No container found with name: ${appName}`);
+ }
- const container = containers[0];
+ const container = containers[0];
- return container;
- } catch (error) {
- throw error;
- }
+ return container;
+ } catch (error) {
+ throw error;
+ }
};