import { apiAction, toast } from './api.js'; const state = { setup: null, loading: false, }; let form; let tablesInput; let tablesPreview; let modeInputs; let importInputs; let directFields; let statusLabel; let loadBtn; let bridgeFields; export function initBridgeSetupPage() { form = document.getElementById('bridgeSetupForm'); if (!form) return; tablesInput = form.elements.tables; tablesPreview = document.getElementById('selectedTables'); bridgeFields = document.getElementById('bridgeFields'); directFields = document.getElementById('directFields'); statusLabel = document.getElementById('setupStatus'); loadBtn = document.getElementById('btn-load-remote'); modeInputs = Array.from(form.querySelectorAll('input[name="db_mode"]')); importInputs = Array.from(form.querySelectorAll('input[name="bridge_import"]')); form.addEventListener('submit', submitBridgeSetup); tablesInput?.addEventListener('input', () => updateTablesPreview(parseTablesInput())); loadBtn?.addEventListener('click', loadTablesFromBridge); modeInputs.forEach(input => { input.addEventListener('change', () => applyModeVisibility(input.value)); }); loadBridgeSetup(); } function defaultSetup() { return { tables: [], mode: 'bridge', import_type: 'schema', direct: { host: '', port: 3306, database: '', user: '', password: '', charset: 'utf8mb4', }, }; } 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 (importInputs.length) { const importType = (data.import_type || 'schema').toLowerCase(); importInputs.forEach(input => { input.checked = input.value === importType; }); } 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; }); } } function applyModeVisibility(mode) { const showDirect = mode === 'direct'; directFields?.classList[showDirect ? 'remove' : 'add']('hidden'); bridgeFields?.classList[showDirect ? 'add' : 'remove']('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 importType = form.querySelector('input[name="bridge_import"]:checked')?.value || 'schema'; const payload = { tables: parseTablesInput(), mode, import_type: importType, 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 || '', }; 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 mode = form?.querySelector('input[name="db_mode"]:checked')?.value || 'bridge'; const importType = form?.querySelector('input[name="bridge_import"]:checked')?.value || 'schema'; const payload = { mode, import_type: importType, 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 || '', }; const res = await apiAction('account.bridge.test', { method: 'POST', data: payload }); 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, }; 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 === 'direct' ? 'direct' : 'bridge'; const importType = (input.import_type || base.import_type).toLowerCase(); const validImport = importType === 'settings' ? 'settings' : 'schema'; 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'; return { tables, mode: validMode, import_type: validImport, direct }; }