import { useState } from 'react'; import { createPortal } from 'react-dom'; import { X, Upload, FileText, AlertCircle, CheckCircle2 } from 'lucide-react'; import { useTranslation } from 'react-i18next'; import { ProxyEntry } from '../../../types/config'; import { generateUUID } from '../../../utils/uuid'; interface BatchImportModalProps { isOpen: boolean; onClose: () => void; onImport: (proxies: ProxyEntry[]) => void; } export default function BatchImportModal({ isOpen, onClose, onImport }: BatchImportModalProps) { const { t } = useTranslation(); const [rawText, setRawText] = useState(''); const [preview, setPreview] = useState([]); const [error, setError] = useState(null); if (!isOpen) return null; const parseProxies = (text: string) => { const lines = text.split('\n').filter(line => line.trim() !== ''); const newProxies: ProxyEntry[] = []; const urlRegex = /([a-zA-Z0-9]+:\/\/[^\s]+)/; // Basic protocol://url matcher lines.forEach((line, index) => { try { const trimmedLine = line.trim(); let url = ''; // Strategy 1: Regex search for protocol://... const match = trimmedLine.match(urlRegex); if (match) { url = match[0]; } else { // Check for host:port:user:pass or host:port // logic: split by space first to get the "proxy part" const firstWord = trimmedLine.split(/\s+/)[0]; const parts = firstWord.split(':'); if (parts.length === 4) { // host:port:user:pass format // Reconstruct to http://user:pass@host:port const [host, port, user, pass] = parts; url = `http://${user}:${pass}@${host}:${port}`; } else if (parts.length === 2) { // host:port format const [host, port] = parts; // Basic sanity check on port if (!isNaN(Number(port))) { url = `http://${host}:${port}`; } } } if (!url) { // console.warn(`Line ${index + 1} skipped: no valid proxy found`); return; } // Validation try { new URL(url); } catch (e) { console.warn(`Line ${index + 1} invalid URL: ${url}`); return; } newProxies.push({ id: generateUUID(), // Name will be assigned when adding to main list or just generic here name: `Imported Proxy`, url: url, enabled: true, priority: 1, tags: ['imported'], is_healthy: false, latency: undefined }); } catch (e) { console.error("Failed to parse line", line, e); } }); // Fix names to be unique/sequential relative to this batch newProxies.forEach((p, i) => { p.name = `Proxy ${i + 1}`; }); if (newProxies.length === 0 && lines.length > 0) { setError(t('settings.proxy_pool.no_valid_proxies', 'No valid proxies found')); } else { setError(null); setPreview(newProxies); } }; const handleTextChange = (e: React.ChangeEvent) => { const text = e.target.value; setRawText(text); parseProxies(text); }; const handleImport = () => { if (preview.length > 0) { onImport(preview); onClose(); setRawText(''); setPreview([]); } }; return createPortal(

{t('settings.proxy_pool.import_title', 'Batch Import Proxies')}

{t('settings.proxy_pool.import_hint', 'Supported formats: protocol://user:pass@host:port, host:port:user:pass')}