/ src / hooks / useApiKeyVerification.ts
useApiKeyVerification.ts
 1  import { useCallback, useState } from 'react'
 2  import { verifyApiKey } from '../services/claude.js'
 3  import { getAnthropicApiKey, isDefaultApiKey } from '../utils/config.js'
 4  
 5  export type VerificationStatus =
 6    | 'loading'
 7    | 'valid'
 8    | 'invalid'
 9    | 'missing'
10    | 'error'
11  
12  export type ApiKeyVerificationResult = {
13    status: VerificationStatus
14    reverify: () => Promise<void>
15    error: Error | null
16  }
17  
18  export function useApiKeyVerification(): ApiKeyVerificationResult {
19    const [status, setStatus] = useState<VerificationStatus>(() => {
20      const apiKey = getAnthropicApiKey()
21      return apiKey ? 'loading' : 'missing'
22    })
23    const [error, setError] = useState<Error | null>(null)
24  
25    const verify = useCallback(async (): Promise<void> => {
26      if (isDefaultApiKey()) {
27        setStatus('valid')
28        return
29      }
30  
31      const apiKey = getAnthropicApiKey()
32      if (!apiKey) {
33        const newStatus = 'missing' as const
34        setStatus(newStatus)
35        return
36      }
37  
38      try {
39        const isValid = await verifyApiKey(apiKey)
40        const newStatus = isValid ? 'valid' : 'invalid'
41        setStatus(newStatus)
42        return
43      } catch (error) {
44        // This happens when there an error response from the API but it's not an invalid API key error
45        // In this case, we still mark the API key as invalid - but we also log the error so we can
46        // display it to the user to be more helpful
47        setError(error as Error)
48        const newStatus = 'error' as const
49        setStatus(newStatus)
50        return
51      }
52    }, [])
53  
54    return {
55      status,
56      reverify: verify,
57      error,
58    }
59  }