// ── API Keys Page (Admin) ── function formatKeyStatus(status) { var map = { 'valid': '✅ Gültig', 'invalid': '❌ Ungültig', 'rate_limited': '⚠️ Rate Limited', 'quota_exceeded': '⚠️ Quota Exceeded', 'error': '⚠️ Fehler', 'unchecked': '⏳ Ungeprüft' }; return map[status] || status; } function createUsageBadge(usage5h, usage7d) { if (usage5h == null && usage7d == null) return null; var badge = document.createElement('div'); badge.className = 'usage-badge'; function addRow(label, pct) { if (pct == null) return; var row = document.createElement('div'); row.className = 'usage-row'; var lbl = document.createElement('span'); lbl.className = 'usage-label'; lbl.textContent = label; var track = document.createElement('div'); track.className = 'usage-track'; var fill = document.createElement('div'); fill.className = 'usage-fill'; if (pct >= 90) fill.className += ' danger'; else if (pct >= 70) fill.className += ' warn'; fill.style.width = Math.min(pct, 100) + '%'; track.appendChild(fill); var val = document.createElement('span'); val.className = 'usage-pct'; val.textContent = pct + '%'; row.appendChild(lbl); row.appendChild(track); row.appendChild(val); badge.appendChild(row); } addRow('Sitzung', usage5h); addRow('Woche', usage7d); return badge; } registerPage('api-keys', { async init() { if (!currentUser || !currentUser.isAdmin) { navigateTo('dashboard'); return; } var keys = []; var editingKeyId = null; var list = document.getElementById('apiKeysList'); var newBtn = document.getElementById('apiKeysNewBtn'); var checkAllBtn = document.getElementById('apiKeysCheckAllBtn'); var form = document.getElementById('apiKeysForm'); var formTitle = document.getElementById('apiKeysFormTitle'); var providerSelect = document.getElementById('apiKeysProvider'); var nameInput = document.getElementById('apiKeysName'); var keyInput = document.getElementById('apiKeysKey'); var saveBtn = document.getElementById('apiKeysFormSaveBtn'); function renderKeys() { list.innerHTML = ''; if (keys.length === 0) { var empty = document.createElement('div'); empty.className = 'admin-user-meta pad-v'; empty.textContent = 'Keine API-Keys konfiguriert.'; list.appendChild(empty); return; } keys.forEach(function(key) { var wrapper = document.createElement('div'); var row = document.createElement('div'); row.className = 'admin-user-row'; var info = document.createElement('div'); info.className = 'admin-user-info'; var nameSpan = document.createElement('span'); nameSpan.className = 'admin-user-name'; nameSpan.textContent = key.name; var metaSpan = document.createElement('span'); metaSpan.className = 'admin-user-meta'; var parts = [key.provider]; parts.push(formatKeyStatus(key.status)); if (key.plan) parts.push(key.plan); if (key.checkedAt) parts.push(new Date(key.checkedAt).toLocaleString()); metaSpan.textContent = parts.join(' · '); info.appendChild(nameSpan); if (key.sourceUrl) { var urlSpan = document.createElement('a'); urlSpan.className = 'admin-user-meta'; urlSpan.href = key.sourceUrl; urlSpan.target = '_blank'; urlSpan.rel = 'noopener'; urlSpan.textContent = key.sourceUrl.replace('https://github.com/', ''); info.appendChild(urlSpan); } info.appendChild(metaSpan); // Toggle + Usage-Badge nebeneinander var badgeRow = document.createElement('div'); badgeRow.className = 'flex-row-tight'; var publicBtn = document.createElement('button'); publicBtn.className = 'icon-btn'; publicBtn.title = key.isPublic ? 'Öffentlich (klicken zum Privatschalten)' : 'Privat (klicken zum Veröffentlichen)'; publicBtn.innerHTML = key.isPublic ? '' : ''; publicBtn.addEventListener('click', function() { togglePublic(key); }); badgeRow.appendChild(publicBtn); var usageBadge = createUsageBadge(key.usage5h, key.usage7d); if (usageBadge) badgeRow.appendChild(usageBadge); // Status-Zeile (für Check-Ergebnis) var statusLine = document.createElement('div'); statusLine.className = 'admin-user-meta'; statusLine.className = 'admin-user-meta check-status-line'; // Check – rechts neben Usage-Badge if (key.provider === 'anthropic' || key.provider === 'openai' || key.provider === 'google') { var checkBtn = document.createElement('button'); checkBtn.className = 'icon-btn'; checkBtn.title = 'Key prüfen'; checkBtn.innerHTML = ''; checkBtn.addEventListener('click', function() { checkKey(key, checkBtn, statusLine); }); badgeRow.appendChild(checkBtn); } info.appendChild(badgeRow); var actions = document.createElement('div'); actions.className = 'admin-user-actions'; // Bearbeiten var editBtn = document.createElement('button'); editBtn.className = 'icon-btn'; editBtn.title = 'Bearbeiten'; editBtn.innerHTML = ''; editBtn.addEventListener('click', function() { startEdit(key); }); actions.appendChild(editBtn); // Löschen var deleteBtn = document.createElement('button'); deleteBtn.className = 'icon-btn'; deleteBtn.title = 'Löschen'; deleteBtn.innerHTML = ''; deleteBtn.addEventListener('click', function() { deleteKey(key); }); actions.appendChild(deleteBtn); row.appendChild(info); row.appendChild(actions); wrapper.appendChild(row); wrapper.appendChild(statusLine); list.appendChild(wrapper); }); } async function loadKeys() { try { keys = await apiGetApiKeys(); renderKeys(); } catch (err) { showToast(err.message, 'error'); } } async function checkKey(key, btn, statusLine) { var origHtml = btn.innerHTML; btn.innerHTML = ''; btn.disabled = true; statusLine.style.display = 'none'; statusLine.innerHTML = ''; try { var result = await apiCheckApiKey(key.keyId); statusLine.style.opacity = '1'; statusLine.innerHTML = ''; if (result.status === 'valid') { var text = document.createElement('span'); var parts = [formatKeyStatus('valid'), result.plan]; if (result.overage != null && result.overage > 0) parts.push('Overage: ' + result.overage + '%'); text.textContent = parts.join(' · '); statusLine.appendChild(text); var badge = createUsageBadge(result.usage5h, result.usage7d); if (badge) { badge.classList.add('mt-xs'); statusLine.appendChild(badge); } } else { statusLine.textContent = '❌ ' + (result.message || 'Unbekannter Fehler'); } await loadKeys(); } catch (err) { statusLine.style.opacity = '1'; statusLine.textContent = '❌ ' + err.message; } finally { btn.innerHTML = origHtml; btn.disabled = false; } } function startEdit(key) { editingKeyId = key.keyId; formTitle.textContent = 'API-Key bearbeiten'; providerSelect.value = key.provider; providerSelect.disabled = true; nameInput.value = key.name; keyInput.value = key.apiKey; saveBtn.textContent = 'Speichern'; form.hidden = false; nameInput.focus(); } async function togglePublic(key) { try { await apiToggleApiKeyPublic(key.keyId); showToast(key.name + (key.isPublic ? ' privat geschaltet' : ' veröffentlicht')); await loadKeys(); } catch (err) { showToast(err.message, 'error'); } } async function deleteKey(key) { if (!confirm('API-Key "' + key.name + '" wirklich löschen?')) return; try { await apiDeleteApiKey(key.keyId); showToast('API-Key gelöscht'); await loadKeys(); } catch (err) { showToast(err.message, 'error'); } } // Alle Keys prüfen checkAllBtn.addEventListener('click', function() { withButtonLoading(checkAllBtn, 'Alle prüfen', async function() { try { var result = await apiCheckAllApiKeys(); showToast(result.checked_ + ' Keys geprüft'); await loadKeys(); } catch (err) { showToast(err.message, 'error'); } }); }); // Neuer Key newBtn.addEventListener('click', function() { editingKeyId = null; formTitle.textContent = 'Neuer API-Key'; providerSelect.value = 'anthropic'; providerSelect.disabled = false; nameInput.value = ''; keyInput.value = ''; saveBtn.textContent = 'Erstellen'; form.hidden = false; nameInput.focus(); }); // Speichern (Erstellen oder Bearbeiten) saveBtn.addEventListener('click', () => withButtonLoading(saveBtn, saveBtn.textContent, async () => { var name = nameInput.value.trim(); if (!name) { showToast('Name erforderlich', 'error'); return; } try { if (editingKeyId) { // Bearbeiten var data = { name: name }; var newKey = keyInput.value.trim(); if (newKey) data.apiKey = newKey; await apiUpdateApiKey(editingKeyId, data); showToast('API-Key aktualisiert'); } else { // Erstellen var provider = providerSelect.value; var apiKey = keyInput.value.trim(); if (!apiKey) { showToast('API-Key erforderlich', 'error'); return; } await apiCreateApiKey({ provider: provider, name: name, apiKey: apiKey }); showToast('API-Key erstellt'); } form.hidden = true; editingKeyId = null; await loadKeys(); } catch (err) { showToast(err.message, 'error'); } })); await loadKeys(); // ── Scanner UI ── var scanQuickBtn = document.getElementById('scanQuickBtn'); var scanWebBtn = document.getElementById('scanWebBtn'); var scanFullBtn = document.getElementById('scanFullBtn'); var recheckAllScannedBtn = document.getElementById('recheckAllScannedBtn'); var scanBtns = [scanQuickBtn, scanWebBtn, scanFullBtn]; var scanStatusEl = document.getElementById('scanStatus'); var scannedList = document.getElementById('scannedKeysList'); var scannedKeys = []; var selectedType = null; var checkUncheckedAbort = false; var pollTimer = null; var typeNames = { anthropic_oauth: 'Anthropic OAuth', anthropic_api: 'Anthropic API', anthropic_unknown: 'Anthropic', openai_project: 'OpenAI Project', openai_service: 'OpenAI Service', openai_legacy: 'OpenAI Legacy', google_api: 'Google / Gemini', aws_access_key: 'AWS Access Key', github_pat: 'GitHub PAT', github_oauth: 'GitHub OAuth', gitlab_pat: 'GitLab PAT', huggingface: 'HuggingFace', stripe_live: 'Stripe Live', stripe_test: 'Stripe Test', sendgrid: 'SendGrid', slack: 'Slack', krea_ai: 'Krea AI' }; var typeIcons = { anthropic_oauth: '🟠', anthropic_api: '🟠', anthropic_unknown: '🟠', openai_project: '🟢', openai_service: '🟢', openai_legacy: '🟢', google_api: '🔵', aws_access_key: '🟡', github_pat: '⚫', github_oauth: '⚫', gitlab_pat: '🟤', huggingface: '🤗', stripe_live: '💳', stripe_test: '💳', sendgrid: '📧', slack: '💬', krea_ai: '🎨' }; function renderSummary(summary) { scannedList.innerHTML = ''; if (!summary || summary.length === 0) { var empty = document.createElement('div'); empty.className = 'admin-user-meta'; empty.className = 'admin-user-meta pad-v'; empty.textContent = 'Keine gescannten Keys vorhanden.'; scannedList.appendChild(empty); return; } summary.forEach(function(s) { var row = document.createElement('div'); row.className = 'admin-user-row clickable'; var info = document.createElement('div'); info.className = 'admin-user-info'; var nameSpan = document.createElement('span'); nameSpan.className = 'admin-user-name'; var icon = typeIcons[s.type] || '🔑'; nameSpan.textContent = icon + ' ' + (typeNames[s.type] || s.type); info.appendChild(nameSpan); var metaSpan = document.createElement('span'); metaSpan.className = 'admin-user-meta'; var parts = [s.total + ' Keys']; if (s.valid > 0) parts.push('✅ ' + s.valid + ' gültig'); if (s.invalid > 0) parts.push('❌ ' + s.invalid + ' ungültig'); if (s.unchecked > 0) parts.push('⏳ ' + s.unchecked + ' ungeprüft'); if (s.adopted > 0) parts.push('📌 ' + s.adopted + ' übernommen'); metaSpan.textContent = parts.join(' · '); info.appendChild(metaSpan); var badge = document.createElement('div'); badge.className = 'admin-user-actions'; badge.innerHTML = ''; row.appendChild(info); row.appendChild(badge); row.addEventListener('click', function() { loadScannedKeysByType(s.type); }); scannedList.appendChild(row); }); } function renderScannedKeys() { scannedList.innerHTML = ''; // Zurück-Button var backRow = document.createElement('div'); backRow.className = 'admin-user-row'; backRow.classList.add('clickable', 'semi-dim'); var backInfo = document.createElement('div'); backInfo.className = 'admin-user-info'; var backName = document.createElement('span'); backName.className = 'admin-user-name'; backName.innerHTML = ' Zurück zur Übersicht'; backInfo.appendChild(backName); backRow.appendChild(backInfo); backRow.addEventListener('click', function() { selectedType = null; checkUncheckedAbort = true; loadSummary(); }); scannedList.appendChild(backRow); // Toolbar: "Ungeprüfte prüfen"-Button var visibleKeys = scannedKeys.filter(function(sk) { return !sk.adoptedApiKeyId; }); var uncheckedKeys = visibleKeys.filter(function(sk) { return sk.status === 'unchecked'; }); if (uncheckedKeys.length > 0) { var toolbar = document.createElement('div'); toolbar.className = 'scanner-toolbar'; var checkUncheckedBtn = document.createElement('button'); checkUncheckedBtn.className = 'section-add-btn'; checkUncheckedBtn.innerHTML = ' ' + uncheckedKeys.length + ' Ungeprüfte prüfen'; var progressSpan = document.createElement('span'); progressSpan.className = 'admin-user-meta'; progressSpan.classList.add('ml-sm'); toolbar.appendChild(checkUncheckedBtn); toolbar.appendChild(progressSpan); scannedList.appendChild(toolbar); checkUncheckedBtn.addEventListener('click', async function() { checkUncheckedAbort = false; checkUncheckedBtn.disabled = true; var stopBtn = document.createElement('button'); stopBtn.className = 'icon-btn'; stopBtn.title = 'Abbrechen'; stopBtn.innerHTML = ''; stopBtn.addEventListener('click', function() { checkUncheckedAbort = true; }); toolbar.appendChild(stopBtn); var checked = 0; var found = 0; for (var i = 0; i < uncheckedKeys.length; i++) { if (checkUncheckedAbort) { progressSpan.textContent = 'Abgebrochen bei ' + checked + '/' + uncheckedKeys.length; break; } var sk = uncheckedKeys[i]; progressSpan.textContent = (i + 1) + '/' + uncheckedKeys.length + ' prüfe…'; checkUncheckedBtn.innerHTML = ' ' + (i + 1) + '/' + uncheckedKeys.length; try { var result = await apiRecheckScannedKey(sk.scannedKeyId); checked++; if (result && result.status === 'valid') { found++; progressSpan.textContent = '✅ Gültigen Key gefunden! Stoppe.'; showToast('Gültigen Key gefunden!', 'success'); break; } } catch (err) { checked++; } } if (!checkUncheckedAbort && found === 0) { progressSpan.textContent = checked + ' geprüft, keinen gültigen gefunden.'; } checkUncheckedBtn.disabled = false; checkUncheckedBtn.innerHTML = ' Ungeprüfte prüfen'; if (stopBtn.parentNode) stopBtn.parentNode.removeChild(stopBtn); await loadScannedKeysByType(selectedType); }); } if (visibleKeys.length === 0) { var empty = document.createElement('div'); empty.className = 'admin-user-meta'; empty.className = 'admin-user-meta pad-v'; empty.textContent = 'Keine nicht-übernommenen Keys in dieser Kategorie.'; scannedList.appendChild(empty); return; } visibleKeys.forEach(function(sk) { var row = document.createElement('div'); row.className = 'admin-user-row'; var info = document.createElement('div'); info.className = 'admin-user-info'; info.classList.add('min-w-0'); if (sk.sourceUrl) { var nameSpan = document.createElement('a'); nameSpan.className = 'admin-user-name'; nameSpan.href = sk.sourceUrl; nameSpan.target = '_blank'; nameSpan.rel = 'noopener'; var displayUrl = sk.sourceUrl.replace('https://github.com/', '').replace('https://www.', '').replace('https://', ''); if (displayUrl.length > 80) displayUrl = displayUrl.substring(0, 77) + '…'; nameSpan.textContent = displayUrl; info.appendChild(nameSpan); } var metaSpan = document.createElement('span'); metaSpan.className = 'admin-user-meta'; var metaParts = []; var sourceIcons = { github: '🐙 GitHub', serper: '🔍 Google', gist: '📋 Gist', pastebin: '📄 Pastebin', github_events: '📡 Events' }; metaParts.push(sourceIcons[sk.source] || sk.source || 'github'); metaParts.push(formatKeyStatus(sk.status)); if (sk.plan && sk.plan !== 'Unknown') metaParts.push(sk.plan); if (sk.checkedAt) metaParts.push(new Date(sk.checkedAt).toLocaleString()); metaSpan.textContent = metaParts.join(' · '); info.appendChild(metaSpan); var skUsageBadge = createUsageBadge(sk.usage5h, sk.usage7d); if (skUsageBadge) info.appendChild(skUsageBadge); var actions = document.createElement('div'); actions.className = 'admin-user-actions'; // Recheck var recheckBtn = document.createElement('button'); recheckBtn.className = 'icon-btn'; recheckBtn.title = 'Erneut prüfen'; recheckBtn.innerHTML = ''; recheckBtn.addEventListener('click', async function(e) { e.stopPropagation(); recheckBtn.innerHTML = ''; recheckBtn.disabled = true; try { await apiRecheckScannedKey(sk.scannedKeyId); await loadScannedKeysByType(selectedType); } catch (err) { showToast(err.message, 'error'); } finally { recheckBtn.innerHTML = ''; recheckBtn.disabled = false; } }); actions.appendChild(recheckBtn); // Übernehmen (bei valid oder quota_exceeded) if (sk.status === 'valid' || sk.status === 'quota_exceeded') { var adoptBtn = document.createElement('button'); adoptBtn.className = 'icon-btn'; adoptBtn.title = 'Als API-Key übernehmen'; adoptBtn.innerHTML = ''; adoptBtn.addEventListener('click', async function(e) { e.stopPropagation(); try { await apiAdoptScannedKey(sk.scannedKeyId); showToast('Key übernommen'); await loadScannedKeysByType(selectedType); await loadKeys(); } catch (err) { showToast(err.message, 'error'); } }); actions.appendChild(adoptBtn); } // Löschen var delBtn = document.createElement('button'); delBtn.className = 'icon-btn'; delBtn.title = 'Ignorieren'; delBtn.innerHTML = ''; delBtn.addEventListener('click', async function(e) { e.stopPropagation(); try { await apiDeleteScannedKey(sk.scannedKeyId); await loadScannedKeysByType(selectedType); } catch (err) { showToast(err.message, 'error'); } }); actions.appendChild(delBtn); row.appendChild(info); row.appendChild(actions); scannedList.appendChild(row); }); } async function loadSummary() { try { var summary = await apiGetScannedKeysSummary(); renderSummary(summary); } catch (err) { showToast(err.message, 'error'); } } async function loadScannedKeysByType(type) { selectedType = type; scannedList.innerHTML = '
Lade Keys…
'; try { scannedKeys = await apiGetScannedKeys(type); renderScannedKeys(); } catch (err) { showToast(err.message, 'error'); } } async function loadScannedKeys() { if (selectedType) { await loadScannedKeysByType(selectedType); } else { await loadSummary(); } } var scanBtnDefaults = [ { btn: scanQuickBtn, icon: 'fa-bolt', label: 'Quick' }, { btn: scanWebBtn, icon: 'fa-globe', label: 'Web' }, { btn: scanFullBtn, icon: 'fa-search', label: 'Full' } ]; function setScanButtonsDisabled(disabled) { scanBtnDefaults.forEach(function(d) { d.btn.disabled = disabled; }); } function resetScanButtons() { scanBtnDefaults.forEach(function(d) { d.btn.innerHTML = ' ' + d.label; d.btn.disabled = false; }); } async function updateEventScannerStatus() { var el = document.getElementById('eventScannerStatus'); if (!el) return; try { var s = await apiEventScannerStatus(); if (!s.enabled) { el.hidden = true; return; } el.hidden = false; // Status-Zeile var parts = ['🔴 Event Scanner']; if (s.running) parts = ['🟢 Event Scanner']; if (s.pollCount > 0) parts.push('Polls: ' + s.pollCount); if (s.eventsPerHour > 0) parts.push('Events/h: ' + s.eventsPerHour); if (s.keysFoundTotal > 0) parts.push('🔑 ' + s.keysFoundTotal + ' Key(s) gefunden'); if (s.lastPoll) { var ago = Math.round((Date.now() - new Date(s.lastPoll).getTime()) / 1000); parts.push('Letzter Poll: vor ' + (ago < 60 ? ago + 's' : Math.round(ago / 60) + 'min')); } if (s.lastError) parts.push('⚠️ ' + s.lastError); var statusLine = parts.join(' · '); // Log abrufen var logData = await apiEventScannerLog(); var logLines = []; if (logData.entries && logData.entries.length > 0) { var last10 = logData.entries.slice(-10).reverse(); logLines = last10.map(function(e) { var t = new Date(e.time).toLocaleTimeString(); var icon = e.type === 'key' ? '🔑' : e.type === 'error' ? '❌' : e.type === 'rate_limit' ? '⚠️' : '📡'; return icon + ' ' + t + ' ' + e.message; }); } // Immer klickbare Status-Zeile rendern var wasOpen = false; var oldLog = document.getElementById('eventScannerLog'); if (oldLog) wasOpen = !oldLog.hidden; el.innerHTML = ''; var statusEl = document.createElement('div'); statusEl.className = 'clickable'; statusEl.title = logLines.length > 0 ? 'Klicken für Log' : ''; statusEl.textContent = statusLine; el.appendChild(statusEl); var logEl = document.createElement('div'); logEl.id = 'eventScannerLog'; logEl.className = 'event-scanner-log'; logEl.hidden = true; logEl.textContent = logLines.length > 0 ? logLines.join('\n') : 'Noch keine Log-Einträge…'; if (wasOpen) logEl.hidden = false; el.appendChild(logEl); statusEl.addEventListener('click', function() { logEl.hidden = !logEl.hidden; }); } catch(e) { el.hidden = true; } } function formatScanProgress(s) { var parts = []; if (s.phase) parts.push(s.phase); if (s.total > 0) parts.push(s.current + '/' + s.total); if (s.detail && s.detail !== s.phase) { var d = s.detail.length > 60 ? s.detail.substring(0, 57) + '…' : s.detail; parts.push(d); } if (s.keysFound > 0) parts.push('🔑 ' + s.keysFound + ' gefunden'); return parts.join(' · '); } async function updateScanStatus() { try { var s = await apiScanStatus(); if (s.running) { var progressText = formatScanProgress(s); scanStatusEl.innerHTML = ' ' + (progressText || 'Scan läuft…'); setScanButtonsDisabled(true); if (!pollTimer) { pollTimer = setInterval(async function() { var st = await apiScanStatus(); if (st.running) { var pt = formatScanProgress(st); scanStatusEl.innerHTML = ' ' + (pt || 'Scan läuft…'); } else { clearInterval(pollTimer); pollTimer = null; resetScanButtons(); await loadScannedKeys(); scanStatusEl.innerHTML = st.lastResult ? '✅ ' + st.lastResult : ''; } }, 1500); } } else { resetScanButtons(); if (s.lastResult) { var timeStr = s.lastRun ? new Date(s.lastRun).toLocaleString() + ' – ' : ''; scanStatusEl.innerHTML = timeStr + s.lastResult; } } } catch (err) { /* ignore */ } } async function triggerScan(mode, btn) { try { btn.innerHTML = ' Läuft…'; setScanButtonsDisabled(true); await apiTriggerScan(mode); showToast(mode.charAt(0).toUpperCase() + mode.slice(1) + '-Scan gestartet'); await updateScanStatus(); } catch (err) { showToast(err.message, 'error'); resetScanButtons(); } } scanQuickBtn.addEventListener('click', function() { triggerScan('quick', scanQuickBtn); }); scanWebBtn.addEventListener('click', function() { triggerScan('web', scanWebBtn); }); scanFullBtn.addEventListener('click', function() { triggerScan('full', scanFullBtn); }); recheckAllScannedBtn.addEventListener('click', async function() { recheckAllScannedBtn.disabled = true; recheckAllScannedBtn.innerHTML = ' Prüfe…'; try { var result = await apiRecheckAllScannedKeys(); showToast('Alle geprüft: ' + result.success + '/' + result.total + ' OK' + (result.errors > 0 ? ', ' + result.errors + ' Fehler' : ''), result.errors > 0 ? 'warning' : 'success'); await loadScannedKeys(); } catch(e) { showToast('Fehler beim Prüfen', 'error'); } finally { recheckAllScannedBtn.disabled = false; recheckAllScannedBtn.innerHTML = ' Alle prüfen'; } }); await loadScannedKeys(); await updateScanStatus(); await updateEventScannerStatus(); // Event Scanner Status auto-refresh var eventScannerTimer = setInterval(updateEventScannerStatus, 30000); this._eventScannerTimer = eventScannerTimer; }, destroy() { if (this._eventScannerTimer) { clearInterval(this._eventScannerTimer); this._eventScannerTimer = null; } } });