IKHSAN
ENID Baca dalam Bahasa Indonesia

homeio

Public

Forked from doctor-io/homeio

Branch
main
Latest commit
958d74616 October 2026

useDockerStats.ts

Open raw
import { useEffect, useState } from "react";
import type { ContainerStats, DockerStatsPayload } from "@/lib/shared/contracts/docker";

/**
 * Hook to subscribe to real-time Docker container stats via SSE.
 * Exposes `daemonAvailable` so the UI can distinguish "no containers" from
 * "Docker daemon is unreachable".
 */
export function useDockerStats() {
  const [stats, setStats] = useState<ContainerStats[]>([]);
  const [daemonAvailable, setDaemonAvailable] = useState<boolean | null>(null);
  const [isConnected, setIsConnected] = useState(false);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    let eventSource: EventSource | null = null;
    let reconnectTimeout: NodeJS.Timeout | null = null;

    const connect = () => {
      try {
        eventSource = new EventSource("/api/v1/docker/stats/stream");

        eventSource.addEventListener("stats.updated", (event) => {
          try {
            const payload = JSON.parse(event.data) as DockerStatsPayload;
            setStats(payload.containers);
            setDaemonAvailable(payload.daemonAvailable);
            setError(null);
          } catch {
            setError("Failed to parse stats data");
          }
        });

        eventSource.addEventListener("heartbeat", () => {
          // Keep connection alive
        });

        eventSource.onopen = () => {
          setIsConnected(true);
          setError(null);
        };

        eventSource.onerror = () => {
          setIsConnected(false);
          setDaemonAvailable(null); // Reset — connection lost, state unknown
          eventSource?.close();

          // Reconnect after 5 seconds
          reconnectTimeout = setTimeout(() => {
            connect();
          }, 5000);
        };
      } catch (err) {
        setError(err instanceof Error ? err.message : "Connection failed");
        setIsConnected(false);
      }
    };

    connect();

    return () => {
      if (eventSource) {
        eventSource.close();
      }
      if (reconnectTimeout) {
        clearTimeout(reconnectTimeout);
      }
    };
  }, []);

  return {
    stats,
    daemonAvailable,
    isConnected,
    error,
  };
}

/**
 * Hook to fetch Docker stats once (non-streaming)
 */
export function useDockerStatsSnapshot() {
  const [stats, setStats] = useState<ContainerStats[]>([]);
  const [daemonAvailable, setDaemonAvailable] = useState<boolean | null>(null);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    const fetchStats = async () => {
      try {
        setIsLoading(true);
        const response = await fetch("/api/v1/docker/stats");

        if (!response.ok) {
          throw new Error(`HTTP ${response.status}`);
        }

        const json = (await response.json()) as { data: DockerStatsPayload };
        setStats(json.data.containers);
        setDaemonAvailable(json.data.daemonAvailable);
        setError(null);
      } catch (err) {
        setError(err instanceof Error ? err.message : "Failed to fetch stats");
      } finally {
        setIsLoading(false);
      }
    };

    void fetchStats();
  }, []);

  return {
    stats,
    daemonAvailable,
    isLoading,
    error,
  };
}
bytes
3,263
lines
119
Type
ts
Latest commit
16 October 2026