const state = {
summary: null,
detail: null,
newKey: null,
loading: false
};
const content = document.getElementById("content");
const notice = document.getElementById("notice");
const pageTitle = document.getElementById("pageTitle");
const pageSubtitle = document.getElementById("pageSubtitle");
const sidebarMeta = document.getElementById("sidebarMeta");
document.getElementById("refreshButton").addEventListener("click", () => refresh(true));
window.addEventListener("hashchange", () => renderRoute());
content.addEventListener("click", async (event) => {
const button = event.target.closest("button[data-action]");
if (!button) {
return;
}
const { action, clientId, model, keyId } = button.dataset;
try {
setBusy(button, true);
if (action === "disable-hour") {
await api(`/clients/${encodeURIComponent(clientId)}/disable`, {
method: "POST",
body: { mode: "duration", durationMinutes: 60 }
});
setNotice(`Disabled ${clientId} for one hour.`);
await refresh();
}
if (action === "disable-manual") {
await api(`/clients/${encodeURIComponent(clientId)}/disable`, {
method: "POST",
body: { mode: "manual" }
});
setNotice(`Disabled ${clientId}.`);
await refresh();
}
if (action === "enable-client") {
await api(`/clients/${encodeURIComponent(clientId)}/enable`, { method: "POST" });
setNotice(`Enabled ${clientId}.`);
await refresh();
}
if (action === "model-command") {
await runModelCommand(clientId, model, button.dataset.modelAction);
}
if (action === "delete-key") {
if (!confirm("Delete this API key?")) {
return;
}
await api(`/api-keys/${encodeURIComponent(keyId)}`, { method: "DELETE" });
setNotice("API key deleted.");
await refresh();
}
if (action === "copy-key") {
await navigator.clipboard.writeText(button.dataset.key);
setNotice("API key copied.");
}
} catch (error) {
setNotice(error.message, true);
} finally {
setBusy(button, false);
}
});
content.addEventListener("submit", async (event) => {
const form = event.target;
if (!(form instanceof HTMLFormElement)) {
return;
}
event.preventDefault();
const data = Object.fromEntries(new FormData(form).entries());
const submit = form.querySelector("button[type=submit]");
try {
setBusy(submit, true);
if (form.dataset.form === "model-action") {
await runModelCommand(data.clientId, data.model, data.action);
form.reset();
}
if (form.dataset.form === "model-detail") {
await loadModelDetail(data.model, data.clientId);
}
if (form.dataset.form === "api-key") {
state.newKey = await api("/api-keys", {
method: "POST",
body: { name: data.name }
});
setNotice("API key created.");
await refresh();
}
} catch (error) {
setNotice(error.message, true);
} finally {
setBusy(submit, false);
}
});
async function boot() {
await refresh();
setInterval(() => refresh(), 15000);
}
async function refresh(showNotice = false, render = true) {
state.loading = true;
try {
state.summary = await api("/summary");
updateShell();
if (render) {
await renderRoute();
}
if (showNotice) {
setNotice("Refreshed.");
}
} catch (error) {
setNotice(error.message, true);
content.innerHTML = `
${escapeHtml(error.message)}
`;
} finally {
state.loading = false;
}
}
async function api(path, options = {}) {
const headers = options.headers ? { ...options.headers } : {};
const init = {
method: options.method || "GET",
credentials: "same-origin",
headers
};
if (options.body !== undefined) {
headers["Content-Type"] = "application/json";
init.body = JSON.stringify(options.body);
}
const response = await fetch(`/api/admin${path}`, init);
const contentType = response.headers.get("content-type") || "";
const body = contentType.includes("application/json")
? await response.json()
: await response.text();
if (!response.ok) {
const message = body?.detail || body?.error || body?.title || response.statusText;
throw new Error(message);
}
return body;
}
async function renderRoute() {
const hash = (window.location.hash || "#clients").slice(1);
const [view, encodedModel] = hash.split("/");
document.querySelectorAll("[data-nav]").forEach((link) => {
link.classList.toggle("active", link.dataset.nav === view);
});
if (view === "models" && encodedModel) {
const model = decodeURIComponent(encodedModel);
await loadModelDetail(model);
return;
}
state.detail = null;
if (view === "models") {
renderModels();
return;
}
if (view === "api-keys") {
renderApiKeys();
return;
}
renderClients();
}
function updateShell() {
const summary = state.summary;
if (!summary) {
return;
}
sidebarMeta.innerHTML = `
${escapeHtml(summary.user?.name || "Signed in")}
${summary.clients.length} clients
${summary.models.length} models
${formatDate(summary.generatedAtUtc)}
`;
}
function renderClients() {
const clients = state.summary?.clients || [];
pageTitle.textContent = "Clients";
pageSubtitle.textContent = "Connected tunnel clients, request counts, and forwarding controls.";
content.innerHTML = `
${clients.length ? clientsTable(clients) : emptyState("No clients have connected yet.")}
`;
}
function clientsTable(clients) {
const rows = clients.map((client) => `
${escapeHtml(client.id)}
${client.connected ? "Connected" : "Offline"}${client.modelsUpdatedAt ? `, models ${formatDate(client.modelsUpdatedAt)}` : ""}
${client.connected ? badge("Connected", "good") : badge("Offline", "")}
${client.disabled ? badge(client.disabledManually ? "Disabled manual" : "Disabled timed", "bad") : badge("Enabled", "good")}
${client.disabled ? `${escapeHtml(disabledText(client))}
` : ""}
${number(client.pendingRequests)}
${number(client.requestStats.total)}
${number(client.requestStats.last10Minutes)} in 10m, ${number(client.requestStats.lastHour)} in 1h
${modelBadges(client.models)}
${modelBadges(client.activeModels)}
Disable 1h
Disable
Enable
`).join("");
return `
Client
Status
Pending
Requests
Listed models
Active models
Actions
${rows}
`;
}
function renderModels() {
const models = state.summary?.models || [];
const clients = connectedClients();
pageTitle.textContent = "Models";
pageSubtitle.textContent = "Listed and active models, recent request volume, and model operations.";
content.innerHTML = `
${modelActionForm(clients)}
${models.length ? modelsTable(models) : emptyState("No models have been reported yet.")}
`;
}
function modelActionForm(clients, selectedModel = "", selectedClient = "") {
return `
`;
}
function modelsTable(models) {
const rows = models.map((model) => `
${escapeHtml(model.name)}
${number(model.metrics.totalRequests)} total requests
${modelBadges(model.listedClients)}
${modelBadges(model.activeClients)}
${number(model.metrics.requestsLast10Minutes)}
${number(model.metrics.requestsLastHour)} in last hour
${number(model.metrics.tokensLast10Minutes)}
${number(model.metrics.tokensLastHour)} in last hour
Details
`).join("");
return `
Model
Listed on
Active on
Requests 10m
Tokens 10m
${rows}
`;
}
async function loadModelDetail(model, clientId) {
pageTitle.textContent = "Model Detail";
pageSubtitle.textContent = model;
content.innerHTML = ``;
const query = new URLSearchParams({ model });
if (clientId) {
query.set("clientId", clientId);
}
state.detail = await api(`/models/detail?${query.toString()}`);
renderModelDetail();
}
function renderModelDetail() {
const detail = state.detail;
const clients = connectedClients();
const model = detail.model;
const selectedClient = detail.selectedClientId || clients[0]?.id || "";
const metrics = detail.metrics;
const showBody = detail.show?.body === undefined ? null : detail.show.body;
pageTitle.textContent = "Model Detail";
pageSubtitle.textContent = model;
content.innerHTML = `
${metric(number(metrics.requestsLast10Minutes), "Requests in 10 minutes")}
${metric(number(metrics.requestsLastHour), "Requests in 1 hour")}
${metric(number(metrics.tokensLast10Minutes), "Tokens in 10 minutes")}
${metric(number(metrics.tokensLastHour), "Tokens in 1 hour")}
${badge("Listed", detail.listedClients.length ? "good" : "")}
${modelBadges(detail.listedClients)}
${badge("Active", detail.activeClients.length ? "good" : "warn")}
${modelBadges(detail.activeClients)}
${detail.show ? `
${escapeHtml(formatJson(showBody))} ` : emptyState("No connected client was available for details.")}
`;
}
async function runModelCommand(clientId, model, action) {
if (!clientId || !model || !action) {
throw new Error("Client, model, and action are required.");
}
if (action === "remove" && !confirm(`Remove ${model} from ${clientId}?`)) {
return;
}
const result = await api("/models/actions", {
method: "POST",
body: { clientId, model, action }
});
if (!result.ok) {
throw new Error(modelActionError(result));
}
const detail = modelActionResultDetail(result);
const completed = `${capitalize(action)} completed for ${model} on ${clientId}${detail ? ` (${detail})` : ""}.`;
if (action === "load" || action === "unload") {
const shouldBeActive = action === "load";
setNotice(`${completed} Waiting for active model snapshot.`);
if (await waitForModelActiveState(clientId, model, shouldBeActive)) {
setNotice(completed);
return;
}
setNotice(`${completed} The active model snapshot has not reflected the change yet.`);
return;
}
setNotice(completed);
await refreshAfterModelCommand(model, clientId);
}
function renderApiKeys() {
const keys = state.summary?.apiKeys || [];
pageTitle.textContent = "API Keys";
pageSubtitle.textContent = "Keys accepted by the proxy token header, bearer auth, query token, and token path.";
content.innerHTML = `
${state.newKey ? newKeyPanel(state.newKey) : ""}
${keys.length ? apiKeysTable(keys) : emptyState("No API keys have been created.")}
`;
}
function newKeyPanel(key) {
return `
New API key
${escapeHtml(key.key)}
Copy
`;
}
function apiKeysTable(keys) {
const rows = keys.map((key) => `
${escapeHtml(key.name)}
${escapeHtml(key.keyPrefix)}...
${formatDate(key.createdAtUtc)}
${key.lastUsedUtc ? formatDate(key.lastUsedUtc) : "Never"}
Delete
`).join("");
return `
Name
Created
Last used
${rows}
`;
}
function connectedClients() {
return (state.summary?.clients || []).filter((client) => client.connected);
}
async function waitForModelActiveState(clientId, model, shouldBeActive) {
const deadline = Date.now() + 15000;
while (Date.now() <= deadline) {
await refreshAfterModelCommand(model, clientId);
const client = (state.summary?.clients || [])
.find((item) => sameText(item.id, clientId));
if (client && modelListContains(client.activeModels, model) === shouldBeActive) {
return true;
}
await delay(1000);
}
return false;
}
async function refreshAfterModelCommand(model, clientId) {
await refresh(false, false);
const hash = window.location.hash || "";
if (hash.startsWith(`#models/${encodeURIComponent(model)}`)) {
await loadModelDetail(model, clientId);
return;
}
await renderRoute();
}
function modelActionResultDetail(result) {
const body = result?.body;
if (!body || typeof body === "string") {
return body || "";
}
if (body.status) {
return body.status;
}
if (body.done_reason) {
return `done: ${body.done_reason}`;
}
if (body.done) {
return "done";
}
return "";
}
function modelActionError(result) {
const body = result?.body;
if (typeof body === "string" && body) {
return body;
}
if (body?.error) {
return body.error;
}
if (body?.message) {
return body.message;
}
return `Model action failed with HTTP ${result.statusCode}.`;
}
function modelListContains(models, model) {
return (models || []).some((item) => sameModelName(item, model));
}
function sameModelName(left, right) {
return stripLatestTag(left).toLowerCase() === stripLatestTag(right).toLowerCase();
}
function sameText(left, right) {
return String(left || "").trim().toLowerCase() === String(right || "").trim().toLowerCase();
}
function stripLatestTag(model) {
const value = String(model || "").trim();
return value.toLowerCase().endsWith(":latest") ? value.slice(0, -":latest".length) : value;
}
function delay(milliseconds) {
return new Promise((resolve) => setTimeout(resolve, milliseconds));
}
function clientOptions(clients, selectedClient) {
if (!clients.length) {
return `No connected clients `;
}
return clients.map((client) => `
${escapeHtml(client.id)}
`).join("");
}
function modelBadges(items) {
if (!items || !items.length) {
return `None `;
}
return `${items.map((item) => badge(item, "")).join("")}
`;
}
function badge(text, kind) {
return `${escapeHtml(text)} `;
}
function metric(value, label) {
return `
${escapeHtml(value)}
${escapeHtml(label)}
`;
}
function emptyState(text) {
return `${escapeHtml(text)}
`;
}
function disabledText(client) {
if (client.disabledManually) {
return "Until enabled manually";
}
return client.disabledUntilUtc ? `Until ${formatDate(client.disabledUntilUtc)}` : "Disabled";
}
function formatDate(value) {
if (!value) {
return "";
}
return new Intl.DateTimeFormat(undefined, {
dateStyle: "short",
timeStyle: "medium"
}).format(new Date(value));
}
function formatJson(value) {
if (typeof value === "string") {
return value;
}
return JSON.stringify(value, null, 2);
}
function number(value) {
return new Intl.NumberFormat().format(value || 0);
}
function capitalize(value) {
return value ? value[0].toUpperCase() + value.slice(1) : value;
}
function setNotice(message, isError = false) {
notice.hidden = false;
notice.textContent = message;
notice.classList.toggle("error", isError);
clearTimeout(setNotice.timer);
setNotice.timer = setTimeout(() => {
notice.hidden = true;
}, isError ? 7000 : 3500);
}
function setBusy(element, busy) {
if (!element) {
return;
}
element.disabled = busy;
}
function escapeHtml(value) {
return String(value ?? "")
.replaceAll("&", "&")
.replaceAll("<", "<")
.replaceAll(">", ">")
.replaceAll('"', """)
.replaceAll("'", "'");
}
function escapeAttr(value) {
return escapeHtml(value);
}
boot();