refactor(NetworkConfigManager): update MikroTik code generation to return structured blocks for interface addresses and recursive routes; enhance modal display for generated code
Publish Fast Tabler Docker image / build-and-push-fast (push) Failing after 58s
Publish Fast Tabler Docker image / build-and-push-fast (push) Failing after 58s
This commit is contained in:
@@ -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 адресов интерфейсов"
|
||||
>
|
||||
<IconCode size={16} className="me-1" />
|
||||
Код для MikroTik
|
||||
@@ -1924,15 +1946,65 @@ function NetworkConfigManager() {
|
||||
/>
|
||||
|
||||
{/* MikroTik Code Modal */}
|
||||
<PreviewConfigModal
|
||||
show={mikrotikCodeModalOpen}
|
||||
config={generatedMikrotikCode}
|
||||
mode="advanced"
|
||||
title="Предварительный просмотр конфигурации"
|
||||
description="Конфигурация MikroTik RouterOS для рекурсивных маршрутов"
|
||||
onClose={() => setMikrotikCodeModalOpen(false)}
|
||||
onCopy={handleCopyMikrotikCode}
|
||||
/>
|
||||
{mikrotikCodeModalOpen && (
|
||||
<div className="modal show d-block" style={{ backgroundColor: 'rgba(0,0,0,0.5)' }}>
|
||||
<div className="modal-dialog modal-xl">
|
||||
<div className="modal-content">
|
||||
<div className="modal-header">
|
||||
<h5 className="modal-title">
|
||||
Предварительный просмотр конфигурации MikroTik
|
||||
</h5>
|
||||
<button type="button" className="btn-close" onClick={() => setMikrotikCodeModalOpen(false)}></button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<div className="alert alert-info mb-3">
|
||||
Конфигурация MikroTik RouterOS для рекурсивных маршрутов и IP адресов интерфейсов
|
||||
</div>
|
||||
|
||||
{Array.isArray(generatedMikrotikCode) && generatedMikrotikCode.length > 0 ? (
|
||||
<div className="d-flex flex-column gap-3">
|
||||
{generatedMikrotikCode.map((block, index) => (
|
||||
<div key={index} className="bg-dark text-white p-3 rounded">
|
||||
<pre className="mb-0" style={{ whiteSpace: 'pre-wrap', wordBreak: 'break-word' }}>
|
||||
<code className="text-white">{block.code}</code>
|
||||
</pre>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="alert alert-warning mb-0">
|
||||
Нет данных для генерации конфигурации
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="mt-3 text-muted small">
|
||||
<p className="mb-1"><strong>Инструкция по применению:</strong></p>
|
||||
<ol className="mb-0">
|
||||
<li>Скопируйте конфигурацию в буфер обмена</li>
|
||||
<li>Подключитесь к MikroTik через SSH или Winbox</li>
|
||||
<li>Вставьте конфигурацию в терминал</li>
|
||||
<li>Проверьте применение командой: <code>/ip route print</code> или <code>/ip address print</code></li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
<div className="modal-footer">
|
||||
<button type="button" className="btn btn-secondary" onClick={() => setMikrotikCodeModalOpen(false)}>
|
||||
Закрыть
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-primary"
|
||||
onClick={handleCopyMikrotikCode}
|
||||
disabled={!Array.isArray(generatedMikrotikCode) || generatedMikrotikCode.length === 0}
|
||||
>
|
||||
<IconCopy className="me-2" />
|
||||
Копировать в буфер
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user