import Head from 'next/head' import Link from 'next/link' import { Service, Status, ServiceType, ServiceLocation } from '../interfaces/CardTypes'; import Dockerode from 'dockerode'; import { ReactElement } from 'react' import useSWR from 'swr'; import { PageCard, CardContentService, PageContentBox, PageDescription, PageTitle } from '../components/styles/content'; const fetcher = (url: string) => fetch(url).then((res) => res.json()) function Services() { const { initialData, fullData, loadingInitial, loadingFull, error } = useServices(); let content: ReactElement = <>; if (error) { content =
Error loading data
} else if (loadingInitial) { content =
Loading
} else if (loadingFull) { content = {initialData?.map((item: Service) => ( ))} } else if (fullData) { content = {fullData.map((item: Service) => ( ))} } else { content =
Error loading data
} return ( <> Neshura Servers Service List Lists all available Services, most likely up-to-date {content} ) } async function getStatus(entry: Service, containers: Dockerode.ContainerInfo[]) { // Currently the only location supporting different fetching depending on type is brr7-4800u // Others to follow but low prio as this is currently the only location used // Location BRR7-4800U if (entry.location === ServiceLocation.brr7_4800u) { // Type APP if (entry.type === ServiceType.app) { await fetch(entry.href) .then((response) => { if (response.ok) { switch (response.status) { case 200: case 301: case 302: entry.status = Status.online; break; default: entry.status = Status.offline; } } else { entry.status = Status.offline; } }) .catch((error) => { console.error("Error pinging Website: ", error); entry.status = Status.error; }) } // Type Docker else if (entry.type === ServiceType.docker) { if (entry.name !== null) { let found = false; for (let i = 0; i < containers.length; i++) { const container = containers[i]; // Docker API returns container names with / prepended if (containers[i].Names.includes("/" + entry.docker_container_name)) { // so far only "running" is properly implemented, mroe cases to follow as needed switch (container.State) { case "running": entry.status = Status.online; break; default: console.log("Container Status " + container.State + " has no case implemented"); entry.status = Status.offline; } found = true; // cancel the for break; } // If container name is not missing the container is set to offline else { entry.status = Status.offline; } } if (!found) { console.warn("Container for " + entry.name + " could not be found"); } } // if name is null do not enter for loop else { console.error("Container Name not specified"); entry.status = Status.error; } } // If no Type matches else { console.warn("Service Type for Service " + entry.name + " not specified or invalid"); entry.status = Status.error; } } // Location Other // TODO: implement docker type for other locations else if (entry.location === ServiceLocation.other) { // Currently uses the same handling as app type for the other location await fetch(entry.href) .then((response) => { if (response.ok) { switch (response.status) { case 200: case 301: case 302: entry.status = Status.online; break; default: entry.status = Status.offline; } } else { entry.status = Status.offline; } }) .catch((error) => { console.error("Error pinging Website: ", error); entry.status = Status.error; }) } // If no Location matches else { console.warn("Service Location for Service " + entry.name + " not specified"); entry.status = Status.error; } return entry; } const fetchFullDataArray = (containerData: Dockerode.ContainerInfo[], dataSet: Service[]) => { const fetchStatus = (entry: Service) => getStatus(entry, containerData); return Promise.all(dataSet.map(fetchStatus)); } function useServices() { const { data: containerData, error: containerError } = useSWR('/api/containers', fetcher); const { data: initialData, error: initialError } = useSWR('/api/services', fetcher); const loadingInitial = !initialData && !initialError const { data: fullData, error: fullError } = useSWR((initialData && containerData) ? [containerData, initialData] : null, fetchFullDataArray) const loadingFull = !fullData && !fullError return { initialData, fullData, loadingInitial, loadingFull, error: initialError || fullError || containerError, }; } export default Services