mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
Merge pull request #637 from xenonwellz/feat/stack-env-support
Feat: added env support to Dokploy stack compose
This commit is contained in:
@@ -211,21 +211,17 @@ const Service = (
|
|||||||
<TabsList
|
<TabsList
|
||||||
className={cn(
|
className={cn(
|
||||||
"md:grid md:w-fit max-md:overflow-y-scroll justify-start",
|
"md:grid md:w-fit max-md:overflow-y-scroll justify-start",
|
||||||
data?.serverId ? "md:grid-cols-6" : "md:grid-cols-7",
|
data?.serverId ? "md:grid-cols-7" : "md:grid-cols-7",
|
||||||
data?.composeType === "docker-compose"
|
data?.composeType === "docker-compose"
|
||||||
? ""
|
? ""
|
||||||
: "md:grid-cols-6",
|
: "md:grid-cols-7",
|
||||||
data?.serverId && data?.composeType === "stack"
|
data?.serverId && data?.composeType === "stack"
|
||||||
? "md:grid-cols-5"
|
? "md:grid-cols-6"
|
||||||
: "",
|
: "",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<TabsTrigger value="general">General</TabsTrigger>
|
<TabsTrigger value="general">General</TabsTrigger>
|
||||||
{data?.composeType === "docker-compose" && (
|
<TabsTrigger value="environment">Environment</TabsTrigger>
|
||||||
<TabsTrigger value="environment">
|
|
||||||
Environment
|
|
||||||
</TabsTrigger>
|
|
||||||
)}
|
|
||||||
{!data?.serverId && (
|
{!data?.serverId && (
|
||||||
<TabsTrigger value="monitoring">Monitoring</TabsTrigger>
|
<TabsTrigger value="monitoring">Monitoring</TabsTrigger>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,59 +1,59 @@
|
|||||||
import {
|
import {
|
||||||
type DomainSchema,
|
type DomainSchema,
|
||||||
type Schema,
|
type Schema,
|
||||||
type Template,
|
type Template,
|
||||||
generateBase64,
|
generateBase64,
|
||||||
generatePassword,
|
generatePassword,
|
||||||
generateRandomDomain,
|
generateRandomDomain,
|
||||||
} from "../utils";
|
} from "../utils";
|
||||||
|
|
||||||
export function generate(schema: Schema): Template {
|
export function generate(schema: Schema): Template {
|
||||||
const mainDomain = generateRandomDomain(schema);
|
const mainDomain = generateRandomDomain(schema);
|
||||||
const apiKey = generateBase64(64);
|
const apiKey = generateBase64(64);
|
||||||
const postgresPassword = generatePassword();
|
const postgresPassword = generatePassword();
|
||||||
|
|
||||||
const domains: DomainSchema[] = [
|
const domains: DomainSchema[] = [
|
||||||
{
|
{
|
||||||
host: mainDomain,
|
host: mainDomain,
|
||||||
port: 8080,
|
port: 8080,
|
||||||
serviceName: "evolution-api",
|
serviceName: "evolution-api",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const envs = [
|
const envs = [
|
||||||
`SERVER_URL=https://${mainDomain}`,
|
`SERVER_URL=https://${mainDomain}`,
|
||||||
"AUTHENTICATION_TYPE=apikey",
|
"AUTHENTICATION_TYPE=apikey",
|
||||||
`AUTHENTICATION_API_KEY=${apiKey}`,
|
`AUTHENTICATION_API_KEY=${apiKey}`,
|
||||||
"AUTHENTICATION_EXPOSE_IN_FETCH_INSTANCES=true",
|
"AUTHENTICATION_EXPOSE_IN_FETCH_INSTANCES=true",
|
||||||
|
|
||||||
"LANGUAGE=en",
|
"LANGUAGE=en",
|
||||||
"CONFIG_SESSION_PHONE_CLIENT=Evolution API",
|
"CONFIG_SESSION_PHONE_CLIENT=Evolution API",
|
||||||
"CONFIG_SESSION_PHONE_NAME=Chrome",
|
"CONFIG_SESSION_PHONE_NAME=Chrome",
|
||||||
"TELEMETRY=false",
|
"TELEMETRY=false",
|
||||||
"TELEMETRY_URL=",
|
"TELEMETRY_URL=",
|
||||||
|
|
||||||
"POSTGRES_DATABASE=evolution",
|
"POSTGRES_DATABASE=evolution",
|
||||||
"POSTGRES_USERNAME=postgresql",
|
"POSTGRES_USERNAME=postgresql",
|
||||||
`POSTGRES_PASSWORD=${postgresPassword}`,
|
`POSTGRES_PASSWORD=${postgresPassword}`,
|
||||||
"DATABASE_ENABLED=true",
|
"DATABASE_ENABLED=true",
|
||||||
"DATABASE_PROVIDER=postgresql",
|
"DATABASE_PROVIDER=postgresql",
|
||||||
`DATABASE_CONNECTION_URI=postgres://postgresql:${postgresPassword}@evolution-postgres:5432/evolution`,
|
`DATABASE_CONNECTION_URI=postgres://postgresql:${postgresPassword}@evolution-postgres:5432/evolution`,
|
||||||
"DATABASE_SAVE_DATA_INSTANCE=true",
|
"DATABASE_SAVE_DATA_INSTANCE=true",
|
||||||
"DATABASE_SAVE_DATA_NEW_MESSAGE=true",
|
"DATABASE_SAVE_DATA_NEW_MESSAGE=true",
|
||||||
"DATABASE_SAVE_MESSAGE_UPDATE=true",
|
"DATABASE_SAVE_MESSAGE_UPDATE=true",
|
||||||
"DATABASE_SAVE_DATA_CONTACTS=true",
|
"DATABASE_SAVE_DATA_CONTACTS=true",
|
||||||
"DATABASE_SAVE_DATA_CHATS=true",
|
"DATABASE_SAVE_DATA_CHATS=true",
|
||||||
"DATABASE_SAVE_DATA_LABELS=true",
|
"DATABASE_SAVE_DATA_LABELS=true",
|
||||||
"DATABASE_SAVE_DATA_HISTORIC=true",
|
"DATABASE_SAVE_DATA_HISTORIC=true",
|
||||||
|
|
||||||
"CACHE_REDIS_ENABLED=true",
|
"CACHE_REDIS_ENABLED=true",
|
||||||
"CACHE_REDIS_URI=redis://evolution-redis:6379",
|
"CACHE_REDIS_URI=redis://evolution-redis:6379",
|
||||||
"CACHE_REDIS_PREFIX_KEY=evolution",
|
"CACHE_REDIS_PREFIX_KEY=evolution",
|
||||||
"CACHE_REDIS_SAVE_INSTANCES=true",
|
"CACHE_REDIS_SAVE_INSTANCES=true",
|
||||||
];
|
];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
domains,
|
domains,
|
||||||
envs,
|
envs,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ export function generate(schema: Schema): Template {
|
|||||||
`NEXTAUTH_SECRET=${secretBase}`,
|
`NEXTAUTH_SECRET=${secretBase}`,
|
||||||
`ENCRYPTION_KEY=${encryptionKey}`,
|
`ENCRYPTION_KEY=${encryptionKey}`,
|
||||||
`CRON_SECRET=${cronSecret}`,
|
`CRON_SECRET=${cronSecret}`,
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const mounts: Template["mounts"] = [];
|
const mounts: Template["mounts"] = [];
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export function generate(schema: Schema): Template {
|
|||||||
];
|
];
|
||||||
|
|
||||||
const envs = [
|
const envs = [
|
||||||
`# visit the page to setup your super admin user`,
|
"# visit the page to setup your super admin user",
|
||||||
"# check config.toml in Advanced / Volumes for more options",
|
"# check config.toml in Advanced / Volumes for more options",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -558,6 +558,17 @@ export const stopCompose = async (composeId: string) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (compose.composeType === "stack") {
|
||||||
|
if (compose.serverId) {
|
||||||
|
await execAsyncRemote(
|
||||||
|
compose.serverId,
|
||||||
|
`docker stack rm ${compose.appName}`,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
await execAsync(`docker stack rm ${compose.appName}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await updateCompose(composeId, {
|
await updateCompose(composeId, {
|
||||||
composeStatus: "idle",
|
composeStatus: "idle",
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import {
|
|||||||
createWriteStream,
|
createWriteStream,
|
||||||
existsSync,
|
existsSync,
|
||||||
mkdirSync,
|
mkdirSync,
|
||||||
|
readFileSync,
|
||||||
writeFileSync,
|
writeFileSync,
|
||||||
} from "node:fs";
|
} from "node:fs";
|
||||||
import { dirname, join } from "node:path";
|
import { dirname, join } from "node:path";
|
||||||
@@ -12,8 +13,12 @@ import {
|
|||||||
writeDomainsToCompose,
|
writeDomainsToCompose,
|
||||||
writeDomainsToComposeRemote,
|
writeDomainsToComposeRemote,
|
||||||
} from "../docker/domain";
|
} from "../docker/domain";
|
||||||
import { encodeBase64, prepareEnvironmentVariables } from "../docker/utils";
|
import {
|
||||||
import { execAsyncRemote } from "../process/execAsync";
|
encodeBase64,
|
||||||
|
getEnviromentVariablesObject,
|
||||||
|
prepareEnvironmentVariables,
|
||||||
|
} from "../docker/utils";
|
||||||
|
import { execAsync, execAsyncRemote } from "../process/execAsync";
|
||||||
import { spawnAsync } from "../process/spawnAsync";
|
import { spawnAsync } from "../process/spawnAsync";
|
||||||
|
|
||||||
export type ComposeNested = InferResultType<
|
export type ComposeNested = InferResultType<
|
||||||
@@ -30,12 +35,12 @@ export const buildCompose = async (compose: ComposeNested, logPath: string) => {
|
|||||||
createEnvFile(compose);
|
createEnvFile(compose);
|
||||||
|
|
||||||
const logContent = `
|
const logContent = `
|
||||||
App Name: ${appName}
|
App Name: ${appName}
|
||||||
Build Compose 🐳
|
Build Compose 🐳
|
||||||
Detected: ${mounts.length} mounts 📂
|
Detected: ${mounts.length} mounts 📂
|
||||||
Command: docker ${command}
|
Command: docker ${command}
|
||||||
Source Type: docker ${sourceType} ✅
|
Source Type: docker ${sourceType} ✅
|
||||||
Compose Type: ${composeType} ✅`;
|
Compose Type: ${composeType} ✅`;
|
||||||
const logBox = boxen(logContent, {
|
const logBox = boxen(logContent, {
|
||||||
padding: {
|
padding: {
|
||||||
left: 1,
|
left: 1,
|
||||||
@@ -46,7 +51,6 @@ Compose Type: ${composeType} ✅`;
|
|||||||
borderStyle: "double",
|
borderStyle: "double",
|
||||||
});
|
});
|
||||||
writeStream.write(`\n${logBox}\n`);
|
writeStream.write(`\n${logBox}\n`);
|
||||||
|
|
||||||
const projectPath = join(COMPOSE_PATH, compose.appName, "code");
|
const projectPath = join(COMPOSE_PATH, compose.appName, "code");
|
||||||
|
|
||||||
await spawnAsync(
|
await spawnAsync(
|
||||||
@@ -62,6 +66,9 @@ Compose Type: ${composeType} ✅`;
|
|||||||
env: {
|
env: {
|
||||||
NODE_ENV: process.env.NODE_ENV,
|
NODE_ENV: process.env.NODE_ENV,
|
||||||
PATH: process.env.PATH,
|
PATH: process.env.PATH,
|
||||||
|
...(composeType === "stack" && {
|
||||||
|
...getEnviromentVariablesObject(compose.env, compose.project.env),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -85,6 +92,7 @@ export const getBuildComposeCommand = async (
|
|||||||
const command = createCommand(compose);
|
const command = createCommand(compose);
|
||||||
const envCommand = getCreateEnvFileCommand(compose);
|
const envCommand = getCreateEnvFileCommand(compose);
|
||||||
const projectPath = join(COMPOSE_PATH, compose.appName, "code");
|
const projectPath = join(COMPOSE_PATH, compose.appName, "code");
|
||||||
|
const exportEnvCommand = getExportEnvCommand(compose);
|
||||||
|
|
||||||
const newCompose = await writeDomainsToComposeRemote(
|
const newCompose = await writeDomainsToComposeRemote(
|
||||||
compose,
|
compose,
|
||||||
@@ -120,6 +128,8 @@ Compose Type: ${composeType} ✅`;
|
|||||||
|
|
||||||
cd "${projectPath}";
|
cd "${projectPath}";
|
||||||
|
|
||||||
|
${exportEnvCommand}
|
||||||
|
|
||||||
docker ${command.split(" ").join(" ")} >> "${logPath}" 2>&1 || { echo "Error: ❌ Docker command failed" >> "${logPath}"; exit 1; }
|
docker ${command.split(" ").join(" ")} >> "${logPath}" 2>&1 || { echo "Error: ❌ Docker command failed" >> "${logPath}"; exit 1; }
|
||||||
|
|
||||||
echo "Docker Compose Deployed: ✅" >> "${logPath}"
|
echo "Docker Compose Deployed: ✅" >> "${logPath}"
|
||||||
@@ -153,9 +163,7 @@ export const createCommand = (compose: ComposeNested) => {
|
|||||||
sourceType === "raw" ? "docker-compose.yml" : compose.composePath;
|
sourceType === "raw" ? "docker-compose.yml" : compose.composePath;
|
||||||
let command = "";
|
let command = "";
|
||||||
|
|
||||||
if (composeType === "docker-compose") {
|
if (composeType === "stack") {
|
||||||
command = `compose -p ${appName} -f ${path} up -d --build --remove-orphans`;
|
|
||||||
} else if (composeType === "stack") {
|
|
||||||
command = `stack deploy -c ${path} ${appName} --prune`;
|
command = `stack deploy -c ${path} ${appName} --prune`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,3 +227,17 @@ touch ${envFilePath};
|
|||||||
echo "${encodedContent}" | base64 -d > "${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` : "";
|
||||||
|
};
|
||||||
|
|||||||
@@ -278,12 +278,15 @@ export const prepareEnvironmentVariables = (
|
|||||||
return resolvedVars;
|
return resolvedVars;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const prepareBuildArgs = (input: string | null) => {
|
export const getEnviromentVariablesObject = (
|
||||||
const pairs = (input ?? "").split("\n");
|
input: string | null,
|
||||||
|
projectEnv?: string | null,
|
||||||
|
) => {
|
||||||
|
const envs = prepareEnvironmentVariables(input, projectEnv);
|
||||||
|
|
||||||
const jsonObject: Record<string, string> = {};
|
const jsonObject: Record<string, string> = {};
|
||||||
|
|
||||||
for (const pair of pairs) {
|
for (const pair of envs) {
|
||||||
const [key, value] = pair.split("=");
|
const [key, value] = pair.split("=");
|
||||||
if (key && value) {
|
if (key && value) {
|
||||||
jsonObject[key] = value;
|
jsonObject[key] = value;
|
||||||
|
|||||||
Reference in New Issue
Block a user