From b0ccbc5c545986391641b01304ca6fb9b93a8d19 Mon Sep 17 00:00:00 2001 From: shats Date: Thu, 22 Jan 2026 13:28:47 +0700 Subject: [PATCH] refactor(NetworkConfigManager): update MikroTik code generation to return structured blocks for interface addresses and recursive routes; enhance modal display for generated code --- frontend/src/NetworkConfigManager.jsx | 210 +++++++++++++++++--------- 1 file changed, 141 insertions(+), 69 deletions(-) diff --git a/frontend/src/NetworkConfigManager.jsx b/frontend/src/NetworkConfigManager.jsx index 7e75e0c..2827f28 100644 --- a/frontend/src/NetworkConfigManager.jsx +++ b/frontend/src/NetworkConfigManager.jsx @@ -131,7 +131,7 @@ function NetworkConfigManager() { // === MikroTik Code Generation === const [mikrotikCodeModalOpen, setMikrotikCodeModalOpen] = useState(false); - const [generatedMikrotikCode, setGeneratedMikrotikCode] = useState(''); + const [generatedMikrotikCode, setGeneratedMikrotikCode] = useState([]); // === Загрузка данных === useEffect(() => { @@ -509,13 +509,8 @@ function NetworkConfigManager() { ); if (interfacesWithBothServers.length === 0) { - return ''; + return []; } - - let code = '\n# ====================================================================\n'; - code += '# === НАСТРОЙКА IP АДРЕСОВ ИНТЕРФЕЙСОВ НА ОБОИХ СЕРВЕРАХ ===\n'; - code += '# Эти команды нужно выполнить на соответствующих серверах\n'; - code += '# ====================================================================\n\n'; // Группируем по серверам const interfacesByServer = {}; @@ -530,7 +525,7 @@ function NetworkConfigManager() { // Имя интерфейса для первого сервера const interfaceName1 = iface.name || `${iface.type}-tunnel`; - // Имя интерфейса для второго сервера (может отличаться) + // Имя интерфейса для второго сервера - ВСЕГДА используем name2 если указано, иначе name const interfaceName2 = iface.name2 || iface.name || `${iface.type}-tunnel`; // Для первого сервера - используем localIp как local, remoteIp как remote @@ -550,6 +545,7 @@ function NetworkConfigManager() { }); // Для второго сервера - МЕНЯЕМ МЕСТАМИ: remoteIp становится localIp, localIp становится remoteIp + // И ВСЕГДА используем name2 (или name если name2 не указано) if (!interfacesByServer[server2Name]) { interfacesByServer[server2Name] = { serverInfo: server2, @@ -557,7 +553,7 @@ function NetworkConfigManager() { }; } interfacesByServer[server2Name].interfaces.push({ - interfaceName: interfaceName2, + interfaceName: interfaceName2, // Используем name2 для второго сервера localIp: iface.remoteIp, // На втором сервере remoteIp первого становится localIp remoteIp: iface.localIp, // На втором сервере localIp первого становится remoteIp type: iface.type, @@ -569,13 +565,11 @@ function NetworkConfigManager() { // Сортируем серверы по имени для консистентности const sortedServers = Object.entries(interfacesByServer).sort(([a], [b]) => a.localeCompare(b)); - sortedServers.forEach(([serverName, serverData], index) => { + const blocks = []; + sortedServers.forEach(([serverName, serverData]) => { const server = serverData.serverInfo; - // Отдельный блок для каждого сервера - code += '\n'; - code += '# ' + '='.repeat(70) + '\n'; - code += `# СЕРВЕР ${index + 1}: ${serverName}\n`; + let code = `# Настройка IP адресов интерфейсов для сервера: ${serverName}\n`; if (server.provider) { code += `# Провайдер: ${server.provider}\n`; } @@ -585,7 +579,6 @@ function NetworkConfigManager() { if (server.ip && server.ip !== serverName) { code += `# IP: ${server.ip}\n`; } - code += '# ' + '='.repeat(70) + '\n'; code += '\n'; serverData.interfaces.forEach((iface, ifaceIndex) => { @@ -599,58 +592,64 @@ function NetworkConfigManager() { code += '# Проверка настроенных адресов на этом сервере:\n'; code += '# /ip address print\n'; - code += '\n'; + + blocks.push({ + type: 'interface-addresses', + serverName, + server, + code + }); }); - code += '# ' + '='.repeat(70) + '\n'; - code += '# КОНЕЦ КОНФИГУРАЦИИ IP АДРЕСОВ ИНТЕРФЕЙСОВ\n'; - code += '# ' + '='.repeat(70) + '\n'; - - return code; + return blocks; }; // === Генерация кода MikroTik для рекурсивных маршрутов === const generateMikrotikRecursiveRoutes = () => { const recursiveGateways = (config.gateways || []).filter(gw => gw.type === 'recursive'); - if (recursiveGateways.length === 0) { - return '// Нет рекурсивных gateway для генерации маршрутов\n// Добавьте рекурсивные gateway в настройках'; - } + const blocks = []; - let code = '// Конфигурация MikroTik RouterOS для рекурсивных маршрутов\n'; - code += '// Генерируется автоматически\n'; - code += `// Дата: ${new Date().toISOString()}\n\n`; - code += '# Удаление существующих рекурсивных маршрутов (опционально)\n'; - code += '/ip route remove [find comment~"Recursive"]\n\n'; - - // Группируем по серверам для удобства - const gatewaysByServer = {}; - recursiveGateways.forEach(gw => { - const serverId = gw.serverId || '__unassigned__'; - if (!gatewaysByServer[serverId]) { - gatewaysByServer[serverId] = []; - } - gatewaysByServer[serverId].push(gw); - }); + // Если есть рекурсивные gateway, генерируем блоки для них + if (recursiveGateways.length > 0) { + // Группируем по серверам для удобства + const gatewaysByServer = {}; + recursiveGateways.forEach(gw => { + const serverId = gw.serverId || '__unassigned__'; + if (!gatewaysByServer[serverId]) { + gatewaysByServer[serverId] = []; + } + gatewaysByServer[serverId].push(gw); + }); - let validRoutesCount = 0; - - Object.entries(gatewaysByServer).forEach(([serverId, gateways]) => { + // Генерируем блоки для рекурсивных маршрутов по серверам + Object.entries(gatewaysByServer).forEach(([serverId, gateways]) => { const server = getServerInfo(serverId); const serverName = server?.dns || server?.ip || serverId; - code += `\n# === Рекурсивные маршруты для сервера: ${serverName} ===\n`; + let code = `# Рекурсивные маршруты для сервера: ${serverName}\n`; + if (server?.provider) { + code += `# Провайдер: ${server.provider}\n`; + } + if (server?.country) { + code += `# Страна: ${server.country}\n`; + } + code += '\n'; + code += '# Удаление существующих рекурсивных маршрутов (опционально)\n'; + code += '/ip route remove [find comment~"Recursive"]\n\n'; + + let validRoutesCount = 0; gateways.forEach(gw => { const parent = getParentGateway(gw.parentGatewayId); if (!parent) { - code += `\n# Gateway "${gw.ip || 'без IP'}" - родительский gateway/интерфейс не найден, пропущен\n`; + code += `# Gateway "${gw.ip || 'без IP'}" - родительский gateway/интерфейс не найден, пропущен\n`; return; } if (!gw.ip) { - code += `\n# Gateway без IP адреса - пропущен\n`; + code += `# Gateway без IP адреса - пропущен\n`; return; } @@ -659,7 +658,7 @@ function NetworkConfigManager() { if (!parentIp) { const parentType = parent.parentType === 'interface' ? 'интерфейс' : 'gateway'; - code += `\n# Gateway "${gw.ip}" - родительский ${parentType} не имеет IP, пропущен\n`; + code += `# Gateway "${gw.ip}" - родительский ${parentType} не имеет IP, пропущен\n`; return; } @@ -670,7 +669,7 @@ function NetworkConfigManager() { ? `интерфейс ${parent.name || parent.interfaceType || 'interface'} (${parent.remoteIp})` : `gateway ${parent.ip}`; - code += `\n# Рекурсивный gateway: ${gw.ip}\n`; + code += `# Рекурсивный gateway: ${gw.ip}\n`; code += `# Родительский: ${parentLabel}\n`; // Создаём маршрут к IP рекурсивного gateway через родительский @@ -685,20 +684,30 @@ function NetworkConfigManager() { const defaultComment = gw.description ? `${gw.description} (default)` : `Recursive default: ${gw.ip}`; code += `/ip route add dst-address=0.0.0.0/0 gateway=${parentIp} comment="${defaultComment}"\n`; } + code += '\n'; }); + + code += '# Проверка созданных маршрутов:\n'; + code += '# /ip route print where comment~"Recursive"\n'; + code += `# Всего создано маршрутов: ${validRoutesCount}\n`; + + if (validRoutesCount > 0) { + blocks.push({ + type: 'recursive-routes', + serverName, + server, + code + }); + } }); - code += '\n# === Проверка созданных маршрутов ===\n'; - code += '# /ip route print where comment~"Recursive"\n'; - code += `\n# Всего создано маршрутов: ${validRoutesCount}\n`; - - // Добавляем код для настройки IP адресов интерфейсов - const interfaceAddressesCode = generateMikrotikInterfaceAddresses(); - if (interfaceAddressesCode) { - code += interfaceAddressesCode; + // Генерируем блоки для IP адресов интерфейсов + const interfaceBlocks = generateMikrotikInterfaceAddresses(); + if (interfaceBlocks && interfaceBlocks.length > 0) { + blocks.push(...interfaceBlocks); } - return code; + return blocks; }; // === Обработчик кнопки "Код для MikroTik" === @@ -710,14 +719,24 @@ function NetworkConfigManager() { // === Копирование кода MikroTik === const handleCopyMikrotikCode = async () => { + // Объединяем все блоки в одну строку для копирования + const fullCode = Array.isArray(generatedMikrotikCode) && generatedMikrotikCode.length > 0 + ? generatedMikrotikCode.map(block => block.code).join('\n\n') + : ''; + + if (!fullCode) { + notify.error('Нет кода для копирования'); + return; + } + try { if (navigator.clipboard && navigator.clipboard.writeText) { - await navigator.clipboard.writeText(generatedMikrotikCode); + await navigator.clipboard.writeText(fullCode); notify.success('Код скопирован в буфер обмена!'); } else { // Fallback для старых браузеров const textArea = document.createElement('textarea'); - textArea.value = generatedMikrotikCode; + textArea.value = fullCode; textArea.style.position = 'fixed'; textArea.style.left = '-999999px'; document.body.appendChild(textArea); @@ -1091,8 +1110,11 @@ function NetworkConfigManager() { type="button" className="btn btn-outline-primary" onClick={handleGenerateMikrotikCode} - disabled={loading || (config.gateways || []).filter(gw => gw.type === 'recursive').length === 0} - title="Генерировать код MikroTik для рекурсивных маршрутов" + disabled={loading || ( + (config.gateways || []).filter(gw => gw.type === 'recursive').length === 0 && + (config.tunnelInterfaces || []).filter(i => i.serverId && i.serverId2 && i.localIp && i.remoteIp).length === 0 + )} + title="Генерировать код MikroTik для рекурсивных маршрутов и IP адресов интерфейсов" > Код для MikroTik @@ -1924,15 +1946,65 @@ function NetworkConfigManager() { /> {/* MikroTik Code Modal */} - setMikrotikCodeModalOpen(false)} - onCopy={handleCopyMikrotikCode} - /> + {mikrotikCodeModalOpen && ( +
+
+
+
+
+ Предварительный просмотр конфигурации MikroTik +
+ +
+
+
+ Конфигурация MikroTik RouterOS для рекурсивных маршрутов и IP адресов интерфейсов +
+ + {Array.isArray(generatedMikrotikCode) && generatedMikrotikCode.length > 0 ? ( +
+ {generatedMikrotikCode.map((block, index) => ( +
+
+                          {block.code}
+                        
+
+ ))} +
+ ) : ( +
+ Нет данных для генерации конфигурации +
+ )} + +
+

Инструкция по применению:

+
    +
  1. Скопируйте конфигурацию в буфер обмена
  2. +
  3. Подключитесь к MikroTik через SSH или Winbox
  4. +
  5. Вставьте конфигурацию в терминал
  6. +
  7. Проверьте применение командой: /ip route print или /ip address print
  8. +
+
+
+
+ + +
+
+
+
+ )} ); }