(() => { const layout = document.querySelector('.layout-body'); if (!layout) return; const enabled = layout.dataset.sidebarEnabled === '1'; const collapsible = layout.dataset.sidebarCollapsible === '1'; const defaultState = layout.dataset.sidebarDefault || 'collapsed'; const toggles = document.querySelectorAll('[data-sidebar-toggle]'); if (!enabled || !collapsible) { toggles.forEach((t) => t.remove()); return; } const saved = localStorage.getItem('sidebar-state'); const initial = saved || defaultState; if (initial === 'open') { layout.classList.add('sidebar-open'); } else { layout.classList.remove('sidebar-open'); } toggles.forEach((toggle) => { toggle.addEventListener('click', () => { layout.classList.toggle('sidebar-open'); localStorage.setItem('sidebar-state', layout.classList.contains('sidebar-open') ? 'open' : 'collapsed'); }); }); })(); (() => { const openBtn = document.querySelector('[data-debug-open]'); const modal = document.getElementById('debug-modal'); if (!openBtn || !modal) return; const listEl = document.getElementById('debug-log-list'); const contentEl = document.getElementById('debug-log-content'); const closeEls = modal.querySelectorAll('[data-debug-close]'); const open = () => { modal.classList.add('is-open'); modal.setAttribute('aria-hidden', 'false'); loadList(); startRefresh(); }; const close = () => { modal.classList.remove('is-open'); modal.setAttribute('aria-hidden', 'true'); if (refreshTimer) { clearInterval(refreshTimer); refreshTimer = null; } }; let activeFile = null; let refreshTimer = null; const loadList = async () => { try { const res = await fetch('/debug?list=1', { cache: 'no-store' }); if (!res.ok) { listEl.innerHTML = `
  • Fehler: ${res.status}
  • `; return; } const files = await res.json(); listEl.innerHTML = ''; files.forEach((f) => { const a = document.createElement('a'); a.href = '#'; a.textContent = f; a.addEventListener('click', (e) => { e.preventDefault(); loadFile(f); }); const li = document.createElement('li'); li.appendChild(a); listEl.appendChild(li); }); if (files.includes('oidc_login.log')) { loadFile('oidc_login.log'); } } catch (e) { listEl.innerHTML = '
  • Fehler beim Laden der Logs.
  • '; } }; const loadFile = async (name) => { activeFile = name; try { const res = await fetch(`/debug?raw=1&file=${encodeURIComponent(name)}&tail=200`, { cache: 'no-store' }); if (!res.ok) { contentEl.textContent = `Fehler: ${res.status}`; return; } const text = await res.text(); contentEl.textContent = formatLog(text); } catch (e) { contentEl.textContent = 'Fehler beim Laden der Datei.'; } }; const formatLog = (text) => { const lines = text.split(/\\r?\\n/).filter(Boolean); const pretty = lines.map((line) => { try { const obj = JSON.parse(line); return JSON.stringify(obj, null, 2); } catch (e) { return line; } }); return pretty.join('\\n\\n'); }; const startRefresh = () => { if (refreshTimer) clearInterval(refreshTimer); refreshTimer = setInterval(() => { if (activeFile) loadFile(activeFile); }, 3000); }; openBtn.addEventListener('click', open); closeEls.forEach((el) => el.addEventListener('click', close)); if (modal.classList.contains('is-open')) { startRefresh(); } })(); (() => { const tabBar = document.querySelector('[data-console-tab-bar]'); const tabPanels = document.querySelector('[data-console-tab-panels]'); if (!tabBar || !tabPanels) return; let tabCount = 0; const idleMs = 5 * 60 * 1000; const idleTimers = new Map(); const activateTab = (id) => { tabBar.querySelectorAll('.console-tab').forEach((btn) => { btn.classList.toggle('is-active', btn.dataset.tabId === id); }); tabPanels.querySelectorAll('.console-panel').forEach((panel) => { panel.classList.toggle('is-active', panel.dataset.tabId === id); }); }; const openTab = (label, url) => { const id = `tab-${++tabCount}`; const btn = document.createElement('button'); btn.type = 'button'; btn.className = 'console-tab'; btn.textContent = label || 'Konsole'; btn.dataset.tabId = id; btn.addEventListener('click', () => activateTab(id)); const closeBtn = document.createElement('span'); closeBtn.className = 'console-tab-close'; closeBtn.textContent = '×'; closeBtn.addEventListener('click', (e) => { e.stopPropagation(); const panel = tabPanels.querySelector(`.console-panel[data-tab-id="${id}"]`); if (panel) panel.remove(); btn.remove(); idleTimers.delete(id); const next = tabBar.querySelector('.console-tab'); if (next) activateTab(next.dataset.tabId); }); btn.appendChild(closeBtn); const panel = document.createElement('div'); panel.className = 'console-panel'; panel.dataset.tabId = id; panel.innerHTML = ``; tabBar.appendChild(btn); tabPanels.appendChild(panel); activateTab(id); const iframe = panel.querySelector('iframe'); const markActive = () => { idleTimers.set(id, Date.now()); }; idleTimers.set(id, Date.now()); iframe.addEventListener('load', () => { try { const doc = iframe.contentWindow.document; ['keydown', 'mousedown', 'wheel', 'touchstart'].forEach((evt) => { doc.addEventListener(evt, markActive, { passive: true }); }); const observer = new MutationObserver(markActive); observer.observe(doc.body, { childList: true, subtree: true, characterData: true }); } catch (e) { // cross-origin or blocked; rely on timer only } }); const idleCheck = setInterval(() => { const last = idleTimers.get(id) || 0; if (Date.now() - last > idleMs) { const panelEl = tabPanels.querySelector(`.console-panel[data-tab-id="${id}"]`); const btnEl = tabBar.querySelector(`.console-tab[data-tab-id="${id}"]`); if (panelEl) panelEl.remove(); if (btnEl) btnEl.remove(); idleTimers.delete(id); clearInterval(idleCheck); const next = tabBar.querySelector('.console-tab'); if (next) activateTab(next.dataset.tabId); } }, 10000); }; document.querySelectorAll('.console-launch').forEach((el) => { const url = el.dataset.url; const host = el.dataset.host || 'Konsole'; if (url) { openTab(host, url); } el.remove(); }); const queueBody = document.querySelector('[data-queue-body]'); const countdownEl = document.querySelector('[data-queue-countdown]'); const refreshBtn = document.querySelector('[data-queue-refresh]'); if (!queueBody || !countdownEl) return; let remaining = 10; let timer = null; const fetchQueue = async () => { const url = new URL(window.location.href); url.searchParams.set('queue_json', '1'); try { const res = await fetch(url.toString(), { cache: 'no-store' }); if (!res.ok) return; const data = await res.json(); if (data && data.html) { queueBody.innerHTML = data.html; } remaining = data && data.next ? data.next : 10; } catch (e) { // ignore } }; const tick = () => { remaining -= 1; if (remaining <= 0) { fetchQueue(); remaining = 10; } countdownEl.textContent = String(remaining); }; timer = setInterval(tick, 1000); if (refreshBtn) { refreshBtn.addEventListener('click', () => { fetchQueue(); remaining = 10; countdownEl.textContent = String(remaining); }); } const consoleForm = document.querySelector('[data-console-form]'); const consoleError = document.querySelector('[data-console-error]'); const consoleNotice = document.querySelector('[data-console-notice]'); const tokenEl = document.querySelector('[data-console-token]'); if (consoleForm) { const submitOpen = async () => { const formData = new FormData(consoleForm); const url = new URL(window.location.href); url.searchParams.set('open_console_json', '1'); const res = await fetch(url.toString(), { method: 'POST', body: formData, cache: 'no-store' }); const data = await res.json(); if (!data.ok) { if (consoleError) { consoleError.textContent = data.error || 'Fehler beim Öffnen der Konsole.'; consoleError.style.display = 'block'; } return; } if (consoleError) consoleError.style.display = 'none'; if (consoleNotice) consoleNotice.style.display = 'none'; if (tokenEl) tokenEl.textContent = data.token || ''; if (data.url) openTab(data.host || 'Konsole', data.url); }; const submitRun = async () => { const formData = new FormData(consoleForm); const url = new URL(window.location.href); url.searchParams.set('run_command_json', '1'); const res = await fetch(url.toString(), { method: 'POST', body: formData, cache: 'no-store' }); const data = await res.json(); if (!data.ok) { if (consoleError) { consoleError.textContent = data.error || 'Fehler beim Ausführen.'; consoleError.style.display = 'block'; } return; } if (consoleError) consoleError.style.display = 'none'; if (consoleNotice) { consoleNotice.textContent = data.notice || ''; consoleNotice.style.display = 'block'; } fetchQueue(); remaining = 10; countdownEl.textContent = String(remaining); }; const openBtn = consoleForm.querySelector('[data-open-console]'); const runBtn = consoleForm.querySelector('[data-run-command]'); if (openBtn) { openBtn.addEventListener('click', (e) => { e.preventDefault(); submitOpen(); }); } if (runBtn) { runBtn.addEventListener('click', (e) => { e.preventDefault(); submitRun(); }); } } })();