/** Operator UI. The token lives only in this page's memory, never in a URL or storage. */
export function showAdmin(root: HTMLElement) {
root.replaceChildren();
const panel = document.createElement('section'); panel.className = 'card picker-panel'; panel.style.maxWidth = '850px';
panel.innerHTML = `
Администрирование Токен оператора Обновить
Комнаты `;
root.appendChild(panel);
const status = panel.querySelector('[role=status]')!;
const results = panel.querySelector('[data-results]')!;
const request = async (path: string, method = 'GET') => {
const token = panel.querySelector('input')!.value;
const response = await fetch(path, { method, headers: { Authorization: `Bearer ${token}` }, signal: AbortSignal.timeout(25_000) });
const data = await response.json();
if (!response.ok) throw new Error(data.error ?? `HTTP ${response.status}`);
return data;
};
const refresh = async () => {
results.replaceChildren(); status.textContent = 'Загрузка…';
try {
const data = await request('/api/admin');
status.textContent = `Снимки: ${(data.saveBytes / 1048576).toFixed(1)} / ${(data.saveQuotaBytes / 1048576).toFixed(1)} МиБ`;
for (const room of data.rooms) {
const row = document.createElement('p');
row.textContent = `${room.id} · ${room.cityId} · ${room.players} игроков · тик ${room.tick} · сохранён ${room.savedTick} · ошибок записи ${room.saveErrors} `;
const button = document.createElement('button'); button.className = 'btn'; button.textContent = 'Сохранить'; button.disabled = !room.active;
button.onclick = async () => {
button.disabled = true;
try { await request(`/api/admin/rooms/${encodeURIComponent(room.id)}/checkpoint`, 'POST'); await refresh(); }
catch (e) { status.textContent = (e as Error).message; button.disabled = false; }
};
row.appendChild(button); results.appendChild(row);
}
for (const snapshot of data.snapshots) if (snapshot.error) {
const row = document.createElement('p'); row.textContent = `${snapshot.id}: ${snapshot.error}`; results.appendChild(row);
}
} catch (e) { status.textContent = (e as Error).message; }
};
panel.querySelector('[data-refresh]')!.onclick = () => { void refresh(); };
}