// Utility: fetch wrapper
async function fetchJSON(url) {
const res = await fetch(url);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json();
}
// Fetch health
async function loadHealth() {
try {
const data = await fetchJSON('/health');
document.getElementById('total-accounts').textContent = data.accounts_total ?? '–';
document.getElementById('alive-accounts').textContent = data.accounts_alive ?? '–';
} catch (e) {
document.getElementById('total-accounts').textContent = 'error';
document.getElementById('alive-accounts').textContent = 'error';
}
}
// Fetch models
async function loadModels() {
const listEl = document.getElementById('model-list');
try {
const data = await fetchJSON('/v1/models');
const models = data.data || [];
if (models.length === 0) {
listEl.innerHTML = '
No models available';
return;
}
listEl.innerHTML = models.map(m => `${m.id} active`).join('');
} catch (e) {
listEl.innerHTML = 'Failed to load models';
}
}
// Create account
async function createAccount() {
const btn = document.getElementById('create-btn');
const msg = document.getElementById('create-msg');
btn.disabled = true;
msg.classList.remove('hidden');
msg.textContent = 'Creating…';
msg.className = '';
try {
const res = await fetch('/admin/create-account', { method: 'POST' });
const data = await res.json();
if (res.ok && data.status === 'ok') {
msg.textContent = '✓ Account created';
msg.classList.add('status-ok');
setTimeout(() => { msg.classList.add('hidden'); }, 3000);
loadHealth();
} else {
throw new Error(data.error || 'Unknown error');
}
} catch (e) {
msg.textContent = '✗ ' + e.message;
msg.classList.add('status-err');
} finally {
btn.disabled = false;
}
}
// SSE Logs
function startLogs() {
const container = document.getElementById('log-container');
const evtSource = new EventSource('/admin/logs/stream');
container.innerHTML = '';
evtSource.onmessage = (event) => {
const div = document.createElement('div');
div.className = 'log-entry';
div.textContent = event.data;
container.appendChild(div);
container.scrollTop = container.scrollHeight;
};
evtSource.onerror = () => {
const div = document.createElement('div');
div.className = 'log-entry status-err';
div.textContent = '[SSE connection lost]';
container.appendChild(div);
evtSource.close();
};
}
// Init
document.addEventListener('DOMContentLoaded', () => {
document.getElementById('create-btn').addEventListener('click', createAccount);
loadHealth();
loadModels();
startLogs();
// Refresh health every 10s
setInterval(loadHealth, 10000);
});