Adds a custom server.ts (http server + Next request handler + a noServer:true WebSocket server on /ws) so the app can push live data without polling. A CollectorScheduler runs one interval-based job per docker/database widget instance, backed by dockerode against /var/run/docker.sock, and reconciles jobs when config.yml changes. Config hot-reload is now fully wired: chokidar watches config.yml/.env, re-validates on change, and broadcasts config:update (or a non-destructive config:error, keeping the last-good config) to every connected browser. The client subscribes to per-widget topics over a single shared WebSocket with exponential-backoff reconnect and last-result caching for instant resubscribe. Verified end-to-end against real throwaway containers (nginx, redis): live CPU/mem/uptime/health streamed over the socket, interval changes picked up without a server restart, and a broken config.yml correctly falls back to the last-valid config instead of crashing the app.
27 lines
866 B
TypeScript
27 lines
866 B
TypeScript
import type { DockerContainerResult } from "@/lib/types/widget-result";
|
|
|
|
interface Props {
|
|
status?: DockerContainerResult["status"];
|
|
health?: DockerContainerResult["health"];
|
|
}
|
|
|
|
function resolveColorClass(status?: Props["status"], health?: Props["health"]): string {
|
|
if (!status) return "bg-fg-muted";
|
|
if (health === "unhealthy") return "bg-status-down";
|
|
if (health === "starting") return "bg-status-degraded";
|
|
if (status === "running") return "bg-status-up";
|
|
if (status === "restarting") return "bg-status-degraded";
|
|
return "bg-status-down";
|
|
}
|
|
|
|
export function StatusDot({ status, health }: Props) {
|
|
const label = status ? `${status}${health && health !== "none" ? ` (${health})` : ""}` : "unknown";
|
|
|
|
return (
|
|
<span
|
|
className={`h-2.5 w-2.5 shrink-0 rounded-full ${resolveColorClass(status, health)}`}
|
|
title={label}
|
|
/>
|
|
);
|
|
}
|