From 011740df1f9df6271e67cd56b133ef2f94300f6f Mon Sep 17 00:00:00 2001 From: shats Date: Thu, 22 Jan 2026 18:43:59 +0700 Subject: [PATCH] feat(NetworkConfigManager): add interface template creation feature; implement modal for generating interfaces from server templates with conflict checking and preview functionality --- frontend/src/NetworkConfigManager.jsx | 222 +++++++++++++++++++++++++- 1 file changed, 218 insertions(+), 4 deletions(-) diff --git a/frontend/src/NetworkConfigManager.jsx b/frontend/src/NetworkConfigManager.jsx index b26b382..3619f0a 100644 --- a/frontend/src/NetworkConfigManager.jsx +++ b/frontend/src/NetworkConfigManager.jsx @@ -20,6 +20,7 @@ import { IconSearch, IconRefresh, IconWand, + IconSparkles, IconFilter, IconX, IconCircleFilled, @@ -131,6 +132,13 @@ function NetworkConfigManager() { const [itemToDelete, setItemToDelete] = useState(null); const [deleteType, setDeleteType] = useState(''); + // === Interface Template Modal === + const [interfaceTemplateModalOpen, setInterfaceTemplateModalOpen] = useState(false); + const [templateServer1, setTemplateServer1] = useState(''); + const [templateServer2, setTemplateServer2] = useState(''); + const [templateType, setTemplateType] = useState('GRE'); + const [templateNamePrefix, setTemplateNamePrefix] = useState(''); + // === MikroTik Code Generation === const [mikrotikCodeModalOpen, setMikrotikCodeModalOpen] = useState(false); const [generatedMikrotikCode, setGeneratedMikrotikCode] = useState([]); @@ -432,6 +440,86 @@ function NetworkConfigManager() { setDeleteModalOpen(true); }; + // === Генерация имени интерфейса на основе серверов === + const generateInterfaceName = (server1, server2, type, prefix = '') => { + const server1Name = server1?.dns?.split('.')[0] || server1?.ip?.split('.').join('-') || 'server1'; + const server2Name = server2?.dns?.split('.')[0] || server2?.ip?.split('.').join('-') || 'server2'; + const typeLower = type.toLowerCase(); + const prefixPart = prefix ? `${prefix}-` : ''; + return `${prefixPart}${typeLower}-${server1Name}-${server2Name}`; + }; + + // === Создание интерфейса из шаблона === + const handleCreateInterfaceFromTemplate = () => { + if (!templateServer1 || !templateServer2) { + notify.error('Выберите оба сервера'); + return; + } + + if (templateServer1 === templateServer2) { + notify.error('Серверы должны быть разными'); + return; + } + + const server1 = getServerInfo(templateServer1); + const server2 = getServerInfo(templateServer2); + + if (!server1 || !server2) { + notify.error('Не удалось найти информацию о серверах'); + return; + } + + // Генерируем имена интерфейсов + const name1 = generateInterfaceName(server1, server2, templateType, templateNamePrefix); + const name2 = generateInterfaceName(server2, server1, templateType, templateNamePrefix); + + // Генерируем IP адреса + const localIp = generateFreePrivateIp(null, false, null); + if (!localIp) { + notify.error('Не удалось найти свободный Local IP адрес'); + return; + } + + const remoteIp = generateFreePrivateIp(null, true, localIp); + if (!remoteIp) { + notify.error('Не удалось найти свободный Remote IP адрес'); + return; + } + + // Проверяем конфликты + const newInterface = { + ...getEmptyInterface(), + name: name1, + name2: name2, + type: templateType, + localIp: localIp, + remoteIp: remoteIp, + serverId: templateServer1, + serverId2: templateServer2 + }; + + const conflicts = checkInterfaceIpConflict(newInterface); + if (conflicts.length > 0) { + notify.error('Обнаружены конфликты IP адресов. Попробуйте еще раз.'); + return; + } + + // Создаем интерфейс + setConfig(prev => ({ + ...prev, + tunnelInterfaces: [...prev.tunnelInterfaces, newInterface], + })); + + notify.success(`Интерфейс создан: ${name1} (${localIp} ↔ ${remoteIp})`); + + // Закрываем модал и сбрасываем значения + setInterfaceTemplateModalOpen(false); + setTemplateServer1(''); + setTemplateServer2(''); + setTemplateType('GRE'); + setTemplateNamePrefix(''); + }; + // === CRUD для Interfaces === const handleAddInterface = () => { setEditingInterface(getEmptyInterface()); @@ -1771,10 +1859,20 @@ function NetworkConfigManager() { )} {activeTab === 'interfaces' && ( - + <> + + + )} {activeTab === 'pools' && (