feat(NetworkConfigManager): add MikroTik code generation for recursive routes; implement modal for previewing and copying generated code
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m43s
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m43s
This commit is contained in:
@@ -5,6 +5,7 @@ import FormModal from './components/FormModal.jsx';
|
|||||||
import FormField from './components/FormField.jsx';
|
import FormField from './components/FormField.jsx';
|
||||||
import ConfirmModal from './components/ConfirmModal.jsx';
|
import ConfirmModal from './components/ConfirmModal.jsx';
|
||||||
import ServerAutocompleteInput from './components/ServerAutocompleteInput.jsx';
|
import ServerAutocompleteInput from './components/ServerAutocompleteInput.jsx';
|
||||||
|
import PreviewConfigModal from './components/filter/PreviewConfigModal.jsx';
|
||||||
import { countryToFlag } from './utils/serverUtils.js';
|
import { countryToFlag } from './utils/serverUtils.js';
|
||||||
import {
|
import {
|
||||||
IconNetwork,
|
IconNetwork,
|
||||||
@@ -22,7 +23,8 @@ import {
|
|||||||
IconX,
|
IconX,
|
||||||
IconCircleFilled,
|
IconCircleFilled,
|
||||||
IconLayoutGrid,
|
IconLayoutGrid,
|
||||||
IconList
|
IconList,
|
||||||
|
IconCode
|
||||||
} from '@tabler/icons-react';
|
} from '@tabler/icons-react';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -125,6 +127,10 @@ function NetworkConfigManager() {
|
|||||||
const [deleteModalOpen, setDeleteModalOpen] = useState(false);
|
const [deleteModalOpen, setDeleteModalOpen] = useState(false);
|
||||||
const [itemToDelete, setItemToDelete] = useState(null);
|
const [itemToDelete, setItemToDelete] = useState(null);
|
||||||
const [deleteType, setDeleteType] = useState('');
|
const [deleteType, setDeleteType] = useState('');
|
||||||
|
|
||||||
|
// === MikroTik Code Generation ===
|
||||||
|
const [mikrotikCodeModalOpen, setMikrotikCodeModalOpen] = useState(false);
|
||||||
|
const [generatedMikrotikCode, setGeneratedMikrotikCode] = useState('');
|
||||||
|
|
||||||
// === Загрузка данных ===
|
// === Загрузка данных ===
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -462,6 +468,93 @@ function NetworkConfigManager() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// === Генерация кода MikroTik для рекурсивных маршрутов ===
|
||||||
|
const generateMikrotikRecursiveRoutes = () => {
|
||||||
|
const recursiveGateways = (config.gateways || []).filter(gw => gw.type === 'recursive');
|
||||||
|
|
||||||
|
if (recursiveGateways.length === 0) {
|
||||||
|
return '// Нет рекурсивных gateway для генерации маршрутов\n// Добавьте рекурсивные gateway в настройках';
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
|
||||||
|
let validRoutesCount = 0;
|
||||||
|
|
||||||
|
Object.entries(gatewaysByServer).forEach(([serverId, gateways]) => {
|
||||||
|
const server = getServerInfo(serverId);
|
||||||
|
const serverName = server?.dns || server?.ip || serverId;
|
||||||
|
|
||||||
|
code += `\n# === Рекурсивные маршруты для сервера: ${serverName} ===\n`;
|
||||||
|
|
||||||
|
gateways.forEach(gw => {
|
||||||
|
const parentGateway = getParentGateway(gw.parentGatewayId);
|
||||||
|
|
||||||
|
if (!parentGateway) {
|
||||||
|
code += `\n# Gateway "${gw.name}" - родительский gateway не найден, пропущен\n`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gw.ip) {
|
||||||
|
code += `\n# Gateway "${gw.name}" - IP адрес не указан, пропущен\n`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!parentGateway.ip) {
|
||||||
|
code += `\n# Gateway "${gw.name}" - родительский gateway "${parentGateway.name}" не имеет IP, пропущен\n`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
validRoutesCount++;
|
||||||
|
|
||||||
|
// Создаём маршрут для рекурсивного gateway
|
||||||
|
// Маршрут к IP рекурсивного gateway направляется через родительский gateway
|
||||||
|
code += `\n# Рекурсивный gateway: ${gw.name} (IP: ${gw.ip})\n`;
|
||||||
|
code += `# Родительский gateway: ${parentGateway.name} (IP: ${parentGateway.ip})\n`;
|
||||||
|
|
||||||
|
// Создаём маршрут к IP рекурсивного gateway через родительский
|
||||||
|
code += `/ip route add dst-address=${gw.ip}/32 gateway=${parentGateway.ip} comment="Recursive: ${gw.name} -> ${parentGateway.name}"\n`;
|
||||||
|
|
||||||
|
// Если у рекурсивного gateway есть описание с указанием на default route
|
||||||
|
if (gw.description && (gw.description.toLowerCase().includes('default') || gw.description.toLowerCase().includes('0.0.0.0'))) {
|
||||||
|
code += `# Дополнительный default route через рекурсивный gateway\n`;
|
||||||
|
code += `/ip route add dst-address=0.0.0.0/0 gateway=${parentGateway.ip} comment="Recursive default: ${gw.name} -> ${parentGateway.name}"\n`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
code += '\n# === Проверка созданных маршрутов ===\n';
|
||||||
|
code += '# /ip route print where comment~"Recursive"\n';
|
||||||
|
code += `\n# Всего создано маршрутов: ${validRoutesCount}\n`;
|
||||||
|
|
||||||
|
return code;
|
||||||
|
};
|
||||||
|
|
||||||
|
// === Обработчик кнопки "Код для MikroTik" ===
|
||||||
|
const handleGenerateMikrotikCode = () => {
|
||||||
|
const code = generateMikrotikRecursiveRoutes();
|
||||||
|
setGeneratedMikrotikCode(code);
|
||||||
|
setMikrotikCodeModalOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
// === Копирование кода MikroTik ===
|
||||||
|
const handleCopyMikrotikCode = async () => {
|
||||||
|
await copyToClipboard(generatedMikrotikCode);
|
||||||
|
};
|
||||||
|
|
||||||
// === Получение цвета провайдера ===
|
// === Получение цвета провайдера ===
|
||||||
const getProviderColor = (provider) => {
|
const getProviderColor = (provider) => {
|
||||||
if (!provider) return 'secondary';
|
if (!provider) return 'secondary';
|
||||||
@@ -754,6 +847,16 @@ function NetworkConfigManager() {
|
|||||||
>
|
>
|
||||||
<IconRefresh size={16} className={loading ? 'spin' : ''} />
|
<IconRefresh size={16} className={loading ? 'spin' : ''} />
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-outline-primary"
|
||||||
|
onClick={handleGenerateMikrotikCode}
|
||||||
|
disabled={loading || (config.gateways || []).filter(gw => gw.type === 'recursive').length === 0}
|
||||||
|
title="Генерировать код MikroTik для рекурсивных маршрутов"
|
||||||
|
>
|
||||||
|
<IconCode size={16} className="me-1" />
|
||||||
|
Код для MikroTik
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="btn btn-primary"
|
className="btn btn-primary"
|
||||||
@@ -1512,6 +1615,15 @@ function NetworkConfigManager() {
|
|||||||
variant="danger"
|
variant="danger"
|
||||||
icon={IconTrash}
|
icon={IconTrash}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* MikroTik Code Modal */}
|
||||||
|
<PreviewConfigModal
|
||||||
|
show={mikrotikCodeModalOpen}
|
||||||
|
config={generatedMikrotikCode}
|
||||||
|
mode="advanced"
|
||||||
|
onClose={() => setMikrotikCodeModalOpen(false)}
|
||||||
|
onCopy={handleCopyMikrotikCode}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user