diff --git a/src/hooks/useGitHubAuth.ts b/src/hooks/useGitHubAuth.ts index a0c24b2a..98267109 100644 --- a/src/hooks/useGitHubAuth.ts +++ b/src/hooks/useGitHubAuth.ts @@ -1,18 +1,73 @@ -import { useState, useMemo } from 'react'; +import { useState, useMemo, useEffect } from 'react'; import { Octokit } from '@octokit/core'; export const useGitHubAuth = () => { const [username, setUsername] = useState(''); const [token, setToken] = useState(''); + const [error, setError] = useState(''); const octokit = useMemo(() => { if (!username) return null; if(token){ - return new Octokit({ auth: token }); + return new Octokit({ auth: token }); } return new Octokit(); }, [username, token]); + // Clear error when username or token changes, before validation runs + useEffect(() => { + setError(''); + }, [username, token]); + + // Validate token format and authentication on mount or change + useEffect(() => { + if (!token || !username) { + return; + } + + const controller = new AbortController(); + + const validateAuth = async () => { + if (!octokit) return; + + try { + // Attempt a simple API call to verify the token is valid + await octokit.request('GET /user', { request: { signal: controller.signal } }); + // Only update state if request was not aborted + if (!controller.signal.aborted) { + setError(''); + } + } catch (err: unknown) { + // Ignore if request was aborted + if (controller.signal.aborted) { + return; + } + + const error = err as { + status?: number; + message?: string; + }; + + if (error.status === 401) { + setError('Invalid personal access token. Please check and try again.'); + } else if (error.status === 403) { + setError('Token has insufficient permissions or has expired.'); + } else if (error.message?.includes('Bad credentials')) { + setError('Bad credentials. Please verify your personal access token.'); + } else { + setError(`Authentication error: ${error.message || 'Unknown error'}`); + } + } + }; + + // Validate on token change (but with a small delay to avoid excessive API calls during typing) + const timeoutId = setTimeout(validateAuth, 500); + return () => { + clearTimeout(timeoutId); + controller.abort(); + }; + }, [token, username, octokit]); + const getOctokit = () => octokit; return { @@ -20,6 +75,8 @@ export const useGitHubAuth = () => { setUsername, token, setToken, + error, + setError, getOctokit, }; }; diff --git a/src/pages/Tracker/Tracker.tsx b/src/pages/Tracker/Tracker.tsx index 576f39bf..26c18f99 100644 --- a/src/pages/Tracker/Tracker.tsx +++ b/src/pages/Tracker/Tracker.tsx @@ -324,9 +324,15 @@ const Home: React.FC = () => { - {(authError || dataError) && ( + {authError && ( - {authError || dataError} + Authentication Error: {authError} + + )} + + {dataError && !authError && ( + + Data Fetch Error: {dataError} )}