Docker tab: full inspect detail — icon, IP, ports, paths per container

- Replace pill view with per-container rows showing full detail
- docker inspect batch call: networks+IPs, port mappings, bind mounts
- Container icon from template XML, name links to WebUI if configured
- Network IP badges (blue), port mapping badges (yellow)
- Bind mount paths shown src→dst, collapsible expand for >3 paths
- Large folders (>7 containers) auto-span full width
- Status badge: RUNNING/STOPPED/PAUSED/EXITED with colour
- Stopped containers dimmed, icons load from template XMLs
This commit is contained in:
Gmer4Lfe
2026-05-28 22:48:06 -04:00
parent 00a8a64c4e
commit 8f05f0d27c
2 changed files with 327 additions and 186 deletions
+90 -23
View File
@@ -88,23 +88,90 @@ function vv_dk_write_conf_map(string $raw, string $id, array $map): string {
return $raw . "\n# ── Docker Folder Map ────────────────────────────────────────────────────────\n" . $block . "\n";
}
// ── Container inventory ───────────────────────────────────────────────────────
// ── Container inventory (full inspect) ───────────────────────────────────────
function vv_dk_webui(string $name, array $portMap): string {
$template = '/boot/config/plugins/dockerMan/templates-user/my-' . $name . '.xml';
if (!file_exists($template)) return '';
$xml = @file_get_contents($template) ?: '';
if (!preg_match('/<WebUI>(.*?)<\/WebUI>/s', $xml, $m)) return '';
$url = trim($m[1]);
if (!$url) return '';
static $localIp = null;
if ($localIp === null) {
$localIp = trim(shell_exec("ip route get 8.8.8.8 2>/dev/null | awk '/src/{for(i=1;i<=NF;i++)if(\$i==\"src\")print \$(i+1)}'") ?: '');
if (!$localIp) $localIp = gethostbyname(gethostname());
}
$url = str_replace('[IP]', $localIp, $url);
$url = preg_replace_callback('/\[PORT:(\d+)\]/', fn($pm) => $portMap[$pm[1]] ?? $pm[1], $url);
return $url;
}
function vv_dk_icon(string $name): string {
$template = '/boot/config/plugins/dockerMan/templates-user/my-' . $name . '.xml';
if (!file_exists($template)) return '';
$xml = @file_get_contents($template) ?: '';
return preg_match('/<Icon>(.*?)<\/Icon>/s', $xml, $m) ? trim($m[1]) : '';
}
function vv_dk_inspect_all(): array {
$ids = trim(shell_exec("docker ps -aq 2>/dev/null") ?: '');
if (!$ids) return [];
$idList = implode(' ', array_map('escapeshellarg', explode("\n", $ids)));
$raw = shell_exec("docker inspect $idList 2>/dev/null") ?: '[]';
$data = json_decode($raw, true) ?: [];
function vv_dk_containers(): array {
$raw = shell_exec("docker ps -a --format '{{.Names}}\t{{.Status}}\t{{.Image}}\t{{.RunningFor}}' 2>/dev/null") ?: '';
$out = [];
foreach (explode("\n", trim($raw)) as $line) {
if (!$line) continue;
[$name, $status, $image, $age] = array_pad(explode("\t", $line, 4), 4, '');
$name = trim($name);
foreach ($data as $c) {
$name = ltrim($c['Name'] ?? '', '/');
if (!$name) continue;
$running = str_starts_with(trim($status), 'Up');
$status = $c['State']['Status'] ?? 'unknown';
$running = $status === 'running';
$image = $c['Config']['Image'] ?? '';
// Networks: name → IP
$networks = [];
foreach ($c['NetworkSettings']['Networks'] ?? [] as $netName => $net) {
$ip = $net['IPAddress'] ?? '';
if ($ip) $networks[$netName] = $ip;
}
// Port mappings: hostPort → containerPort/proto
$ports = [];
$portMap = []; // containerPort → hostPort (for WebUI resolution)
foreach ($c['HostConfig']['PortBindings'] ?? [] as $containerPort => $bindings) {
foreach ($bindings ?? [] as $b) {
$hp = $b['HostPort'] ?? '';
if ($hp) {
$ports[] = $hp . '→' . $containerPort;
[$cp] = explode('/', $containerPort);
$portMap[$cp] = $hp;
}
}
}
// Bind mounts only
$mounts = [];
foreach ($c['Mounts'] ?? [] as $m) {
if (($m['Type'] ?? '') === 'bind') {
$mounts[] = ['src' => $m['Source'] ?? '', 'dst' => $m['Destination'] ?? ''];
}
}
$webui = vv_dk_webui($name, $portMap);
$icon = vv_dk_icon($name);
$out[$name] = [
'name' => $name,
'running' => $running,
'status' => trim($status),
'image' => trim($image),
'age' => trim($age),
'name' => $name,
'running' => $running,
'status' => $status,
'image' => $image,
'networks' => $networks,
'ports' => $ports,
'mounts' => $mounts,
'webui' => $webui,
'icon' => $icon,
];
}
return $out;
@@ -114,12 +181,14 @@ function vv_dk_containers(): array {
function vv_dk_all(): array {
$jsonData = vv_dk_read_json();
$containers = vv_dk_containers();
$containers = vv_dk_inspect_all();
$currentHost= vv_detect_host();
$myId = strtoupper($currentHost);
$myRaw = vv_read_conf_raw($currentHost . '.conf');
$confMap = vv_dk_read_conf_map($myRaw, $myId);
$empty = ['name'=>'','running'=>false,'status'=>'','image'=>'','networks'=>[],'ports'=>[],'mounts'=>[],'webui'=>'','icon'=>''];
// Build folder list enriched with live container data
$assigned = [];
$folders = [];
@@ -127,23 +196,21 @@ function vv_dk_all(): array {
$ctrs = [];
foreach ($f['containers'] ?? [] as $cname) {
$assigned[$cname] = true;
$ctrs[] = array_merge(['name' => $cname, 'running' => false, 'status' => '', 'image' => '', 'age' => ''],
$containers[$cname] ?? []);
$ctrs[] = array_merge($empty, ['name' => $cname], $containers[$cname] ?? []);
}
usort($ctrs, fn($a, $b) => $b['running'] <=> $a['running'] ?: strcmp($a['name'], $b['name']));
$running = count(array_filter($ctrs, fn($c) => $c['running']));
$folders[] = [
'id' => $fid,
'name' => $f['name'] ?? 'Unnamed',
'icon' => $f['icon'] ?? '',
'total' => count($ctrs),
'running' => $running,
'containers'=> $ctrs,
'id' => $fid,
'name' => $f['name'] ?? 'Unnamed',
'total' => count($ctrs),
'running' => $running,
'containers' => $ctrs,
];
}
usort($folders, fn($a, $b) => strcmp($a['name'], $b['name']));
// Ungrouped: in docker ps but not in any folder
// Ungrouped: running but not in any folder
$ungrouped = [];
foreach ($containers as $name => $c) {
if (isset($assigned[$name])) continue;