This commit is contained in:
jarek committed 2026-05-17 08:02:31 +02:00
1 parent d9054ff347
commit e7100f8926
44 files changed
+1672 -226

No files matched your search

+46
View File
@@ -1046,6 +1046,7 @@ export interface ContainerInfo {
networks: { [networkName: string]: { ipAddress: string } };
health?: string;
restartCount: number;
exitCode?: number;
mounts: Array<{ type: string; source: string; destination: string; mode: string; rw: boolean }>;
labels: { [key: string]: string };
command: string;
@@ -1081,6 +1082,39 @@ export async function listContainers(all = true, envId?: number | null): Promise
})
);
// Fetch health status via inspect for containers with healthchecks where Status
// string doesn't include health info (Podman compat API omits it from the list endpoint).
// Skip entirely if any container already has health in Status (Docker includes it natively).
const healthStatuses = new Map<string, string>();
const healthRegex = /\((healthy|unhealthy|health:\s*starting)\)/i;
const anyHasHealthInStatus = containers.some(c => healthRegex.test(c.Status || ''));
if (!anyHasHealthInStatus) {
// Single API call to find which containers have healthchecks configured
try {
const healthFiltered = await dockerJsonRequest<any[]>(
'/containers/json?filters=' + encodeURIComponent(JSON.stringify({ health: ['healthy', 'unhealthy', 'starting'] })),
{}, envId
);
const healthIds = new Set((healthFiltered || []).map((c: any) => c.Id));
// Only inspect those specific containers (typically very few)
if (healthIds.size > 0) {
await Promise.all(
[...healthIds].map(async (id) => {
try {
const inspect = await inspectContainer(id, envId);
const h = inspect.State?.Health?.Status;
if (h) healthStatuses.set(id, h);
} catch {
// Ignore errors
}
})
);
}
} catch {
// Ignore - health filter may not be supported
}
}
return containers.map((container) => {
// Extract network info with IP addresses
const networks: { [networkName: string]: { ipAddress: string } } = {};
@@ -1103,14 +1137,25 @@ export async function listContainers(all = true, envId?: number | null): Promise
// Extract health status from Status string
// Docker formats: "(healthy)", "(unhealthy)", "(health: starting)"
// Podman compat API omits health from Status string, so fall back to inspect data
let health: string | undefined;
const healthMatch = container.Status?.match(/\((healthy|unhealthy|health:\s*starting)\)/i);
if (healthMatch) {
const matched = healthMatch[1].toLowerCase();
// Normalize "health: starting" to just "starting"
health = matched.includes('starting') ? 'starting' : matched;
} else {
const inspectHealth = healthStatuses.get(container.Id);
if (inspectHealth) {
health = inspectHealth === 'health: starting' ? 'starting' : inspectHealth;
}
}
// Parse exit code from Status string (e.g., "Exited (0) 5 minutes ago")
const exitCode = container.State === 'exited'
? parseInt(container.Status?.match(/\((\d+)\)/)?.[1] ?? '-1')
: undefined;
return {
id: container.Id,
name: container.Names[0]?.replace(/^\//, '') || 'unnamed',
@@ -1122,6 +1167,7 @@ export async function listContainers(all = true, envId?: number | null): Promise
networks,
health,
restartCount: restartCounts.get(container.Id) || 0,
exitCode,
mounts,
labels: container.Labels || {},
command: container.Command || '',