import { useState } from 'react'; import { Button } from './ui/Button'; import { Input } from './ui/Input'; import { Switch } from './ui/Switch'; import { X, Plus, ExternalLink, Shield, AlertTriangle, Info, Download, Trash2 } from 'lucide-react'; import type { AccessList, AccessListRule } from '../api/accessLists'; import { SECURITY_PRESETS, calculateTotalIPs, formatIPCount, type SecurityPreset } from '../data/securityPresets'; import { getMyIP } from '../api/system'; import toast from 'react-hot-toast'; interface AccessListFormProps { initialData?: AccessList; onSubmit: (data: AccessListFormData) => void; onCancel: () => void; onDelete?: () => void; isLoading?: boolean; isDeleting?: boolean; } export interface AccessListFormData { name: string; description: string; type: 'whitelist' | 'blacklist' | 'geo_whitelist' | 'geo_blacklist'; ip_rules: string; country_codes: string; local_network_only: boolean; enabled: boolean; } const COUNTRIES = [ { code: 'US', name: 'United States' }, { code: 'CA', name: 'Canada' }, { code: 'GB', name: 'United Kingdom' }, { code: 'DE', name: 'Germany' }, { code: 'FR', name: 'France' }, { code: 'IT', name: 'Italy' }, { code: 'ES', name: 'Spain' }, { code: 'NL', name: 'Netherlands' }, { code: 'BE', name: 'Belgium' }, { code: 'SE', name: 'Sweden' }, { code: 'NO', name: 'Norway' }, { code: 'DK', name: 'Denmark' }, { code: 'FI', name: 'Finland' }, { code: 'PL', name: 'Poland' }, { code: 'CZ', name: 'Czech Republic' }, { code: 'AT', name: 'Austria' }, { code: 'CH', name: 'Switzerland' }, { code: 'AU', name: 'Australia' }, { code: 'NZ', name: 'New Zealand' }, { code: 'JP', name: 'Japan' }, { code: 'CN', name: 'China' }, { code: 'IN', name: 'India' }, { code: 'BR', name: 'Brazil' }, { code: 'MX', name: 'Mexico' }, { code: 'AR', name: 'Argentina' }, { code: 'RU', name: 'Russia' }, { code: 'UA', name: 'Ukraine' }, { code: 'TR', name: 'Turkey' }, { code: 'IL', name: 'Israel' }, { code: 'SA', name: 'Saudi Arabia' }, { code: 'AE', name: 'United Arab Emirates' }, { code: 'EG', name: 'Egypt' }, { code: 'ZA', name: 'South Africa' }, { code: 'KR', name: 'South Korea' }, { code: 'SG', name: 'Singapore' }, { code: 'MY', name: 'Malaysia' }, { code: 'TH', name: 'Thailand' }, { code: 'ID', name: 'Indonesia' }, { code: 'PH', name: 'Philippines' }, { code: 'VN', name: 'Vietnam' }, ]; export function AccessListForm({ initialData, onSubmit, onCancel, onDelete, isLoading, isDeleting }: AccessListFormProps) { const [formData, setFormData] = useState({ name: initialData?.name || '', description: initialData?.description || '', type: initialData?.type || 'whitelist', ip_rules: initialData?.ip_rules || '', country_codes: initialData?.country_codes || '', local_network_only: initialData?.local_network_only || false, enabled: initialData?.enabled ?? true, }); const [ipRules, setIPRules] = useState(() => { if (initialData?.ip_rules) { try { return JSON.parse(initialData.ip_rules); } catch { return []; } } return []; }); const [selectedCountries, setSelectedCountries] = useState(() => { if (initialData?.country_codes) { return initialData.country_codes.split(',').map((c) => c.trim()); } return []; }); const [newIP, setNewIP] = useState(''); const [newIPDescription, setNewIPDescription] = useState(''); const [showPresets, setShowPresets] = useState(false); const [loadingMyIP, setLoadingMyIP] = useState(false); const isGeoType = formData.type.startsWith('geo_'); const isIPType = !isGeoType; // Calculate total IPs in current rules const totalIPs = isIPType && !formData.local_network_only ? calculateTotalIPs(ipRules.map(r => r.cidr)) : 0; const handleAddIP = () => { if (!newIP.trim()) return; const newRule: AccessListRule = { cidr: newIP.trim(), description: newIPDescription.trim(), }; const updatedRules = [...ipRules, newRule]; setIPRules(updatedRules); setNewIP(''); setNewIPDescription(''); }; const handleRemoveIP = (index: number) => { setIPRules(ipRules.filter((_, i) => i !== index)); }; const handleAddCountry = (countryCode: string) => { if (!selectedCountries.includes(countryCode)) { setSelectedCountries([...selectedCountries, countryCode]); } }; const handleRemoveCountry = (countryCode: string) => { setSelectedCountries(selectedCountries.filter((c) => c !== countryCode)); }; const handleApplyPreset = (preset: SecurityPreset) => { if (preset.type === 'geo_blacklist' && preset.countryCodes) { setFormData({ ...formData, type: 'geo_blacklist' }); setSelectedCountries([...new Set([...selectedCountries, ...preset.countryCodes])]); toast.success(`Applied preset: ${preset.name}`); } else if (preset.type === 'blacklist' && preset.ipRanges) { setFormData({ ...formData, type: 'blacklist' }); const newRules = preset.ipRanges.filter( (newRule) => !ipRules.some((existing) => existing.cidr === newRule.cidr) ); setIPRules([...ipRules, ...newRules]); toast.success(`Applied preset: ${preset.name} (${newRules.length} rules added)`); } setShowPresets(false); }; const handleGetMyIP = async () => { setLoadingMyIP(true); try { const result = await getMyIP(); setNewIP(result.ip); toast.success(`Your IP: ${result.ip} (from ${result.source})`); } catch { toast.error('Failed to fetch your IP address'); } finally { setLoadingMyIP(false); } }; const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); const data: AccessListFormData = { ...formData, ip_rules: isIPType && !formData.local_network_only ? JSON.stringify(ipRules) : '', country_codes: isGeoType ? selectedCountries.join(',') : '', }; onSubmit(data); }; return (
{/* Basic Info */}
setFormData({ ...formData, name: e.target.value })} placeholder="My Access List" required />