From 1e6f5badec7b14f683af8a7a781098449e0e8b3c Mon Sep 17 00:00:00 2001 From: shats Date: Thu, 22 Jan 2026 10:59:50 +0700 Subject: [PATCH] feat(NetworkConfigManager): enhance gateway configuration with new fields for country and type; add recursive gateway support and improve filtering options --- example/s3/network-config.json | 30 +++++- frontend/src/NetworkConfigManager.jsx | 142 +++++++++++++++++++++++--- 2 files changed, 151 insertions(+), 21 deletions(-) diff --git a/example/s3/network-config.json b/example/s3/network-config.json index 13c56c3..a33c6e5 100644 --- a/example/s3/network-config.json +++ b/example/s3/network-config.json @@ -5,35 +5,55 @@ "name": "SWE-IHOR", "ip": "94.142.140.1", "description": "Cloudflare Sweden - основной выход", - "serverId": "SWE-HIPHOST" + "serverId": "SWE-HIPHOST", + "country": "SWE", + "type": "direct" }, { "id": "gw-bunny-swe", "name": "SWE-BUNNY", "ip": "94.142.140.2", "description": "Bunny CDN Sweden", - "serverId": "SWE-HIPHOST" + "serverId": "SWE-HIPHOST", + "country": "SWE", + "type": "direct" }, { "id": "gw-telegram-nl", "name": "TG-PROXY-1", "ip": "149.154.167.50", "description": "Telegram DC2", - "serverId": "NL-AMSTERDAM" + "serverId": "NL-AMSTERDAM", + "country": "NL", + "type": "direct" }, { "id": "gw-hetzner-de", "name": "DE-HETZNER", "ip": "195.100.30.21", "description": "Hetzner Frankfurt", - "serverId": "DE-FRANKFURT" + "serverId": "DE-FRANKFURT", + "country": "DE", + "type": "direct" }, { "id": "gw-fastly-us", "name": "US-FASTLY", "ip": "151.101.1.1", "description": "Fastly US East", - "serverId": "US-NEWYORK" + "serverId": "US-NEWYORK", + "country": "US", + "type": "direct" + }, + { + "id": "gw-recursive-swe", + "name": "SWE-RECURSIVE", + "ip": "10.0.0.1", + "description": "Рекурсивный gateway через SWE-IHOR", + "serverId": "SWE-HIPHOST", + "country": "SWE", + "type": "recursive", + "parentGatewayId": "gw-cloudflare-swe" } ], "tunnelInterfaces": [ diff --git a/frontend/src/NetworkConfigManager.jsx b/frontend/src/NetworkConfigManager.jsx index af43002..40afd81 100644 --- a/frontend/src/NetworkConfigManager.jsx +++ b/frontend/src/NetworkConfigManager.jsx @@ -50,6 +50,12 @@ const INTERFACE_TYPES = [ { value: 'OpenVPN', label: 'OpenVPN', color: 'orange' }, ]; +// Типы gateway +const GATEWAY_TYPES = [ + { value: 'direct', label: 'Прямой', description: 'Прямой доступ в интернет', color: 'green' }, + { value: 'recursive', label: 'Рекурсивный', description: 'Ссылается на другой gateway', color: 'blue' }, +]; + // Пустые объекты const getEmptyGateway = () => ({ id: `gw-${Date.now()}-${Math.random().toString(16).slice(2, 6)}`, @@ -57,6 +63,9 @@ const getEmptyGateway = () => ({ ip: '', description: '', serverId: '', + country: '', + type: 'direct', + parentGatewayId: '', // Для рекурсивных - ID родительского gateway }); const getEmptyInterface = () => ({ @@ -97,6 +106,7 @@ function NetworkConfigManager() { const [providerFilter, setProviderFilter] = useState(''); const [serverFilter, setServerFilter] = useState(''); const [typeFilter, setTypeFilter] = useState(''); + const [gatewayTypeFilter, setGatewayTypeFilter] = useState(''); // Для фильтрации gateway по типу const [viewMode, setViewMode] = useState('cards'); // 'cards' | 'table' // === Modals === @@ -207,6 +217,10 @@ function NetworkConfigManager() { result = result.filter(g => g.serverId === serverFilter); } + if (gatewayTypeFilter) { + result = result.filter(g => g.type === gatewayTypeFilter); + } + if (searchTerm) { const term = searchTerm.toLowerCase(); result = result.filter(g => { @@ -214,6 +228,7 @@ function NetworkConfigManager() { return ( g.name?.toLowerCase().includes(term) || g.ip?.toLowerCase().includes(term) || + g.country?.toLowerCase().includes(term) || server?.country?.toLowerCase().includes(term) || server?.provider?.toLowerCase().includes(term) || g.description?.toLowerCase().includes(term) || @@ -223,7 +238,7 @@ function NetworkConfigManager() { } return result; - }, [config.gateways, providerFilter, serverFilter, searchTerm, servers]); + }, [config.gateways, providerFilter, serverFilter, typeFilter, searchTerm, servers]); const filteredInterfaces = useMemo(() => { let result = [...(config.tunnelInterfaces || [])]; @@ -292,9 +307,10 @@ function NetworkConfigManager() { setProviderFilter(''); setServerFilter(''); setTypeFilter(''); + setGatewayTypeFilter(''); }; - const hasActiveFilters = searchTerm || providerFilter || serverFilter || typeFilter; + const hasActiveFilters = searchTerm || providerFilter || serverFilter || typeFilter || gatewayTypeFilter; // === CRUD для Gateways === const handleAddGateway = () => { @@ -517,22 +533,39 @@ function NetworkConfigManager() { ); }; + // === Получение родительского gateway === + const getParentGateway = (gatewayId) => { + if (!gatewayId) return null; + return config.gateways?.find(g => g.id === gatewayId); + }; + // === Рендер карточки Gateway === const renderGatewayCard = (gateway) => { const server = getServerInfo(gateway.serverId); const provider = server?.provider || ''; - const country = server?.country || ''; + const serverCountry = server?.country || ''; + const gatewayCountry = gateway.country || ''; + const displayCountry = gatewayCountry || serverCountry; + const gatewayType = GATEWAY_TYPES.find(t => t.value === gateway.type) || GATEWAY_TYPES[0]; + const parentGateway = gateway.type === 'recursive' && gateway.parentGatewayId + ? getParentGateway(gateway.parentGatewayId) + : null; return (
- - + +
-
{gateway.name || '—'}
+
+ {gateway.name || '—'} + + {gatewayType.label} + +
{provider || '—'}
@@ -567,10 +600,20 @@ function NetworkConfigManager() { )}
- {country && ( -
- {countryToFlag(country)} - {country} + {displayCountry && ( +
+ {countryToFlag(displayCountry)} + {displayCountry} + {gatewayCountry && serverCountry && gatewayCountry !== serverCountry && ( + (сервер: {serverCountry}) + )} +
+ )} + + {parentGateway && ( +
+ + Родитель: {parentGateway.name}
)} @@ -906,6 +949,7 @@ function NetworkConfigManager() { Имя + Тип IP адрес Провайдер Сервер @@ -917,18 +961,34 @@ function NetworkConfigManager() { {filteredGateways.map(gateway => { const server = getServerInfo(gateway.serverId); const provider = server?.provider || ''; - const country = server?.country || ''; + const serverCountry = server?.country || ''; + const gatewayCountry = gateway.country || ''; + const displayCountry = gatewayCountry || serverCountry; + const gatewayType = GATEWAY_TYPES.find(t => t.value === gateway.type) || GATEWAY_TYPES[0]; + const parentGateway = gateway.type === 'recursive' && gateway.parentGatewayId + ? getParentGateway(gateway.parentGatewayId) + : null; return (
- - + + {gateway.name || '—'}
+ + + {gatewayType.label} + + {parentGateway && ( +
+ → {parentGateway.name} +
+ )} + {gateway.ip || '—'} @@ -963,8 +1023,15 @@ function NetworkConfigManager() { )} - {country ? ( - {countryToFlag(country)} {country} + {displayCountry ? ( +
+ {countryToFlag(displayCountry)} {displayCountry} + {gatewayCountry && serverCountry && gatewayCountry !== serverCountry && ( +
+ сервер: {serverCountry} +
+ )} +
) : '—'} @@ -1215,7 +1282,7 @@ function NetworkConfigManager() { placeholder="Выберите сервер..." />
- Страна и провайдер будут автоматически взяты из данных сервера + Провайдер будет автоматически взят из данных сервера
@@ -1237,6 +1304,49 @@ function NetworkConfigManager() { placeholder="94.142.140.1" />
+
+ setEditingGateway({ ...editingGateway, type: val, parentGatewayId: val === 'direct' ? '' : editingGateway.parentGatewayId })} + options={GATEWAY_TYPES.map(t => ({ value: t.value, label: `${t.label} - ${t.description}` }))} + required + /> +
+
+ setEditingGateway({ ...editingGateway, country: val.toUpperCase().slice(0, 2) })} + placeholder="SWE" + /> +
2-буквенный код страны (ISO 3166-1 alpha-2)
+
+ {editingGateway.type === 'recursive' && ( +
+ + +
+ Рекурсивный gateway ссылается на другой gateway (обычно прямой) +
+
+ )}