/ web / src / lib / docker_socket.svelte.ts
docker_socket.svelte.ts
  1  // place files you want to import through the `$lib` alias in this folder.
  2  import { ws_url } from './utils';
  3  import { wsStatus, type DockerInfo } from './types';
  4  
  5  export const ddata = $state({
  6      data: {} as DockerInfo,
  7      status: wsStatus.INIT as wsStatus
  8  });
  9  
 10  
 11  let d_ws: WebSocket | null = null;
 12  let reconnectAttempt = 0;
 13  let reconnectTimeout: number | null = null;
 14  let isReconnecting = false;
 15  
 16  function getReconnectDelay(): number {
 17      // Backoff with a maximum of 10 seconds
 18      return Math.min(2000 * (reconnectAttempt + 1), 10000);
 19  }
 20  
 21  function scheduleReconnect() {
 22      if (isReconnecting) return;
 23  
 24      if (ddata.data === null) return; // Don't flood the log with errors if docker is inaccessible
 25  
 26      isReconnecting = true;
 27      const delay = getReconnectDelay();
 28  
 29      //console.log(`Scheduling reconnect attempt in ${delay}ms`);
 30  
 31      if (reconnectTimeout !== null) {
 32          clearTimeout(reconnectTimeout);
 33      }
 34  
 35      reconnectTimeout = setTimeout(() => {
 36          reconnectAttempt++;;
 37          isReconnecting = false;
 38          open_ws();
 39      }, delay) as unknown as number;
 40  }
 41  
 42  export function close_ws() {
 43      if (d_ws !== null) {
 44          d_ws.close();
 45      }
 46  }
 47  
 48  export function open_ws() {
 49      // Clean up existing connection if any
 50      if (d_ws !== null) {
 51          try {
 52              d_ws.onopen = null;
 53              d_ws.onclose = null;
 54              d_ws.onerror = null;
 55              d_ws.onmessage = null;
 56              d_ws.close();
 57          } catch (e) {
 58              console.error("Error while closing existing WebSocket:", e);
 59          }
 60          d_ws = null;
 61      }
 62  
 63      try {
 64          d_ws = new WebSocket(import.meta.env.PROD
 65              ? ws_url('ws/d')
 66              : "ws://localhost:30000/ws/d");
 67      }
 68      catch (e) {
 69          console.error("WebSocket connection failed: ", e);
 70          ddata.status = wsStatus.ERROR;
 71          scheduleReconnect();
 72          return;
 73      }
 74  
 75      d_ws.onopen = function (event) {
 76          ddata.status = wsStatus.WAITING;
 77          //console.log("WebSocket opened:", event);
 78          reconnectAttempt = 0;
 79      }
 80  
 81      d_ws.onerror = function (event) {
 82          ddata.status = wsStatus.ERROR;
 83          console.error("WebSocket error observed:", event);
 84          // We'll let onclose handle the reconnection
 85      }
 86  
 87      d_ws.onclose = function (event) {
 88          ddata.status = wsStatus.DISCONNECTED;
 89          //console.log("WebSocket closed:", event);
 90          scheduleReconnect();
 91      }
 92  
 93      d_ws.onmessage = async function (event) {
 94          ddata.status = wsStatus.CONNECTED;
 95          const compressedData = await event.data.arrayBuffer();
 96          const decompressStream = new DecompressionStream('gzip');
 97          const decompressedStream = new ReadableStream({
 98              start(controller) {
 99                  controller.enqueue(compressedData);
100                  controller.close();
101              }
102          }).pipeThrough(decompressStream);
103  
104          const responseData = await new Response(decompressedStream).text();
105  
106          ddata.data = JSON.parse(responseData) as DockerInfo;
107      }
108  }