270 lines
8.7 KiB
JavaScript
270 lines
8.7 KiB
JavaScript
import { apiAction, toast } from './api.js';
|
|
|
|
const state = {
|
|
setup: null,
|
|
loading: false,
|
|
};
|
|
|
|
let form;
|
|
let tablesInput;
|
|
let tablesPreview;
|
|
let modeInputs;
|
|
let directFields;
|
|
let configFields;
|
|
let statusLabel;
|
|
let loadBtn;
|
|
let configExampleBtn;
|
|
let configExampleDialog;
|
|
|
|
export function initBridgeSetupPage() {
|
|
form = document.getElementById('bridgeSetupForm');
|
|
if (!form) return;
|
|
tablesInput = form.elements.tables;
|
|
tablesPreview = document.getElementById('selectedTables');
|
|
directFields = document.getElementById('directFields');
|
|
configFields = document.getElementById('configFields');
|
|
statusLabel = document.getElementById('setupStatus');
|
|
loadBtn = document.getElementById('btn-load-remote');
|
|
configExampleBtn = document.getElementById('btn-config-example');
|
|
configExampleDialog = document.getElementById('configExampleDialog');
|
|
modeInputs = Array.from(form.querySelectorAll('input[name="db_mode"]'));
|
|
|
|
form.addEventListener('submit', submitBridgeSetup);
|
|
tablesInput?.addEventListener('input', () => updateTablesPreview(parseTablesInput()));
|
|
loadBtn?.addEventListener('click', loadTablesFromBridge);
|
|
configExampleBtn?.addEventListener('click', () => {
|
|
if (configExampleDialog?.showModal) configExampleDialog.showModal();
|
|
});
|
|
modeInputs.forEach(input => {
|
|
input.addEventListener('change', () => applyModeVisibility(input.value));
|
|
});
|
|
|
|
loadBridgeSetup();
|
|
}
|
|
|
|
function defaultSetup() {
|
|
return {
|
|
tables: [],
|
|
mode: 'direct',
|
|
direct: {
|
|
host: '',
|
|
port: 3306,
|
|
database: '',
|
|
user: '',
|
|
password: '',
|
|
charset: 'utf8mb4',
|
|
},
|
|
config: {
|
|
file: '',
|
|
base: '',
|
|
host_key: '',
|
|
port_key: '',
|
|
database_key: '',
|
|
user_key: '',
|
|
password_key: '',
|
|
charset_key: '',
|
|
},
|
|
};
|
|
}
|
|
|
|
async function loadBridgeSetup() {
|
|
state.loading = true;
|
|
try {
|
|
const res = await apiAction('account.bridge.setup.get', { method: 'GET' });
|
|
if (!res?.ok) throw new Error(res?.error || 'Bridge-Setup konnte nicht geladen werden');
|
|
fillForm(res.setup || state.setup || defaultSetup());
|
|
updateStatus('Daten geladen.');
|
|
} catch (err) {
|
|
console.error(err);
|
|
toast(err.message || 'Bridge-Setup konnte nicht geladen werden', false);
|
|
} finally {
|
|
state.loading = false;
|
|
}
|
|
}
|
|
|
|
function fillForm(setup) {
|
|
const data = normalizeSetupInput(setup);
|
|
state.setup = data;
|
|
if (tablesInput) {
|
|
tablesInput.value = data.tables.join(', ');
|
|
updateTablesPreview(data.tables);
|
|
}
|
|
const activeMode = (data.mode || 'direct').toLowerCase();
|
|
modeInputs.forEach(input => {
|
|
input.checked = input.value === activeMode;
|
|
});
|
|
applyModeVisibility(activeMode);
|
|
|
|
if (directFields) {
|
|
const directMap = {
|
|
direct_host: data.direct.host || '',
|
|
direct_port: String(data.direct.port || 3306),
|
|
direct_database: data.direct.database || '',
|
|
direct_charset: data.direct.charset || 'utf8mb4',
|
|
direct_user: data.direct.user || '',
|
|
direct_password: data.direct.password || '',
|
|
};
|
|
Object.entries(directMap).forEach(([name, value]) => {
|
|
const input = directFields.querySelector(`[name="${name}"]`);
|
|
if (input) input.value = value;
|
|
});
|
|
}
|
|
|
|
if (configFields) {
|
|
const configMap = {
|
|
config_file: data.config.file || '',
|
|
config_base: data.config.base || '',
|
|
config_host_key: data.config.host_key || '',
|
|
config_port_key: data.config.port_key || '',
|
|
config_database_key: data.config.database_key || '',
|
|
config_user_key: data.config.user_key || '',
|
|
config_password_key: data.config.password_key || '',
|
|
config_charset_key: data.config.charset_key || '',
|
|
};
|
|
Object.entries(configMap).forEach(([name, value]) => {
|
|
const input = configFields.querySelector(`[name="${name}"]`);
|
|
if (input) input.value = value;
|
|
});
|
|
}
|
|
}
|
|
|
|
function applyModeVisibility(mode) {
|
|
const direct = mode === 'config' ? 'add' : 'remove';
|
|
const config = mode === 'config' ? 'remove' : 'add';
|
|
directFields?.classList[direct]('hidden');
|
|
configFields?.classList[config]('hidden');
|
|
}
|
|
|
|
function parseTablesInput() {
|
|
if (!tablesInput) return [];
|
|
return tablesInput.value
|
|
.split(/[\s,]+/)
|
|
.map(part => part.trim())
|
|
.filter(Boolean)
|
|
.filter((value, index, arr) => arr.indexOf(value) === index);
|
|
}
|
|
|
|
function updateTablesPreview(list) {
|
|
if (!tablesPreview) return;
|
|
if (!list.length) {
|
|
tablesPreview.innerHTML = '<span class="text-xs text-slate-500">Noch keine Tabellen angegeben.</span>';
|
|
return;
|
|
}
|
|
tablesPreview.innerHTML = list.map(name => `<span class="chip">${escapeHtml(name)}</span>`).join('');
|
|
}
|
|
|
|
async function submitBridgeSetup(ev) {
|
|
ev.preventDefault();
|
|
if (!form) return;
|
|
const mode = form.querySelector('input[name="db_mode"]:checked')?.value || 'direct';
|
|
const payload = {
|
|
tables: parseTablesInput(),
|
|
mode,
|
|
direct_host: form.direct_host?.value.trim() || '',
|
|
direct_port: Number(form.direct_port?.value || 0) || 3306,
|
|
direct_database: form.direct_database?.value.trim() || '',
|
|
direct_charset: form.direct_charset?.value.trim() || 'utf8mb4',
|
|
direct_user: form.direct_user?.value.trim() || '',
|
|
direct_password: form.direct_password?.value || '',
|
|
config_file: form.config_file?.value.trim() || '',
|
|
config_base: form.config_base?.value.trim() || '',
|
|
config_host_key: form.config_host_key?.value.trim() || '',
|
|
config_port_key: form.config_port_key?.value.trim() || '',
|
|
config_database_key: form.config_database_key?.value.trim() || '',
|
|
config_user_key: form.config_user_key?.value.trim() || '',
|
|
config_password_key: form.config_password_key?.value.trim() || '',
|
|
config_charset_key: form.config_charset_key?.value.trim() || '',
|
|
};
|
|
|
|
try {
|
|
const res = await apiAction('account.bridge.setup.save', { method: 'POST', data: payload });
|
|
if (!res?.ok) throw new Error(res?.error || 'Bridge-Setup konnte nicht gespeichert werden');
|
|
fillForm(res.setup || payload);
|
|
updateStatus('Bridge-Setup gespeichert.');
|
|
toast('Bridge-Setup gespeichert', true);
|
|
} catch (err) {
|
|
console.error(err);
|
|
toast(err.message || 'Bridge-Setup konnte nicht gespeichert werden', false);
|
|
}
|
|
}
|
|
|
|
async function loadTablesFromBridge(ev) {
|
|
ev?.preventDefault();
|
|
if (!loadBtn) return;
|
|
loadBtn.disabled = true;
|
|
try {
|
|
const res = await apiAction('account.bridge.test', { method: 'POST', data: {} });
|
|
if (!res?.ok) throw new Error(res?.error || 'Bridge konnte nicht abgefragt werden');
|
|
const fetchedTables = normalizeTableNames(res.tables);
|
|
const allowedTables = normalizeTableNames(res.setup_hint?.tables ?? fetchedTables);
|
|
const merged = {
|
|
...(state.setup || {}),
|
|
tables: allowedTables,
|
|
...(res.setup_hint || {}),
|
|
};
|
|
if (res.setup_hint) {
|
|
merged.mode = res.setup_hint.mode || merged.mode;
|
|
merged.direct = { ...(merged.direct || {}), ...(res.setup_hint.direct || {}) };
|
|
merged.config = { ...(merged.config || {}), ...(res.setup_hint.config || {}) };
|
|
}
|
|
fillForm(merged);
|
|
updateStatus(`Tabellen geladen (${fetchedTables.length}).`);
|
|
toast('Tabellen erfolgreich geladen', true);
|
|
} catch (err) {
|
|
console.error(err);
|
|
toast(err.message || 'Bridge konnte nicht geprüft werden', false);
|
|
} finally {
|
|
loadBtn.disabled = false;
|
|
}
|
|
}
|
|
|
|
function normalizeTableNames(list) {
|
|
if (!Array.isArray(list)) return [];
|
|
const result = [];
|
|
const seen = new Set();
|
|
for (const entry of list) {
|
|
let name = '';
|
|
if (typeof entry === 'string') {
|
|
name = entry;
|
|
} else if (entry && typeof entry === 'object') {
|
|
name = entry.name || entry.table || entry.label || '';
|
|
}
|
|
if (typeof name === 'string') {
|
|
const trimmed = name.trim();
|
|
if (trimmed && !seen.has(trimmed)) {
|
|
seen.add(trimmed);
|
|
result.push(trimmed);
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function updateStatus(msg) {
|
|
if (!statusLabel) return;
|
|
const time = new Date().toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit', second: '2-digit' });
|
|
statusLabel.textContent = `${msg} (${time})`;
|
|
}
|
|
|
|
function escapeHtml(str) {
|
|
return String(str || '')
|
|
.replace(/&/g, '&')
|
|
.replace(/</g, '<')
|
|
.replace(/>/g, '>')
|
|
.replace(/"/g, '"')
|
|
.replace(/'/g, ''');
|
|
}
|
|
|
|
function normalizeSetupInput(input) {
|
|
const base = defaultSetup();
|
|
if (!input || typeof input !== 'object') return base;
|
|
const mode = (input.mode || base.mode).toLowerCase();
|
|
const validMode = mode === 'config' ? 'config' : 'direct';
|
|
const tables = normalizeTableNames(input.tables || base.tables);
|
|
const direct = { ...base.direct, ...(input.direct || {}) };
|
|
direct.port = Number(direct.port || 3306) || 3306;
|
|
direct.charset = direct.charset || 'utf8mb4';
|
|
const config = { ...base.config, ...(input.config || {}) };
|
|
return { tables, mode: validMode, direct, config };
|
|
}
|