import $ from 'jquery'; import { JSONRPC } from '@/json-rpc.js'; const ROS_COMPONENT_NAME = 'ru.kirillius.pf.sdn.External.API.Components.ROS'; const FIELD_IDS = { container: 'ros-config-container', instancesList: 'ros-instances-list', addInstanceButton: 'ros-add-instance-btn', saveButton: 'save-ros-btn', status: 'ros-status-message' }; const CLASS_NAMES = { instance: 'ros-instance-entry', removeInstanceButton: 'ros-remove-instance-btn', host: 'ros-host', username: 'ros-username', password: 'ros-password', vrf: 'ros-vrf', gateway: 'ros-gateway' }; const SELECTORS = { container: `#${FIELD_IDS.container}`, instancesList: `#${FIELD_IDS.instancesList}`, addInstanceButton: `#${FIELD_IDS.addInstanceButton}`, saveButton: `#${FIELD_IDS.saveButton}`, status: `#${FIELD_IDS.status}` }; let currentConfig = { instances: [] }; let statusTimeoutId = null; let instanceCounter = 0; const getStatusElement = () => $(SELECTORS.status); function normalizeConfig(config) { if (!config || !Array.isArray(config.instances)) { return { instances: [] }; } return { instances: config.instances.map(instance => ({ host: instance?.host || '', username: instance?.username || '', password: instance?.password || '', VRF: instance?.VRF || 'main', gateway: instance?.gateway || '' })) }; } function clearStatus() { const $status = getStatusElement(); if (!$status.length) { return; } if (statusTimeoutId) { clearTimeout(statusTimeoutId); statusTimeoutId = null; } $status.stop(true, true).hide().text('').removeClass('success-message error-message'); } function updateStatus(message, type) { const $status = getStatusElement(); if (!$status.length) { return; } if (statusTimeoutId) { clearTimeout(statusTimeoutId); } $status .removeClass('success-message error-message') .addClass(type === 'success' ? 'success-message' : 'error-message') .text(message) .show(); statusTimeoutId = window.setTimeout(() => { $status.fadeOut(); }, 5000); } async function runAction($button, pendingText, action, messages) { if (!$button.length) { return; } const originalText = $button.text(); $button.prop('disabled', true).text(pendingText); clearStatus(); try { const result = await action(); if (messages?.success) { const successMessage = typeof messages.success === 'function' ? messages.success(result) : messages.success; if (successMessage) { updateStatus(successMessage, 'success'); } } } catch (error) { console.error(messages?.log || 'Ошибка выполнения действия ROS:', error); if (messages?.error) { updateStatus(messages.error, 'error'); } } finally { $button.prop('disabled', false).text(originalText); } } function resetCounters() { instanceCounter = 0; } function getNextInstanceId() { instanceCounter += 1; return instanceCounter; } function createInstanceRow(instance = {}) { const instanceId = getNextInstanceId(); return `

Инстанс

`; } function populateInstances() { const instances = currentConfig.instances.length ? currentConfig.instances : []; const $list = $(SELECTORS.instancesList); resetCounters(); $list.empty(); instances.forEach(instance => { $list.append(createInstanceRow(instance)); }); } function renderROSForm() { const $container = $(SELECTORS.container); $container.html(`

Инстансы RouterOS

Настройте параметры подключения к MikroTik RouterOS для синхронизации маршрутов. Можно добавить несколько инстансов.

`); populateInstances(); attachEventHandlers(); } function collectConfigFromForm() { const instances = []; $(SELECTORS.instancesList).find(`.${CLASS_NAMES.instance}`).each((_, element) => { const $instance = $(element); const host = $instance.find(`.${CLASS_NAMES.host}`).val().trim(); const username = $instance.find(`.${CLASS_NAMES.username}`).val().trim(); const $passwordField = $instance.find(`.${CLASS_NAMES.password}`); const newPassword = $passwordField.val(); const originalPassword = $passwordField.data('original-password') || ''; const password = newPassword ? newPassword : originalPassword; const VRF = $instance.find(`.${CLASS_NAMES.vrf}`).val().trim(); const gateway = $instance.find(`.${CLASS_NAMES.gateway}`).val().trim(); instances.push({ host, username, password, VRF, gateway }); }); return { instances }; } async function loadConfig() { try { const fullConfig = await JSONRPC.System.getComponentConfig(ROS_COMPONENT_NAME); currentConfig = normalizeConfig(fullConfig); return true; } catch (error) { console.error('Ошибка при загрузке конфига ROS:', error); currentConfig = { instances: [] }; return false; } } function handleAddInstance() { const $list = $(SELECTORS.instancesList); $list.append(createInstanceRow({ host: '', username: '', password: '', VRF: 'main', gateway: '' })); } function handleRemoveInstance(event) { event.preventDefault(); $(event.currentTarget).closest(`.${CLASS_NAMES.instance}`).remove(); } async function handleSave() { const $button = $(SELECTORS.saveButton); clearStatus(); const newConfig = collectConfigFromForm(); await runAction($button, 'Применение...', async () => { await JSONRPC.System.setComponentConfig(ROS_COMPONENT_NAME, newConfig); currentConfig = normalizeConfig(newConfig); populateInstances(); }, { success: 'Конфигурация ROS успешно сохранена.', error: 'Ошибка при сохранении конфигурации ROS.', log: 'Ошибка сохранения конфига ROS' }); } function attachEventHandlers() { $(SELECTORS.saveButton).off('click').on('click', handleSave); $(SELECTORS.addInstanceButton).off('click').on('click', handleAddInstance); $(SELECTORS.instancesList) .off('click', `.${CLASS_NAMES.removeInstanceButton}`).on('click', `.${CLASS_NAMES.removeInstanceButton}`, handleRemoveInstance); } function detachEventHandlers() { $(SELECTORS.saveButton).off('click'); $(SELECTORS.addInstanceButton).off('click'); $(SELECTORS.instancesList) .off('click', `.${CLASS_NAMES.removeInstanceButton}`); } export const ROSConfig = { render: () => `

Настройка ROS

Загрузка конфигурации...

`, mount: async () => { const success = await loadConfig(); if (success) { renderROSForm(); } else { $(SELECTORS.container).html('

Не удалось загрузить конфигурацию ROS.

'); } }, unmount: () => { detachEventHandlers(); clearStatus(); currentConfig = { instances: [] }; } };