import React, { useState } from 'react'; import { toast } from 'react-toastify'; import { Button } from '~/components/ui/Button'; import { Badge } from '~/components/ui/Badge'; import { classNames } from '~/utils/classNames'; import { Collapsible, CollapsibleTrigger, CollapsibleContent } from '~/components/ui/Collapsible'; import { CodeBracketIcon, ChevronDownIcon } from '@heroicons/react/24/outline'; // Helper function to safely parse JSON const safeJsonParse = (item: string | null) => { if (!item) { return null; } try { return JSON.parse(item); } catch (e) { console.error('Failed to parse JSON from localStorage:', e); return null; } }; /** * A diagnostics component to help troubleshoot connection issues */ export default function ConnectionDiagnostics() { const [diagnosticResults, setDiagnosticResults] = useState(null); const [isRunning, setIsRunning] = useState(false); const [showDetails, setShowDetails] = useState(false); // Run diagnostics when requested const runDiagnostics = async () => { try { setIsRunning(true); setDiagnosticResults(null); // Check browser-side storage const localStorageChecks = { githubConnection: localStorage.getItem('github_connection'), netlifyConnection: localStorage.getItem('netlify_connection'), vercelConnection: localStorage.getItem('vercel_connection'), supabaseConnection: localStorage.getItem('supabase_connection'), }; // Get diagnostic data from server const response = await fetch('/api/system/diagnostics'); if (!response.ok) { throw new Error(`Diagnostics API error: ${response.status}`); } const serverDiagnostics = await response.json(); // === GitHub Checks === const githubConnectionParsed = safeJsonParse(localStorageChecks.githubConnection); const githubToken = githubConnectionParsed?.token; const githubAuthHeaders = { ...(githubToken ? { Authorization: `Bearer ${githubToken}` } : {}), 'Content-Type': 'application/json', }; console.log('Testing GitHub endpoints with token:', githubToken ? 'present' : 'missing'); const githubEndpoints = [ { name: 'User', url: '/api/system/git-info?action=getUser' }, { name: 'Repos', url: '/api/system/git-info?action=getRepos' }, { name: 'Default', url: '/api/system/git-info' }, ]; const githubResults = await Promise.all( githubEndpoints.map(async (endpoint) => { try { const resp = await fetch(endpoint.url, { headers: githubAuthHeaders }); return { endpoint: endpoint.name, status: resp.status, ok: resp.ok }; } catch (error) { return { endpoint: endpoint.name, error: error instanceof Error ? error.message : String(error), ok: false, }; } }), ); // === Netlify Checks === const netlifyConnectionParsed = safeJsonParse(localStorageChecks.netlifyConnection); const netlifyToken = netlifyConnectionParsed?.token; let netlifyUserCheck = null; if (netlifyToken) { try { const netlifyResp = await fetch('https://api.netlify.com/api/v1/user', { headers: { Authorization: `Bearer ${netlifyToken}` }, }); netlifyUserCheck = { status: netlifyResp.status, ok: netlifyResp.ok }; } catch (error) { netlifyUserCheck = { error: error instanceof Error ? error.message : String(error), ok: false, }; } } // === Vercel Checks === const vercelConnectionParsed = safeJsonParse(localStorageChecks.vercelConnection); const vercelToken = vercelConnectionParsed?.token; let vercelUserCheck = null; if (vercelToken) { try { const vercelResp = await fetch('https://api.vercel.com/v2/user', { headers: { Authorization: `Bearer ${vercelToken}` }, }); vercelUserCheck = { status: vercelResp.status, ok: vercelResp.ok }; } catch (error) { vercelUserCheck = { error: error instanceof Error ? error.message : String(error), ok: false, }; } } // === Supabase Checks === const supabaseConnectionParsed = safeJsonParse(localStorageChecks.supabaseConnection); const supabaseUrl = supabaseConnectionParsed?.projectUrl; const supabaseAnonKey = supabaseConnectionParsed?.anonKey; let supabaseCheck = null; if (supabaseUrl && supabaseAnonKey) { supabaseCheck = { ok: true, status: 200, message: 'URL and Key present in localStorage' }; } else { supabaseCheck = { ok: false, message: 'URL or Key missing in localStorage' }; } // Compile results const results = { timestamp: new Date().toISOString(), localStorage: { hasGithubConnection: Boolean(localStorageChecks.githubConnection), hasNetlifyConnection: Boolean(localStorageChecks.netlifyConnection), hasVercelConnection: Boolean(localStorageChecks.vercelConnection), hasSupabaseConnection: Boolean(localStorageChecks.supabaseConnection), githubConnectionParsed, netlifyConnectionParsed, vercelConnectionParsed, supabaseConnectionParsed, }, apiEndpoints: { github: githubResults, netlify: netlifyUserCheck, vercel: vercelUserCheck, supabase: supabaseCheck, }, serverDiagnostics, }; setDiagnosticResults(results); // Display simple results if (results.localStorage.hasGithubConnection && results.apiEndpoints.github.some((r: { ok: boolean }) => !r.ok)) { toast.error('GitHub API connections are failing. Try reconnecting.'); } if (results.localStorage.hasNetlifyConnection && netlifyUserCheck && !netlifyUserCheck.ok) { toast.error('Netlify API connection is failing. Try reconnecting.'); } if (results.localStorage.hasVercelConnection && vercelUserCheck && !vercelUserCheck.ok) { toast.error('Vercel API connection is failing. Try reconnecting.'); } if (results.localStorage.hasSupabaseConnection && supabaseCheck && !supabaseCheck.ok) { toast.warning('Supabase connection check failed or missing details. Verify settings.'); } if ( !results.localStorage.hasGithubConnection && !results.localStorage.hasNetlifyConnection && !results.localStorage.hasVercelConnection && !results.localStorage.hasSupabaseConnection ) { toast.info('No connection data found in browser storage.'); } } catch (error) { console.error('Diagnostics error:', error); toast.error('Error running diagnostics'); setDiagnosticResults({ error: error instanceof Error ? error.message : String(error) }); } finally { setIsRunning(false); } }; // Helper to reset GitHub connection const resetGitHubConnection = () => { try { localStorage.removeItem('github_connection'); document.cookie = 'githubToken=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;'; document.cookie = 'githubUsername=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;'; document.cookie = 'git:github.com=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;'; toast.success('GitHub connection data cleared. Please refresh the page and reconnect.'); setDiagnosticResults(null); } catch (error) { console.error('Error clearing GitHub data:', error); toast.error('Failed to clear GitHub connection data'); } }; // Helper to reset Netlify connection const resetNetlifyConnection = () => { try { localStorage.removeItem('netlify_connection'); document.cookie = 'netlifyToken=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;'; toast.success('Netlify connection data cleared. Please refresh the page and reconnect.'); setDiagnosticResults(null); } catch (error) { console.error('Error clearing Netlify data:', error); toast.error('Failed to clear Netlify connection data'); } }; // Helper to reset Vercel connection const resetVercelConnection = () => { try { localStorage.removeItem('vercel_connection'); toast.success('Vercel connection data cleared. Please refresh the page and reconnect.'); setDiagnosticResults(null); } catch (error) { console.error('Error clearing Vercel data:', error); toast.error('Failed to clear Vercel connection data'); } }; // Helper to reset Supabase connection const resetSupabaseConnection = () => { try { localStorage.removeItem('supabase_connection'); toast.success('Supabase connection data cleared. Please refresh the page and reconnect.'); setDiagnosticResults(null); } catch (error) { console.error('Error clearing Supabase data:', error); toast.error('Failed to clear Supabase connection data'); } }; return (
{/* Connection Status Cards */}
{/* GitHub Connection Card */}
GitHub Connection
{diagnosticResults ? ( <>
{diagnosticResults.localStorage.hasGithubConnection ? 'Connected' : 'Not Connected'}
{diagnosticResults.localStorage.hasGithubConnection && ( <>
User: {diagnosticResults.localStorage.githubConnectionParsed?.user?.login || 'N/A'}
API Status:{' '} r.ok) ? 'default' : 'destructive' } className="ml-1" > {diagnosticResults.apiEndpoints.github.every((r: { ok: boolean }) => r.ok) ? 'OK' : 'Failed'}
)} {!diagnosticResults.localStorage.hasGithubConnection && ( )} ) : (
Run diagnostics to check connection status
)}
{/* Netlify Connection Card */}
Netlify Connection
{diagnosticResults ? ( <>
{diagnosticResults.localStorage.hasNetlifyConnection ? 'Connected' : 'Not Connected'}
{diagnosticResults.localStorage.hasNetlifyConnection && ( <>
User:{' '} {diagnosticResults.localStorage.netlifyConnectionParsed?.user?.full_name || diagnosticResults.localStorage.netlifyConnectionParsed?.user?.email || 'N/A'}
API Status:{' '} {diagnosticResults.apiEndpoints.netlify?.ok ? 'OK' : 'Failed'}
)} {!diagnosticResults.localStorage.hasNetlifyConnection && ( )} ) : (
Run diagnostics to check connection status
)}
{/* Vercel Connection Card */}
Vercel Connection
{diagnosticResults ? ( <>
{diagnosticResults.localStorage.hasVercelConnection ? 'Connected' : 'Not Connected'}
{diagnosticResults.localStorage.hasVercelConnection && ( <>
User:{' '} {diagnosticResults.localStorage.vercelConnectionParsed?.user?.username || diagnosticResults.localStorage.vercelConnectionParsed?.user?.user?.username || 'N/A'}
API Status:{' '} {diagnosticResults.apiEndpoints.vercel?.ok ? 'OK' : 'Failed'}
)} {!diagnosticResults.localStorage.hasVercelConnection && ( )} ) : (
Run diagnostics to check connection status
)}
{/* Supabase Connection Card */}
Supabase Connection
{diagnosticResults ? ( <>
{diagnosticResults.localStorage.hasSupabaseConnection ? 'Configured' : 'Not Configured'}
{diagnosticResults.localStorage.hasSupabaseConnection && ( <>
Project URL: {diagnosticResults.localStorage.supabaseConnectionParsed?.projectUrl || 'N/A'}
Config Status:{' '} {diagnosticResults.apiEndpoints.supabase?.ok ? 'OK' : 'Check Failed'}
)} {!diagnosticResults.localStorage.hasSupabaseConnection && ( )} ) : (
Run diagnostics to check connection status
)}
{/* Action Buttons */}
{/* Details Panel */} {diagnosticResults && (
Diagnostic Details
                  {JSON.stringify(diagnosticResults, null, 2)}
                
)}
); }