update
This commit is contained in:
@@ -3,8 +3,7 @@ $appBaseUrl = $GLOBALS['app_base_url'] ?? '';
|
|||||||
|
|
||||||
$defaultNavLinks = [
|
$defaultNavLinks = [
|
||||||
['id' => 'dashboard', 'label' => 'Dashboard', 'href' => $appBaseUrl . '/admin/dashboard.php'],
|
['id' => 'dashboard', 'label' => 'Dashboard', 'href' => $appBaseUrl . '/admin/dashboard.php'],
|
||||||
['id' => 'settings', 'label' => 'Administration','href' => $appBaseUrl . '/admin/settings.php'],
|
['id' => 'settings', 'label' => 'API & Tabellen', 'href' => $appBaseUrl . '/admin/settings.php'],
|
||||||
['id' => 'bridge', 'label' => 'Bridge Setup', 'href' => $appBaseUrl . '/admin/bridge.php'],
|
|
||||||
['id' => 'profile', 'label' => 'Mein Konto', 'href' => $appBaseUrl . '/admin/profile.php'],
|
['id' => 'profile', 'label' => 'Mein Konto', 'href' => $appBaseUrl . '/admin/profile.php'],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -21,10 +21,22 @@ require dirname(__DIR__) . '/../structure/layout_start.php';
|
|||||||
</div>
|
</div>
|
||||||
<form id="bridgeSetupForm" class="space-y-4">
|
<form id="bridgeSetupForm" class="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-sm text-slate-600">Tabellen-Whitelist (optional)</label>
|
<label class="block text-sm text-slate-600">Tabellen-Whitelist (Bridge-Datei)</label>
|
||||||
<textarea name="tables" class="input mt-1" rows="3" placeholder="z.B. customers, orders"></textarea>
|
<div class="grid md:grid-cols-[1fr_auto_1fr] gap-3 mt-2">
|
||||||
<p class="text-xs text-slate-500 mt-1">Kommagetrennt oder je Zeile eine Tabelle. Leer lassen = keine Einschränkung.</p>
|
<div>
|
||||||
<div id="selectedTables" class="flex flex-wrap gap-2 text-sm text-slate-600 mt-2">Noch keine Tabellen angegeben.</div>
|
<p class="text-xs text-slate-500 mb-1">Alle Tabellen (Bridge-Endpunkt)</p>
|
||||||
|
<select id="bridgeTablesAll" class="input" size="8" multiple></select>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col gap-2 justify-center">
|
||||||
|
<button type="button" id="bridgeTablesAdd" class="btn" aria-label="Zur Whitelist hinzufuegen">→</button>
|
||||||
|
<button type="button" id="bridgeTablesRemove" class="btn" aria-label="Aus Whitelist entfernen">←</button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p class="text-xs text-slate-500 mb-1">Whitelist fuer Bridge-Datei</p>
|
||||||
|
<select id="bridgeTablesSelected" class="input" size="8" multiple></select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="text-xs text-slate-500 mt-2">Die Auswahl bestimmt, welche Tabellen in der Bridge-Datei erlaubt sind.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<fieldset class="border border-slate-200 rounded-xl p-4">
|
<fieldset class="border border-slate-200 rounded-xl p-4">
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ require dirname(__DIR__) . '/../structure/layout_start.php';
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="section-card">
|
<section class="section-card">
|
||||||
<p class="text-sm text-slate-600">Teammitglieder, Absender und Integrationen verwaltest du im Bereich <strong>Administration</strong>. Öffne ihn über das Avatar-Menü oben rechts.</p>
|
<p class="text-sm text-slate-600">Teammitglieder, Absender und Integrationen verwaltest du im Bereich <strong>API & Tabellen</strong>. Öffne ihn über das Avatar-Menü oben rechts.</p>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
$pageTitle = 'Email Template System – Administration';
|
$pageTitle = 'Email Template System – API & Tabellen';
|
||||||
$pageId = 'admin';
|
$pageId = 'admin';
|
||||||
$navActive = 'settings';
|
$navActive = 'settings';
|
||||||
require __DIR__ . '/accountsetup_config.php';
|
require __DIR__ . '/accountsetup_config.php';
|
||||||
@@ -110,10 +110,6 @@ require dirname(__DIR__) . '/../structure/layout_start.php';
|
|||||||
<button type="button" class="btn" data-rotate="external">Neu erstellen</button>
|
<button type="button" class="btn" data-rotate="external">Neu erstellen</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col gap-2">
|
|
||||||
<a href="<?= htmlspecialchars($appBaseUrl . '/admin/bridge.php') ?>" class="btn w-max" data-role="admin">Bridge-Setup & Tabellen öffnen</a>
|
|
||||||
<p class="text-xs text-slate-500">Dort kannst du Tabellen-Filter sowie DB-Quellen für die Bridge-Datei konfigurieren.</p>
|
|
||||||
</div>
|
|
||||||
<div class="flex justify-between gap-2 flex-wrap pt-2">
|
<div class="flex justify-between gap-2 flex-wrap pt-2">
|
||||||
<div class="flex gap-2" data-role="admin">
|
<div class="flex gap-2" data-role="admin">
|
||||||
<button type="button" class="btn" data-download="bridge">Bridge-Datei</button>
|
<button type="button" class="btn" data-download="bridge">Bridge-Datei</button>
|
||||||
@@ -123,9 +119,180 @@ require dirname(__DIR__) . '/../structure/layout_start.php';
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section class="section-card" data-role="admin">
|
||||||
|
<div class="flex items-center justify-between flex-wrap gap-3 mb-3">
|
||||||
|
<div>
|
||||||
|
<h4>Bridge-Datei & Tabellenfreigaben</h4>
|
||||||
|
<p class="text-sm text-slate-600">Die Bridge-Konfiguration dient ausschliesslich der Erstellung der Bridge-Datei und steuert, welche Tabellen dort freigegeben sind.</p>
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-2 flex-wrap">
|
||||||
|
<button type="button" id="btn-open-bridge-setup" class="btn">Bridge-Setup oeffnen</button>
|
||||||
|
<button type="button" id="btn-admin-load-bridge" class="btn">Tabellen vom Bridge-Endpunkt laden</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid md:grid-cols-[1fr_auto_1fr] gap-3">
|
||||||
|
<div>
|
||||||
|
<p class="text-xs text-slate-500 mb-1">Freigegebene Tabellen (Bridge-Datei)</p>
|
||||||
|
<select id="adminBridgeTablesAll" class="input" size="8" multiple></select>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col gap-2 justify-center">
|
||||||
|
<button type="button" id="adminBridgeTablesAdd" class="btn" aria-label="Zur Placeholder-Auswahl hinzufuegen">→</button>
|
||||||
|
<button type="button" id="adminBridgeTablesRemove" class="btn" aria-label="Aus Placeholder-Auswahl entfernen">←</button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p class="text-xs text-slate-500 mb-1">Tabellen fuer Placeholder-Auswahl</p>
|
||||||
|
<select id="adminBridgeTablesSelected" class="input" size="8" multiple></select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="text-xs text-slate-500 mt-2">Nur diese Tabellen erscheinen spaeter bei der Placeholder-Auswahl.</p>
|
||||||
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="toast-root"></div>
|
<div id="toast-root"></div>
|
||||||
|
|
||||||
|
<dialog id="bridgeSetupDialog" class="rounded-xl max-w-4xl w-[92vw]">
|
||||||
|
<div class="flex items-center justify-between gap-3 border-b border-slate-200 pb-3 mb-4">
|
||||||
|
<h3 class="text-lg font-semibold">Bridge-Setup</h3>
|
||||||
|
<button type="button" id="btn-close-bridge-setup" class="btn">Schliessen</button>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<p class="text-sm text-slate-600">
|
||||||
|
Diese Angaben werden nur verwendet, um die <strong>emailtemplate_bridge.php</strong> zu generieren. Das EmailTemplate-System selbst behält Zugriff auf alle Tabellen; die hier definierten Whitelists greifen ausschliesslich in der Bridge-Datei.
|
||||||
|
</p>
|
||||||
|
<form id="bridgeSetupForm" class="space-y-4">
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm text-slate-600">Tabellen-Whitelist (Bridge-Datei)</label>
|
||||||
|
<div class="grid md:grid-cols-[1fr_auto_1fr] gap-3 mt-2">
|
||||||
|
<div>
|
||||||
|
<p class="text-xs text-slate-500 mb-1">Alle Tabellen (Bridge-Endpunkt)</p>
|
||||||
|
<select id="bridgeTablesAll" class="input" size="8" multiple></select>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col gap-2 justify-center">
|
||||||
|
<button type="button" id="bridgeTablesAdd" class="btn" aria-label="Zur Whitelist hinzufuegen">→</button>
|
||||||
|
<button type="button" id="bridgeTablesRemove" class="btn" aria-label="Aus Whitelist entfernen">←</button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p class="text-xs text-slate-500 mb-1">Whitelist fuer Bridge-Datei</p>
|
||||||
|
<select id="bridgeTablesSelected" class="input" size="8" multiple></select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="text-xs text-slate-500 mt-2">Die Auswahl bestimmt, welche Tabellen in der Bridge-Datei erlaubt sind.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<fieldset class="border border-slate-200 rounded-xl p-4">
|
||||||
|
<legend class="px-2 text-sm font-semibold">Datenbankquelle</legend>
|
||||||
|
<p class="text-xs text-slate-500 mb-3">Die Auswahl bestimmt, welche Werte in die Bridge-Datei geschrieben werden.</p>
|
||||||
|
<div class="flex flex-wrap gap-4 text-sm text-slate-600 mb-3">
|
||||||
|
<label class="inline-flex items-center gap-2">
|
||||||
|
<input type="radio" name="db_mode" value="direct" checked> Direkte Angaben (Host, DB, Benutzer …)
|
||||||
|
</label>
|
||||||
|
<label class="inline-flex items-center gap-2">
|
||||||
|
<input type="radio" name="db_mode" value="config"> Aus bestehender Konfigurationsdatei laden
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="directFields" class="grid md:grid-cols-2 gap-3">
|
||||||
|
<label class="block text-sm text-slate-600">Server / Host
|
||||||
|
<input type="text" name="direct_host" class="input mt-1" placeholder="127.0.0.1">
|
||||||
|
</label>
|
||||||
|
<label class="block text-sm text-slate-600">Port
|
||||||
|
<input type="number" name="direct_port" class="input mt-1" placeholder="3306">
|
||||||
|
</label>
|
||||||
|
<label class="block text-sm text-slate-600">Datenbankname
|
||||||
|
<input type="text" name="direct_database" class="input mt-1" placeholder="kunden_db">
|
||||||
|
</label>
|
||||||
|
<label class="block text-sm text-slate-600">Zeichensatz
|
||||||
|
<input type="text" name="direct_charset" class="input mt-1" value="utf8mb4">
|
||||||
|
</label>
|
||||||
|
<label class="block text-sm text-slate-600">Benutzername
|
||||||
|
<input type="text" name="direct_user" class="input mt-1" placeholder="db_user">
|
||||||
|
</label>
|
||||||
|
<label class="block text-sm text-slate-600">Passwort
|
||||||
|
<input type="text" name="direct_password" class="input mt-1" placeholder="••••">
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="configFields" class="hidden space-y-3">
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm text-slate-600">Pfad zur Konfigurationsdatei</label>
|
||||||
|
<input type="text" name="config_file" class="input mt-1" placeholder="../config/database.php">
|
||||||
|
<p class="text-xs text-slate-500 mt-1">Relativ zur Bridge-Datei oder absolut. Die Datei sollte ein Array oder Objekt mit den Zugangsdaten liefern.</p>
|
||||||
|
<button type="button" id="btn-config-example" class="btn mt-2 text-xs" data-role="admin">Beispiel: Array-Mapping</button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm text-slate-600">Basis-Pfad im Array (optional)</label>
|
||||||
|
<input type="text" name="config_base" class="input mt-1" placeholder="database.connections.mysql">
|
||||||
|
<p class="text-xs text-slate-500 mt-1">Dot-Notation, um in verschachtelte Arrays zu springen.</p>
|
||||||
|
</div>
|
||||||
|
<div class="grid md:grid-cols-2 gap-3">
|
||||||
|
<label class="block text-sm text-slate-600">Host-Key
|
||||||
|
<input type="text" name="config_host_key" class="input mt-1" placeholder="host">
|
||||||
|
</label>
|
||||||
|
<label class="block text-sm text-slate-600">Port-Key
|
||||||
|
<input type="text" name="config_port_key" class="input mt-1" placeholder="port">
|
||||||
|
</label>
|
||||||
|
<label class="block text-sm text-slate-600">DB-Name-Key
|
||||||
|
<input type="text" name="config_database_key" class="input mt-1" placeholder="database">
|
||||||
|
</label>
|
||||||
|
<label class="block text-sm text-slate-600">Charset-Key
|
||||||
|
<input type="text" name="config_charset_key" class="input mt-1" placeholder="charset">
|
||||||
|
</label>
|
||||||
|
<label class="block text-sm text-slate-600">User-Key
|
||||||
|
<input type="text" name="config_user_key" class="input mt-1" placeholder="username">
|
||||||
|
</label>
|
||||||
|
<label class="block text-sm text-slate-600">Passwort-Key
|
||||||
|
<input type="text" name="config_password_key" class="input mt-1" placeholder="password">
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<p class="text-xs text-slate-500">Alle Keys nutzen Dot-Notation relativ zum Basis-Pfad.</p>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<div class="flex flex-wrap gap-2 items-center">
|
||||||
|
<button type="button" id="btn-load-remote" class="btn" data-role="admin">Tabellen vom Bridge-Endpunkt laden</button>
|
||||||
|
<span class="text-xs text-slate-500">Nutzt Bridge-URL/Token aus den Einstellungen und uebernimmt optional DB-Settings aus der Bridge-Datei.</span>
|
||||||
|
<button type="submit" class="btn">Bridge-Setup speichern</button>
|
||||||
|
</div>
|
||||||
|
<div id="setupStatus" class="text-xs text-slate-500">Noch nicht gespeichert.</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
|
|
||||||
|
<dialog id="configExampleDialog" class="rounded-xl max-w-2xl w-[90vw]">
|
||||||
|
<form method="dialog" class="space-y-3">
|
||||||
|
<h3 class="text-lg font-semibold">Beispiel: Mapping einer Config-Datei</h3>
|
||||||
|
<p class="text-sm text-slate-600">Angenommen, deine <code>../config/database.php</code> liefert folgendes Array:</p>
|
||||||
|
<pre class="bg-slate-900 text-slate-100 text-xs p-3 rounded-lg overflow-auto"><?php echo htmlspecialchars(<<<'PHP'
|
||||||
|
<?php
|
||||||
|
return [
|
||||||
|
'database' => [
|
||||||
|
'connections' => [
|
||||||
|
'default' => [
|
||||||
|
'host' => '127.0.0.1',
|
||||||
|
'port' => 3306,
|
||||||
|
'database' => 'kunden_db',
|
||||||
|
'username' => 'dbuser',
|
||||||
|
'password' => 'secret',
|
||||||
|
'charset' => 'utf8mb4',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
PHP, ENT_QUOTES); ?></pre>
|
||||||
|
<p class="text-sm text-slate-600">Dann trägst du ein:</p>
|
||||||
|
<ul class="text-sm text-slate-700 list-disc ps-5">
|
||||||
|
<li><strong>Pfad zur Konfigurationsdatei:</strong> <code>../config/database.php</code></li>
|
||||||
|
<li><strong>Basis-Pfad:</strong> <code>database.connections.default</code></li>
|
||||||
|
<li><strong>Host-/Port-/DB-/User-/Pass-/Charset-Key:</strong> jeweils <code>host</code>, <code>port</code>, <code>database</code>, <code>username</code>, <code>password</code>, <code>charset</code></li>
|
||||||
|
</ul>
|
||||||
|
<p class="text-sm text-slate-600">Die Bridge liest dann automatisch die Werte aus diesem Array und baut daraus den DSN.</p>
|
||||||
|
<div class="text-right">
|
||||||
|
<button type="submit" class="btn">Verstanden</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</dialog>
|
||||||
<?php
|
<?php
|
||||||
tpl_add_script(app_asset_url('/assets/js/toast.js'));
|
tpl_add_script(app_asset_url('/assets/js/toast.js'));
|
||||||
tpl_add_script(app_asset_url('/assets/js/account.js'), 'footer', false, false, '', null, true);
|
tpl_add_script(app_asset_url('/assets/js/account.js'), 'footer', false, false, '', null, true);
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ $appBaseUrl = $layoutContext['app_base_url'] ?? '';
|
|||||||
|
|
||||||
$navLinks = $navLinks ?? [
|
$navLinks = $navLinks ?? [
|
||||||
['id' => 'dashboard', 'label' => 'Dashboard', 'href' => $appBaseUrl . '/admin/dashboard.php'],
|
['id' => 'dashboard', 'label' => 'Dashboard', 'href' => $appBaseUrl . '/admin/dashboard.php'],
|
||||||
['id' => 'settings', 'label' => 'Administration','href' => $appBaseUrl . '/admin/settings.php'],
|
['id' => 'settings', 'label' => 'API & Tabellen', 'href' => $appBaseUrl . '/admin/settings.php'],
|
||||||
['id' => 'bridge', 'label' => 'Bridge Setup', 'href' => $appBaseUrl . '/admin/bridge.php'],
|
|
||||||
['id' => 'profile', 'label' => 'Mein Konto', 'href' => $appBaseUrl . '/admin/profile.php'],
|
['id' => 'profile', 'label' => 'Mein Konto', 'href' => $appBaseUrl . '/admin/profile.php'],
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -48,8 +47,7 @@ $showNavLinks = !$hasHeaderTabs && !empty($navLinks);
|
|||||||
<div id="userMenuPanel" class="user-menu hidden" role="menu">
|
<div id="userMenuPanel" class="user-menu hidden" role="menu">
|
||||||
<a href="<?= htmlspecialchars($appBaseUrl . '/admin/profile.php') ?>" class="user-menu-item" data-menu="profile">Profil</a>
|
<a href="<?= htmlspecialchars($appBaseUrl . '/admin/profile.php') ?>" class="user-menu-item" data-menu="profile">Profil</a>
|
||||||
<a href="<?= htmlspecialchars($appBaseUrl . '/admin/dashboard.php') ?>" class="user-menu-item" data-role="admin">Dashboard</a>
|
<a href="<?= htmlspecialchars($appBaseUrl . '/admin/dashboard.php') ?>" class="user-menu-item" data-role="admin">Dashboard</a>
|
||||||
<a href="<?= htmlspecialchars($appBaseUrl . '/admin/settings.php') ?>" class="user-menu-item" data-role="admin">Administration</a>
|
<a href="<?= htmlspecialchars($appBaseUrl . '/admin/settings.php') ?>" class="user-menu-item" data-role="admin">API & Tabellen</a>
|
||||||
<a href="<?= htmlspecialchars($appBaseUrl . '/admin/bridge.php') ?>" class="user-menu-item" data-role="admin">Bridge Setup</a>
|
|
||||||
<button id="btn-logout" type="button" class="user-menu-item text-red-600">Logout</button>
|
<button id="btn-logout" type="button" class="user-menu-item text-red-600">Logout</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { apiAction } from './api.js';
|
import { apiAction } from './api.js';
|
||||||
import { initUserPanel, initAccountPage } from './ui-user.js';
|
import { initUserPanel, initAccountPage } from './ui-user.js';
|
||||||
|
import { initBridgeSetupPage } from './bridge-setup-page.js';
|
||||||
import { mountLogoutButton, ensureFloatingLogout } from './ui-auth.js';
|
import { mountLogoutButton, ensureFloatingLogout } from './ui-auth.js';
|
||||||
|
|
||||||
async function ensureAuthenticated() {
|
async function ensureAuthenticated() {
|
||||||
@@ -24,6 +25,7 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||||||
if (!ok) return;
|
if (!ok) return;
|
||||||
initUserPanel();
|
initUserPanel();
|
||||||
initAccountPage();
|
initAccountPage();
|
||||||
|
initBridgeSetupPage();
|
||||||
mountLogoutButton('#btn-logout', { redirect: '/login.php' });
|
mountLogoutButton('#btn-logout', { redirect: '/login.php' });
|
||||||
ensureFloatingLogout({ redirect: '/login.php' });
|
ensureFloatingLogout({ redirect: '/login.php' });
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,11 +3,15 @@ import { apiAction, toast } from './api.js';
|
|||||||
const state = {
|
const state = {
|
||||||
setup: null,
|
setup: null,
|
||||||
loading: false,
|
loading: false,
|
||||||
|
allTables: [],
|
||||||
|
selectedTables: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
let form;
|
let form;
|
||||||
let tablesInput;
|
let tablesAllSelect;
|
||||||
let tablesPreview;
|
let tablesSelectedSelect;
|
||||||
|
let tablesAddBtn;
|
||||||
|
let tablesRemoveBtn;
|
||||||
let modeInputs;
|
let modeInputs;
|
||||||
let directFields;
|
let directFields;
|
||||||
let configFields;
|
let configFields;
|
||||||
@@ -15,12 +19,18 @@ let statusLabel;
|
|||||||
let loadBtn;
|
let loadBtn;
|
||||||
let configExampleBtn;
|
let configExampleBtn;
|
||||||
let configExampleDialog;
|
let configExampleDialog;
|
||||||
|
let bridgeSetupDialog;
|
||||||
|
let openBridgeSetupBtn;
|
||||||
|
let adminLoadBridgeBtn;
|
||||||
|
let closeBridgeSetupBtn;
|
||||||
|
|
||||||
export function initBridgeSetupPage() {
|
export function initBridgeSetupPage() {
|
||||||
form = document.getElementById('bridgeSetupForm');
|
form = document.getElementById('bridgeSetupForm');
|
||||||
if (!form) return;
|
if (!form) return;
|
||||||
tablesInput = form.elements.tables;
|
tablesAllSelect = document.getElementById('bridgeTablesAll');
|
||||||
tablesPreview = document.getElementById('selectedTables');
|
tablesSelectedSelect = document.getElementById('bridgeTablesSelected');
|
||||||
|
tablesAddBtn = document.getElementById('bridgeTablesAdd');
|
||||||
|
tablesRemoveBtn = document.getElementById('bridgeTablesRemove');
|
||||||
directFields = document.getElementById('directFields');
|
directFields = document.getElementById('directFields');
|
||||||
configFields = document.getElementById('configFields');
|
configFields = document.getElementById('configFields');
|
||||||
statusLabel = document.getElementById('setupStatus');
|
statusLabel = document.getElementById('setupStatus');
|
||||||
@@ -28,13 +38,33 @@ export function initBridgeSetupPage() {
|
|||||||
configExampleBtn = document.getElementById('btn-config-example');
|
configExampleBtn = document.getElementById('btn-config-example');
|
||||||
configExampleDialog = document.getElementById('configExampleDialog');
|
configExampleDialog = document.getElementById('configExampleDialog');
|
||||||
modeInputs = Array.from(form.querySelectorAll('input[name="db_mode"]'));
|
modeInputs = Array.from(form.querySelectorAll('input[name="db_mode"]'));
|
||||||
|
bridgeSetupDialog = document.getElementById('bridgeSetupDialog');
|
||||||
|
openBridgeSetupBtn = document.getElementById('btn-open-bridge-setup');
|
||||||
|
adminLoadBridgeBtn = document.getElementById('btn-admin-load-bridge');
|
||||||
|
closeBridgeSetupBtn = document.getElementById('btn-close-bridge-setup');
|
||||||
|
|
||||||
form.addEventListener('submit', submitBridgeSetup);
|
form.addEventListener('submit', submitBridgeSetup);
|
||||||
tablesInput?.addEventListener('input', () => updateTablesPreview(parseTablesInput()));
|
tablesAddBtn?.addEventListener('click', () => addSelectedTables(getSelectedOptions(tablesAllSelect)));
|
||||||
|
tablesRemoveBtn?.addEventListener('click', () => removeSelectedTables(getSelectedOptions(tablesSelectedSelect)));
|
||||||
loadBtn?.addEventListener('click', loadTablesFromBridge);
|
loadBtn?.addEventListener('click', loadTablesFromBridge);
|
||||||
configExampleBtn?.addEventListener('click', () => {
|
configExampleBtn?.addEventListener('click', () => {
|
||||||
if (configExampleDialog?.showModal) configExampleDialog.showModal();
|
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();
|
||||||
|
});
|
||||||
|
adminLoadBridgeBtn?.addEventListener('click', (ev) => {
|
||||||
|
if (bridgeSetupDialog?.showModal && !bridgeSetupDialog.open) {
|
||||||
|
bridgeSetupDialog.showModal();
|
||||||
|
}
|
||||||
|
loadTablesFromBridge(ev, { preserveSelection: state.selectedTables.length > 0 });
|
||||||
|
});
|
||||||
modeInputs.forEach(input => {
|
modeInputs.forEach(input => {
|
||||||
input.addEventListener('change', () => applyModeVisibility(input.value));
|
input.addEventListener('change', () => applyModeVisibility(input.value));
|
||||||
});
|
});
|
||||||
@@ -82,12 +112,12 @@ async function loadBridgeSetup() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function fillForm(setup) {
|
function fillForm(setup, options = {}) {
|
||||||
const data = normalizeSetupInput(setup);
|
const data = normalizeSetupInput(setup);
|
||||||
state.setup = data;
|
state.setup = data;
|
||||||
if (tablesInput) {
|
if (!options.skipTables) {
|
||||||
tablesInput.value = data.tables.join(', ');
|
state.selectedTables = data.tables.slice();
|
||||||
updateTablesPreview(data.tables);
|
updateTableSelects();
|
||||||
}
|
}
|
||||||
const activeMode = (data.mode || 'direct').toLowerCase();
|
const activeMode = (data.mode || 'direct').toLowerCase();
|
||||||
modeInputs.forEach(input => {
|
modeInputs.forEach(input => {
|
||||||
@@ -135,22 +165,50 @@ function applyModeVisibility(mode) {
|
|||||||
configFields?.classList[config]('hidden');
|
configFields?.classList[config]('hidden');
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseTablesInput() {
|
function updateTableSelects() {
|
||||||
if (!tablesInput) return [];
|
const all = state.allTables || [];
|
||||||
return tablesInput.value
|
const selected = state.selectedTables || [];
|
||||||
.split(/[\s,]+/)
|
const selectedSet = new Set(selected);
|
||||||
.map(part => part.trim())
|
const orderedSelected = all.length ? all.filter(name => selectedSet.has(name)) : selected;
|
||||||
.filter(Boolean)
|
const available = all.length ? all.filter(name => !selectedSet.has(name)) : [];
|
||||||
.filter((value, index, arr) => arr.indexOf(value) === index);
|
renderSelect(tablesAllSelect, available, 'Noch keine Tabellen geladen.');
|
||||||
|
renderSelect(tablesSelectedSelect, orderedSelected, 'Noch keine Tabellen ausgewaehlt.');
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateTablesPreview(list) {
|
function renderSelect(selectEl, list, emptyLabel) {
|
||||||
if (!tablesPreview) return;
|
if (!selectEl) return;
|
||||||
|
selectEl.innerHTML = '';
|
||||||
if (!list.length) {
|
if (!list.length) {
|
||||||
tablesPreview.innerHTML = '<span class="text-xs text-slate-500">Noch keine Tabellen angegeben.</span>';
|
const opt = document.createElement('option');
|
||||||
|
opt.textContent = emptyLabel;
|
||||||
|
opt.disabled = true;
|
||||||
|
selectEl.appendChild(opt);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
tablesPreview.innerHTML = list.map(name => `<span class="chip">${escapeHtml(name)}</span>`).join('');
|
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) {
|
async function submitBridgeSetup(ev) {
|
||||||
@@ -158,7 +216,7 @@ async function submitBridgeSetup(ev) {
|
|||||||
if (!form) return;
|
if (!form) return;
|
||||||
const mode = form.querySelector('input[name="db_mode"]:checked')?.value || 'direct';
|
const mode = form.querySelector('input[name="db_mode"]:checked')?.value || 'direct';
|
||||||
const payload = {
|
const payload = {
|
||||||
tables: parseTablesInput(),
|
tables: normalizeTableNames(state.selectedTables),
|
||||||
mode,
|
mode,
|
||||||
direct_host: form.direct_host?.value.trim() || '',
|
direct_host: form.direct_host?.value.trim() || '',
|
||||||
direct_port: Number(form.direct_port?.value || 0) || 3306,
|
direct_port: Number(form.direct_port?.value || 0) || 3306,
|
||||||
@@ -182,13 +240,17 @@ async function submitBridgeSetup(ev) {
|
|||||||
fillForm(res.setup || payload);
|
fillForm(res.setup || payload);
|
||||||
updateStatus('Bridge-Setup gespeichert.');
|
updateStatus('Bridge-Setup gespeichert.');
|
||||||
toast('Bridge-Setup gespeichert', true);
|
toast('Bridge-Setup gespeichert', true);
|
||||||
|
if (bridgeSetupDialog?.open) {
|
||||||
|
bridgeSetupDialog.close();
|
||||||
|
}
|
||||||
|
window.dispatchEvent(new CustomEvent('bridge-setup-updated', { detail: res.setup || payload }));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
toast(err.message || 'Bridge-Setup konnte nicht gespeichert werden', false);
|
toast(err.message || 'Bridge-Setup konnte nicht gespeichert werden', false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadTablesFromBridge(ev) {
|
async function loadTablesFromBridge(ev, options = {}) {
|
||||||
ev?.preventDefault();
|
ev?.preventDefault();
|
||||||
if (!loadBtn) return;
|
if (!loadBtn) return;
|
||||||
loadBtn.disabled = true;
|
loadBtn.disabled = true;
|
||||||
@@ -196,18 +258,32 @@ async function loadTablesFromBridge(ev) {
|
|||||||
const res = await apiAction('account.bridge.test', { method: 'POST', data: {} });
|
const res = await apiAction('account.bridge.test', { method: 'POST', data: {} });
|
||||||
if (!res?.ok) throw new Error(res?.error || 'Bridge konnte nicht abgefragt werden');
|
if (!res?.ok) throw new Error(res?.error || 'Bridge konnte nicht abgefragt werden');
|
||||||
const fetchedTables = normalizeTableNames(res.tables);
|
const fetchedTables = normalizeTableNames(res.tables);
|
||||||
const allowedTables = normalizeTableNames(res.setup_hint?.tables ?? fetchedTables);
|
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 = {
|
const merged = {
|
||||||
...(state.setup || {}),
|
...(state.setup || {}),
|
||||||
tables: allowedTables,
|
|
||||||
...(res.setup_hint || {}),
|
...(res.setup_hint || {}),
|
||||||
};
|
};
|
||||||
if (res.setup_hint) {
|
|
||||||
merged.mode = res.setup_hint.mode || merged.mode;
|
merged.mode = res.setup_hint.mode || merged.mode;
|
||||||
merged.direct = { ...(merged.direct || {}), ...(res.setup_hint.direct || {}) };
|
merged.direct = { ...(merged.direct || {}), ...(res.setup_hint.direct || {}) };
|
||||||
merged.config = { ...(merged.config || {}), ...(res.setup_hint.config || {}) };
|
merged.config = { ...(merged.config || {}), ...(res.setup_hint.config || {}) };
|
||||||
|
fillForm(merged, { skipTables: true });
|
||||||
}
|
}
|
||||||
fillForm(merged);
|
|
||||||
|
updateTableSelects();
|
||||||
updateStatus(`Tabellen geladen (${fetchedTables.length}).`);
|
updateStatus(`Tabellen geladen (${fetchedTables.length}).`);
|
||||||
toast('Tabellen erfolgreich geladen', true);
|
toast('Tabellen erfolgreich geladen', true);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -246,15 +322,6 @@ function updateStatus(msg) {
|
|||||||
statusLabel.textContent = `${msg} (${time})`;
|
statusLabel.textContent = `${msg} (${time})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function escapeHtml(str) {
|
|
||||||
return String(str || '')
|
|
||||||
.replace(/&/g, '&')
|
|
||||||
.replace(/</g, '<')
|
|
||||||
.replace(/>/g, '>')
|
|
||||||
.replace(/"/g, '"')
|
|
||||||
.replace(/'/g, ''');
|
|
||||||
}
|
|
||||||
|
|
||||||
function normalizeSetupInput(input) {
|
function normalizeSetupInput(input) {
|
||||||
const base = defaultSetup();
|
const base = defaultSetup();
|
||||||
if (!input || typeof input !== 'object') return base;
|
if (!input || typeof input !== 'object') return base;
|
||||||
|
|||||||
@@ -36,6 +36,10 @@ let consoleContainer;
|
|||||||
let debugStylesInjected = false;
|
let debugStylesInjected = false;
|
||||||
let consolePatched = false;
|
let consolePatched = false;
|
||||||
const consoleBuffer = [];
|
const consoleBuffer = [];
|
||||||
|
let adminTablesAllSelect;
|
||||||
|
let adminTablesSelectedSelect;
|
||||||
|
let adminTablesAddBtn;
|
||||||
|
let adminTablesRemoveBtn;
|
||||||
|
|
||||||
ensureConsoleCapture();
|
ensureConsoleCapture();
|
||||||
|
|
||||||
@@ -61,6 +65,10 @@ export function initAccountPage() {
|
|||||||
userForm = document.getElementById('userForm');
|
userForm = document.getElementById('userForm');
|
||||||
senderTable = document.getElementById('senderTable');
|
senderTable = document.getElementById('senderTable');
|
||||||
senderForm = document.getElementById('senderForm');
|
senderForm = document.getElementById('senderForm');
|
||||||
|
adminTablesAllSelect = document.getElementById('adminBridgeTablesAll');
|
||||||
|
adminTablesSelectedSelect = document.getElementById('adminBridgeTablesSelected');
|
||||||
|
adminTablesAddBtn = document.getElementById('adminBridgeTablesAdd');
|
||||||
|
adminTablesRemoveBtn = document.getElementById('adminBridgeTablesRemove');
|
||||||
|
|
||||||
document.getElementById('btn-user-add')?.addEventListener('click', () => openUserForm());
|
document.getElementById('btn-user-add')?.addEventListener('click', () => openUserForm());
|
||||||
document.getElementById('userFormCancel')?.addEventListener('click', () => closeUserForm());
|
document.getElementById('userFormCancel')?.addEventListener('click', () => closeUserForm());
|
||||||
@@ -98,6 +106,18 @@ export function initAccountPage() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
adminTablesAddBtn?.addEventListener('click', () => {
|
||||||
|
addAdminTables(getSelectedOptions(adminTablesAllSelect));
|
||||||
|
});
|
||||||
|
adminTablesRemoveBtn?.addEventListener('click', () => {
|
||||||
|
removeAdminTables(getSelectedOptions(adminTablesSelectedSelect));
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener('bridge-setup-updated', (ev) => {
|
||||||
|
const setup = ev?.detail || {};
|
||||||
|
refreshAdminTables(setup.tables || [], state.settings.bridge_tables || []);
|
||||||
|
});
|
||||||
|
|
||||||
switchTab(state.currentTab);
|
switchTab(state.currentTab);
|
||||||
loadAccountData();
|
loadAccountData();
|
||||||
updateRoleVisibility();
|
updateRoleVisibility();
|
||||||
@@ -244,6 +264,7 @@ function fillSettingsForm(settings) {
|
|||||||
settingsForm.sender_token.value = settings.sender_token || '';
|
settingsForm.sender_token.value = settings.sender_token || '';
|
||||||
settingsForm.external_api_token.value = settings.external_api_token || '';
|
settingsForm.external_api_token.value = settings.external_api_token || '';
|
||||||
state.rotate = { bridge: false, sender: false, external: false };
|
state.rotate = { bridge: false, sender: false, external: false };
|
||||||
|
refreshAdminTables(settings.bridge_setup?.tables || [], settings.bridge_tables || []);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function submitProfileForm(ev) {
|
async function submitProfileForm(ev) {
|
||||||
@@ -281,11 +302,13 @@ async function submitPasswordForm(ev) {
|
|||||||
|
|
||||||
async function submitSettingsForm(ev) {
|
async function submitSettingsForm(ev) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
const bridgeTables = normalizeTableList(state.settings.bridge_tables || []);
|
||||||
const data = {
|
const data = {
|
||||||
bridge_url: settingsForm.bridge_url.value.trim(),
|
bridge_url: settingsForm.bridge_url.value.trim(),
|
||||||
bridge_token: settingsForm.bridge_token.value.trim(),
|
bridge_token: settingsForm.bridge_token.value.trim(),
|
||||||
sender_token: settingsForm.sender_token.value.trim(),
|
sender_token: settingsForm.sender_token.value.trim(),
|
||||||
external_api_token: settingsForm.external_api_token.value.trim(),
|
external_api_token: settingsForm.external_api_token.value.trim(),
|
||||||
|
bridge_tables: bridgeTables,
|
||||||
rotate_bridge_token: state.rotate.bridge ? 1 : 0,
|
rotate_bridge_token: state.rotate.bridge ? 1 : 0,
|
||||||
rotate_sender_token: state.rotate.sender ? 1 : 0,
|
rotate_sender_token: state.rotate.sender ? 1 : 0,
|
||||||
rotate_external_token: state.rotate.external ? 1 : 0,
|
rotate_external_token: state.rotate.external ? 1 : 0,
|
||||||
@@ -322,6 +345,83 @@ async function downloadFile(type) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeTableList(input) {
|
||||||
|
const items = Array.isArray(input) ? input : (typeof input === 'string' ? input.split(/[\s,]+/) : []);
|
||||||
|
const result = [];
|
||||||
|
const seen = new Set();
|
||||||
|
items.forEach(entry => {
|
||||||
|
const name = String(entry || '').trim();
|
||||||
|
if (name && !seen.has(name)) {
|
||||||
|
seen.add(name);
|
||||||
|
result.push(name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function refreshAdminTables(availableTables, selectedTables) {
|
||||||
|
const whitelist = normalizeTableList(availableTables);
|
||||||
|
let selected = normalizeTableList(selectedTables);
|
||||||
|
if (!selected.length) {
|
||||||
|
selected = whitelist.slice();
|
||||||
|
}
|
||||||
|
if (whitelist.length) {
|
||||||
|
selected = selected.filter(name => whitelist.includes(name));
|
||||||
|
}
|
||||||
|
state.settings.bridge_tables = selected;
|
||||||
|
state.settings.bridge_setup = state.settings.bridge_setup || {};
|
||||||
|
state.settings.bridge_setup.tables = whitelist;
|
||||||
|
updateAdminTableSelects(whitelist, selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateAdminTableSelects(availableTables, selectedTables) {
|
||||||
|
const selectedSet = new Set(selectedTables);
|
||||||
|
const available = availableTables.filter(name => !selectedSet.has(name));
|
||||||
|
renderSelect(adminTablesAllSelect, available, 'Keine Tabellen freigegeben.');
|
||||||
|
renderSelect(adminTablesSelectedSelect, selectedTables, '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 addAdminTables(list) {
|
||||||
|
const whitelist = normalizeTableList(state.settings.bridge_setup?.tables || []);
|
||||||
|
if (!whitelist.length) return;
|
||||||
|
const selected = normalizeTableList(state.settings.bridge_tables || []);
|
||||||
|
const merged = normalizeTableList([...selected, ...list]).filter(name => whitelist.includes(name));
|
||||||
|
state.settings.bridge_tables = merged;
|
||||||
|
updateAdminTableSelects(whitelist, merged);
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeAdminTables(list) {
|
||||||
|
const whitelist = normalizeTableList(state.settings.bridge_setup?.tables || []);
|
||||||
|
if (!whitelist.length) return;
|
||||||
|
const removeSet = new Set(list);
|
||||||
|
const next = normalizeTableList(state.settings.bridge_tables || []).filter(name => !removeSet.has(name));
|
||||||
|
state.settings.bridge_tables = next;
|
||||||
|
updateAdminTableSelects(whitelist, next);
|
||||||
|
}
|
||||||
|
|
||||||
async function loadUsers() {
|
async function loadUsers() {
|
||||||
try {
|
try {
|
||||||
const res = await apiAction('account.users.list', { method: 'GET' });
|
const res = await apiAction('account.users.list', { method: 'GET' });
|
||||||
|
|||||||
@@ -1473,6 +1473,22 @@ class ApiKernel
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$settings = $this->getCustomerSettings($customerId);
|
||||||
|
$allowedTables = $this->normalizeBridgeTables($settings['bridge_tables'] ?? []);
|
||||||
|
if ($allowedTables && !empty($schema['tables']) && is_array($schema['tables'])) {
|
||||||
|
$allowedMap = array_fill_keys($allowedTables, true);
|
||||||
|
$schema['tables'] = array_values(array_filter($schema['tables'], static function ($entry) use ($allowedMap) {
|
||||||
|
if (is_string($entry)) {
|
||||||
|
$name = $entry;
|
||||||
|
} elseif (is_array($entry)) {
|
||||||
|
$name = $entry['name'] ?? $entry['table'] ?? $entry['label'] ?? '';
|
||||||
|
} else {
|
||||||
|
$name = '';
|
||||||
|
}
|
||||||
|
return $name !== '' && isset($allowedMap[$name]);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
$this->respond([
|
$this->respond([
|
||||||
'ok' => true,
|
'ok' => true,
|
||||||
'tables' => $schema['tables'] ?? [],
|
'tables' => $schema['tables'] ?? [],
|
||||||
@@ -1664,6 +1680,7 @@ class ApiKernel
|
|||||||
$rotateBridge = !empty($this->in['rotate_bridge_token']);
|
$rotateBridge = !empty($this->in['rotate_bridge_token']);
|
||||||
$rotateSender = !empty($this->in['rotate_sender_token']);
|
$rotateSender = !empty($this->in['rotate_sender_token']);
|
||||||
$rotateExternal = !empty($this->in['rotate_external_token']);
|
$rotateExternal = !empty($this->in['rotate_external_token']);
|
||||||
|
$bridgeTables = $this->normalizeBridgeTables($this->in['bridge_tables'] ?? []);
|
||||||
|
|
||||||
if ($bridgeUrl && !filter_var($bridgeUrl, FILTER_VALIDATE_URL)) {
|
if ($bridgeUrl && !filter_var($bridgeUrl, FILTER_VALIDATE_URL)) {
|
||||||
$this->fail('Ungültige Bridge-URL', null, 422);
|
$this->fail('Ungültige Bridge-URL', null, 422);
|
||||||
@@ -1679,6 +1696,7 @@ class ApiKernel
|
|||||||
'bridge_token' => $bridgeToken,
|
'bridge_token' => $bridgeToken,
|
||||||
'sender_token' => $senderToken,
|
'sender_token' => $senderToken,
|
||||||
'external_api_token' => $externalToken,
|
'external_api_token' => $externalToken,
|
||||||
|
'bridge_tables' => $bridgeTables,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->respond(['ok' => true, 'settings' => $settings]);
|
$this->respond(['ok' => true, 'settings' => $settings]);
|
||||||
|
|||||||
Reference in New Issue
Block a user