import { useEffect, useState } from 'react' import { useQueryClient } from '@tanstack/react-query' import { AlertTriangle, CheckCircle2, Loader2, QrCode, RefreshCw, X } from 'lucide-react' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' import { useCancelQrLogin, useQrLoginStatus, useStartQrLogin } from '@/hooks/useMonitor' import { useCurrentPlatform } from '@/hooks/usePlatform' /** * Scan-to-login for a host with no display. * * The crawler's own QR flow prints the code into the terminal via PIL's * `Image.show()`, which needs a desktop image viewer. On a server Chrome runs * under Xvfb and there is no such viewer, so the backend reads the code out of * that same browser over CDP and hands it here instead. * * That browser is the one monitor runs attach to, which is the point: scanning * once leaves the login in the profile that every later unattended run reuses. */ export function QrLoginPanel() { const { capability, platform } = useCurrentPlatform() const queryClient = useQueryClient() const [polling, setPolling] = useState(false) const { data: state } = useQrLoginStatus(polling) const start = useStartQrLogin() const cancel = useCancelQrLogin() const label = capability?.label ?? platform const status = state?.status ?? 'idle' // Stop polling the moment the outcome is known, and refresh the cookie panel: // a completed scan is what makes it start reporting a healthy login. useEffect(() => { if (status === 'waiting' || status === 'idle') return setPolling(false) if (status === 'success') { queryClient.invalidateQueries({ queryKey: ['monitorCookie'] }) } }, [status, queryClient]) const begin = () => { setPolling(true) start.mutate() } const busy = start.isPending || cancel.isPending return (
用{label} App扫码。 扫码成功后登录态会写入服务器上那台 Chrome 的 profile, 之后定时监控无需再登录。
剩余 {state.expires_in} 秒
走 CDP 接管服务器上已开启远程调试的 Chrome,把二维码取回来显示在这里。 需要先在「系统设置」里打开 接管已有 Chrome(CDP), 并确保那台 Chrome 正以 9222 端口运行。