// External API Status tab — polls /admin/external-api-status every 15s while mounted. const ExternalApiStatusTab = ({ lang }) => { const t = lang === 'zh'; const [data, setData] = React.useState(null); const [error, setError] = React.useState(null); React.useEffect(() => { let cancelled = false; const controller = new AbortController(); const fetchOnce = async () => { try { const res = await fetch(`${API_BASE}/admin/external-api-status`, { signal: controller.signal }); if (!res.ok) throw new Error(`HTTP ${res.status}`); const json = await res.json(); if (!cancelled) { setData(json); setError(null); } } catch (err) { if (cancelled || err.name === 'AbortError') return; setError(err.message || String(err)); } }; fetchOnce(); const id = setInterval(fetchOnce, 15000); return () => { cancelled = true; controller.abort(); clearInterval(id); }; }, []); const apiNames = { openai_whisper: 'OpenAI Whisper', openai_chat: 'OpenAI Chat', openai_embedding: 'OpenAI Embedding', }; return (

{t ? '外部 API 最近呼叫狀態,每 15 秒自動更新。' : 'Most recent external API call status; auto-refreshes every 15 seconds.'}

{error && !data && (
{(t ? '載入失敗:' : 'Failed to load: ') + error}
)}
{(data?.apis || []).map(api => ( ))}
); }; const ApiStatusCard = ({ entry, label, lang }) => { const t = lang === 'zh'; const { latest, degraded } = entry; const isHealthy = latest && latest.ok; const badge = latest ? (isHealthy ? { variant: 'success', label: t ? '正常' : 'Healthy' } : categoryToBadge(latest.error_category, lang)) : null; return (
{label} {badge && {badge.label}}
{!latest && (
{t ? '尚無紀錄' : 'No calls recorded yet'}
)} {latest && (
{formatRelativeTime(latest.ts_ms, lang)} {latest.http_status != null && ( HTTP {latest.http_status} )} {typeof latest.duration_ms === 'number' && ( {latest.duration_ms} ms )}
)} {degraded && (
{t ? '監測服務異常,最近狀態可能不準確' : 'Monitoring service degraded; status may be stale'}
)}
); }; Object.assign(window, { ExternalApiStatusTab });