import { useState, useEffect, useCallback } from 'react'; import './Developer.css'; const API_BASE = '/app/api'; interface Webhook { id: string; name: string; url: string; events: string[]; has_secret: boolean; headers: Record; status: string; last_triggered_at: string | null; last_status: string | null; total_deliveries: number; success_count: number; fail_count: number; success_rate: number; created_at: string; updated_at: string; } interface WebhookEvent { value: string; label: string; desc: string; } interface WebhookDelivery { id: string; event: string; status: string; response_code: number | null; response_time_ms: number | null; attempts: number; error_message: string | null; payload: any; response_body: string; created_at: string; } interface SDKPackage { language: string; name: string; version: string; description: string; install_cmd: string; download_url: string; github_url: string; min_version: string; features: string[]; code_example: string; } interface ApiDocSection { section: string; icon: string; description: string; endpoints: Array<{ method: string; path: string; title: string; description: string; params: Array<{ name: string; type: string; required: boolean; desc: string }>; example_request: string; example_response: string; }>; } interface PageData { total: number; page: number; page_size: number; items: T[] } async function api(path: string, options?: RequestInit) { const token = localStorage.getItem('token'); const res = await fetch(`${API_BASE}${path}`, { ...options, headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}`, ...(options?.headers || {}), }, }); if (res.status === 307) { const location = res.headers.get('Location'); if (location) return api(location.replace(API_BASE, ''), options); } const data = await res.json(); if (!res.ok) throw new Error(data.detail || `HTTP ${res.status}`); return data; } export default function AdminDeveloperTools() { const [activeTab, setActiveTab] = useState<'webhooks' | 'sdk' | 'docs'>('webhooks'); const [loading, setLoading] = useState(false); // Webhooks state const [webhooks, setWebhooks] = useState>({ total: 0, page: 1, page_size: 20, items: [] }); const [webhookEvents, setWebhookEvents] = useState([]); const [showWebhookModal, setShowWebhookModal] = useState(false); const [webhookForm, setWebhookForm] = useState({ name: '', url: '', events: [] as string[] }); const [selectedWebhook, setSelectedWebhook] = useState(null); const [deliveries, setDeliveries] = useState>({ total: 0, page: 1, page_size: 20, items: [] }); const [testResult, setTestResult] = useState(null); // SDK state const [sdkPackages, setSdkPackages] = useState([]); const [selectedSdk, setSelectedSdk] = useState('python'); // API Docs state const [docSections, setDocSections] = useState([]); const [expandedSection, setExpandedSection] = useState(null); const [expandedEndpoint, setExpandedEndpoint] = useState(null); const loadWebhooks = useCallback(async (page = 1) => { setLoading(true); try { const res = await api(`/admin/developer-tools/webhooks?page=${page}&page_size=20`); if (res.code === 0) setWebhooks(res.data); } catch (e) { console.error('Webhooks error:', e); } setLoading(false); }, []); const loadWebhookEvents = useCallback(async () => { try { const res = await api('/admin/developer-tools/webhooks/events'); if (res.code === 0) setWebhookEvents(res.data.events); } catch (e) { console.error('Events error:', e); } }, []); const loadDeliveries = useCallback(async (webhookId: string, page = 1) => { try { const res = await api(`/admin/developer-tools/webhooks/${webhookId}/deliveries?page=${page}&page_size=20`); if (res.code === 0) setDeliveries(res.data); } catch (e) { console.error('Deliveries error:', e); } }, []); const loadSdkPackages = useCallback(async () => { try { const res = await api('/admin/developer-tools/sdk/packages'); if (res.code === 0) setSdkPackages(res.data.packages); } catch (e) { console.error('SDK error:', e); } }, []); const loadApiDocs = useCallback(async () => { try { const res = await api('/admin/developer-tools/api-docs'); if (res.code === 0) setDocSections(res.data.sections); } catch (e) { console.error('Docs error:', e); } }, []); useEffect(() => { if (activeTab === 'webhooks') { loadWebhooks(); loadWebhookEvents(); } }, [activeTab, loadWebhooks, loadWebhookEvents]); useEffect(() => { if (activeTab === 'sdk') loadSdkPackages(); }, [activeTab, loadSdkPackages]); useEffect(() => { if (activeTab === 'docs') loadApiDocs(); }, [activeTab, loadApiDocs]); const handleCreateWebhook = async () => { try { const body = { name: webhookForm.name, url: webhookForm.url, events: webhookForm.events, headers: {} }; const res = await api('/admin/developer-tools/webhooks/', { method: 'POST', body: JSON.stringify(body) }); setShowWebhookModal(false); setWebhookForm({ name: '', url: '', events: [] }); loadWebhooks(); if (res.data?.secret) { alert(`Webhook created! Secret: ${res.data.secret}\nSave this secret for HMAC verification.`); } } catch (e: any) { alert(e.message); } }; const handleDeleteWebhook = async (id: string) => { if (!confirm('确定删除此 Webhook?')) return; try { await api(`/admin/developer-tools/webhooks/${id}`, { method: 'DELETE' }); loadWebhooks(); setSelectedWebhook(null); } catch (e: any) { alert(e.message); } }; const handleToggleWebhook = async (wh: Webhook) => { const newStatus = wh.status === 'active' ? 'paused' : 'active'; try { await api(`/admin/developer-tools/webhooks/${wh.id}`, { method: 'PUT', body: JSON.stringify({ status: newStatus }) }); loadWebhooks(); } catch (e: any) { alert(e.message); } }; const handleTestWebhook = async (id: string) => { setTestResult(null); try { const res = await api(`/admin/developer-tools/webhooks/${id}/test`, { method: 'POST', body: JSON.stringify({ event: 'test', payload: { message: 'Test delivery from admin' } }) }); setTestResult(res.data); loadWebhooks(); } catch (e: any) { alert(e.message); } }; const handleViewDeliveries = (wh: Webhook) => { setSelectedWebhook(wh); setDeliveries({ total: 0, page: 1, page_size: 20, items: [] }); loadDeliveries(wh.id); }; const fmtTime = (t: string | null) => { if (!t) return '-'; try { return new Date(t).toLocaleString('zh-CN'); } catch { return t; } }; const statusBadge = (status: string) => { const cls = status === 'active' ? 'badge-active' : status === 'paused' ? 'badge-disabled' : status === 'success' ? 'badge-active' : 'badge-revoked'; return {status}; }; const methodBadge = (method: string) => { const colors: Record = { GET: 'method-get', POST: 'method-post', PUT: 'method-put', DELETE: 'method-delete' }; return {method}; }; const copyToClipboard = (text: string) => { navigator.clipboard.writeText(text).then(() => { alert('Copied to clipboard'); }).catch(() => alert('Copy failed')); }; return (

开发者工具

Webhook 配置、SDK 下载中心、交互式 API 文档

{/* Tab Navigation */}
{/* Tab Content */}
{loading &&
加载中...
} {/* ===== Webhooks Tab ===== */} {activeTab === 'webhooks' && !loading && (
{selectedWebhook ? ( /* Webhook Detail / Deliveries View */

{selectedWebhook.name} - Delivery History

{testResult && (
Test Result: {testResult.status} {testResult.response_code && | HTTP {testResult.response_code}} {testResult.response_time_ms != null && | {testResult.response_time_ms}ms} {testResult.error_message &&
{testResult.error_message}
}
)} {deliveries.items.map(d => ( ))} {deliveries.items.length === 0 && }
事件状态响应码响应时间尝试次数时间错误
{d.event} {statusBadge(d.status)} {d.response_code || '-'} {d.response_time_ms != null ? `${d.response_time_ms}ms` : '-'} {d.attempts} {fmtTime(d.created_at)} {d.error_message || '-'}
No delivery records
{deliveries.total > 20 && (
Page {deliveries.page} / {Math.ceil(deliveries.total / 20)}
)}
) : ( /* Webhook List View */
{webhooks.items.map(w => ( ))} {webhooks.items.length === 0 && }
名称地址事件状态投递数成功率最后触发操作
{w.name} {w.url} {w.events.length > 0 ? ( {w.events.length} events ) : All events} {statusBadge(w.status)} {w.total_deliveries} = 80 ? '#52c41a' : w.success_rate >= 50 ? '#faad14' : '#ff4d4f' }}> {w.success_rate}% ({w.success_count}/{w.total_deliveries}) {fmtTime(w.last_triggered_at)}
No webhooks configured
{webhooks.total > 20 && (
Page {webhooks.page} / {Math.ceil(webhooks.total / 20)}
)} {/* 支持的事件 Info */} {webhookEvents.length > 0 && (

支持的事件

{webhookEvents.map(ev => (
{ev.value}
{ev.desc}
))}
)}
)} {/* Create Webhook Modal */} {showWebhookModal && (
setShowWebhookModal(false)}>
e.stopPropagation()}>

新建 Webhook

setWebhookForm({ ...webhookForm, name: e.target.value })} placeholder="My Webhook" />
setWebhookForm({ ...webhookForm, url: e.target.value })} placeholder="https://example.com/webhook" />
{webhookEvents.map(ev => ( ))}
)}
)} {/* ===== SDK Download Center Tab ===== */} {activeTab === 'sdk' && !loading && (
{/* Language Selector */}
{sdkPackages.map(pkg => ( ))}
{sdkPackages.filter(p => p.language === selectedSdk).map(pkg => (
{/* Package Info */}

{pkg.name} v{pkg.version}

{pkg.description}

Requires: {pkg.min_version}

Download {pkg.github_url && GitHub}
{/* Install Command */}
{pkg.install_cmd}
{/* 功能特性 */}
{pkg.features.map(f => ( {f} ))}
{/* Code Example */}
                      {pkg.code_example}
                    
))} {sdkPackages.length === 0 &&
正在加载 SDK 包...
}
)} {/* ===== Interactive API Docs Tab ===== */} {activeTab === 'docs' && !loading && (
Base URL: http://210.16.161.218:18080 API Version: v1
{docSections.map(section => { const isExpanded = expandedSection === section.section; return (
{/* Section Header */}
setExpandedSection(isExpanded ? null : section.section)} style={{ padding: '14px 16px', background: 'rgba(0,0,0,0.04)', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }} >
{section.icon === 'lock' ? '🔒' : section.icon === 'chat' ? '💬' : section.icon === 'book' ? '📚' : section.icon === 'robot' ? '🤖' : section.icon === 'workflow' ? '🔄' : section.icon === 'storage' ? '💾' : section.icon === 'webhook' ? '🔗' : '📋'} {section.section} {section.endpoints.length} endpoints
{isExpanded ? '▼' : '▶'}
{/* Section Content */} {isExpanded && (

{section.description}

{section.endpoints.map(ep => { const epKey = `${section.section}-${ep.path}`; const isEpExpanded = expandedEndpoint === epKey; return (
setExpandedEndpoint(isEpExpanded ? null : epKey)} style={{ padding: '10px 16px', cursor: 'pointer', display: 'flex', alignItems: 'center', gap: '12px' }} > {methodBadge(ep.method)} {ep.path} {ep.title} {isEpExpanded ? '▼' : '▶'}
{isEpExpanded && (

{ep.description}

{/* 参数 */} {ep.params.length > 0 && (
{ep.params.map(p => ( ))}
名称类型必填描述
{p.name} {p.type} {p.required ? 是 : 否} {p.desc}
)} {/* 请求示例 */} {ep.example_request && (
                                      {ep.example_request}
                                    
)} {/* 响应示例 */} {ep.example_response && (
                                      {ep.example_response}
                                    
)}
)}
); })}
)}
); })} {docSections.length === 0 &&
正在加载 API 文档...
}
)}
); }