/** 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(); }; }