import { useState } from 'react' import { Pencil, Play, Plus, Shield, Trash2, X } from 'lucide-react' import clsx from 'clsx' import { useI18n } from '../../i18n' async function readApiResponse(res, nonJsonMessage) { const contentType = String(res.headers.get('content-type') || '').toLowerCase() const raw = await res.text() const trimmed = raw.trim() if (!trimmed) { return {} } if (contentType.includes('application/json')) { try { return JSON.parse(trimmed) } catch (_err) { if (!res.ok) { return { detail: trimmed } } throw new Error(nonJsonMessage) } } if (!res.ok) { return { detail: trimmed } } throw new Error(nonJsonMessage) } const EMPTY_FORM = { name: '', type: 'socks5h', host: '', port: 1080, username: '', password: '', } function createEmptyProxyForm() { return { ...EMPTY_FORM } } function ProxyStatusBadge({ t, result, testing = false }) { if (testing) { return ( {t('proxyManager.testing')} ) } if (!result) { return ( {t('proxyManager.untested')} ) } return ( {result.success ? t('proxyManager.testSuccessShort', { time: result.response_time ?? 0 }) : t('proxyManager.testFailedShort')} ) } function ProxiesTable({ t, proxies, testing, testResults, onCreate, onTest, onEdit, onDelete, }) { return (

{t('proxyManager.title')}

{t('proxyManager.desc')}

{proxies.length === 0 ? (
{t('proxyManager.noProxies')}
) : (
{proxies.map((proxy) => { const result = testResults[proxy.id] return (
{proxy.name || `${proxy.host}:${proxy.port}`}
{proxy.type} {proxy.username && ( {proxy.username} )}
{proxy.host}:{proxy.port} {proxy.has_password && ( {t('proxyManager.authEnabled')} )} {result?.message && ( {result.message} )}
) })}
)}
) } function ProxyFormModal({ show, t, form, setForm, editingProxy, loading, onClose, onSubmit, }) { if (!show) { return null } const isEditing = Boolean(editingProxy?.id) return (

{isEditing ? t('proxyManager.modalEditTitle') : t('proxyManager.modalAddTitle')}

{t('proxyManager.modalDesc')}

setForm({ ...form, name: e.target.value })} />
setForm({ ...form, host: e.target.value })} />
setForm({ ...form, port: Number(e.target.value) || '' })} />
setForm({ ...form, username: e.target.value })} />
setForm({ ...form, password: e.target.value })} /> {isEditing && (

{t('proxyManager.passwordKeepHint')}

)}
{t('proxyManager.typeHelp')}
) } export default function ProxyManagerContainer({ config, onRefresh, onMessage, authFetch }) { const { t } = useI18n() const apiFetch = authFetch || fetch const [showModal, setShowModal] = useState(false) const [editingProxy, setEditingProxy] = useState(null) const [form, setForm] = useState(createEmptyProxyForm()) const [saving, setSaving] = useState(false) const [testing, setTesting] = useState({}) const [testResults, setTestResults] = useState({}) const proxies = config?.proxies || [] const openCreate = () => { setEditingProxy(null) setForm(createEmptyProxyForm()) setShowModal(true) } const openEdit = (proxy) => { setEditingProxy(proxy) setForm({ name: proxy.name || '', type: proxy.type || 'socks5h', host: proxy.host || '', port: proxy.port || 1080, username: proxy.username || '', password: '', }) setShowModal(true) } const closeModal = () => { setShowModal(false) setEditingProxy(null) setForm(createEmptyProxyForm()) } const saveProxy = async () => { if (!form.host || !form.port) { onMessage('error', t('proxyManager.requiredFields')) return } setSaving(true) try { const url = editingProxy?.id ? `/admin/proxies/${encodeURIComponent(editingProxy.id)}` : '/admin/proxies' const method = editingProxy?.id ? 'PUT' : 'POST' const res = await apiFetch(url, { method, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: form.name, type: form.type, host: form.host, port: Number(form.port), username: form.username, password: form.password, }), }) const data = await readApiResponse(res, t('settings.nonJsonResponse', { status: res.status })) if (!res.ok) { onMessage('error', data.detail || t('messages.requestFailed')) return } await onRefresh?.() onMessage('success', editingProxy?.id ? t('proxyManager.updateSuccess') : t('proxyManager.addSuccess')) closeModal() } catch (err) { onMessage('error', err?.message || t('messages.networkError')) } finally { setSaving(false) } } const deleteProxy = async (proxy) => { if (!confirm(t('proxyManager.deleteConfirm', { name: proxy.name || `${proxy.host}:${proxy.port}` }))) return try { const res = await apiFetch(`/admin/proxies/${encodeURIComponent(proxy.id)}`, { method: 'DELETE' }) const data = await readApiResponse(res, t('settings.nonJsonResponse', { status: res.status })) if (!res.ok) { onMessage('error', data.detail || t('messages.deleteFailed')) return } await onRefresh?.() onMessage('success', t('messages.deleted')) setTestResults(prev => { const next = { ...prev } delete next[proxy.id] return next }) } catch (err) { onMessage('error', err?.message || t('messages.networkError')) } } const testProxy = async (proxy) => { setTesting(prev => ({ ...prev, [proxy.id]: true })) try { const res = await apiFetch('/admin/proxies/test', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ proxy_id: proxy.id }), }) const data = await readApiResponse(res, t('settings.nonJsonResponse', { status: res.status })) setTestResults(prev => ({ ...prev, [proxy.id]: data })) onMessage(data.success ? 'success' : 'error', data.message || t('messages.requestFailed')) } catch (err) { onMessage('error', err?.message || t('messages.networkError')) } finally { setTesting(prev => ({ ...prev, [proxy.id]: false })) } } return (
{t('proxyManager.totalProxies')}
{proxies.length}
{t('proxyManager.socks5hCount')}
{proxies.filter(proxy => proxy.type === 'socks5h').length}
{t('proxyManager.authProxyCount')}
{proxies.filter(proxy => proxy.username || proxy.has_password).length}
) }