From b1ef5dc2c67d6631b5a89ac2454c30595a3eaae6 Mon Sep 17 00:00:00 2001 From: Mauricio Siu Date: Tue, 24 Mar 2026 12:56:25 -0600 Subject: [PATCH 1/2] feat(docker): implement container removal functionality - Added RemoveContainerDialog component for user confirmation before removing a Docker container. - Integrated the dialog into the container management UI. - Implemented server-side logic for container removal, including permission checks and error handling. - Updated API router to include the new removeContainer mutation. --- .../docker/remove/remove-container.tsx | 66 +++++++++++++++++++ .../dashboard/docker/show/colums.tsx | 2 + apps/dokploy/server/api/routers/docker.ts | 27 ++++++++ packages/server/src/services/docker.ts | 15 +++++ 4 files changed, 110 insertions(+) create mode 100644 apps/dokploy/components/dashboard/docker/remove/remove-container.tsx diff --git a/apps/dokploy/components/dashboard/docker/remove/remove-container.tsx b/apps/dokploy/components/dashboard/docker/remove/remove-container.tsx new file mode 100644 index 000000000..3b347e957 --- /dev/null +++ b/apps/dokploy/components/dashboard/docker/remove/remove-container.tsx @@ -0,0 +1,66 @@ +import { toast } from "sonner"; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, + AlertDialogTrigger, +} from "@/components/ui/alert-dialog"; +import { DropdownMenuItem } from "@/components/ui/dropdown-menu"; +import { api } from "@/utils/api"; + +interface Props { + containerId: string; + serverId?: string; +} + +export const RemoveContainerDialog = ({ containerId, serverId }: Props) => { + const utils = api.useUtils(); + const { mutateAsync, isPending } = api.docker.removeContainer.useMutation(); + + return ( + + + e.preventDefault()} + > + Remove Container + + + + + Are you sure? + + This will permanently remove the container{" "} + {containerId}. If the + container is running, it will be forcefully stopped and removed. This + action cannot be undone. + + + + Cancel + { + await mutateAsync({ containerId, serverId }) + .then(async () => { + toast.success("Container removed successfully"); + await utils.docker.getContainers.invalidate(); + }) + .catch((err) => { + toast.error(err.message); + }); + }} + > + Confirm + + + + + ); +}; diff --git a/apps/dokploy/components/dashboard/docker/show/colums.tsx b/apps/dokploy/components/dashboard/docker/show/colums.tsx index 74fe6819e..901d92252 100644 --- a/apps/dokploy/components/dashboard/docker/show/colums.tsx +++ b/apps/dokploy/components/dashboard/docker/show/colums.tsx @@ -10,6 +10,7 @@ import { } from "@/components/ui/dropdown-menu"; import { ShowContainerConfig } from "../config/show-container-config"; import { ShowDockerModalLogs } from "../logs/show-docker-modal-logs"; +import { RemoveContainerDialog } from "../remove/remove-container"; import { DockerTerminalModal } from "../terminal/docker-terminal-modal"; import type { Container } from "./show-containers"; @@ -127,6 +128,7 @@ export const columns: ColumnDef[] = [ > Terminal + ); diff --git a/apps/dokploy/server/api/routers/docker.ts b/apps/dokploy/server/api/routers/docker.ts index d4a7ddd57..a310d693c 100644 --- a/apps/dokploy/server/api/routers/docker.ts +++ b/apps/dokploy/server/api/routers/docker.ts @@ -1,4 +1,5 @@ import { + containerRemove, containerRestart, findServerById, getConfig, @@ -52,6 +53,32 @@ export const dockerRouter = createTRPCRouter({ return result; }), + removeContainer: withPermission("docker", "read") + .input( + z.object({ + containerId: z + .string() + .min(1) + .regex(containerIdRegex, "Invalid container id."), + serverId: z.string().optional(), + }), + ) + .mutation(async ({ input, ctx }) => { + if (input.serverId) { + const server = await findServerById(input.serverId); + if (server.organizationId !== ctx.session?.activeOrganizationId) { + throw new TRPCError({ code: "UNAUTHORIZED" }); + } + } + await containerRemove(input.containerId, input.serverId); + await audit(ctx, { + action: "delete", + resourceType: "docker", + resourceId: input.containerId, + resourceName: input.containerId, + }); + }), + getConfig: withPermission("docker", "read") .input( z.object({ diff --git a/packages/server/src/services/docker.ts b/packages/server/src/services/docker.ts index d24cae72b..92f5e3d2e 100644 --- a/packages/server/src/services/docker.ts +++ b/packages/server/src/services/docker.ts @@ -371,6 +371,21 @@ export const containerRestart = async (containerId: string) => { } catch {} }; +export const containerRemove = async ( + containerId: string, + serverId?: string, +) => { + const command = `docker rm -f ${containerId}`; + const { stderr } = serverId + ? await execAsyncRemote(serverId, command) + : await execAsync(command); + + if (stderr) { + console.error(`Error: ${stderr}`); + throw new Error(stderr); + } +}; + export const getSwarmNodes = async (serverId?: string) => { try { let stdout = ""; From b7e30d7ec32508b2e14f6972e28b82eb0adb84f4 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 24 Mar 2026 18:57:02 +0000 Subject: [PATCH 2/2] [autofix.ci] apply automated fixes --- .../components/dashboard/docker/remove/remove-container.tsx | 4 ++-- apps/dokploy/components/dashboard/docker/show/colums.tsx | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/apps/dokploy/components/dashboard/docker/remove/remove-container.tsx b/apps/dokploy/components/dashboard/docker/remove/remove-container.tsx index 3b347e957..3b6cd9875 100644 --- a/apps/dokploy/components/dashboard/docker/remove/remove-container.tsx +++ b/apps/dokploy/components/dashboard/docker/remove/remove-container.tsx @@ -38,8 +38,8 @@ export const RemoveContainerDialog = ({ containerId, serverId }: Props) => { This will permanently remove the container{" "} {containerId}. If the - container is running, it will be forcefully stopped and removed. This - action cannot be undone. + container is running, it will be forcefully stopped and removed. + This action cannot be undone. diff --git a/apps/dokploy/components/dashboard/docker/show/colums.tsx b/apps/dokploy/components/dashboard/docker/show/colums.tsx index 901d92252..8507261a2 100644 --- a/apps/dokploy/components/dashboard/docker/show/colums.tsx +++ b/apps/dokploy/components/dashboard/docker/show/colums.tsx @@ -128,7 +128,10 @@ export const columns: ColumnDef[] = [ > Terminal - + );