yxyX
This commit is contained in:
@@ -13,6 +13,8 @@ const state = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const pageType = document.body?.dataset?.page || 'account';
|
const pageType = document.body?.dataset?.page || 'account';
|
||||||
|
const DEBUG_EMAIL = 'madmin@papa-kind-treff.info';
|
||||||
|
const MAX_CONSOLE_LINES = 200;
|
||||||
|
|
||||||
let avatarBtn;
|
let avatarBtn;
|
||||||
let userMenuPanel;
|
let userMenuPanel;
|
||||||
@@ -27,12 +29,22 @@ let bridgePreview;
|
|||||||
let validateBridgeBtn;
|
let validateBridgeBtn;
|
||||||
let menuInitialized = false;
|
let menuInitialized = false;
|
||||||
let menuOpen = false;
|
let menuOpen = false;
|
||||||
|
let debugButton;
|
||||||
|
let debugDialog;
|
||||||
|
let debugPhpLoaded = false;
|
||||||
|
let debugPhpLoading = false;
|
||||||
|
let debugActiveTab = 'php';
|
||||||
|
let phpInfoContainer;
|
||||||
|
let consoleContainer;
|
||||||
|
let debugStylesInjected = false;
|
||||||
|
let consolePatched = false;
|
||||||
|
const consoleBuffer = [];
|
||||||
|
|
||||||
export function initUserPanel() {
|
export function initUserPanel() {
|
||||||
avatarBtn = document.getElementById('btn-user');
|
avatarBtn = document.getElementById('btn-user');
|
||||||
userMenuPanel = document.getElementById('userMenuPanel');
|
userMenuPanel = document.getElementById('userMenuPanel');
|
||||||
updateAvatar();
|
ensureConsoleCapture();
|
||||||
updateRoleVisibility();
|
handleUserContextChange();
|
||||||
if (!menuInitialized && avatarBtn && userMenuPanel) {
|
if (!menuInitialized && avatarBtn && userMenuPanel) {
|
||||||
avatarBtn.addEventListener('click', toggleUserMenu);
|
avatarBtn.addEventListener('click', toggleUserMenu);
|
||||||
document.addEventListener('click', handleDocumentClick, true);
|
document.addEventListener('click', handleDocumentClick, true);
|
||||||
@@ -112,6 +124,13 @@ function enforcePageAccess() {
|
|||||||
window.location.href = '/account.php';
|
window.location.href = '/account.php';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleUserContextChange() {
|
||||||
|
updateAvatar();
|
||||||
|
updateRoleVisibility();
|
||||||
|
enforcePageAccess();
|
||||||
|
refreshDebugAccess();
|
||||||
|
}
|
||||||
|
|
||||||
function updateAvatar() {
|
function updateAvatar() {
|
||||||
const target = document.getElementById('userAvatar');
|
const target = document.getElementById('userAvatar');
|
||||||
if (!target) return;
|
if (!target) return;
|
||||||
@@ -191,9 +210,7 @@ async function loadAccountData() {
|
|||||||
if (!res?.ok) throw new Error(res?.error || 'Profil konnte nicht geladen werden');
|
if (!res?.ok) throw new Error(res?.error || 'Profil konnte nicht geladen werden');
|
||||||
if (res.user) {
|
if (res.user) {
|
||||||
window.__currentUser = res.user;
|
window.__currentUser = res.user;
|
||||||
updateAvatar();
|
handleUserContextChange();
|
||||||
updateRoleVisibility();
|
|
||||||
enforcePageAccess();
|
|
||||||
}
|
}
|
||||||
fillProfileForm(res.user);
|
fillProfileForm(res.user);
|
||||||
fillSettingsForm(res.settings || {});
|
fillSettingsForm(res.settings || {});
|
||||||
@@ -246,7 +263,7 @@ async function submitProfileForm(ev) {
|
|||||||
const res = await apiAction('account.profile.update', { method: 'POST', data });
|
const res = await apiAction('account.profile.update', { method: 'POST', data });
|
||||||
if (!res?.ok) throw new Error(res?.error || 'Profil konnte nicht gespeichert werden');
|
if (!res?.ok) throw new Error(res?.error || 'Profil konnte nicht gespeichert werden');
|
||||||
window.__currentUser = res.user;
|
window.__currentUser = res.user;
|
||||||
updateAvatar();
|
handleUserContextChange();
|
||||||
toast('Profil aktualisiert', true);
|
toast('Profil aktualisiert', true);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
toast(err.message || 'Fehler beim Speichern', false);
|
toast(err.message || 'Fehler beim Speichern', false);
|
||||||
@@ -591,3 +608,183 @@ function escapeHtml(str) {
|
|||||||
.replace(/"/g, '"')
|
.replace(/"/g, '"')
|
||||||
.replace(/'/g, ''');
|
.replace(/'/g, ''');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function refreshDebugAccess() {
|
||||||
|
const allowed = (window.__currentUser?.email || '').toLowerCase() === DEBUG_EMAIL;
|
||||||
|
if (!allowed) {
|
||||||
|
debugButton?.remove();
|
||||||
|
debugButton = null;
|
||||||
|
if (debugDialog?.open) {
|
||||||
|
debugDialog.close();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ensureDebugStyles();
|
||||||
|
ensureConsoleCapture();
|
||||||
|
ensureDebugDialog();
|
||||||
|
if (!debugButton) {
|
||||||
|
debugButton = document.createElement('button');
|
||||||
|
debugButton.id = 'debugToggleButton';
|
||||||
|
debugButton.className = 'debug-floating-btn';
|
||||||
|
debugButton.type = 'button';
|
||||||
|
debugButton.textContent = 'Debug';
|
||||||
|
debugButton.addEventListener('click', () => openDebugDialog('php'));
|
||||||
|
document.body.appendChild(debugButton);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureDebugStyles() {
|
||||||
|
if (debugStylesInjected) return;
|
||||||
|
const style = document.createElement('style');
|
||||||
|
style.id = 'debugStyles';
|
||||||
|
style.textContent = `
|
||||||
|
.debug-floating-btn{position:fixed;left:16px;bottom:16px;padding:.5rem 1rem;border-radius:999px;border:none;background:#0ea5e9;color:#fff;font-weight:600;box-shadow:0 10px 25px rgba(15,23,42,.25);z-index:60;cursor:pointer}
|
||||||
|
.debug-floating-btn:hover{background:#0284c7}
|
||||||
|
dialog#debugDialog::backdrop{background:rgba(15,23,42,.45)}
|
||||||
|
#debugDialog{border:none;border-radius:1rem;padding:0;width:80vw;max-width:960px}
|
||||||
|
.debug-shell{display:flex;flex-direction:column;height:80vh}
|
||||||
|
.debug-header{display:flex;align-items:center;justify-content:space-between;padding:1rem;border-bottom:1px solid #e2e8f0}
|
||||||
|
.debug-tabs{display:flex;border-bottom:1px solid #e2e8f0}
|
||||||
|
.debug-tabs button{flex:1;padding:.75rem 1rem;border:none;background:transparent;cursor:pointer;font-weight:600}
|
||||||
|
.debug-tabs button.active{background:#e0f2fe;color:#0c4a6e}
|
||||||
|
.debug-panel{flex:1;overflow:auto;padding:1rem;background:#f8fafc}
|
||||||
|
#debugConsoleContent{font-family:monospace;font-size:.85rem;white-space:pre-wrap}
|
||||||
|
.debug-console-entry{margin-bottom:.35rem}
|
||||||
|
.debug-console-entry.log{color:#15803d}
|
||||||
|
.debug-console-entry.warn{color:#b45309}
|
||||||
|
.debug-console-entry.error{color:#b91c1c}
|
||||||
|
`;
|
||||||
|
document.head.appendChild(style);
|
||||||
|
debugStylesInjected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureDebugDialog() {
|
||||||
|
if (debugDialog) return;
|
||||||
|
debugDialog = document.createElement('dialog');
|
||||||
|
debugDialog.id = 'debugDialog';
|
||||||
|
debugDialog.innerHTML = `
|
||||||
|
<div class="debug-shell bg-white rounded-2xl shadow-2xl">
|
||||||
|
<div class="debug-header">
|
||||||
|
<strong>Debug Tools</strong>
|
||||||
|
<button type="button" class="btn" data-debug-close>Schließen</button>
|
||||||
|
</div>
|
||||||
|
<div class="debug-tabs">
|
||||||
|
<button type="button" data-debug-tab="php" class="active">PHP Debug</button>
|
||||||
|
<button type="button" data-debug-tab="console">Console</button>
|
||||||
|
</div>
|
||||||
|
<div class="debug-panel" data-debug-panel="php">
|
||||||
|
<div id="debugPhpContent" class="text-sm text-slate-700">Lade Daten…</div>
|
||||||
|
</div>
|
||||||
|
<div class="debug-panel hidden" data-debug-panel="console">
|
||||||
|
<pre id="debugConsoleContent"></pre>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
document.body.appendChild(debugDialog);
|
||||||
|
phpInfoContainer = debugDialog.querySelector('#debugPhpContent');
|
||||||
|
consoleContainer = debugDialog.querySelector('#debugConsoleContent');
|
||||||
|
debugDialog.querySelector('[data-debug-close]')?.addEventListener('click', () => closeDebugDialog());
|
||||||
|
debugDialog.addEventListener('close', () => setDebugTab('php'));
|
||||||
|
debugDialog.querySelectorAll('[data-debug-tab]').forEach(btn => {
|
||||||
|
btn.addEventListener('click', () => setDebugTab(btn.getAttribute('data-debug-tab')));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function openDebugDialog(tab = 'php') {
|
||||||
|
ensureDebugDialog();
|
||||||
|
setDebugTab(tab);
|
||||||
|
if (!debugDialog.open) debugDialog.showModal();
|
||||||
|
if (tab === 'php') {
|
||||||
|
loadPhpInfo();
|
||||||
|
} else {
|
||||||
|
renderConsolePanel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeDebugDialog() {
|
||||||
|
if (debugDialog?.open) debugDialog.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setDebugTab(tab) {
|
||||||
|
if (!debugDialog) return;
|
||||||
|
debugActiveTab = tab || 'php';
|
||||||
|
debugDialog.querySelectorAll('[data-debug-tab]').forEach(btn => {
|
||||||
|
const isActive = btn.getAttribute('data-debug-tab') === debugActiveTab;
|
||||||
|
btn.classList.toggle('active', isActive);
|
||||||
|
});
|
||||||
|
debugDialog.querySelectorAll('[data-debug-panel]').forEach(panel => {
|
||||||
|
panel.classList.toggle('hidden', panel.getAttribute('data-debug-panel') !== debugActiveTab);
|
||||||
|
});
|
||||||
|
if (debugActiveTab === 'php') {
|
||||||
|
loadPhpInfo();
|
||||||
|
} else {
|
||||||
|
renderConsolePanel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadPhpInfo() {
|
||||||
|
if (debugPhpLoaded || debugPhpLoading || !phpInfoContainer) return;
|
||||||
|
debugPhpLoading = true;
|
||||||
|
phpInfoContainer.textContent = 'Lade phpinfo…';
|
||||||
|
try {
|
||||||
|
const res = await apiAction('debug.phpinfo', { method: 'GET' });
|
||||||
|
if (!res?.ok) throw new Error(res?.error || 'Fehler beim Laden');
|
||||||
|
const frame = document.createElement('iframe');
|
||||||
|
frame.style.width = '100%';
|
||||||
|
frame.style.height = '100%';
|
||||||
|
frame.style.minHeight = '400px';
|
||||||
|
frame.style.border = 'none';
|
||||||
|
frame.srcdoc = res.html || '<p>Keine Daten</p>';
|
||||||
|
phpInfoContainer.innerHTML = '';
|
||||||
|
phpInfoContainer.appendChild(frame);
|
||||||
|
debugPhpLoaded = true;
|
||||||
|
} catch (err) {
|
||||||
|
phpInfoContainer.textContent = err.message || 'Fehler beim Laden';
|
||||||
|
} finally {
|
||||||
|
debugPhpLoading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderConsolePanel() {
|
||||||
|
if (!consoleContainer) return;
|
||||||
|
if (!consoleBuffer.length) {
|
||||||
|
consoleContainer.textContent = 'Noch keine Konsolenmeldungen in dieser Sitzung.';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const lines = consoleBuffer.map(entry => {
|
||||||
|
const time = entry.time.toLocaleTimeString();
|
||||||
|
return `<div class="debug-console-entry ${entry.type}">[${time}] ${escapeHtml(entry.text)}</div>`;
|
||||||
|
});
|
||||||
|
consoleContainer.innerHTML = lines.join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureConsoleCapture() {
|
||||||
|
if (consolePatched) return;
|
||||||
|
['log', 'warn', 'error'].forEach(type => {
|
||||||
|
const original = console[type];
|
||||||
|
console[type] = function (...args) {
|
||||||
|
appendConsoleMessage(type, args);
|
||||||
|
if (typeof original === 'function') {
|
||||||
|
original.apply(console, args);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
consolePatched = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function appendConsoleMessage(type, args) {
|
||||||
|
const text = args.map(formatConsoleArg).join(' ');
|
||||||
|
consoleBuffer.push({ type, text, time: new Date() });
|
||||||
|
if (consoleBuffer.length > MAX_CONSOLE_LINES) consoleBuffer.shift();
|
||||||
|
if (debugActiveTab === 'console' && debugDialog?.open) {
|
||||||
|
renderConsolePanel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatConsoleArg(arg) {
|
||||||
|
if (typeof arg === 'string') return arg;
|
||||||
|
try {
|
||||||
|
return JSON.stringify(arg);
|
||||||
|
} catch (err) {
|
||||||
|
return String(arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -859,6 +859,9 @@ class ApiKernel
|
|||||||
case 'placeholders.schema':
|
case 'placeholders.schema':
|
||||||
$this->handlePlaceholderSchema();
|
$this->handlePlaceholderSchema();
|
||||||
break;
|
break;
|
||||||
|
case 'debug.phpinfo':
|
||||||
|
$this->handleDebugPhpInfo();
|
||||||
|
break;
|
||||||
case 'templates.test_send':
|
case 'templates.test_send':
|
||||||
$this->handleTemplateTestSend();
|
$this->handleTemplateTestSend();
|
||||||
break;
|
break;
|
||||||
@@ -1820,6 +1823,16 @@ class ApiKernel
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function handleDebugPhpInfo(): void
|
||||||
|
{
|
||||||
|
$user = $this->authService->requireAuth();
|
||||||
|
$this->ensureDebugUser($user);
|
||||||
|
ob_start();
|
||||||
|
phpinfo(INFO_GENERAL | INFO_CONFIGURATION | INFO_MODULES | INFO_ENVIRONMENT);
|
||||||
|
$html = ob_get_clean() ?: '';
|
||||||
|
$this->respond(['ok' => true, 'html' => $html]);
|
||||||
|
}
|
||||||
|
|
||||||
private function resolveBridgeConfig(?int $customerId): array
|
private function resolveBridgeConfig(?int $customerId): array
|
||||||
{
|
{
|
||||||
$fileConf = $this->conf['placeholders']['bridge'] ?? [];
|
$fileConf = $this->conf['placeholders']['bridge'] ?? [];
|
||||||
@@ -2146,6 +2159,14 @@ class ApiKernel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function ensureDebugUser(array $user): void
|
||||||
|
{
|
||||||
|
$email = strtolower((string)($user['email'] ?? ''));
|
||||||
|
if ($email !== 'madmin@papa-kind-treff.info') {
|
||||||
|
$this->fail('Debug nicht erlaubt', null, 403);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private function defaultApiBase(): string
|
private function defaultApiBase(): string
|
||||||
{
|
{
|
||||||
$base = $this->conf['base_url'] ?? '';
|
$base = $this->conf['base_url'] ?? '';
|
||||||
|
|||||||
Reference in New Issue
Block a user