Files
emailtemplate.it/public/assets/js/bridge-setup-page.js
2026-01-13 00:05:49 +01:00

329 lines
11 KiB
JavaScript

import { apiAction, toast } from './api.js';
const state = {
setup: null,
loading: false,
allTables: [],
selectedTables: [],
};
let form;
let tablesAllSelect;
let tablesSelectedSelect;
let tablesAddBtn;
let tablesRemoveBtn;
let modeInputs;
let directFields;
let configFields;
let statusLabel;
let loadBtn;
let configExampleBtn;
let configExampleDialog;
let bridgeSetupDialog;
let openBridgeSetupBtn;
let closeBridgeSetupBtn;
export function initBridgeSetupPage() {
form = document.getElementById('bridgeSetupForm');
if (!form) return;
tablesAllSelect = document.getElementById('bridgeTablesAll');
tablesSelectedSelect = document.getElementById('bridgeTablesSelected');
tablesAddBtn = document.getElementById('bridgeTablesAdd');
tablesRemoveBtn = document.getElementById('bridgeTablesRemove');
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"]'));
bridgeSetupDialog = document.getElementById('bridgeSetupDialog');
openBridgeSetupBtn = document.getElementById('btn-open-bridge-setup');
closeBridgeSetupBtn = document.getElementById('btn-close-bridge-setup');
form.addEventListener('submit', submitBridgeSetup);
tablesAddBtn?.addEventListener('click', () => addSelectedTables(getSelectedOptions(tablesAllSelect)));
tablesRemoveBtn?.addEventListener('click', () => removeSelectedTables(getSelectedOptions(tablesSelectedSelect)));
loadBtn?.addEventListener('click', loadTablesFromBridge);
configExampleBtn?.addEventListener('click', () => {
if (configExampleDialog?.showModal) configExampleDialog.showModal();
});
openBridgeSetupBtn?.addEventListener('click', () => {
if (bridgeSetupDialog?.showModal) bridgeSetupDialog.showModal();
if (!state.allTables.length) {
loadTablesFromBridge(null, { preserveSelection: state.selectedTables.length > 0 });
}
});
closeBridgeSetupBtn?.addEventListener('click', () => {
if (bridgeSetupDialog?.open) bridgeSetupDialog.close();
});
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, options = {}) {
const data = normalizeSetupInput(setup);
state.setup = data;
if (!options.skipTables) {
state.selectedTables = data.tables.slice();
updateTableSelects();
}
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 updateTableSelects() {
const all = state.allTables || [];
const selected = state.selectedTables || [];
const selectedSet = new Set(selected);
const orderedSelected = all.length ? all.filter(name => selectedSet.has(name)) : selected;
const available = all.length ? all.filter(name => !selectedSet.has(name)) : [];
renderSelect(tablesAllSelect, available, 'Noch keine Tabellen geladen.');
renderSelect(tablesSelectedSelect, orderedSelected, 'Noch keine Tabellen ausgewaehlt.');
}
function renderSelect(selectEl, list, emptyLabel) {
if (!selectEl) return;
selectEl.innerHTML = '';
if (!list.length) {
const opt = document.createElement('option');
opt.textContent = emptyLabel;
opt.disabled = true;
selectEl.appendChild(opt);
return;
}
list.forEach(name => {
const opt = document.createElement('option');
opt.value = name;
opt.textContent = name;
selectEl.appendChild(opt);
});
}
function getSelectedOptions(selectEl) {
if (!selectEl) return [];
return Array.from(selectEl.selectedOptions || []).map(opt => opt.value);
}
function addSelectedTables(list) {
if (!list.length) return;
state.selectedTables = normalizeTableNames([...state.selectedTables, ...list]);
updateTableSelects();
}
function removeSelectedTables(list) {
if (!list.length) return;
const removeSet = new Set(list);
state.selectedTables = state.selectedTables.filter(name => !removeSet.has(name));
updateTableSelects();
}
async function submitBridgeSetup(ev) {
ev.preventDefault();
if (!form) return;
const mode = form.querySelector('input[name="db_mode"]:checked')?.value || 'direct';
const payload = {
tables: normalizeTableNames(state.selectedTables),
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);
if (bridgeSetupDialog?.open) {
bridgeSetupDialog.close();
}
window.dispatchEvent(new CustomEvent('bridge-setup-updated', { detail: res.setup || payload }));
} catch (err) {
console.error(err);
toast(err.message || 'Bridge-Setup konnte nicht gespeichert werden', false);
}
}
async function loadTablesFromBridge(ev, options = {}) {
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);
state.allTables = fetchedTables;
const preserveSelection = !!options.preserveSelection;
const hasSelection = state.selectedTables.length > 0;
if (preserveSelection || hasSelection) {
state.selectedTables = normalizeTableNames(state.selectedTables);
} else {
state.selectedTables = fetchedTables.slice();
}
if (fetchedTables.length && state.selectedTables.length) {
const fetchedSet = new Set(fetchedTables);
state.selectedTables = state.selectedTables.filter(name => fetchedSet.has(name));
}
if (res.setup_hint) {
const merged = {
...(state.setup || {}),
...(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, { skipTables: true });
}
updateTableSelects();
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 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 };
}