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 = 'Noch keine Tabellen angegeben.'; return; } tablesPreview.innerHTML = list.map(name => `${escapeHtml(name)}`).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, '''); } 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 }; }