File tree Expand file tree Collapse file tree 4 files changed +113
-0
lines changed
components/dashboard/docker
packages/server/src/services Expand file tree Collapse file tree 4 files changed +113
-0
lines changed Original file line number Diff line number Diff line change 1+ import { toast } from "sonner" ;
2+ import {
3+ AlertDialog ,
4+ AlertDialogAction ,
5+ AlertDialogCancel ,
6+ AlertDialogContent ,
7+ AlertDialogDescription ,
8+ AlertDialogFooter ,
9+ AlertDialogHeader ,
10+ AlertDialogTitle ,
11+ AlertDialogTrigger ,
12+ } from "@/components/ui/alert-dialog" ;
13+ import { DropdownMenuItem } from "@/components/ui/dropdown-menu" ;
14+ import { api } from "@/utils/api" ;
15+
16+ interface Props {
17+ containerId : string ;
18+ serverId ?: string ;
19+ }
20+
21+ export const RemoveContainerDialog = ( { containerId, serverId } : Props ) => {
22+ const utils = api . useUtils ( ) ;
23+ const { mutateAsync, isPending } = api . docker . removeContainer . useMutation ( ) ;
24+
25+ return (
26+ < AlertDialog >
27+ < AlertDialogTrigger asChild >
28+ < DropdownMenuItem
29+ className = "w-full cursor-pointer text-red-500 hover:!text-red-600"
30+ onSelect = { ( e ) => e . preventDefault ( ) }
31+ >
32+ Remove Container
33+ </ DropdownMenuItem >
34+ </ AlertDialogTrigger >
35+ < AlertDialogContent >
36+ < AlertDialogHeader >
37+ < AlertDialogTitle > Are you sure?</ AlertDialogTitle >
38+ < AlertDialogDescription >
39+ This will permanently remove the container{ " " }
40+ < span className = "font-semibold" > { containerId } </ span > . If the
41+ container is running, it will be forcefully stopped and removed.
42+ This action cannot be undone.
43+ </ AlertDialogDescription >
44+ </ AlertDialogHeader >
45+ < AlertDialogFooter >
46+ < AlertDialogCancel > Cancel</ AlertDialogCancel >
47+ < AlertDialogAction
48+ disabled = { isPending }
49+ onClick = { async ( ) => {
50+ await mutateAsync ( { containerId, serverId } )
51+ . then ( async ( ) => {
52+ toast . success ( "Container removed successfully" ) ;
53+ await utils . docker . getContainers . invalidate ( ) ;
54+ } )
55+ . catch ( ( err ) => {
56+ toast . error ( err . message ) ;
57+ } ) ;
58+ } }
59+ >
60+ Confirm
61+ </ AlertDialogAction >
62+ </ AlertDialogFooter >
63+ </ AlertDialogContent >
64+ </ AlertDialog >
65+ ) ;
66+ } ;
Original file line number Diff line number Diff line change @@ -10,6 +10,7 @@ import {
1010} from "@/components/ui/dropdown-menu" ;
1111import { ShowContainerConfig } from "../config/show-container-config" ;
1212import { ShowDockerModalLogs } from "../logs/show-docker-modal-logs" ;
13+ import { RemoveContainerDialog } from "../remove/remove-container" ;
1314import { DockerTerminalModal } from "../terminal/docker-terminal-modal" ;
1415import type { Container } from "./show-containers" ;
1516
@@ -127,6 +128,10 @@ export const columns: ColumnDef<Container>[] = [
127128 >
128129 Terminal
129130 </ DockerTerminalModal >
131+ < RemoveContainerDialog
132+ containerId = { container . containerId }
133+ serverId = { container . serverId }
134+ />
130135 </ DropdownMenuContent >
131136 </ DropdownMenu >
132137 ) ;
Original file line number Diff line number Diff line change 11import {
2+ containerRemove ,
23 containerRestart ,
34 findServerById ,
45 getConfig ,
@@ -52,6 +53,32 @@ export const dockerRouter = createTRPCRouter({
5253 return result ;
5354 } ) ,
5455
56+ removeContainer : withPermission ( "docker" , "read" )
57+ . input (
58+ z . object ( {
59+ containerId : z
60+ . string ( )
61+ . min ( 1 )
62+ . regex ( containerIdRegex , "Invalid container id." ) ,
63+ serverId : z . string ( ) . optional ( ) ,
64+ } ) ,
65+ )
66+ . mutation ( async ( { input, ctx } ) => {
67+ if ( input . serverId ) {
68+ const server = await findServerById ( input . serverId ) ;
69+ if ( server . organizationId !== ctx . session ?. activeOrganizationId ) {
70+ throw new TRPCError ( { code : "UNAUTHORIZED" } ) ;
71+ }
72+ }
73+ await containerRemove ( input . containerId , input . serverId ) ;
74+ await audit ( ctx , {
75+ action : "delete" ,
76+ resourceType : "docker" ,
77+ resourceId : input . containerId ,
78+ resourceName : input . containerId ,
79+ } ) ;
80+ } ) ,
81+
5582 getConfig : withPermission ( "docker" , "read" )
5683 . input (
5784 z . object ( {
Original file line number Diff line number Diff line change @@ -371,6 +371,21 @@ export const containerRestart = async (containerId: string) => {
371371 } catch { }
372372} ;
373373
374+ export const containerRemove = async (
375+ containerId : string ,
376+ serverId ?: string ,
377+ ) => {
378+ const command = `docker rm -f ${ containerId } ` ;
379+ const { stderr } = serverId
380+ ? await execAsyncRemote ( serverId , command )
381+ : await execAsync ( command ) ;
382+
383+ if ( stderr ) {
384+ console . error ( `Error: ${ stderr } ` ) ;
385+ throw new Error ( stderr ) ;
386+ }
387+ } ;
388+
374389export const getSwarmNodes = async ( serverId ?: string ) => {
375390 try {
376391 let stdout = "" ;
You can’t perform that action at this time.
0 commit comments