import { useState, useCallback, useEffect } from 'react'; import { Card } from '@/components/ui/Card'; import { Button } from '@/components/ui/Button'; import { Select } from '@/components/ui/Select'; import { Badge } from '@/components/ui/Badge'; import { Tabs } from '@/components/ui/Tabs'; import { JsonViewer } from '@/components/ui/JsonViewer'; import { Spinner } from '@/components/ui/Spinner'; import { API_ENDPOINTS } from '@/lib/constants'; import { apiFetch, generateCurl } from '@/api/client'; import { connect, disconnect, listTools, callTool, getMcpConnectionState, onConnectionStateChange, type McpToolInfo, type McpConnectionState, } from '@/api/mcp-client'; // --------------------------------------------------------------------------- // Shared types // --------------------------------------------------------------------------- interface HistoryEntry { id: number; mode: 'REST' | 'MCP'; label: string; status: 'success' | 'error'; time: string; duration: number; response: unknown; } let historyId = 0; // --------------------------------------------------------------------------- // Page // --------------------------------------------------------------------------- export function ApiTesterPage() { const [mode, setMode] = useState<'rest' | 'mcp'>('rest'); const [response, setResponse] = useState(null); const [responseStatus, setResponseStatus] = useState<'success' | 'error' | null>(null); const [duration, setDuration] = useState(null); const [history, setHistory] = useState([]); const addHistory = useCallback((entry: Omit) => { setHistory((prev) => [{ ...entry, id: historyId++ }, ...prev].slice(0, 30)); }, []); return (

API 测试

setMode(k as 'rest' | 'mcp')} />
{mode === 'rest' ? ( ) : ( )} {/* Response */} {response !== null && (

响应

{responseStatus && ( {responseStatus === 'success' ? '成功' : '失败'} )} {duration !== null && ( {duration}ms )}
)}
{/* History */}

历史记录

{history.length === 0 ? (

暂无请求

) : (
{history.map((entry) => (
{ setResponse(entry.response); setResponseStatus(entry.status); setDuration(entry.duration); }} >
{entry.mode} {entry.label}
{entry.status === 'success' ? '成功' : '失败'} {entry.time} {entry.duration}ms
))}
)}
); } // --------------------------------------------------------------------------- // REST Panel // --------------------------------------------------------------------------- function RestPanel({ setResponse, setResponseStatus, setDuration, addHistory, }: { response: unknown; setResponse: (v: unknown) => void; setResponseStatus: (v: 'success' | 'error' | null) => void; setDuration: (v: number | null) => void; addHistory: (e: Omit) => void; }) { const [selectedKey, setSelectedKey] = useState(API_ENDPOINTS[0]!.key); const [bodyText, setBodyText] = useState(''); const [loading, setLoading] = useState(false); const endpoint = API_ENDPOINTS.find((e) => e.key === selectedKey)!; const categories = [...new Set(API_ENDPOINTS.map((e) => e.category))]; const handleEndpointChange = useCallback((key: string) => { setSelectedKey(key); setResponse(null); setResponseStatus(null); setDuration(null); const ep = API_ENDPOINTS.find((e) => e.key === key); if (ep && 'body' in ep && ep.body) { setBodyText(JSON.stringify(ep.body, null, 2)); } else { setBodyText(''); } }, [setResponse, setResponseStatus, setDuration]); const handleSend = useCallback(async () => { setLoading(true); setResponse(null); const start = Date.now(); try { let body: unknown = undefined; if (bodyText.trim() && endpoint.method !== 'GET') { body = JSON.parse(bodyText); } const res = await apiFetch(endpoint.path, { method: endpoint.method, ...(body ? { body: JSON.stringify(body) } : {}), }); const dur = Date.now() - start; setResponse(res); setResponseStatus('success'); setDuration(dur); addHistory({ mode: 'REST', label: `${endpoint.method} ${endpoint.path}`, status: 'success', time: new Date().toLocaleTimeString(), duration: dur, response: res }); } catch (err) { const dur = Date.now() - start; const errData = err instanceof Error ? { error: err.message } : { error: String(err) }; setResponse(errData); setResponseStatus('error'); setDuration(dur); addHistory({ mode: 'REST', label: `${endpoint.method} ${endpoint.path}`, status: 'error', time: new Date().toLocaleTimeString(), duration: dur, response: errData }); } finally { setLoading(false); } }, [endpoint, bodyText, setResponse, setResponseStatus, setDuration, addHistory]); const curl = generateCurl( endpoint.method, endpoint.path, bodyText.trim() && endpoint.method !== 'GET' ? (() => { try { return JSON.parse(bodyText) as unknown; } catch { return undefined; } })() : undefined, ); return ( <>

REST 请求