'use client' import {useEffect, useRef, useState} from 'react' import {CheckCircle2, Loader2, ShieldAlert, ShieldCheck, ShieldQuestion} from 'lucide-react' import {getCapToken} from '@/lib/cap-solver' type CapStatus = 'idle' | 'solving' | 'solved' | 'error' interface CapWidgetProps { /** Called with the one-time token once the challenge is solved */ onToken: (token: string) => void /** Called when the challenge fails or encounters an error */ onError?: (err: Error) => void /** Whether to auto-start solving when the component mounts. Defaults to true. */ autoStart?: boolean /** PoW scope sent to the backend */ scope?: string } /** * Cap 人机验证小部件 * * autoStart=true(默认):挂载后立即在后台运行 PoW 求解,完成后回调 onToken。 * autoStart=false:显示「点击开始验证」按钮,用户手动触发后才开始求解。 */ export function CapWidget({ onToken, onError, autoStart = true, scope = 'login' }: CapWidgetProps) { const [status, setStatus] = useState('idle') const [errorMsg, setErrorMsg] = useState('') const solving = useRef(false) const solve = async () => { if (solving.current) return solving.current = true setStatus('solving') setErrorMsg('') try { const token = await getCapToken(scope) setStatus('solved') onToken(token) } catch (err) { const error = err instanceof Error ? err : new Error(String(err)) setStatus('error') setErrorMsg(error.message) onError?.(error) } finally { solving.current = false } } // Auto-start on mount (only when autoStart is enabled) useEffect(() => { if (autoStart) { void solve() } // eslint-disable-next-line react-hooks/exhaustive-deps }, []) return (
{/* idle + autoStart=false: 手动触发按钮 */} {status === 'idle' && !autoStart && ( )} {/* idle + autoStart=true: 等待自动开始(极短暂状态) */} {status === 'idle' && autoStart && ( <> 等待人机验证… )} {status === 'solving' && ( <> 正在完成人机验证… )} {status === 'solved' && ( <> 人机验证通过 )} {status === 'error' && ( )}
) }