- {safeContainers.length === 0 ? (
-
No containers running for this stack.
- {safeContainers.map(container => {
- let mainPort: number | undefined;
- let mainPortPrivate: number | undefined;
- let mainPortProto: string | undefined;
- if (container.Ports && container.Ports.length > 0) {
- const WEB_UI_PORTS = [32400, 8989, 7878, 9696, 5055, 8080, 80, 443, 3000, 9000];
- const IGNORE_PORTS = [1900, 53, 22];
- let match = container.Ports.find(p => WEB_UI_PORTS.includes(p.PrivatePort));
- if (!match) match = container.Ports.find(p => WEB_UI_PORTS.includes(p.PublicPort));
- if (!match) match = container.Ports.find(p => !IGNORE_PORTS.includes(p.PrivatePort) && !IGNORE_PORTS.includes(p.PublicPort));
- const chosen = match || container.Ports[0];
- mainPort = chosen.PublicPort;
- mainPortPrivate = chosen.PrivatePort;
- mainPortProto = 'tcp';
- }
-
- const containerName = container?.Names?.[0]?.replace(/^\//, '') || container?.Id?.slice(0, 12) || 'container';
- const isActive = container.State === 'running' || container.State === 'paused';
- const health = container.healthStatus;
- const uptime = isActive ? extractUptime(container.Status) : null;
- const hcLabel = healthcheckLabel(health);
- const stats = containerStats[container?.Id];
- const history = stats?.history;
-
- const badgeClass = health === 'unhealthy' || !isActive
- ? 'bg-destructive text-destructive-foreground'
- : health === 'starting'
- ? 'bg-warning text-warning-foreground'
- : 'bg-success text-success-foreground';
- const badgeGlyph = health === 'unhealthy' || !isActive ? '✗' : health === 'starting' ? '…' : '✓';
- const sparkStroke = health === 'unhealthy' ? 'var(--destructive)' : health === 'starting' ? 'var(--warning)' : 'var(--chart-1)';
-
- return (
-
-
-
-
- {badgeGlyph}
-
-
-
{containerName}
-
- {uptime ?
{uptime} :
{(container.State || 'unknown').toLowerCase()} }
- {hcLabel ? <>
· {hcLabel} > : null}
- {mainPort && mainPortPrivate ? (
- <>
-
·
-
{mainPort} → {mainPortPrivate}/{mainPortProto}
-
{
- const host = activeNode?.type === 'remote' && activeNode?.api_url
- ? new URL(activeNode.api_url).hostname
- : window.location.hostname;
- window.open(`http://${host}:${mainPort}`, '_blank');
- }}
- className="inline-flex items-center gap-1 text-brand hover:underline"
- >
- open
-
- >
- ) : null}
-
-
-
-
- openLogViewer(container?.Id, containerName)}
- disabled={!isActive}
- aria-label="View logs"
- >
-
-
- {isAdmin && (
- openBashModal(container?.Id, containerName)}
- disabled={!isActive}
- aria-label="Open bash shell"
- >
-
-
- )}
- {container.Service && (
-
-
-
-
-
-
-
- {isActive ? (
- <>
- serviceAction('restart', container.Service!)}>
- Restart service
-
- serviceAction('stop', container.Service!)}>
- Stop service
-
- >
- ) : (
- serviceAction('start', container.Service!)}>
- Start service
-
- )}
-
-
- )}
-
-
- {isActive ? (
-
-
-
- cpu
- {stats?.cpu ?? '-'}
-
-
-
-
-
-
-
- mem
- {stats?.ram ?? '-'}
-
-
-
-
-
-
-
- net i/o
- {stats?.net ?? '-'}
-
-
-
-
-
-
- ) : null}
-
- );
- })}
-