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);
});
}