Compare commits

..

4 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
e416aa6a10 Improve: Use consistent ExecOptions type and proper fallback for maxBuffer
Co-authored-by: Siumauricio <47042324+Siumauricio@users.noreply.github.com>
2025-12-28 19:33:30 +00:00
copilot-swe-agent[bot]
222bcba91b Refactor: Extract MAX_EXEC_BUFFER_SIZE constant and add maxBuffer to ExecOptions interface
Co-authored-by: Siumauricio <47042324+Siumauricio@users.noreply.github.com>
2025-12-28 19:32:16 +00:00
copilot-swe-agent[bot]
34d86030e7 Fix: Add maxBuffer option to execAsync functions to handle large backup restores
Co-authored-by: Siumauricio <47042324+Siumauricio@users.noreply.github.com>
2025-12-28 19:30:43 +00:00
copilot-swe-agent[bot]
19b3dde034 Initial plan 2025-12-28 19:25:51 +00:00
12 changed files with 40 additions and 81 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "dokploy",
"version": "v0.26.4",
"version": "v0.26.3",
"private": true,
"license": "Apache-2.0",
"type": "module",

View File

@@ -5,7 +5,6 @@ import {
shouldDeploy,
} from "@dokploy/server";
import { eq } from "drizzle-orm";
import { nanoid } from "nanoid";
import type { NextApiRequest, NextApiResponse } from "next";
import { db } from "@/server/db";
import { applications } from "@/server/db/schema";
@@ -232,7 +231,6 @@ export default async function handler(
}
try {
const jobId = nanoid();
const jobData: DeploymentJob = {
applicationId: application.applicationId as string,
titleLog: deploymentTitle,
@@ -240,7 +238,6 @@ export default async function handler(
type: "deploy",
applicationType: "application",
server: !!application.serverId,
jobId,
};
if (IS_CLOUD && application.serverId) {

View File

@@ -1,6 +1,5 @@
import { IS_CLOUD, shouldDeploy } from "@dokploy/server";
import { eq } from "drizzle-orm";
import { nanoid } from "nanoid";
import type { NextApiRequest, NextApiResponse } from "next";
import { db } from "@/server/db";
import { compose } from "@/server/db/schema";
@@ -169,7 +168,6 @@ export default async function handler(
}
try {
const jobId = nanoid();
const jobData: DeploymentJob = {
composeId: composeResult.composeId as string,
titleLog: deploymentTitle,
@@ -177,7 +175,6 @@ export default async function handler(
applicationType: "compose",
descriptionLog: `Hash: ${deploymentHash}`,
server: !!composeResult.serverId,
jobId,
};
if (IS_CLOUD && composeResult.serverId) {

View File

@@ -11,7 +11,6 @@ import {
} from "@dokploy/server";
import { Webhooks } from "@octokit/webhooks";
import { and, eq } from "drizzle-orm";
import { nanoid } from "nanoid";
import type { NextApiRequest, NextApiResponse } from "next";
import { db } from "@/server/db";
import { applications, compose, github } from "@/server/db/schema";
@@ -118,7 +117,6 @@ export default async function handler(
});
for (const app of apps) {
const jobId = nanoid();
const jobData: DeploymentJob = {
applicationId: app.applicationId as string,
titleLog: deploymentTitle,
@@ -126,7 +124,6 @@ export default async function handler(
type: "deploy",
applicationType: "application",
server: !!app.serverId,
jobId,
};
if (IS_CLOUD && app.serverId) {
@@ -159,7 +156,6 @@ export default async function handler(
});
for (const composeApp of composeApps) {
const jobId = nanoid();
const jobData: DeploymentJob = {
composeId: composeApp.composeId as string,
titleLog: deploymentTitle,
@@ -167,7 +163,6 @@ export default async function handler(
applicationType: "compose",
descriptionLog: `Hash: ${deploymentHash}`,
server: !!composeApp.serverId,
jobId,
};
if (IS_CLOUD && composeApp.serverId) {
@@ -235,7 +230,6 @@ export default async function handler(
});
for (const app of apps) {
const jobId = nanoid();
const jobData: DeploymentJob = {
applicationId: app.applicationId as string,
titleLog: deploymentTitle,
@@ -243,7 +237,6 @@ export default async function handler(
type: "deploy",
applicationType: "application",
server: !!app.serverId,
jobId,
};
const shouldDeployPaths = shouldDeploy(
@@ -285,7 +278,6 @@ export default async function handler(
});
for (const composeApp of composeApps) {
const jobId = nanoid();
const jobData: DeploymentJob = {
composeId: composeApp.composeId as string,
titleLog: deploymentTitle,
@@ -293,7 +285,6 @@ export default async function handler(
applicationType: "compose",
descriptionLog: `Hash: ${deploymentHash}`,
server: !!composeApp.serverId,
jobId,
};
const shouldDeployPaths = shouldDeploy(
@@ -496,7 +487,6 @@ export default async function handler(
previewDeploymentId = previewDeployment.previewDeploymentId;
}
const jobId = nanoid();
const jobData: DeploymentJob = {
applicationId: app.applicationId as string,
titleLog: "Preview Deployment",
@@ -505,7 +495,6 @@ export default async function handler(
applicationType: "application-preview",
server: !!app.serverId,
previewDeploymentId,
jobId,
};
if (IS_CLOUD && app.serverId) {

View File

@@ -325,7 +325,6 @@ export const applicationRouter = createTRPCRouter({
message: "You are not authorized to redeploy this application",
});
}
const jobId = nanoid();
const jobData: DeploymentJob = {
applicationId: input.applicationId,
titleLog: input.title || "Rebuild deployment",
@@ -333,7 +332,6 @@ export const applicationRouter = createTRPCRouter({
type: "redeploy",
applicationType: "application",
server: !!application.serverId,
jobId,
};
if (IS_CLOUD && application.serverId) {
@@ -341,7 +339,7 @@ export const applicationRouter = createTRPCRouter({
deploy(jobData).catch((error) => {
console.error("Background deployment failed:", error);
});
return { jobId };
return true;
}
await myQueue.add(
"deployments",
@@ -351,7 +349,6 @@ export const applicationRouter = createTRPCRouter({
removeOnFail: true,
},
);
return { jobId };
}),
saveEnvironment: protectedProcedure
.input(apiSaveEnvironmentVariables)
@@ -696,7 +693,6 @@ export const applicationRouter = createTRPCRouter({
message: "You are not authorized to deploy this application",
});
}
const jobId = nanoid();
const jobData: DeploymentJob = {
applicationId: input.applicationId,
titleLog: input.title || "Manual deployment",
@@ -704,7 +700,6 @@ export const applicationRouter = createTRPCRouter({
type: "deploy",
applicationType: "application",
server: !!application.serverId,
jobId,
};
if (IS_CLOUD && application.serverId) {
jobData.serverId = application.serverId;
@@ -712,7 +707,7 @@ export const applicationRouter = createTRPCRouter({
console.error("Background deployment failed:", error);
});
return { jobId };
return true;
}
await myQueue.add(
"deployments",
@@ -722,7 +717,6 @@ export const applicationRouter = createTRPCRouter({
removeOnFail: true,
},
);
return { jobId };
}),
cleanQueues: protectedProcedure

View File

@@ -406,7 +406,6 @@ export const composeRouter = createTRPCRouter({
message: "You are not authorized to deploy this compose",
});
}
const jobId = nanoid();
const jobData: DeploymentJob = {
composeId: input.composeId,
titleLog: input.title || "Manual deployment",
@@ -414,7 +413,6 @@ export const composeRouter = createTRPCRouter({
applicationType: "compose",
descriptionLog: input.description || "",
server: !!compose.serverId,
jobId,
};
if (IS_CLOUD && compose.serverId) {
@@ -422,7 +420,7 @@ export const composeRouter = createTRPCRouter({
deploy(jobData).catch((error) => {
console.error("Background deployment failed:", error);
});
return { jobId };
return true;
}
await myQueue.add(
"deployments",
@@ -432,7 +430,7 @@ export const composeRouter = createTRPCRouter({
removeOnFail: true,
},
);
return { jobId };
return { success: true, message: "Deployment queued" };
}),
redeploy: protectedProcedure
.input(apiRedeployCompose)
@@ -447,7 +445,6 @@ export const composeRouter = createTRPCRouter({
message: "You are not authorized to redeploy this compose",
});
}
const jobId = nanoid();
const jobData: DeploymentJob = {
composeId: input.composeId,
titleLog: input.title || "Rebuild deployment",
@@ -455,14 +452,13 @@ export const composeRouter = createTRPCRouter({
applicationType: "compose",
descriptionLog: input.description || "",
server: !!compose.serverId,
jobId,
};
if (IS_CLOUD && compose.serverId) {
jobData.serverId = compose.serverId;
deploy(jobData).catch((error) => {
console.error("Background deployment failed:", error);
});
return { jobId };
return true;
}
await myQueue.add(
"deployments",
@@ -472,7 +468,7 @@ export const composeRouter = createTRPCRouter({
removeOnFail: true,
},
);
return { jobId };
return { success: true, message: "Redeployment queued" };
}),
stop: protectedProcedure
.input(apiFindCompose)

View File

@@ -24,14 +24,12 @@ export const deploymentWorker = new Worker(
applicationId: job.data.applicationId,
titleLog: job.data.titleLog,
descriptionLog: job.data.descriptionLog,
jobId: job.data.jobId,
});
} else if (job.data.type === "deploy") {
await deployApplication({
applicationId: job.data.applicationId,
titleLog: job.data.titleLog,
descriptionLog: job.data.descriptionLog,
jobId: job.data.jobId,
});
}
} else if (job.data.applicationType === "compose") {
@@ -43,14 +41,12 @@ export const deploymentWorker = new Worker(
composeId: job.data.composeId,
titleLog: job.data.titleLog,
descriptionLog: job.data.descriptionLog,
jobId: job.data.jobId,
});
} else if (job.data.type === "redeploy") {
await rebuildCompose({
composeId: job.data.composeId,
titleLog: job.data.titleLog,
descriptionLog: job.data.descriptionLog,
jobId: job.data.jobId,
});
}
} else if (job.data.applicationType === "application-preview") {
@@ -64,7 +60,6 @@ export const deploymentWorker = new Worker(
titleLog: job.data.titleLog,
descriptionLog: job.data.descriptionLog,
previewDeploymentId: job.data.previewDeploymentId,
jobId: job.data.jobId,
});
}
}

View File

@@ -7,7 +7,6 @@ type DeployJob =
type: "deploy" | "redeploy";
applicationType: "application";
serverId?: string;
jobId?: string;
}
| {
composeId: string;
@@ -17,7 +16,6 @@ type DeployJob =
type: "deploy" | "redeploy";
applicationType: "compose";
serverId?: string;
jobId?: string;
}
| {
applicationId: string;
@@ -28,7 +26,6 @@ type DeployJob =
applicationType: "application-preview";
previewDeploymentId: string;
serverId?: string;
jobId?: string;
};
export type DeploymentJob = DeployJob;

View File

@@ -167,12 +167,10 @@ export const deployApplication = async ({
applicationId,
titleLog = "Manual deployment",
descriptionLog = "",
jobId,
}: {
applicationId: string;
titleLog: string;
descriptionLog: string;
jobId?: string;
}) => {
const application = await findApplicationById(applicationId);
const serverId = application.buildServerId || application.serverId;
@@ -182,7 +180,6 @@ export const deployApplication = async ({
applicationId: applicationId,
title: titleLog,
description: descriptionLog,
jobId,
});
try {
@@ -273,12 +270,10 @@ export const rebuildApplication = async ({
applicationId,
titleLog = "Rebuild deployment",
descriptionLog = "",
jobId,
}: {
applicationId: string;
titleLog: string;
descriptionLog: string;
jobId?: string;
}) => {
const application = await findApplicationById(applicationId);
const serverId = application.buildServerId || application.serverId;
@@ -288,7 +283,6 @@ export const rebuildApplication = async ({
applicationId: applicationId,
title: titleLog,
description: descriptionLog,
jobId,
});
try {
@@ -343,13 +337,11 @@ export const deployPreviewApplication = async ({
titleLog = "Preview Deployment",
descriptionLog = "",
previewDeploymentId,
jobId,
}: {
applicationId: string;
titleLog: string;
descriptionLog: string;
previewDeploymentId: string;
jobId?: string;
}) => {
const application = await findApplicationById(applicationId);
@@ -357,7 +349,6 @@ export const deployPreviewApplication = async ({
title: titleLog,
description: descriptionLog,
previewDeploymentId: previewDeploymentId,
jobId,
});
const previewDeployment =

View File

@@ -205,12 +205,10 @@ export const deployCompose = async ({
composeId,
titleLog = "Manual deployment",
descriptionLog = "",
jobId,
}: {
composeId: string;
titleLog: string;
descriptionLog: string;
jobId?: string;
}) => {
const compose = await findComposeById(composeId);
@@ -221,7 +219,6 @@ export const deployCompose = async ({
composeId: composeId,
title: titleLog,
description: descriptionLog,
jobId,
});
try {
@@ -324,12 +321,10 @@ export const rebuildCompose = async ({
composeId,
titleLog = "Rebuild deployment",
descriptionLog = "",
jobId,
}: {
composeId: string;
titleLog: string;
descriptionLog: string;
jobId?: string;
}) => {
const compose = await findComposeById(composeId);
@@ -337,7 +332,6 @@ export const rebuildCompose = async ({
composeId: composeId,
title: titleLog,
description: descriptionLog,
jobId,
});
try {

View File

@@ -71,7 +71,7 @@ export const createDeployment = async (
deployment: Omit<
typeof apiCreateDeployment._type,
"deploymentId" | "createdAt" | "status" | "logPath"
> & { jobId?: string },
>,
) => {
const application = await findApplicationById(deployment.applicationId);
try {
@@ -107,7 +107,6 @@ export const createDeployment = async (
const deploymentCreate = await db
.insert(deployments)
.values({
...(deployment.jobId && { deploymentId: deployment.jobId }),
applicationId: deployment.applicationId,
title: deployment.title || "Deployment",
status: "running",
@@ -153,7 +152,7 @@ export const createDeploymentPreview = async (
deployment: Omit<
typeof apiCreateDeploymentPreview._type,
"deploymentId" | "createdAt" | "status" | "logPath"
> & { jobId?: string },
>,
) => {
const previewDeployment = await findPreviewDeploymentById(
deployment.previewDeploymentId,
@@ -192,7 +191,6 @@ export const createDeploymentPreview = async (
const deploymentCreate = await db
.insert(deployments)
.values({
...(deployment.jobId && { deploymentId: deployment.jobId }),
title: deployment.title || "Deployment",
status: "running",
logPath: logFilePath,
@@ -237,7 +235,7 @@ export const createDeploymentCompose = async (
deployment: Omit<
typeof apiCreateDeploymentCompose._type,
"deploymentId" | "createdAt" | "status" | "logPath"
> & { jobId?: string },
>,
) => {
const compose = await findComposeById(deployment.composeId);
try {
@@ -270,7 +268,6 @@ echo "Initializing deployment\n" >> ${logFilePath};
const deploymentCreate = await db
.insert(deployments)
.values({
...(deployment.jobId && { deploymentId: deployment.jobId }),
composeId: deployment.composeId,
title: deployment.title || "Deployment",
description: deployment.description || "",

View File

@@ -9,12 +9,19 @@ export { ExecError } from "./ExecError";
const execAsyncBase = util.promisify(exec);
// Set maxBuffer to 100MB to handle large backup restore operations
// Default is 1MB which can cause "maxBuffer length exceeded" errors
const MAX_EXEC_BUFFER_SIZE = 100 * 1024 * 1024;
export const execAsync = async (
command: string,
options?: { cwd?: string; env?: NodeJS.ProcessEnv; shell?: string },
options?: ExecOptions & { shell?: string },
): Promise<{ stdout: string; stderr: string }> => {
try {
const result = await execAsyncBase(command, options);
const result = await execAsyncBase(command, {
...options,
maxBuffer: options?.maxBuffer ?? MAX_EXEC_BUFFER_SIZE,
});
return {
stdout: result.stdout.toString(),
stderr: result.stderr.toString(),
@@ -43,6 +50,7 @@ export const execAsync = async (
interface ExecOptions {
cwd?: string;
env?: NodeJS.ProcessEnv;
maxBuffer?: number;
}
export const execAsyncStream = (
@@ -54,22 +62,26 @@ export const execAsyncStream = (
let stdoutComplete = "";
let stderrComplete = "";
const childProcess = exec(command, options, (error) => {
if (error) {
reject(
new ExecError(`Command execution failed: ${error.message}`, {
command,
stdout: stdoutComplete,
stderr: stderrComplete,
// @ts-ignore
exitCode: error.code,
originalError: error,
}),
);
return;
}
resolve({ stdout: stdoutComplete, stderr: stderrComplete });
});
const childProcess = exec(
command,
{ ...options, maxBuffer: options?.maxBuffer ?? MAX_EXEC_BUFFER_SIZE },
(error) => {
if (error) {
reject(
new ExecError(`Command execution failed: ${error.message}`, {
command,
stdout: stdoutComplete,
stderr: stderrComplete,
// @ts-ignore
exitCode: error.code,
originalError: error,
}),
);
return;
}
resolve({ stdout: stdoutComplete, stderr: stderrComplete });
},
);
childProcess.stdout?.on("data", (data: Buffer | string) => {
const stringData = data.toString();