diff --git a/root/app/www/public/ajax/orphans.php b/root/app/www/public/ajax/orphans.php
index 347ef46..96b4b3d 100644
--- a/root/app/www/public/ajax/orphans.php
+++ b/root/app/www/public/ajax/orphans.php
@@ -10,18 +10,19 @@
require 'shared.php';
if ($_POST['m'] == 'init') {
- $images = apiRequest('docker-getOrphanContainers');
- $images = json_decode($images['result'], true);
- $volumes = apiRequest('docker-getOrphanVolumes');
- $volumes = json_decode($volumes['result'], true);
- $networks = apiRequest('docker-getOrphanNetworks');
- $networks = json_decode($networks['result'], true);
-
+ $images = apiRequest('docker-getOrphanContainers');
+ $images = json_decode($images['result'], true);
+ $volumes = apiRequest('docker-getOrphanVolumes');
+ $volumes = json_decode($volumes['result'], true);
+ $networks = apiRequest('docker-getOrphanNetworks');
+ $networks = json_decode($networks['result'], true);
+ $unusedContainers = apiRequest('docker-getUnusedContainers');
+ $unusedContainers = json_decode($unusedContainers['result'], true);
?>
Images
-
docker images -f dangling=true
+
= DockerSock::ORPHAN_CONTAINERS ?>
+
Unused containers
+
= DockerSock::UNUSED_CONTAINERS ?>
+
Volumes
-
docker volume ls -qf dangling=true
+
= DockerSock::ORPHAN_VOLUMES ?>
Networks
-
docker network ls -qf dangling=true
+
= DockerSock::ORPHAN_NETWORKS ?>
@@ -125,21 +154,21 @@
if ($_POST['m'] == 'removeOrphans') {
switch ($_POST['action']) {
case 'remove':
- if ($_POST['type'] == 'image') {
+ if ($_POST['type'] == 'image' || $_POST['type'] == 'unused') {
$apiRequest = apiRequest('docker-removeImage', [], ['image' => $_POST['orphan']])['result'];
- if (stripos($apiRequest, 'error') !== false || stripos($apiRequest, 'help') !== false) {
+ if (stri_contains($apiRequest, 'error') || stri_contains($apiRequest, 'help')) {
echo $apiRequest;
}
}
if ($_POST['type'] == 'network') {
$apiRequest = apiRequest('docker-removeNetwork', [], ['id' => $_POST['orphan']])['result'];
- if (stripos($apiRequest, 'error') !== false || stripos($apiRequest, 'help') !== false) {
+ if (stri_contains($apiRequest, 'error') || stri_contains($apiRequest, 'help')) {
echo $apiRequest;
}
}
if ($_POST['type'] == 'volume') {
$apiRequest = apiRequest('docker-removeVolume', [], ['name' => $_POST['orphan']])['result'];
- if (stripos($apiRequest, 'error') !== false || stripos($apiRequest, 'help') !== false) {
+ if (stri_contains($apiRequest, 'error') || stri_contains($apiRequest, 'help')) {
echo $apiRequest;
}
}
diff --git a/root/app/www/public/classes/interfaces/Docker.php b/root/app/www/public/classes/interfaces/Docker.php
index 25a1c31..b3851d6 100644
--- a/root/app/www/public/classes/interfaces/Docker.php
+++ b/root/app/www/public/classes/interfaces/Docker.php
@@ -32,6 +32,7 @@ interface DockerSock
public const START_CONTAINER = '/usr/bin/docker container start %s';
public const STOP_CONTAINER = '/usr/bin/docker container stop %s%s';
public const ORPHAN_CONTAINERS = '/usr/bin/docker images -f dangling=true --format="{{json . }}" | jq -s --tab .';
+ public const UNUSED_CONTAINERS = '/usr/bin/docker images --format \'{{.ID}}:{{.Repository}}:{{.Tag}}\' | grep -v "$(docker ps --format {{.Image}})"';
public const CONTAINER_PORT = '/usr/bin/docker port %s %s';
//-- IMAGE SPECIFIC
public const REMOVE_IMAGE = '/usr/bin/docker image rm %s';
diff --git a/root/app/www/public/classes/traits/Docker/Container.php b/root/app/www/public/classes/traits/Docker/Container.php
index 817c112..52d982f 100644
--- a/root/app/www/public/classes/traits/Docker/Container.php
+++ b/root/app/www/public/classes/traits/Docker/Container.php
@@ -42,6 +42,28 @@ public function stopContainer($containerName)
return $this->shell->exec($cmd . ' 2>&1');
}
+ public function getUnusedContainers()
+ {
+ $unused = [];
+ $cmd = DockerSock::UNUSED_CONTAINERS;
+ $containers = $this->shell->exec($cmd . ' 2>&1');
+
+ if ($containers) {
+ $containerList = explode("\n", $containers);
+ foreach ($containerList as $container) {
+ list($id, $image, $tag) = explode(':', $container);
+
+ if (!$id) {
+ continue;
+ }
+
+ $unused[] = ['ID' => $id, 'Repository' => $image, 'Tag' => $tag];
+ }
+ }
+
+ return json_encode($unused);
+ }
+
public function getOrphanContainers()
{
$cmd = DockerSock::ORPHAN_CONTAINERS;
diff --git a/root/app/www/public/functions/api.php b/root/app/www/public/functions/api.php
index 0e4174f..1523120 100644
--- a/root/app/www/public/functions/api.php
+++ b/root/app/www/public/functions/api.php
@@ -204,6 +204,8 @@ function apiRequestLocal($endpoint, $parameters = [], $payload = [])
return $docker->getOrphanNetworks();
case 'docker-getOrphanVolumes':
return $docker->getOrphanVolumes();
+ case 'docker-getUnusedContainers':
+ return $docker->getUnusedContainers();
case 'docker-imageSizes':
return $docker->getImageSizes();
case 'docker-inspect':
diff --git a/root/app/www/public/functions/helpers/strings.php b/root/app/www/public/functions/helpers/strings.php
index 2a187b8..f4c26b5 100644
--- a/root/app/www/public/functions/helpers/strings.php
+++ b/root/app/www/public/functions/helpers/strings.php
@@ -7,6 +7,11 @@
----------------------------------
*/
+function stri_contains(string|null $haystack, string $needle)
+{
+ return str_contains(strtolower($haystack), strtolower($needle));
+}
+
function str_equals_any(string|null $haystack, array $needles): bool
{
if (!$haystack) {
diff --git a/root/app/www/public/js/orphans.js b/root/app/www/public/js/orphans.js
index 9e0e79e..f66074b 100644
--- a/root/app/www/public/js/orphans.js
+++ b/root/app/www/public/js/orphans.js
@@ -28,6 +28,8 @@ function removeOrphans()
type = 'volume';
} else if (split[0] == 'orphanNetwork') {
type = 'network';
+ } else if (split[0] == 'unusedContainer') {
+ type = 'unused';
}
selectedOrphans.push({'orphan': split[1], 'type': type});