import React, { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { request as invoke } from '../../utils/request'; import { Trash2, Check, Plus, Search, X, ShieldCheck } from 'lucide-react'; interface IpWhitelistEntry { ip_pattern: string; description?: string; added_at: number; added_by?: string; } interface Props { refreshKey?: number; } export const WhitelistManager: React.FC = ({ refreshKey }) => { const { t } = useTranslation(); const [entries, setEntries] = useState([]); const [loading, setLoading] = useState(false); const [search, setSearch] = useState(''); // Add Modal State const [isAddOpen, setIsAddOpen] = useState(false); const [newIp, setNewIp] = useState(''); const [newDescription, setNewDescription] = useState(''); const loadWhitelist = async () => { setLoading(true); try { const data = await invoke('get_ip_whitelist'); setEntries(data); } catch (e) { console.error('Failed to load whitelist', e); } finally { setLoading(false); } }; useEffect(() => { loadWhitelist(); }, [refreshKey]); const handleAdd = async () => { try { await invoke('add_ip_to_whitelist', { request: { ipPattern: newIp, description: newDescription || null, } }); setIsAddOpen(false); setNewIp(''); setNewDescription(''); loadWhitelist(); } catch (e) { console.error('Failed to add to whitelist', e); alert('Failed to add IP: ' + e); } }; const handleRemove = async (ipPattern: string) => { // 乐观更新:立即从UI中移除 setEntries(prev => prev.filter(e => e.ip_pattern !== ipPattern)); try { await invoke('remove_ip_from_whitelist', { ipPattern: ipPattern }); } catch (e) { console.error('Failed to remove from whitelist', e); // 如果删除失败,重新加载数据恢复UI loadWhitelist(); } }; const filteredEntries = entries.filter(e => e.ip_pattern.includes(search) || (e.description && e.description.toLowerCase().includes(search.toLowerCase())) ); return (
setSearch(e.target.value)} />
{filteredEntries.map(entry => (

{entry.ip_pattern}

{entry.description && (

{entry.description}

)}
{t('security.blacklist.added_at')}: {new Date(entry.added_at * 1000).toLocaleString()}
))} {!loading && filteredEntries.length === 0 && (
{t('security.whitelist.no_data')}
)}
{/* Add Modal */} {isAddOpen && (

{t('security.whitelist.add_title')}

setNewIp(e.target.value)} />
setNewDescription(e.target.value)} />
)}
); };