import { XCircleFillIcon } from '@primer/octicons-react'; import { type LoaderFunctionArgs, redirect } from 'react-router'; import { Outlet, useLoaderData } from 'react-router'; import cn from '~/utils/cn'; import { HeadscaleError, healthcheck, pull } from '~/utils/headscale'; import { destroySession, getSession } from '~/utils/sessions.server'; import { useLiveData } from '~/utils/useLiveData'; import log from '~server/utils/log'; export async function loader({ request }: LoaderFunctionArgs) { let healthy = false; try { healthy = await healthcheck(); } catch (error) { log.debug('APIC', 'Healthcheck failed %o', error); } // We shouldn't session invalidate if Headscale is down if (healthy) { // We can assert because shell ensures this is set const session = await getSession(request.headers.get('Cookie')); const apiKey = session.get('hsApiKey')!; try { await pull('v1/apikey', apiKey); } catch (error) { if (error instanceof HeadscaleError) { log.debug('APIC', 'API Key validation failed %o', error); return redirect('/login', { headers: { 'Set-Cookie': await destroySession(session), }, }); } } } return { healthy, }; } export default function Layout() { useLiveData({ interval: 3000 }); const { healthy } = useLoaderData(); return ( <> {!healthy ? (
Headscale is unreachable
) : undefined}
); }