diff --git a/plugins/dynamix.docker.manager/DockerContainers.page b/plugins/dynamix.docker.manager/DockerContainers.page index 85320fdf5..0900162d9 100644 --- a/plugins/dynamix.docker.manager/DockerContainers.page +++ b/plugins/dynamix.docker.manager/DockerContainers.page @@ -33,6 +33,7 @@ img.stopped{opacity:0.3} .iconstatus.stopped{font-size:1.2em} .started{color:#009900} .stopped{color:#EF3D47} +.paused{color:#F0DD33} .switch-button-label.off{color:inherit} th.five{width:5%} th.eight{width:8%} diff --git a/plugins/dynamix.docker.manager/include/DockerClient.php b/plugins/dynamix.docker.manager/include/DockerClient.php index b4a896b2e..df0496652 100644 --- a/plugins/dynamix.docker.manager/include/DockerClient.php +++ b/plugins/dynamix.docker.manager/include/DockerClient.php @@ -260,6 +260,7 @@ class DockerTemplates { $image = $ct['Image']; $tmp = &$info[$name] ?? []; $tmp['running'] = $ct['Running']; + $tmp['paused'] = $ct['Paused']; $tmp['autostart'] = in_array($name, $autoStart); if (!is_file($tmp['icon']) || $reload) $tmp['icon'] = $this->getIcon($image); if ($ct['Running']) { @@ -628,12 +629,24 @@ class DockerClient { return $code; } + public function pauseContainer($id) { + $this->getDockerJSON("/containers/$id/pause", 'POST', $code); + $this->flushCache($this::$containersCache); + return $code; + } + public function stopContainer($id) { $this->getDockerJSON("/containers/$id/stop", 'POST', $code); $this->flushCache($this::$containersCache); return $code; } + public function resumeContainer($id) { + $this->getDockerJSON("/containers/$id/unpause", 'POST', $code); + $this->flushCache($this::$containersCache); + return $code; + } + public function restartContainer($id) { $this->getDockerJSON("/containers/$id/restart", 'POST', $code); $this->flushCache($this::$containersCache); @@ -696,6 +709,7 @@ class DockerClient { $c['Name'] = substr($info['Name'], 1); $c['Status'] = $ct['Status'] ?: 'None'; $c['Running'] = $info['State']['Running']; + $c['Paused'] = $info['State']['Paused']; $c['Cmd'] = $ct['Command']; $c['Id'] = $this->extractID($ct['Id']); $c['Volumes'] = $info['HostConfig']['Binds']; diff --git a/plugins/dynamix.docker.manager/include/DockerContainers.php b/plugins/dynamix.docker.manager/include/DockerContainers.php index 039326397..6618985ad 100644 --- a/plugins/dynamix.docker.manager/include/DockerContainers.php +++ b/plugins/dynamix.docker.manager/include/DockerContainers.php @@ -43,18 +43,19 @@ $n = 0; foreach ($containers as $ct) { $name = $ct['Name']; $id = $ct['Id']; - $running = $ct['Running'] ? 1 : 0; $info = &$allInfo[$name]; + $running = $info['running'] ? 1 : 0; + $paused = $info['paused'] ? 1 : 0; $is_autostart = $info['autostart'] ? 'true':'false'; $updateStatus = $info['updated']=='true'||$info['updated']=='undef' ? 'true':'false'; $template = $info['template']; $webGui = html_entity_decode($info['url']); $support = html_entity_decode($info['Support']); $project = html_entity_decode($info['Project']); - $menu[] = sprintf("addDockerContainerContext('%s','%s','%s',%s,%s,%s,'%s','%s','%s','%s');", addslashes($name), addslashes($ct['ImageId']), addslashes($template), $running, $updateStatus, $is_autostart, addslashes($webGui), $id, addslashes($support), addslashes($project)); + $menu[] = sprintf("addDockerContainerContext('%s','%s','%s',%s,%s,%s,%s,'%s','%s','%s','%s');", addslashes($name), addslashes($ct['ImageId']), addslashes($template), $running, $paused, $updateStatus, $is_autostart, addslashes($webGui), $id, addslashes($support), addslashes($project)); $docker[] = "docker.push({name:'$name',id:'$id',state:$running,update:'$updateStatus'});"; - $shape = $running ? 'play':'square'; - $status = $running ? 'started':'stopped'; + $shape = $running ? ($paused ? 'pause' : 'play') : 'square'; + $status = $running ? ($paused ? 'paused' : 'started') : 'stopped'; $icon = $info['icon'] ?: '/plugins/dynamix.docker.manager/images/question.png'; $ports = []; foreach ($ct['Ports'] as $port) { @@ -69,15 +70,15 @@ foreach ($containers as $ct) { } echo ""; echo "
"; - echo ""; - echo "
"; + echo ""; + echo ""; echo ""; if ($template) { echo "".htmlspecialchars($name).""; } else { echo htmlspecialchars($name); } - echo "
Container ID: ".htmlspecialchars($id)."
"; + echo "
Container ID: $id
"; if ($ct['BaseImage']) echo "
".htmlspecialchars(${ct['BaseImage']})."
"; echo "
By:"; $registry = $info['registry']; diff --git a/plugins/dynamix.docker.manager/include/Events.php b/plugins/dynamix.docker.manager/include/Events.php index cb0030dc6..845741a17 100644 --- a/plugins/dynamix.docker.manager/include/Events.php +++ b/plugins/dynamix.docker.manager/include/Events.php @@ -27,9 +27,15 @@ switch ($action) { case 'start': if ($container) $arrResponse = ['success' => $DockerClient->startContainer($container)]; break; + case 'pause': + if ($container) $arrResponse = ['success' => $DockerClient->pauseContainer($container)]; + break; case 'stop': if ($container) $arrResponse = ['success' => $DockerClient->stopContainer($container)]; break; + case 'resume': + if ($container) $arrResponse = ['success' => $DockerClient->resumeContainer($container)]; + break; case 'restart': if ($container) $arrResponse = ['success' => $DockerClient->restartContainer($container)]; break; @@ -85,8 +91,13 @@ switch ($action) { exit; } break; + case 'terminal': + exec("kill \$(pgrep -a ttyd|awk '/\/$name\.sock/{print \$1}') 2>/dev/null"); + @unlink("/var/tmp/$name.sock"); + exec("exec ttyd -d 0 -i '/var/tmp/$name.sock' docker exec -it '$name' sh &>/dev/null &"); + break; default: - $arrResponse = ['error' => 'Unknown action \'' . $action . '\'']; + $arrResponse = ['error' => "Unknown action '$action'"]; break; } diff --git a/plugins/dynamix.docker.manager/javascript/docker.js b/plugins/dynamix.docker.manager/javascript/docker.js index 913b3f90b..00a002b61 100644 --- a/plugins/dynamix.docker.manager/javascript/docker.js +++ b/plugins/dynamix.docker.manager/javascript/docker.js @@ -1,9 +1,10 @@ var eventURL = '/plugins/dynamix.docker.manager/include/Events.php'; -function addDockerContainerContext(container, image, template, started, update, autostart, webui, id, Support, Project) { +function addDockerContainerContext(container, image, template, started, paused, update, autostart, webui, id, Support, Project) { var opts = [{header:container, image:'/plugins/dynamix.docker.manager/images/dynamix.docker.manager.png'}]; - if (started && (webui !== '' && webui != '#')) { - opts.push({text:'WebUI', icon:'fa-globe', href:webui, target:'_blank'}); + if (started && !paused) { + if (webui !== '' && webui != '#') opts.push({text:'WebUI', icon:'fa-globe', href:webui, target:'_blank'}); + opts.push({text:'Console', icon:'fa-terminal', action:function(e){e.preventDefault(); dockerTerminal(container);}}); opts.push({divider:true}); } if (!update) { @@ -11,7 +12,12 @@ function addDockerContainerContext(container, image, template, started, update, opts.push({divider:true}); } if (started) { - opts.push({text:'Stop', icon:'fa-stop', action:function(e){e.preventDefault(); eventControl({action:'stop', container:id}, 'loadlist');}}); + if (paused) { + opts.push({text:'Resume', icon:'fa-play', action:function(e){e.preventDefault(); eventControl({action:'resume', container:id}, 'loadlist');}}); + } else { + opts.push({text:'Pause', icon:'fa-pause', action:function(e){e.preventDefault(); eventControl({action:'pause', container:id}, 'loadlist');}}); + opts.push({text:'Stop', icon:'fa-stop', action:function(e){e.preventDefault(); eventControl({action:'stop', container:id}, 'loadlist');}}); + } opts.push({text:'Restart', icon:'fa-refresh', action:function(e){e.preventDefault(); eventControl({action:'restart', container:id}, 'loadlist');}}); } else { opts.push({text:'Start', icon:'fa-play', action:function(e){e.preventDefault(); eventControl({action:'start', container:id}, 'loadlist');}}); @@ -39,6 +45,14 @@ function addDockerImageContext(image, imageTag) { opts.push({text:'Remove', icon:'fa-trash', action:function(e){e.preventDefault(); rmImage(image, imageTag);}}); context.attach('#'+image, opts); } +function dockerTerminal(container) { + var height = 600; + var width = 900; + var top = (screen.height-height)/2; + var left = (screen.width-width)/2; + $.get(eventURL,{action:'terminal',name:container}); + setTimeout(function(){window.open('/dockerterminal/'+container+'/', container, 'resizeable=yes,scrollbars=yes,height='+height+',width='+width+',top='+top+',left='+left).focus();},180); +} function execUpContainer(container) { var title = 'Updating the container: '+container; var address = '/plugins/dynamix.docker.manager/include/CreateDocker.php?updateContainer=true&ct[]='+encodeURIComponent(container); @@ -135,7 +149,7 @@ function rmImage(image, imageName) { }); } function eventControl(params, spin) { - if (spin) $('#'+params['container']).find('i').removeClass('fa-play fa-square').addClass('fa-refresh fa-spin'); + if (spin) $('#'+params['container']).find('i').removeClass('fa-play fa-square fa-pause').addClass('fa-refresh fa-spin'); $.post(eventURL, params, function(data) { if (data.success === true) { if (spin) setTimeout(spin+'()',500); else location=window.location.href; @@ -156,7 +170,7 @@ function startAll() { } function stopAll() { $('input[type=button]').prop('disabled',true); - for (var i=0,ct; ct=docker[i]; i++) if (ct.state==1) $('#'+ct.id).find('i').removeClass('fa-play').addClass('fa-refresh fa-spin'); + for (var i=0,ct; ct=docker[i]; i++) if (ct.state==1) $('#'+ct.id).find('i').removeClass('fa-play fa-pause').addClass('fa-refresh fa-spin'); $.post('/plugins/dynamix.docker.manager/include/ContainerManager.php',{action:'stop'},function(){loadlist();}); } function checkAll() { diff --git a/plugins/dynamix/DashboardApps.page b/plugins/dynamix/DashboardApps.page index 8d38da738..8ab8f6043 100644 --- a/plugins/dynamix/DashboardApps.page +++ b/plugins/dynamix/DashboardApps.page @@ -38,7 +38,7 @@ div.Panel:hover{overflow:visible;z-index:10;background-color:unset} - +
No apps available to show
@@ -51,8 +51,8 @@ function loadlist() { $('#apps_icons').html(data[0]); $('head').append('