fosrl.pangolin/src/hooks/useDockerSocket.ts

169 lines
6.3 KiB
TypeScript
Raw Normal View History

2025-05-29 22:34:05 +05:30
import { createApiClient, formatAxiosError } from "@app/lib/api";
import { useCallback, useEffect, useState } from "react";
import { useEnvContext } from "./useEnvContext";
import {
Container,
GetDockerStatusResponse,
ListContainersResponse,
TriggerFetchResponse
} from "@server/routers/site";
import { AxiosResponse } from "axios";
import { toast } from "./useToast";
2025-06-04 16:02:45 -04:00
import { Site } from "@server/db";
2025-05-29 22:34:05 +05:30
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
2025-06-04 16:02:45 -04:00
export function useDockerSocket(site: Site) {
console.log(`useDockerSocket initialized for site ID: ${site.siteId}`);
2025-05-29 22:34:05 +05:30
const [dockerSocket, setDockerSocket] = useState<GetDockerStatusResponse>();
const [containers, setContainers] = useState<Container[]>([]);
const api = createApiClient(useEnvContext());
2025-06-06 12:15:15 -04:00
const { dockerSocketEnabled: rawIsEnabled = true, type: siteType } = site || {};
const isEnabled = rawIsEnabled && siteType === "newt";
2025-05-29 22:34:05 +05:30
const { isAvailable = false, socketPath } = dockerSocket || {};
const checkDockerSocket = useCallback(async () => {
if (!isEnabled) {
console.warn("Docker socket is not enabled for this site.");
return;
}
try {
2025-06-04 16:02:45 -04:00
const res = await api.post(`/site/${site.siteId}/docker/check`);
2025-05-29 22:34:05 +05:30
console.log("Docker socket check response:", res);
} catch (error) {
console.error("Failed to check Docker socket:", error);
}
2025-06-04 16:02:45 -04:00
}, [api, site.siteId, isEnabled]);
2025-05-29 22:34:05 +05:30
const getDockerSocketStatus = useCallback(async () => {
if (!isEnabled) {
console.warn("Docker socket is not enabled for this site.");
return;
}
try {
const res = await api.get<AxiosResponse<GetDockerStatusResponse>>(
2025-06-04 16:02:45 -04:00
`/site/${site.siteId}/docker/status`
2025-05-29 22:34:05 +05:30
);
if (res.status === 200) {
setDockerSocket(res.data.data);
} else {
console.error("Failed to get Docker status:", res);
toast({
variant: "destructive",
title: "Failed to get Docker status",
description:
"An error occurred while fetching Docker status."
});
}
} catch (error) {
console.error("Failed to get Docker status:", error);
toast({
variant: "destructive",
title: "Failed to get Docker status",
description: "An error occurred while fetching Docker status."
});
}
2025-06-04 16:02:45 -04:00
}, [api, site.siteId, isEnabled]);
2025-05-29 22:34:05 +05:30
const getContainers = useCallback(
async (maxRetries: number = 3) => {
if (!isEnabled || !isAvailable) {
console.warn("Docker socket is not enabled or available.");
return;
}
const fetchContainerList = async () => {
if (!isEnabled || !isAvailable) {
return;
}
let attempt = 0;
while (attempt < maxRetries) {
try {
const res = await api.get<
AxiosResponse<ListContainersResponse>
2025-06-04 16:02:45 -04:00
>(`/site/${site.siteId}/docker/containers`);
2025-05-29 22:34:05 +05:30
setContainers(res.data.data);
2025-06-04 16:05:41 -04:00
return res.data.data;
2025-05-29 22:34:05 +05:30
} catch (error: any) {
attempt++;
// Check if the error is a 425 (Too Early) status
if (error?.response?.status === 425) {
if (attempt < maxRetries) {
console.log(
2025-06-03 21:04:08 -04:00
`Containers not ready yet (attempt ${attempt}/${maxRetries}). Retrying in 250ms...`
2025-05-29 22:34:05 +05:30
);
2025-06-03 21:04:08 -04:00
await sleep(250);
2025-05-29 22:34:05 +05:30
continue;
} else {
console.warn(
"Max retry attempts reached. Containers may still be loading."
);
2025-06-05 14:43:47 -04:00
// toast({
// variant: "destructive",
// title: "Containers not ready",
// description:
// "Containers are still loading. Please try again in a moment."
// });
2025-05-29 22:34:05 +05:30
}
} else {
console.error(
"Failed to fetch Docker containers:",
error
);
toast({
variant: "destructive",
title: "Failed to fetch containers",
description: formatAxiosError(
error,
"An error occurred while fetching containers"
)
});
}
break;
}
}
};
try {
const res = await api.post<AxiosResponse<TriggerFetchResponse>>(
2025-06-04 16:02:45 -04:00
`/site/${site.siteId}/docker/trigger`
2025-05-29 22:34:05 +05:30
);
// TODO: identify a way to poll the server for latest container list periodically?
await fetchContainerList();
return res.data.data;
} catch (error) {
console.error("Failed to trigger Docker containers:", error);
}
},
2025-06-04 16:02:45 -04:00
[api, site.siteId, isEnabled, isAvailable]
2025-05-29 22:34:05 +05:30
);
// 2. Docker socket status monitoring
useEffect(() => {
if (!isEnabled || isAvailable) {
return;
}
checkDockerSocket();
2025-06-03 21:04:08 -04:00
getDockerSocketStatus();
2025-05-29 22:34:05 +05:30
}, [isEnabled, isAvailable, checkDockerSocket, getDockerSocketStatus]);
return {
isEnabled,
isAvailable: isEnabled && isAvailable,
socketPath,
containers,
check: checkDockerSocket,
status: getDockerSocketStatus,
fetchContainers: getContainers
};
}