import { AlertCircle } from 'lucide-react'; import { isRouteErrorResponse } from 'react-router'; import { isApiError, isConnectionError, } from '~/server/headscale/api/error-client'; import cn from '~/utils/cn'; import Card from './Card'; import Code from './Code'; import Link from './Link'; export function getErrorMessage(error: Error | unknown): { title: string; jsxMessage: React.ReactNode; } { if (isRouteErrorResponse(error)) { if (isApiError(error.data)) { const { statusCode, rawData, data, requestUrl } = error.data; if (statusCode >= 500) { return { title: 'Headscale API Error', jsxMessage: ( <> There was an error communicating with the Headscale API.
The server responded with a status code of{' '} {statusCode}, indicating a server-side issue. Please check the Headscale server status and try again later.
{(error.data.data != null || error.data.rawData != null) && (
									{error.data.data != null ? (
										{JSON.stringify(error.data.data, null, 2)}
									) : (
										{error.data.rawData}
									)}
								
)} ), }; } const authError = error.data.statusCode === 401 || error.data.statusCode === 403; return { title: 'Invalid response from Headscale API', jsxMessage: ( <> The Headscale API returned an unexpected response. {authError ? ( <> {' '} The status code indicates an authentication error. Please verify your API key and Headplane configuration. ) : ( <> {' '} You may be using an unsupported version of Headscale or this may be a bug. )} Error Details
							{JSON.stringify(error.data, null, 2)}
						
), }; } if (isConnectionError(error.data)) { const { requestUrl, errorCode, errorMessage, extraData } = error.data; return { title: 'Cannot connect to Headscale API', jsxMessage: ( <> Headplane was unable to reach the Headscale API. Please check your network setup and configuration to ensure Headplane is able to connect. Error Details
							{requestUrl}
							
{errorCode}: {errorMessage} {extraData != null && ( <>

{JSON.stringify(extraData, null, 2)} )}
), }; } return { title: `Error ${error.status}`, jsxMessage: ( <> There was an error processing your request.
Status Code: {error.status}
Status Text: {error.data} ), }; } if (!(error instanceof Error)) { return { title: 'Unexpected Error', jsxMessage: ( <> An unexpected error occurred which is most likely a bug. Please consider reporting filing an issue on the{' '} Headplane GitHub {' '} repository with the details below. Error Details
						{JSON.stringify(error, null, 2)}
					
), }; } // Traverse the error chain to find the root cause let rootError = error; if (error.cause != null) { rootError = error.cause as Error; while (rootError.cause != null) { rootError = rootError.cause as Error; } } // TODO: If we are aggregate, concat into a single message if (rootError instanceof AggregateError) { throw new Error('AggregateError handling not implemented yet'); } return { title: rootError.name.length > 0 && rootError.name !== 'Error' ? `Error: ${rootError.name}` : 'Error', jsxMessage: rootError.message, }; } interface ErrorBannerProps { error: unknown; className?: string; } export function ErrorBanner({ error, className }: ErrorBannerProps) { const { title, jsxMessage } = getErrorMessage(error); return (
{title}
{jsxMessage}
); }