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] : '';
+}