mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
refactor: lint
This commit is contained in:
@@ -1,117 +1,117 @@
|
||||
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);
|
||||
|
||||
if (compose.isolatedDeployment) {
|
||||
await execAsync(
|
||||
`docker network inspect ${compose.appName} >/dev/null 2>&1 || docker network create --attachable ${compose.appName}`
|
||||
);
|
||||
}
|
||||
if (compose.isolatedDeployment) {
|
||||
await execAsync(
|
||||
`docker network inspect ${compose.appName} >/dev/null 2>&1 || docker network create --attachable ${compose.appName}`,
|
||||
);
|
||||
}
|
||||
|
||||
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),
|
||||
}),
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (compose.isolatedDeployment) {
|
||||
await execAsync(
|
||||
`docker network connect ${compose.appName} $(docker ps --filter "name=dokploy-traefik" -q) >/dev/null 2>&1`
|
||||
);
|
||||
}
|
||||
if (compose.isolatedDeployment) {
|
||||
await execAsync(
|
||||
`docker network connect ${compose.appName} $(docker ps --filter "name=dokploy-traefik" -q) >/dev/null 2>&1`,
|
||||
);
|
||||
}
|
||||
|
||||
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 📂
|
||||
@@ -119,17 +119,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}"
|
||||
@@ -152,106 +152,106 @@ 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;
|
||||
if (compose.command) {
|
||||
return `${sanitizeCommand(compose.command)}`;
|
||||
}
|
||||
const { composeType, appName, sourceType } = compose;
|
||||
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 === "docker-compose") {
|
||||
command = `compose -p ${appName} -f ${path} up -d --build --remove-orphans`;
|
||||
} else if (composeType === "stack") {
|
||||
command = `stack deploy -c ${path} ${appName} --prune`;
|
||||
}
|
||||
if (composeType === "docker-compose") {
|
||||
command = `compose -p ${appName} -f ${path} up -d --build --remove-orphans`;
|
||||
} else 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 "";
|
||||
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");
|
||||
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` : "";
|
||||
return exports ? `\n# Export environment variables\n${exports}\n` : "";
|
||||
};
|
||||
|
||||
@@ -6,41 +6,41 @@ import { addSuffixToAllVolumes } from "./compose/volume";
|
||||
import type { ComposeSpecification } from "./types";
|
||||
|
||||
export const addAppNameToPreventCollision = (
|
||||
composeData: ComposeSpecification,
|
||||
appName: string
|
||||
composeData: ComposeSpecification,
|
||||
appName: string,
|
||||
): ComposeSpecification => {
|
||||
let updatedComposeData = { ...composeData };
|
||||
let updatedComposeData = { ...composeData };
|
||||
|
||||
updatedComposeData = addAppNameToAllServiceNames(updatedComposeData, appName);
|
||||
updatedComposeData = addSuffixToAllVolumes(updatedComposeData, appName);
|
||||
return updatedComposeData;
|
||||
updatedComposeData = addAppNameToAllServiceNames(updatedComposeData, appName);
|
||||
updatedComposeData = addSuffixToAllVolumes(updatedComposeData, appName);
|
||||
return updatedComposeData;
|
||||
};
|
||||
|
||||
export const randomizeIsolatedDeploymentComposeFile = async (
|
||||
composeId: string,
|
||||
suffix?: string
|
||||
composeId: string,
|
||||
suffix?: string,
|
||||
) => {
|
||||
const compose = await findComposeById(composeId);
|
||||
const composeFile = compose.composeFile;
|
||||
const composeData = load(composeFile) as ComposeSpecification;
|
||||
const compose = await findComposeById(composeId);
|
||||
const composeFile = compose.composeFile;
|
||||
const composeData = load(composeFile) as ComposeSpecification;
|
||||
|
||||
const randomSuffix = suffix || compose.appName || generateRandomHash();
|
||||
const randomSuffix = suffix || compose.appName || generateRandomHash();
|
||||
|
||||
const newComposeFile = addAppNameToPreventCollision(
|
||||
composeData,
|
||||
randomSuffix
|
||||
);
|
||||
const newComposeFile = addAppNameToPreventCollision(
|
||||
composeData,
|
||||
randomSuffix,
|
||||
);
|
||||
|
||||
return dump(newComposeFile);
|
||||
return dump(newComposeFile);
|
||||
};
|
||||
|
||||
export const randomizeDeployableSpecificationFile = (
|
||||
composeSpec: ComposeSpecification,
|
||||
suffix?: string
|
||||
composeSpec: ComposeSpecification,
|
||||
suffix?: string,
|
||||
) => {
|
||||
if (!suffix) {
|
||||
return composeSpec;
|
||||
}
|
||||
const newComposeFile = addAppNameToPreventCollision(composeSpec, suffix);
|
||||
return newComposeFile;
|
||||
if (!suffix) {
|
||||
return composeSpec;
|
||||
}
|
||||
const newComposeFile = addAppNameToPreventCollision(composeSpec, suffix);
|
||||
return newComposeFile;
|
||||
};
|
||||
|
||||
@@ -7,346 +7,346 @@ import type { Domain } from "@dokploy/server/services/domain";
|
||||
import { dump, load } from "js-yaml";
|
||||
import { execAsyncRemote } from "../process/execAsync";
|
||||
import {
|
||||
cloneRawBitbucketRepository,
|
||||
cloneRawBitbucketRepositoryRemote,
|
||||
cloneRawBitbucketRepository,
|
||||
cloneRawBitbucketRepositoryRemote,
|
||||
} from "../providers/bitbucket";
|
||||
import {
|
||||
cloneGitRawRepository,
|
||||
cloneRawGitRepositoryRemote,
|
||||
cloneGitRawRepository,
|
||||
cloneRawGitRepositoryRemote,
|
||||
} from "../providers/git";
|
||||
import {
|
||||
cloneRawGithubRepository,
|
||||
cloneRawGithubRepositoryRemote,
|
||||
cloneRawGithubRepository,
|
||||
cloneRawGithubRepositoryRemote,
|
||||
} from "../providers/github";
|
||||
import {
|
||||
cloneRawGitlabRepository,
|
||||
cloneRawGitlabRepositoryRemote,
|
||||
cloneRawGitlabRepository,
|
||||
cloneRawGitlabRepositoryRemote,
|
||||
} from "../providers/gitlab";
|
||||
import {
|
||||
createComposeFileRaw,
|
||||
createComposeFileRawRemote,
|
||||
createComposeFileRaw,
|
||||
createComposeFileRawRemote,
|
||||
} from "../providers/raw";
|
||||
import { randomizeDeployableSpecificationFile } from "./collision";
|
||||
import { randomizeSpecificationFile } from "./compose";
|
||||
import type {
|
||||
ComposeSpecification,
|
||||
DefinitionsService,
|
||||
PropertiesNetworks,
|
||||
ComposeSpecification,
|
||||
DefinitionsService,
|
||||
PropertiesNetworks,
|
||||
} from "./types";
|
||||
import { encodeBase64 } from "./utils";
|
||||
|
||||
export const cloneCompose = async (compose: Compose) => {
|
||||
if (compose.sourceType === "github") {
|
||||
await cloneRawGithubRepository(compose);
|
||||
} else if (compose.sourceType === "gitlab") {
|
||||
await cloneRawGitlabRepository(compose);
|
||||
} else if (compose.sourceType === "bitbucket") {
|
||||
await cloneRawBitbucketRepository(compose);
|
||||
} else if (compose.sourceType === "git") {
|
||||
await cloneGitRawRepository(compose);
|
||||
} else if (compose.sourceType === "raw") {
|
||||
await createComposeFileRaw(compose);
|
||||
}
|
||||
if (compose.sourceType === "github") {
|
||||
await cloneRawGithubRepository(compose);
|
||||
} else if (compose.sourceType === "gitlab") {
|
||||
await cloneRawGitlabRepository(compose);
|
||||
} else if (compose.sourceType === "bitbucket") {
|
||||
await cloneRawBitbucketRepository(compose);
|
||||
} else if (compose.sourceType === "git") {
|
||||
await cloneGitRawRepository(compose);
|
||||
} else if (compose.sourceType === "raw") {
|
||||
await createComposeFileRaw(compose);
|
||||
}
|
||||
};
|
||||
|
||||
export const cloneComposeRemote = async (compose: Compose) => {
|
||||
if (compose.sourceType === "github") {
|
||||
await cloneRawGithubRepositoryRemote(compose);
|
||||
} else if (compose.sourceType === "gitlab") {
|
||||
await cloneRawGitlabRepositoryRemote(compose);
|
||||
} else if (compose.sourceType === "bitbucket") {
|
||||
await cloneRawBitbucketRepositoryRemote(compose);
|
||||
} else if (compose.sourceType === "git") {
|
||||
await cloneRawGitRepositoryRemote(compose);
|
||||
} else if (compose.sourceType === "raw") {
|
||||
await createComposeFileRawRemote(compose);
|
||||
}
|
||||
if (compose.sourceType === "github") {
|
||||
await cloneRawGithubRepositoryRemote(compose);
|
||||
} else if (compose.sourceType === "gitlab") {
|
||||
await cloneRawGitlabRepositoryRemote(compose);
|
||||
} else if (compose.sourceType === "bitbucket") {
|
||||
await cloneRawBitbucketRepositoryRemote(compose);
|
||||
} else if (compose.sourceType === "git") {
|
||||
await cloneRawGitRepositoryRemote(compose);
|
||||
} else if (compose.sourceType === "raw") {
|
||||
await createComposeFileRawRemote(compose);
|
||||
}
|
||||
};
|
||||
|
||||
export const getComposePath = (compose: Compose) => {
|
||||
const { COMPOSE_PATH } = paths(!!compose.serverId);
|
||||
const { appName, sourceType, composePath } = compose;
|
||||
let path = "";
|
||||
const { COMPOSE_PATH } = paths(!!compose.serverId);
|
||||
const { appName, sourceType, composePath } = compose;
|
||||
let path = "";
|
||||
|
||||
if (sourceType === "raw") {
|
||||
path = "docker-compose.yml";
|
||||
} else {
|
||||
path = composePath;
|
||||
}
|
||||
if (sourceType === "raw") {
|
||||
path = "docker-compose.yml";
|
||||
} else {
|
||||
path = composePath;
|
||||
}
|
||||
|
||||
return join(COMPOSE_PATH, appName, "code", path);
|
||||
return join(COMPOSE_PATH, appName, "code", path);
|
||||
};
|
||||
|
||||
export const loadDockerCompose = async (
|
||||
compose: Compose
|
||||
compose: Compose,
|
||||
): Promise<ComposeSpecification | null> => {
|
||||
const path = getComposePath(compose);
|
||||
const path = getComposePath(compose);
|
||||
|
||||
if (existsSync(path)) {
|
||||
const yamlStr = readFileSync(path, "utf8");
|
||||
const parsedConfig = load(yamlStr) as ComposeSpecification;
|
||||
return parsedConfig;
|
||||
}
|
||||
return null;
|
||||
if (existsSync(path)) {
|
||||
const yamlStr = readFileSync(path, "utf8");
|
||||
const parsedConfig = load(yamlStr) as ComposeSpecification;
|
||||
return parsedConfig;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export const loadDockerComposeRemote = async (
|
||||
compose: Compose
|
||||
compose: Compose,
|
||||
): Promise<ComposeSpecification | null> => {
|
||||
const path = getComposePath(compose);
|
||||
try {
|
||||
if (!compose.serverId) {
|
||||
return null;
|
||||
}
|
||||
const { stdout, stderr } = await execAsyncRemote(
|
||||
compose.serverId,
|
||||
`cat ${path}`
|
||||
);
|
||||
const path = getComposePath(compose);
|
||||
try {
|
||||
if (!compose.serverId) {
|
||||
return null;
|
||||
}
|
||||
const { stdout, stderr } = await execAsyncRemote(
|
||||
compose.serverId,
|
||||
`cat ${path}`,
|
||||
);
|
||||
|
||||
if (stderr) {
|
||||
return null;
|
||||
}
|
||||
if (!stdout) return null;
|
||||
const parsedConfig = load(stdout) as ComposeSpecification;
|
||||
return parsedConfig;
|
||||
} catch (err) {
|
||||
return null;
|
||||
}
|
||||
if (stderr) {
|
||||
return null;
|
||||
}
|
||||
if (!stdout) return null;
|
||||
const parsedConfig = load(stdout) as ComposeSpecification;
|
||||
return parsedConfig;
|
||||
} catch (err) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export const readComposeFile = async (compose: Compose) => {
|
||||
const path = getComposePath(compose);
|
||||
if (existsSync(path)) {
|
||||
const yamlStr = readFileSync(path, "utf8");
|
||||
return yamlStr;
|
||||
}
|
||||
return null;
|
||||
const path = getComposePath(compose);
|
||||
if (existsSync(path)) {
|
||||
const yamlStr = readFileSync(path, "utf8");
|
||||
return yamlStr;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export const writeDomainsToCompose = async (
|
||||
compose: Compose,
|
||||
domains: Domain[]
|
||||
compose: Compose,
|
||||
domains: Domain[],
|
||||
) => {
|
||||
if (!domains.length) {
|
||||
return;
|
||||
}
|
||||
const composeConverted = await addDomainToCompose(compose, domains);
|
||||
if (!domains.length) {
|
||||
return;
|
||||
}
|
||||
const composeConverted = await addDomainToCompose(compose, domains);
|
||||
|
||||
const path = getComposePath(compose);
|
||||
const composeString = dump(composeConverted, { lineWidth: 1000 });
|
||||
try {
|
||||
await writeFile(path, composeString, "utf8");
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
const path = getComposePath(compose);
|
||||
const composeString = dump(composeConverted, { lineWidth: 1000 });
|
||||
try {
|
||||
await writeFile(path, composeString, "utf8");
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export const writeDomainsToComposeRemote = async (
|
||||
compose: Compose,
|
||||
domains: Domain[],
|
||||
logPath: string
|
||||
compose: Compose,
|
||||
domains: Domain[],
|
||||
logPath: string,
|
||||
) => {
|
||||
if (!domains.length) {
|
||||
return "";
|
||||
}
|
||||
if (!domains.length) {
|
||||
return "";
|
||||
}
|
||||
|
||||
try {
|
||||
const composeConverted = await addDomainToCompose(compose, domains);
|
||||
const path = getComposePath(compose);
|
||||
try {
|
||||
const composeConverted = await addDomainToCompose(compose, domains);
|
||||
const path = getComposePath(compose);
|
||||
|
||||
if (!composeConverted) {
|
||||
return `
|
||||
if (!composeConverted) {
|
||||
return `
|
||||
echo "❌ Error: Compose file not found" >> ${logPath};
|
||||
exit 1;
|
||||
`;
|
||||
}
|
||||
if (compose.serverId) {
|
||||
const composeString = dump(composeConverted, { lineWidth: 1000 });
|
||||
const encodedContent = encodeBase64(composeString);
|
||||
return `echo "${encodedContent}" | base64 -d > "${path}";`;
|
||||
}
|
||||
} catch (error) {
|
||||
// @ts-ignore
|
||||
return `echo "❌ Has occured an error: ${error?.message || error}" >> ${logPath};
|
||||
}
|
||||
if (compose.serverId) {
|
||||
const composeString = dump(composeConverted, { lineWidth: 1000 });
|
||||
const encodedContent = encodeBase64(composeString);
|
||||
return `echo "${encodedContent}" | base64 -d > "${path}";`;
|
||||
}
|
||||
} catch (error) {
|
||||
// @ts-ignore
|
||||
return `echo "❌ Has occured an error: ${error?.message || error}" >> ${logPath};
|
||||
exit 1;
|
||||
`;
|
||||
}
|
||||
}
|
||||
};
|
||||
// (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[]
|
||||
compose: Compose,
|
||||
domains: Domain[],
|
||||
) => {
|
||||
const { appName } = compose;
|
||||
const { appName } = compose;
|
||||
|
||||
let result: ComposeSpecification | null;
|
||||
let result: ComposeSpecification | null;
|
||||
|
||||
if (compose.serverId) {
|
||||
result = await loadDockerComposeRemote(compose); // aca hay que ir al servidor e ir a traer el compose file al servidor
|
||||
} else {
|
||||
result = await loadDockerCompose(compose);
|
||||
}
|
||||
if (compose.serverId) {
|
||||
result = await loadDockerComposeRemote(compose); // aca hay que ir al servidor e ir a traer el compose file al servidor
|
||||
} else {
|
||||
result = await loadDockerCompose(compose);
|
||||
}
|
||||
|
||||
if (!result || domains.length === 0) {
|
||||
return null;
|
||||
}
|
||||
if (!result || domains.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (compose.isolatedDeployment) {
|
||||
const randomized = randomizeDeployableSpecificationFile(
|
||||
result,
|
||||
compose.suffix || compose.appName
|
||||
);
|
||||
result = randomized;
|
||||
} else if (compose.randomize) {
|
||||
const randomized = randomizeSpecificationFile(result, compose.suffix);
|
||||
result = randomized;
|
||||
}
|
||||
if (compose.isolatedDeployment) {
|
||||
const randomized = randomizeDeployableSpecificationFile(
|
||||
result,
|
||||
compose.suffix || compose.appName,
|
||||
);
|
||||
result = randomized;
|
||||
} else if (compose.randomize) {
|
||||
const randomized = randomizeSpecificationFile(result, compose.suffix);
|
||||
result = randomized;
|
||||
}
|
||||
|
||||
for (const domain of domains) {
|
||||
const { serviceName, https } = domain;
|
||||
if (!serviceName) {
|
||||
throw new Error("Service name not found");
|
||||
}
|
||||
if (!result?.services?.[serviceName]) {
|
||||
throw new Error(`The service ${serviceName} not found in the compose`);
|
||||
}
|
||||
for (const domain of domains) {
|
||||
const { serviceName, https } = domain;
|
||||
if (!serviceName) {
|
||||
throw new Error("Service name not found");
|
||||
}
|
||||
if (!result?.services?.[serviceName]) {
|
||||
throw new Error(`The service ${serviceName} not found in the compose`);
|
||||
}
|
||||
|
||||
const httpLabels = await createDomainLabels(appName, domain, "web");
|
||||
if (https) {
|
||||
const httpsLabels = await createDomainLabels(
|
||||
appName,
|
||||
domain,
|
||||
"websecure"
|
||||
);
|
||||
httpLabels.push(...httpsLabels);
|
||||
}
|
||||
const httpLabels = await createDomainLabels(appName, domain, "web");
|
||||
if (https) {
|
||||
const httpsLabels = await createDomainLabels(
|
||||
appName,
|
||||
domain,
|
||||
"websecure",
|
||||
);
|
||||
httpLabels.push(...httpsLabels);
|
||||
}
|
||||
|
||||
let labels: DefinitionsService["labels"] = [];
|
||||
if (compose.composeType === "docker-compose") {
|
||||
if (!result.services[serviceName].labels) {
|
||||
result.services[serviceName].labels = [];
|
||||
}
|
||||
let labels: DefinitionsService["labels"] = [];
|
||||
if (compose.composeType === "docker-compose") {
|
||||
if (!result.services[serviceName].labels) {
|
||||
result.services[serviceName].labels = [];
|
||||
}
|
||||
|
||||
labels = result.services[serviceName].labels;
|
||||
} else {
|
||||
// Stack Case
|
||||
if (!result.services[serviceName].deploy) {
|
||||
result.services[serviceName].deploy = {};
|
||||
}
|
||||
if (!result.services[serviceName].deploy.labels) {
|
||||
result.services[serviceName].deploy.labels = [];
|
||||
}
|
||||
labels = result.services[serviceName].labels;
|
||||
} else {
|
||||
// Stack Case
|
||||
if (!result.services[serviceName].deploy) {
|
||||
result.services[serviceName].deploy = {};
|
||||
}
|
||||
if (!result.services[serviceName].deploy.labels) {
|
||||
result.services[serviceName].deploy.labels = [];
|
||||
}
|
||||
|
||||
labels = result.services[serviceName].deploy.labels;
|
||||
}
|
||||
labels = result.services[serviceName].deploy.labels;
|
||||
}
|
||||
|
||||
if (Array.isArray(labels)) {
|
||||
if (!labels.includes("traefik.enable=true")) {
|
||||
labels.push("traefik.enable=true");
|
||||
}
|
||||
labels.push(...httpLabels);
|
||||
}
|
||||
if (Array.isArray(labels)) {
|
||||
if (!labels.includes("traefik.enable=true")) {
|
||||
labels.push("traefik.enable=true");
|
||||
}
|
||||
labels.push(...httpLabels);
|
||||
}
|
||||
|
||||
if (!compose.isolatedDeployment) {
|
||||
// Add the dokploy-network to the service
|
||||
result.services[serviceName].networks = addDokployNetworkToService(
|
||||
result.services[serviceName].networks
|
||||
);
|
||||
}
|
||||
}
|
||||
if (!compose.isolatedDeployment) {
|
||||
// Add the dokploy-network to the service
|
||||
result.services[serviceName].networks = addDokployNetworkToService(
|
||||
result.services[serviceName].networks,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Add dokploy-network to the root of the compose file
|
||||
if (!compose.isolatedDeployment) {
|
||||
result.networks = addDokployNetworkToRoot(result.networks);
|
||||
}
|
||||
// Add dokploy-network to the root of the compose file
|
||||
if (!compose.isolatedDeployment) {
|
||||
result.networks = addDokployNetworkToRoot(result.networks);
|
||||
}
|
||||
|
||||
return result;
|
||||
return result;
|
||||
};
|
||||
|
||||
export const writeComposeFile = async (
|
||||
compose: Compose,
|
||||
composeSpec: ComposeSpecification
|
||||
compose: Compose,
|
||||
composeSpec: ComposeSpecification,
|
||||
) => {
|
||||
const path = getComposePath(compose);
|
||||
const path = getComposePath(compose);
|
||||
|
||||
try {
|
||||
const composeFile = dump(composeSpec, {
|
||||
lineWidth: 1000,
|
||||
});
|
||||
fs.writeFileSync(path, composeFile, "utf8");
|
||||
} catch (e) {
|
||||
console.error("Error saving the YAML config file:", e);
|
||||
}
|
||||
try {
|
||||
const composeFile = dump(composeSpec, {
|
||||
lineWidth: 1000,
|
||||
});
|
||||
fs.writeFileSync(path, composeFile, "utf8");
|
||||
} catch (e) {
|
||||
console.error("Error saving the YAML config file:", e);
|
||||
}
|
||||
};
|
||||
|
||||
export const createDomainLabels = async (
|
||||
appName: string,
|
||||
domain: Domain,
|
||||
entrypoint: "web" | "websecure"
|
||||
appName: string,
|
||||
domain: Domain,
|
||||
entrypoint: "web" | "websecure",
|
||||
) => {
|
||||
const { host, port, https, uniqueConfigKey, certificateType, path } = domain;
|
||||
const routerName = `${appName}-${uniqueConfigKey}-${entrypoint}`;
|
||||
const labels = [
|
||||
`traefik.http.routers.${routerName}.rule=Host(\`${host}\`)${path && path !== "/" ? ` && PathPrefix(\`${path}\`)` : ""}`,
|
||||
`traefik.http.routers.${routerName}.entrypoints=${entrypoint}`,
|
||||
`traefik.http.services.${routerName}.loadbalancer.server.port=${port}`,
|
||||
`traefik.http.routers.${routerName}.service=${routerName}`,
|
||||
];
|
||||
const { host, port, https, uniqueConfigKey, certificateType, path } = domain;
|
||||
const routerName = `${appName}-${uniqueConfigKey}-${entrypoint}`;
|
||||
const labels = [
|
||||
`traefik.http.routers.${routerName}.rule=Host(\`${host}\`)${path && path !== "/" ? ` && PathPrefix(\`${path}\`)` : ""}`,
|
||||
`traefik.http.routers.${routerName}.entrypoints=${entrypoint}`,
|
||||
`traefik.http.services.${routerName}.loadbalancer.server.port=${port}`,
|
||||
`traefik.http.routers.${routerName}.service=${routerName}`,
|
||||
];
|
||||
|
||||
if (entrypoint === "web" && https) {
|
||||
labels.push(
|
||||
`traefik.http.routers.${routerName}.middlewares=redirect-to-https@file`
|
||||
);
|
||||
}
|
||||
if (entrypoint === "web" && https) {
|
||||
labels.push(
|
||||
`traefik.http.routers.${routerName}.middlewares=redirect-to-https@file`,
|
||||
);
|
||||
}
|
||||
|
||||
if (entrypoint === "websecure") {
|
||||
if (certificateType === "letsencrypt") {
|
||||
labels.push(
|
||||
`traefik.http.routers.${routerName}.tls.certresolver=letsencrypt`
|
||||
);
|
||||
}
|
||||
}
|
||||
if (entrypoint === "websecure") {
|
||||
if (certificateType === "letsencrypt") {
|
||||
labels.push(
|
||||
`traefik.http.routers.${routerName}.tls.certresolver=letsencrypt`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return labels;
|
||||
return labels;
|
||||
};
|
||||
|
||||
export const addDokployNetworkToService = (
|
||||
networkService: DefinitionsService["networks"]
|
||||
networkService: DefinitionsService["networks"],
|
||||
) => {
|
||||
let networks = networkService;
|
||||
const network = "dokploy-network";
|
||||
if (!networks) {
|
||||
networks = [];
|
||||
}
|
||||
let networks = networkService;
|
||||
const network = "dokploy-network";
|
||||
if (!networks) {
|
||||
networks = [];
|
||||
}
|
||||
|
||||
if (Array.isArray(networks)) {
|
||||
if (!networks.includes(network)) {
|
||||
networks.push(network);
|
||||
}
|
||||
} else if (networks && typeof networks === "object") {
|
||||
if (!(network in networks)) {
|
||||
networks[network] = {};
|
||||
}
|
||||
}
|
||||
if (Array.isArray(networks)) {
|
||||
if (!networks.includes(network)) {
|
||||
networks.push(network);
|
||||
}
|
||||
} else if (networks && typeof networks === "object") {
|
||||
if (!(network in networks)) {
|
||||
networks[network] = {};
|
||||
}
|
||||
}
|
||||
|
||||
return networks;
|
||||
return networks;
|
||||
};
|
||||
|
||||
export const addDokployNetworkToRoot = (
|
||||
networkRoot: PropertiesNetworks | undefined
|
||||
networkRoot: PropertiesNetworks | undefined,
|
||||
) => {
|
||||
let networks = networkRoot;
|
||||
const network = "dokploy-network";
|
||||
let networks = networkRoot;
|
||||
const network = "dokploy-network";
|
||||
|
||||
if (!networks) {
|
||||
networks = {};
|
||||
}
|
||||
if (!networks) {
|
||||
networks = {};
|
||||
}
|
||||
|
||||
if (networks[network] || !networks[network]) {
|
||||
networks[network] = {
|
||||
external: true,
|
||||
};
|
||||
}
|
||||
if (networks[network] || !networks[network]) {
|
||||
networks[network] = {
|
||||
external: true,
|
||||
};
|
||||
}
|
||||
|
||||
return networks;
|
||||
return networks;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user