let adminKey = ''; document.addEventListener('DOMContentLoaded', () => { const loginBtn = document.getElementById('login-btn'); const masterKeyInput = document.getElementById('master-key-input'); loginBtn.addEventListener('click', attemptLogin); masterKeyInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') attemptLogin(); }); document.getElementById('logout-btn').addEventListener('click', () => { adminKey = ''; document.getElementById('admin-section').style.display = 'none'; document.getElementById('login-section').style.display = 'block'; document.getElementById('logout-btn').style.display = 'none'; document.getElementById('master-key-input').value = ''; }); document.getElementById('generate-key-btn').addEventListener('click', generateKey); document.getElementById('refresh-btn').addEventListener('click', fetchKeys); document.getElementById('copy-btn').addEventListener('click', copyKey); document.getElementById('revoke-all-btn').addEventListener('click', revokeAllKeys); document.getElementById('refresh-logs-btn').addEventListener('click', fetchLogs); }); async function attemptLogin() { const key = document.getElementById('master-key-input').value.trim(); const errorEl = document.getElementById('login-error'); errorEl.style.display = 'none'; try { const res = await fetch('/api/admin/keys', { method: 'GET', headers: { 'admin-key': key } }); if (res.ok) { adminKey = key; document.getElementById('login-section').style.display = 'none'; document.getElementById('admin-section').style.display = 'block'; document.getElementById('logout-btn').style.display = 'block'; fetchKeys(); fetchLogs(); } else { errorEl.textContent = 'Invalid Master Key'; errorEl.style.display = 'block'; } } catch (e) { errorEl.textContent = 'Connection Error'; errorEl.style.display = 'block'; } } async function fetchKeys() { if (!adminKey) return; try { const res = await fetch('/api/admin/keys', { method: 'GET', headers: { 'admin-key': adminKey } }); if (res.ok) { const data = await res.json(); renderKeysTable(data.keys); } } catch (e) { console.error('Failed to fetch keys', e); } } function renderKeysTable(keysObj) { const tbody = document.getElementById('keys-table-body'); tbody.innerHTML = ''; const keysArray = Object.entries(keysObj).map(([key, data]) => ({ key, ...data })); // Sort by newest first keysArray.sort((a, b) => new Date(b.created_at) - new Date(a.created_at)); if (keysArray.length === 0) { tbody.innerHTML = 'No access keys generated yet.'; return; } keysArray.forEach(k => { const now = new Date(); const expiresAt = new Date(k.expires_at); let status = 'Active'; let badgeClass = 'badge-active'; if (k.revoked) { status = 'Revoked'; badgeClass = 'badge-revoked'; } else if (now > expiresAt) { status = 'Expired'; badgeClass = 'badge-expired'; } const tr = document.createElement('tr'); tr.innerHTML = ` ${k.key} ${status} ${new Date(k.created_at).toLocaleString()} ${expiresAt.toLocaleString()} ${k.used_by_ip || 'Never Used'} ${(status === 'Active') ? `` : ''} `; tbody.appendChild(tr); }); } async function generateKey() { if (!adminKey) return; const hours = parseInt(document.getElementById('key-hours').value); try { const res = await fetch('/api/admin/generate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ admin_key: adminKey, hours: hours }) }); if (res.ok) { const data = await res.json(); document.getElementById('new-key-display').textContent = data.key; document.getElementById('new-key-result').style.display = 'block'; fetchKeys(); } else { alert('Failed to generate key'); } } catch (e) { console.error('Generation failed', e); } } async function revokeKey(targetKey) { if (!adminKey) return; if (!confirm(`Are you sure you want to revoke key: ${targetKey}?`)) return; try { const res = await fetch('/api/admin/revoke', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ admin_key: adminKey, target_key: targetKey }) }); if (res.ok) { fetchKeys(); } else { alert('Failed to revoke key'); } } catch (e) { console.error('Revoke failed', e); } } async function revokeAllKeys() { if (!adminKey) return; if (!confirm('Are you absolutely sure you want to revoke ALL active keys?')) return; try { const res = await fetch('/api/admin/revoke_all', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ admin_key: adminKey }) }); if (res.ok) { const data = await res.json(); alert(`Successfully revoked ${data.revoked_count} active keys.`); fetchKeys(); } else { alert('Failed to revoke keys'); } } catch (e) { console.error('Revoke all failed', e); } } async function fetchLogs() { if (!adminKey) return; try { const res = await fetch('/api/admin/logs', { method: 'GET', headers: { 'admin-key': adminKey } }); if (res.ok) { const data = await res.json(); const container = document.getElementById('logs-container'); if (data.logs && data.logs.length > 0) { // Reverse to show newest at top container.innerHTML = data.logs.reverse().map(l => { let color = '#a1a1aa'; if (l.includes('✅')) color = '#4ade80'; if (l.includes('⛔')) color = '#f87171'; return `
${l}
`; }).join(''); } else { container.innerHTML = '
No logs found for the last 24 hours.
'; } } } catch (e) { console.error('Failed to fetch logs', e); } } function copyKey() { const key = document.getElementById('new-key-display').textContent; navigator.clipboard.writeText(key).then(() => { const btn = document.getElementById('copy-btn'); btn.textContent = 'Copied!'; setTimeout(() => { btn.textContent = 'Copy to Clipboard'; }, 2000); }); }