diff --git a/partials/landingpage/admin/bridge.php b/partials/landingpage/admin/bridge.php index f6fd5a6..91e80ba 100644 --- a/partials/landingpage/admin/bridge.php +++ b/partials/landingpage/admin/bridge.php @@ -143,8 +143,11 @@ $debugRedirect = isset($_GET['debug_redirect']);
+ +
+

Der Import liest nur den automatisch generierten Kommentarblock aus einer Bridge-Datei.

Noch nicht gespeichert.
diff --git a/public/assets/js/bridge-setup-page.js b/public/assets/js/bridge-setup-page.js index d049eb5..2143e59 100644 --- a/public/assets/js/bridge-setup-page.js +++ b/public/assets/js/bridge-setup-page.js @@ -13,6 +13,8 @@ let directFields; let configFields; let statusLabel; let loadBtn; +let importBtn; +let importInput; export function initBridgeSetupPage() { form = document.getElementById('bridgeSetupForm'); @@ -23,11 +25,15 @@ export function initBridgeSetupPage() { configFields = document.getElementById('configFields'); statusLabel = document.getElementById('setupStatus'); loadBtn = document.getElementById('btn-load-remote'); + importBtn = document.getElementById('btn-import-bridge'); + importInput = document.getElementById('bridgeImportInput'); modeInputs = Array.from(form.querySelectorAll('input[name="db_mode"]')); form.addEventListener('submit', submitBridgeSetup); tablesInput?.addEventListener('input', () => updateTablesPreview(parseTablesInput())); loadBtn?.addEventListener('click', loadTablesFromBridge); + importBtn?.addEventListener('click', () => importInput?.click()); + importInput?.addEventListener('change', handleBridgeImport); modeInputs.forEach(input => { input.addEventListener('change', () => applyModeVisibility(input.value)); }); @@ -121,6 +127,31 @@ function fillForm(setup) { } } +async function handleBridgeImport(ev) { + const file = ev.target?.files?.[0]; + if (!file) return; + try { + const text = await file.text(); + const parsed = parseBridgeFile(text); + if (!parsed) { + toast('Bridge-Datei konnte nicht erkannt werden', false); + return; + } + state.setup = { ...defaultSetup(), ...state.setup, ...parsed }; + fillForm(state.setup); + updateTablesPreview(parseTablesInput()); + updateStatus('Bridge-Datei importiert'); + toast('Bridge-Daten übernommen', true); + } catch (err) { + console.error(err); + toast('Bridge-Datei konnte nicht gelesen werden', false); + } finally { + if (importInput) { + importInput.value = ''; + } + } +} + function applyModeVisibility(mode) { const direct = mode === 'config' ? 'add' : 'remove'; const config = mode === 'config' ? 'remove' : 'add'; @@ -240,3 +271,76 @@ function escapeHtml(str) { .replace(/"/g, '"') .replace(/'/g, '''); } + +function parseBridgeFile(text) { + if (typeof text !== 'string' || !text.includes('EmailTemplate Bridge')) { + return null; + } + const result = defaultSetup(); + const tablesMatch = text.match(/'tables_allow'\s*=>\s*\[([^\]]*)\]/s); + if (tablesMatch) { + result.tables = tablesMatch[1] + .split(',') + .map(entry => entry.replace(/['\s]/g, '').trim()) + .filter(Boolean) + .filter((value, index, arr) => arr.indexOf(value) === index); + } + + const dsnMatch = text.match(/'dsn'\s*=>\s*'([^']+)'/); + const userMatch = text.match(/'user'\s*=>\s*'([^']*)'/); + const passMatch = text.match(/'pass'\s*=>\s*'([^']*)'/); + if (dsnMatch) { + const dsn = dsnMatch[1]; + const dsnParts = Object.fromEntries(dsn.split(';').map(part => { + const [key, value] = part.split('='); + return [key?.trim(), value?.trim()]; + })); + result.direct.host = dsnParts['mysql:host'] || ''; + result.direct.port = Number(dsnParts.port || 3306) || 3306; + result.direct.database = dsnParts.dbname || ''; + result.direct.charset = dsnParts.charset || 'utf8mb4'; + result.mode = 'direct'; + } + if (userMatch) result.direct.user = userMatch[1]; + if (passMatch) result.direct.password = passMatch[1]; + + const snippetMatch = text.match(/\*\* Bridge DB Setup: automatisch generiertes Mapping \*\/([\s\S]+?)\n\}/); + if (snippetMatch) { + result.mode = 'config'; + const snippet = snippetMatch[1]; + result.config.file = parseConfigFileExpression(snippet); + result.config.base = parseBridgeBasePath(snippet); + result.config.host_key = parseBridgeArrayPath(snippet, 'bridgeDbHost'); + result.config.port_key = parseBridgeArrayPath(snippet, 'bridgeDbPort'); + result.config.database_key = parseBridgeArrayPath(snippet, 'bridgeDbName'); + result.config.user_key = parseBridgeArrayPath(snippet, 'bridgeDbUser'); + result.config.password_key = parseBridgeArrayPath(snippet, 'bridgeDbPass'); + result.config.charset_key = parseBridgeArrayPath(snippet, 'bridgeDbCharset'); + } + + return result; +} + +function parseConfigFileExpression(snippet) { + const match = snippet.match(/\$bridgeConfigFile\s*=\s*(.+);/); + if (!match) return ''; + const expr = match[1].trim(); + const dirMatch = expr.match(/__DIR__\s*\.\s*'([^']+)'/); + if (dirMatch) { + return dirMatch[1]; + } + const quoteMatch = expr.match(/'([^']+)'/); + return quoteMatch ? quoteMatch[1] : expr.replace(/[';]/g, '').trim(); +} + +function parseBridgeArrayPath(snippet, variableName) { + const regex = new RegExp('\\$' + variableName + "[\\s=\\(\\)a-zA-Z0-9]*bridge_array_get\\\\(\\$bridgeConfigData,\\s*'([^']*)'", 'i'); + const match = snippet.match(regex); + return match ? match[1] : ''; +} + +function parseBridgeBasePath(snippet) { + const regex = /bridge_array_get\(\$bridgeConfigSource,\s*'([^']*)',\s*\$bridgeConfigData\)/; + const match = snippet.match(regex); + return match ? match[1] : ''; +}