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
|
|
|
|
};
|
|
|
|
}
|