Skip to content

Commit 9e2788e

Browse files
authored
Merge pull request #3052 from Dokploy/360-request-for-adding-the-functionality-to-terminate-container-startup-process
feat: add KillBuild component and API mutation for terminating Docker…
2 parents 82cfe06 + 4884ee3 commit 9e2788e

File tree

5 files changed

+140
-3
lines changed

5 files changed

+140
-3
lines changed
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { Scissors } from "lucide-react";
2+
import { toast } from "sonner";
3+
import {
4+
AlertDialog,
5+
AlertDialogAction,
6+
AlertDialogCancel,
7+
AlertDialogContent,
8+
AlertDialogDescription,
9+
AlertDialogFooter,
10+
AlertDialogHeader,
11+
AlertDialogTitle,
12+
AlertDialogTrigger,
13+
} from "@/components/ui/alert-dialog";
14+
import { Button } from "@/components/ui/button";
15+
import { api } from "@/utils/api";
16+
17+
interface Props {
18+
id: string;
19+
type: "application" | "compose";
20+
}
21+
22+
export const KillBuild = ({ id, type }: Props) => {
23+
const { mutateAsync, isLoading } =
24+
type === "application"
25+
? api.application.killBuild.useMutation()
26+
: api.compose.killBuild.useMutation();
27+
28+
return (
29+
<AlertDialog>
30+
<AlertDialogTrigger asChild>
31+
<Button variant="outline" className="w-fit" isLoading={isLoading}>
32+
Kill Build
33+
<Scissors className="size-4" />
34+
</Button>
35+
</AlertDialogTrigger>
36+
<AlertDialogContent>
37+
<AlertDialogHeader>
38+
<AlertDialogTitle>Are you sure to kill the build?</AlertDialogTitle>
39+
<AlertDialogDescription>
40+
This will kill the build process
41+
</AlertDialogDescription>
42+
</AlertDialogHeader>
43+
<AlertDialogFooter>
44+
<AlertDialogCancel>Cancel</AlertDialogCancel>
45+
<AlertDialogAction
46+
onClick={async () => {
47+
await mutateAsync({
48+
applicationId: id || "",
49+
composeId: id || "",
50+
})
51+
.then(() => {
52+
toast.success("Build killed successfully");
53+
})
54+
.catch((err) => {
55+
toast.error(err.message);
56+
});
57+
}}
58+
>
59+
Confirm
60+
</AlertDialogAction>
61+
</AlertDialogFooter>
62+
</AlertDialogContent>
63+
</AlertDialog>
64+
);
65+
};

apps/dokploy/components/dashboard/application/deployments/show-deployments.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
import { api, type RouterOutputs } from "@/utils/api";
2626
import { ShowRollbackSettings } from "../rollbacks/show-rollback-settings";
2727
import { CancelQueues } from "./cancel-queues";
28+
import { KillBuild } from "./kill-build";
2829
import { RefreshToken } from "./refresh-token";
2930
import { ShowDeployment } from "./show-deployment";
3031

@@ -143,6 +144,9 @@ export const ShowDeployments = ({
143144
</CardDescription>
144145
</div>
145146
<div className="flex flex-row items-center gap-2">
147+
{(type === "application" || type === "compose") && (
148+
<KillBuild id={id} type={type} />
149+
)}
146150
{(type === "application" || type === "compose") && (
147151
<CancelQueues id={id} type={type} />
148152
)}

apps/dokploy/server/api/routers/application.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,11 @@ import {
5858
applications,
5959
} from "@/server/db/schema";
6060
import type { DeploymentJob } from "@/server/queues/queue-types";
61-
import { cleanQueuesByApplication, myQueue } from "@/server/queues/queueSetup";
61+
import {
62+
cleanQueuesByApplication,
63+
killDockerBuild,
64+
myQueue,
65+
} from "@/server/queues/queueSetup";
6266
import { cancelDeployment, deploy } from "@/server/utils/deploy";
6367
import { uploadFileSchema } from "@/utils/schema";
6468

@@ -725,7 +729,21 @@ export const applicationRouter = createTRPCRouter({
725729
}
726730
await cleanQueuesByApplication(input.applicationId);
727731
}),
728-
732+
killBuild: protectedProcedure
733+
.input(apiFindOneApplication)
734+
.mutation(async ({ input, ctx }) => {
735+
const application = await findApplicationById(input.applicationId);
736+
if (
737+
application.environment.project.organizationId !==
738+
ctx.session.activeOrganizationId
739+
) {
740+
throw new TRPCError({
741+
code: "UNAUTHORIZED",
742+
message: "You are not authorized to kill this build",
743+
});
744+
}
745+
await killDockerBuild("application", application.serverId);
746+
}),
729747
readTraefikConfig: protectedProcedure
730748
.input(apiFindOneApplication)
731749
.query(async ({ input, ctx }) => {

apps/dokploy/server/api/routers/compose.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,11 @@ import {
5959
compose as composeTable,
6060
} from "@/server/db/schema";
6161
import type { DeploymentJob } from "@/server/queues/queue-types";
62-
import { cleanQueuesByCompose, myQueue } from "@/server/queues/queueSetup";
62+
import {
63+
cleanQueuesByCompose,
64+
killDockerBuild,
65+
myQueue,
66+
} from "@/server/queues/queueSetup";
6367
import { cancelDeployment, deploy } from "@/server/utils/deploy";
6468
import { generatePassword } from "@/templates/utils";
6569
import { createTRPCRouter, protectedProcedure, publicProcedure } from "../trpc";
@@ -248,6 +252,21 @@ export const composeRouter = createTRPCRouter({
248252
await cleanQueuesByCompose(input.composeId);
249253
return { success: true, message: "Queues cleaned successfully" };
250254
}),
255+
killBuild: protectedProcedure
256+
.input(apiFindCompose)
257+
.mutation(async ({ input, ctx }) => {
258+
const compose = await findComposeById(input.composeId);
259+
if (
260+
compose.environment.project.organizationId !==
261+
ctx.session.activeOrganizationId
262+
) {
263+
throw new TRPCError({
264+
code: "UNAUTHORIZED",
265+
message: "You are not authorized to kill this build",
266+
});
267+
}
268+
await killDockerBuild("compose", compose.serverId);
269+
}),
251270

252271
loadServices: protectedProcedure
253272
.input(apiFetchServices)

apps/dokploy/server/queues/queueSetup.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
import {
2+
execAsync,
3+
execAsyncRemote,
4+
} from "@dokploy/server/utils/process/execAsync";
15
import { Queue } from "bullmq";
26
import { redisConfig } from "./redis-connection";
37

@@ -41,4 +45,31 @@ export const cleanQueuesByCompose = async (composeId: string) => {
4145
}
4246
};
4347

48+
export const killDockerBuild = async (
49+
type: "application" | "compose",
50+
serverId: string | null,
51+
) => {
52+
try {
53+
if (type === "application") {
54+
const command = `pkill -2 -f "docker build"`;
55+
56+
if (serverId) {
57+
await execAsyncRemote(serverId, command);
58+
} else {
59+
await execAsync(command);
60+
}
61+
} else if (type === "compose") {
62+
const command = `pkill -2 -f "docker compose"`;
63+
64+
if (serverId) {
65+
await execAsyncRemote(serverId, command);
66+
} else {
67+
await execAsync(command);
68+
}
69+
}
70+
} catch (error) {
71+
console.error(error);
72+
}
73+
};
74+
4475
export { myQueue };

0 commit comments

Comments
 (0)