263 lines
9.3 KiB
JavaScript
263 lines
9.3 KiB
JavaScript
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: [] };
|
||
}
|
||
};
|