// Zabbix 数据源管理 — 参照 Grafana Zabbix 数据源配置界面 // Logan.Li import { useState, useEffect, useCallback } from 'react'; import { Database, Plus, Trash2, Star, RefreshCw, Plug, Shield, Globe, Settings, ChevronDown, ChevronUp, Edit, Clock, CheckCircle2, XCircle, } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Badge } from '@/components/ui/badge'; import { Card, CardContent} from '@/components/ui/card'; import { Label } from '@/components/ui/label'; import { Switch } from '@/components/ui/switch'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from '@/components/ui/dialog'; import { toast } from 'sonner'; import { useConfirm } from '@/components/ui/confirm-dialog'; import { zabbixDatasourceApi } from '@/api'; import type { JsonData } from '@/types/common'; import i18n from '@/i18n'; interface ZabbixDS { id: number; name: string; url: string; username: string; password: string; trends_after: number; trends_range: number; trends_cache_ttl: number; trends_timeout: number; direct_db: boolean; disable_acknowledges: boolean; disable_data_alignment: boolean; is_default: boolean; status: number; created_at: string; } const defaultForm = { name: '', url: '', username: '', password: '', trends_after: 0, trends_range: 0, trends_cache_ttl: 3600, trends_timeout: 30, direct_db: false, disable_acknowledges: false, disable_data_alignment: false, is_default: false, }; export default function ZabbixDatasourceManager() { const _confirm = useConfirm(); const [sources, setSources] = useState([]); const [loading, setLoading] = useState(true); const [dialogOpen, setDialogOpen] = useState(false); const [editingId, setEditingId] = useState(null); const [form, setForm] = useState({ ...defaultForm }); const [showAuth, setShowAuth] = useState(false); const [showTrends, setShowTrends] = useState(false); const [showOther, setShowOther] = useState(false); const [testing, setTesting] = useState(false); const [testResult, setTestResult] = useState<{ success: boolean; message: string } | null>(null); const fetchList = useCallback(async () => { try { const res: JsonData = await zabbixDatasourceApi.list(); setSources(res?.data || res || []); } catch { toast.error(i18n.t('monitor.zabbixdatasourc.s45f81749')); } finally { setLoading(false); } }, []); useEffect(() => { fetchList(); }, [fetchList]); const openCreate = () => { setEditingId(null); setForm({ ...defaultForm }); setTestResult(null); setShowAuth(false); setShowTrends(false); setShowOther(false); setDialogOpen(true); }; const openEdit = (ds: ZabbixDS) => { setEditingId(ds.id); setForm({ name: ds.name, url: ds.url, username: ds.username || '', password: ds.password || '', trends_after: ds.trends_after || 0, trends_range: ds.trends_range || 0, trends_cache_ttl: ds.trends_cache_ttl || 3600, trends_timeout: ds.trends_timeout || 30, direct_db: ds.direct_db || false, disable_acknowledges: ds.disable_acknowledges || false, disable_data_alignment: ds.disable_data_alignment || false, is_default: ds.is_default || false, }); setTestResult(null); setDialogOpen(true); }; const updateForm = (field: string, value: string | number | boolean) => { setForm(prev => ({ ...prev, [field]: value })); }; const handleSave = async () => { if (!form.name.trim()) { toast.error(i18n.t('monitor.promDs.nameRequired')); return; } if (!form.url.trim()) { toast.error(i18n.t('monitor.zabbixdatasourc.s2ba385d6')); return; } if (!form.username.trim()) { toast.error(i18n.t('pipeline.usernameRequired')); return; } if (!form.password.trim()) { toast.error(i18n.t('login.passwordRequired')); return; } try { if (editingId) { await zabbixDatasourceApi.update(editingId, form); toast.success(i18n.t('monitor.zabbixdatasourc.s8dfdd2f4')); } else { await zabbixDatasourceApi.create(form); toast.success(i18n.t('monitor.zabbixdatasourc.sa6def01f')); } setDialogOpen(false); fetchList(); } catch (err) { toast.error(err?.message || i18n.t('cloud.operationFailed')); } }; const handleDelete = async (ds: ZabbixDS) => { const ok = await _confirm({ title: i18n.t('monitor.deleteDatasource'), description: `确定删除「${ds.name}」?`, variant: 'destructive', }); if (!ok) return; try { await zabbixDatasourceApi.delete(ds.id); toast.success(i18n.t('monitor.silenceRules.deleted')); fetchList(); } catch { toast.error(i18n.t('kubernetes.msg.deleteFailed2')); } }; const handleSetDefault = async (ds: ZabbixDS) => { try { await zabbixDatasourceApi.setDefault(ds.id); toast.success(`已将「${ds.name}」设为默认`); fetchList(); } catch { toast.error(i18n.t('monitor.zabbixdatasourc.s9f96034c')); } }; const handleTest = async () => { if (!form.url.trim() || !form.username.trim()) { toast.error(i18n.t('monitor.zabbixdatasourc.s1d3562b7')); return; } setTesting(true); setTestResult(null); try { const res: JsonData = await zabbixDatasourceApi.testConnection({ url: form.url.trim(), username: form.username.trim(), password: form.password || '', }); const data = res?.data || res; setTestResult(data); } catch (err) { setTestResult({ success: false, message: err?.message || i18n.t('monitor.alertChannels.testFailed') }); } finally { setTesting(false); } }; if (loading) { return (
); } return (

{ (e.target as HTMLImageElement).style.display = 'none'; }} /> Zabbix 数据源

{i18n.t('monitor.zabbixdatasourc.s79e929d8')}

{sources.length === 0 ? (

{i18n.t('monitor.zabbixdatasourc.sc9c9bd18')}

) : (
{sources.map(ds => (
{ds.name} {ds.is_default && {i18n.t('k8s.storage.default')} } {ds.status === 1 ? {i18n.t('system.userActive')} : {i18n.t('system.userInactive')} }
{ds.url} 用户: {ds.username} {ds.direct_db && {i18n.t('monitor.zabbixdatasourc.s85260abd')} }
{!ds.is_default && ( )}
))}
)} {/* 编辑 Dialog — 参照 Grafana Zabbix 数据源配置 */} {editingId ? i18n.t('monitor.zabbixdatasourc.sc1c87ed6') : i18n.t('monitor.zabbixdatasourc.sa77d384f')} 配置 Zabbix API 连接,用于主机监控与告警数据获取
{/* Name + Default */}
updateForm('name', e.target.value)} />
updateForm('is_default', v)} />
{/* HTTP + Auth */}
HTTP
updateForm('url', e.target.value)} />
{/* API Details — 折叠 */}
{showAuth && (
updateForm('username', e.target.value)} /> updateForm('password', e.target.value)} />
)}
{/* Trends — 折叠 */}
{showTrends && (
updateForm('trends_after', parseInt(e.target.value) || 0)} />
updateForm('trends_range', parseInt(e.target.value) || 0)} />
updateForm('trends_cache_ttl', parseInt(e.target.value) || 3600)} />
updateForm('trends_timeout', parseInt(e.target.value) || 30)} />
)}
{/* 其他设置 — 折叠 */}
{showOther && (
updateForm('direct_db', v)} />
updateForm('disable_acknowledges', v)} />
updateForm('disable_data_alignment', v)} />
)}
{/* 测试结果 */} {testResult && (
{testResult.success ? : } {testResult.message}
)}
); }