pf-sdn/webui/src/pages/DNSMASQ.js

285 lines
10 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import $ from 'jquery';
import { JSONRPC } from '@/json-rpc.js';
const DNSMASQ_COMPONENT_NAME = 'ru.kirillius.pf.sdn.External.API.Components.DNSMASQ';
const FIELD_IDS = {
container: 'dnsmasq-config-container',
instancesList: 'dnsmasq-instances-list',
addInstanceButton: 'dnsmasq-add-instance-btn',
saveButton: 'save-dnsmasq-btn',
status: 'dnsmasq-status-message'
};
const CLASS_NAMES = {
instance: 'dnsmasq-instance-entry',
removeInstanceButton: 'dnsmasq-remove-instance-btn',
host: 'dnsmasq-host',
user: 'dnsmasq-user',
password: 'dnsmasq-password',
forwarder: 'dnsmasq-forwarder'
};
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 || '127.0.0.1',
user: instance?.user || '',
password: instance?.password || '',
forwarder: instance?.forwarder || '127.0.0.1'
}))
};
}
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 || 'Ошибка выполнения действия DNSMASQ:', 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 `
<div class="${CLASS_NAMES.instance}" data-instance-id="${instanceId}" style="border: 1px solid var(--color-border); padding: 20px; border-radius: 12px; margin-bottom: 20px; background: var(--color-surface, #1f2333);">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px;">
<h4 style="margin: 0;">Инстанс</h4>
<button type="button" class="btn-link ${CLASS_NAMES.removeInstanceButton}">Удалить</button>
</div>
<div class="form-group">
<label for="dnsmasq-host-${instanceId}">Хост</label>
<input type="text" id="dnsmasq-host-${instanceId}" class="form-control ${CLASS_NAMES.host}" placeholder="http://127.0.0.1:9090" value="${instance.host || '127.0.0.1'}">
</div>
<div class="form-group">
<label for="dnsmasq-user-${instanceId}">Имя пользователя</label>
<input type="text" id="dnsmasq-user-${instanceId}" class="form-control ${CLASS_NAMES.user}" value="${instance.user || ''}">
</div>
<div class="form-group">
<label for="dnsmasq-password-${instanceId}">Пароль</label>
<input type="password" id="dnsmasq-password-${instanceId}" class="form-control ${CLASS_NAMES.password}" placeholder="Оставьте пустым для сохранения текущего пароля" data-original-password="${instance.password || ''}">
</div>
<div class="form-group">
<label for="dnsmasq-forwarder-${instanceId}">Forwarder</label>
<input type="text" id="dnsmasq-forwarder-${instanceId}" class="form-control ${CLASS_NAMES.forwarder}" placeholder="127.0.0.1" value="${instance.forwarder || '127.0.0.1'}">
</div>
</div>
`;
}
function populateInstances() {
const instances = currentConfig.instances.length ? currentConfig.instances : [];
const $list = $(SELECTORS.instancesList);
resetCounters();
$list.empty();
instances.forEach(instance => {
$list.append(createInstanceRow(instance));
});
}
function renderDNSMASQForm() {
const $container = $(SELECTORS.container);
$container.html(`
<div class="component-config-form">
<h3 class="config-section-title">Инстансы DNSMASQ</h3>
<p class="hint-text" style="margin-bottom: 20px;">Настройте параметры подключения к DNSMASQ для синхронизации списков серверов. Можно добавить несколько инстансов.</p>
<div id="${FIELD_IDS.instancesList}"></div>
<div class="form-group" style="margin-top: 10px;">
<button type="button" id="${FIELD_IDS.addInstanceButton}" class="btn-secondary" style="width: 220px;">Добавить инстанс</button>
</div>
<div class="action-buttons" style="margin-top: 40px;">
<button id="${FIELD_IDS.saveButton}" class="btn-primary" style="width: 220px;">Применить Конфигурацию</button>
</div>
<div id="${FIELD_IDS.status}" class="error-message" style="display: none; margin-top: 20px;"></div>
</div>
`);
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 user = $instance.find(`.${CLASS_NAMES.user}`).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 forwarder = $instance.find(`.${CLASS_NAMES.forwarder}`).val().trim();
instances.push({
host,
user,
password,
forwarder
});
});
return { instances };
}
async function loadConfig() {
try {
const fullConfig = await JSONRPC.System.getComponentConfig(DNSMASQ_COMPONENT_NAME);
currentConfig = normalizeConfig(fullConfig);
return true;
} catch (error) {
console.error('Ошибка при загрузке конфига DNSMASQ:', error);
currentConfig = { instances: [] };
return false;
}
}
function handleAddInstance() {
const $list = $(SELECTORS.instancesList);
$list.append(createInstanceRow({
host: '127.0.0.1',
user: '',
password: '',
forwarder: '127.0.0.1'
}));
}
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(DNSMASQ_COMPONENT_NAME, newConfig);
currentConfig = normalizeConfig(newConfig);
populateInstances();
}, {
success: 'Конфигурация DNSMASQ успешно сохранена.',
error: 'Ошибка при сохранении конфигурации DNSMASQ.',
log: 'Ошибка сохранения конфига DNSMASQ'
});
}
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 DNSMASQConfig = {
render: () => `
<h1 class="page-title">Настройка DNSMASQ</h1>
<div id="${FIELD_IDS.container}">
<p>Загрузка конфигурации...</p>
</div>
`,
mount: async () => {
const success = await loadConfig();
if (success) {
renderDNSMASQForm();
} else {
$(SELECTORS.container).html('<p class="error-message">Не удалось загрузить конфигурацию DNSMASQ.</p>');
}
},
unmount: () => {
detachEventHandlers();
clearStatus();
currentConfig = { instances: [] };
}
};