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

263 lines
9.3 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 TDNS_COMPONENT_NAME = 'ru.kirillius.pf.sdn.External.API.Components.TDNS';
const FIELD_IDS = {
container: 'tdns-config-container',
instancesList: 'tdns-instances-list',
addInstanceButton: 'tdns-add-instance-btn',
saveButton: 'save-tdns-btn',
status: 'tdns-status-message'
};
const CLASS_NAMES = {
instance: 'tdns-instance-entry',
removeButton: 'tdns-remove-instance-btn',
serverInput: 'tdns-server-input',
tokenInput: 'tdns-token-input',
forwarderInput: 'tdns-forwarder-input'
};
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 => ({
server: instance?.server || '',
token: instance?.token || '',
forwarder: instance?.forwarder || ''
}))
};
}
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 || 'Ошибка выполнения действия TDNS:', error);
if (messages?.error) {
updateStatus(messages.error, 'error');
}
} finally {
$button.prop('disabled', false).text(originalText);
}
}
function resetInstanceCounter() {
instanceCounter = 0;
}
function getNextInstanceId() {
instanceCounter += 1;
return instanceCounter;
}
function createInstanceRow(instance = {}) {
const uid = getNextInstanceId();
const serverId = `tdns-server-${uid}`;
const tokenId = `tdns-token-${uid}`;
const forwarderId = `tdns-forwarder-${uid}`;
return `
<div class="${CLASS_NAMES.instance}" 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.removeButton}">Удалить</button>
</div>
<div class="form-group">
<label for="${serverId}">TDNS сервер</label>
<input type="text" id="${serverId}" class="form-control ${CLASS_NAMES.serverInput}" placeholder="https://tdns.example.com" value="${instance.server || ''}">
</div>
<div class="form-group">
<label for="${tokenId}">Токен доступа</label>
<input type="text" id="${tokenId}" class="form-control ${CLASS_NAMES.tokenInput}" placeholder="Сгенерируйте токен на странице API" value="${instance.token || ''}">
</div>
<div class="form-group">
<label for="${forwarderId}">Forwarder</label>
<input type="text" id="${forwarderId}" class="form-control ${CLASS_NAMES.forwarderInput}" placeholder="http://127.0.0.1:8080" value="${instance.forwarder || ''}">
<small class="hint-text">Адрес DNS сервера, на который будут перенаправляться запросы.</small>
</div>
</div>
`;
}
function populateInstances() {
const instances = currentConfig.instances.length ? currentConfig.instances : [];
const $list = $(SELECTORS.instancesList);
resetInstanceCounter();
$list.empty();
instances.forEach(instance => {
$list.append(createInstanceRow(instance));
});
}
function renderTDNSForm() {
const $container = $(SELECTORS.container);
$container.html(`
<div class="component-config-form">
<h3 class="config-section-title">Инстансы TDNS</h3>
<p class="hint-text" style="margin-bottom: 20px;">Настройте подключения TDNS. Можно указать несколько серверов с собственными токенами.</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 $row = $(element);
const server = $row.find(`.${CLASS_NAMES.serverInput}`).val().trim();
const token = $row.find(`.${CLASS_NAMES.tokenInput}`).val().trim();
const forwarder = $row.find(`.${CLASS_NAMES.forwarderInput}`).val().trim();
if (server || token || forwarder) {
instances.push({ server, token, forwarder });
}
});
return { instances };
}
async function loadConfig() {
try {
const fullConfig = await JSONRPC.System.getComponentConfig(TDNS_COMPONENT_NAME);
currentConfig = normalizeConfig(fullConfig);
return true;
} catch (error) {
console.error('Ошибка при загрузке конфига TDNS:', error);
currentConfig = { instances: [] };
return false;
}
}
function handleAddInstance() {
const $list = $(SELECTORS.instancesList);
$list.append(createInstanceRow({}));
}
function handleRemoveInstance(event) {
event.preventDefault();
$(event.currentTarget).closest(`.${CLASS_NAMES.instance}`).remove();
}
async function handleSave() {
const $button = $(SELECTORS.saveButton);
await runAction($button, 'Применение...', async () => {
const newConfig = collectConfigFromForm();
await JSONRPC.System.setComponentConfig(TDNS_COMPONENT_NAME, newConfig);
currentConfig = normalizeConfig(newConfig);
populateInstances();
}, {
success: 'Конфигурация TDNS успешно сохранена.',
error: 'Ошибка при сохранении конфигурации TDNS.',
log: 'Ошибка сохранения конфига TDNS'
});
}
function attachEventHandlers() {
$(SELECTORS.saveButton).off('click').on('click', handleSave);
$(SELECTORS.addInstanceButton).off('click').on('click', handleAddInstance);
$(SELECTORS.instancesList).off('click', `.${CLASS_NAMES.removeButton}`).on('click', `.${CLASS_NAMES.removeButton}`, handleRemoveInstance);
}
function detachEventHandlers() {
$(SELECTORS.saveButton).off('click');
$(SELECTORS.addInstanceButton).off('click');
$(SELECTORS.instancesList).off('click', `.${CLASS_NAMES.removeButton}`);
}
export const TDNSConfig = {
render: () => `
<h1 class="page-title">Настройка TDNS</h1>
<div id="${FIELD_IDS.container}">
<p>Загрузка конфигурации...</p>
</div>
`,
mount: async () => {
const success = await loadConfig();
if (success) {
renderTDNSForm();
} else {
$(SELECTORS.container).html('<p class="error-message">Не удалось загрузить конфигурацию TDNS.</p>');
}
},
unmount: () => {
detachEventHandlers();
clearStatus();
currentConfig = { instances: [] };
}
};