node and account names

This commit is contained in:
Dobromir Popov
2026-07-08 21:33:42 +03:00
parent 52629d7762
commit 1e44e8e578
7 changed files with 275 additions and 8 deletions

View File

@@ -280,6 +280,41 @@ const copies = v => (v === null || v === undefined) ? "?" : Number(v).toFixed(2)
const hp = v => (v === null || v === undefined) ? "?HP" : `${copies(v)}HP`;
const short = (s, n=14) => { s = String(s); return s.length > n ? s.slice(0, 6) + "…" + s.slice(-5) : s; };
function accountDisplayName(account) {
if (!account) return "?";
const nickname = (account.nickname || "").trim();
if (nickname) return nickname;
return account.email || account.wallet || account.account_id || "?";
}
function buildNodeNameMap(map) {
const byId = new Map();
for (const node of (map && map.nodes) || []) {
const name = (node.friendly_name || "").trim();
if (node.node_id && name) byId.set(node.node_id, name);
}
return byId;
}
function nodeDisplayName(nodeOrId, nameMap) {
if (nodeOrId && typeof nodeOrId === "object") {
const friendly = (nodeOrId.friendly_name || "").trim();
if (friendly) return friendly;
return short(nodeOrId.node_id || "?");
}
const friendly = nameMap && nameMap.get(nodeOrId);
return friendly || short(nodeOrId || "?");
}
function nodeDisplayCell(node) {
const label = esc(nodeDisplayName(node));
const friendly = (node.friendly_name || "").trim();
if (friendly && node.node_id) {
return `<span title="${esc(node.node_id)}">${label}</span>`;
}
return label;
}
function modelAliasKey(value) {
if (!value) return "";
const text = String(value).trim();
@@ -398,7 +433,7 @@ function renderNodes(map) {
html += table(["node", "shard", "tps (1h)", "queue", "served"], group.map(n => {
const modelStats = (n.throughput && (n.throughput[n.hf_repo] || n.throughput[n.model])) || {};
return [
esc(short(n.node_id || "?")),
nodeDisplayCell(n),
esc(`${n.shard_start ?? "?"}-${n.shard_end ?? "?"}`),
`<span class="num">${esc(tps(modelStats.tokens_per_sec_last_hour))}</span>`,
esc(String((n.stats && n.stats.queue_depth) ?? 0)),
@@ -475,11 +510,12 @@ function renderStats(stats) {
function renderThroughputHtml(stats) {
const nodes = (stats && stats.nodes) || {};
const nameMap = buildNodeNameMap(lastNetworkMap);
const rows = [];
for (const [nodeId, nodeStats] of Object.entries(nodes)) {
for (const [model, s] of Object.entries((nodeStats && nodeStats.models) || {})) {
rows.push([
esc(short(nodeId)),
esc(nodeDisplayName(nodeId, nameMap)),
esc(short(model, 24)),
`<span class="num">${esc(tps(s.tokens_per_sec_last_hour))}</span>`,
`<span class="num">${esc(String(s.sample_count_last_hour ?? 0))}</span>`,
@@ -612,6 +648,7 @@ function renderRouting(routing) {
return;
}
const cfg = (routing && routing.config) || {};
const nameMap = buildNodeNameMap(lastNetworkMap);
let html = `<div class="dim" style="margin-bottom:6px">` +
`explore share: <b>${esc(String(cfg.explore_share ?? "?"))}</b> · ` +
`traffic ∝ tps^<b>${esc(String(cfg.weight_alpha ?? "?"))}</b> · ` +
@@ -621,7 +658,7 @@ function renderRouting(routing) {
html += `<div style="margin-top:6px"><b>${esc(model)}</b> ` +
`<span class="dim">(epoch ${esc(String(info.epoch ?? 0))} · ${routes.length} route${routes.length === 1 ? "" : "s"})</span></div>`;
html += table(["route", "tps", "coeff", "share", "samples", "status"], routes.map(r => {
const hops = (r.hops || []).map(h => `${short(h.node_id, 12)}[${h.shard}]`).join(" → ");
const hops = (r.hops || []).map(h => `${nodeDisplayName(h.node_id, nameMap)}[${h.shard}]`).join(" → ");
const statusCls = r.status === "proven" ? "ok" : r.status === "stale" ? "warn" : "dim";
const coeff = (r.coefficient === null || r.coefficient === undefined)
? "—" : Number(r.coefficient).toFixed(2) + "×";
@@ -1259,7 +1296,8 @@ function renderAuthForms(errorMsg) {
`<a class="${authTab === name ? "active" : ""}" onclick="switchAuthTab('${name}')">${label}</a>`;
const identityFields =
'<input id="auth-email" type="email" placeholder="email (or leave blank)">' +
'<input id="auth-wallet" type="text" placeholder="wallet address (or leave blank)">';
'<input id="auth-wallet" type="text" placeholder="wallet address (or leave blank)">' +
'<input id="auth-nickname" type="text" placeholder="nickname (optional)">';
const form = authTab === "login"
? '<input id="auth-identifier" type="text" placeholder="email or wallet address">' +
'<input id="auth-password" type="password" placeholder="password">' +
@@ -1281,9 +1319,10 @@ function switchAuthTab(name) { authTab = name; renderAuthForms(); }
async function doRegister() {
const email = $("auth-email").value.trim();
const wallet = $("auth-wallet").value.trim();
const nickname = $("auth-nickname").value.trim();
const password = $("auth-password").value;
const r = await apiCall("/v1/auth/register", "POST",
{ email: email || null, wallet: wallet || null, password });
{ email: email || null, wallet: wallet || null, nickname: nickname || null, password });
if (!r.ok) { renderAuthForms(r.data.error || "registration failed"); return; }
setSession(r.data.session_token);
await renderAccountPanel();
@@ -1382,6 +1421,13 @@ function renderChatAuthHint() {
}
}
async function saveNickname() {
const nickname = $("account-nickname").value.trim();
const r = await apiCall("/v1/account/profile", "POST", { nickname: nickname || null });
if (!r.ok) alert(r.data.error || "nickname update failed");
else await renderAccountPanel();
}
async function renderAccountPanel() {
const r = await apiCall("/v1/account");
if (r.status === 404) { // accounts disabled on this tracker
@@ -1397,10 +1443,14 @@ async function renderAccountPanel() {
const { account, api_keys, balances, total_balance, usage, topup_amount } = r.data;
accountApiKeys = Array.isArray(api_keys) ? api_keys.slice() : [];
accountUsageRecords = (usage && (usage.records || usage.recent)) || [];
const who = account.email || account.wallet || account.account_id;
const who = accountDisplayName(account);
let html =
`<div><b>${esc(who)}</b> <span class="pill">${esc(account.role)}</span> ` +
`<button class="small" style="float:right" onclick="doLogout()">log out</button></div>` +
`<div style="margin:6px 0;display:flex;gap:8px;align-items:center;flex-wrap:wrap">` +
`<label class="dim" style="margin:0">nickname</label>` +
`<input id="account-nickname" type="text" value="${esc(account.nickname || "")}" placeholder="display name" style="min-width:180px">` +
`<button class="small" type="button" onclick="saveNickname()">save</button></div>` +
`<div style="margin:6px 0">balance: <b class="${total_balance > 0 ? "ok" : "bad"}">` +
`${usdt(total_balance)}</b> USDT · requests: <b>${usage.requests}</b> · ` +
`tokens: <b>${usage.total_tokens}</b> · spent: <b>${usdt(usage.total_cost)}</b> USDT</div>`;
@@ -1617,7 +1667,7 @@ async function renderAdminPanel() {
const rows = (r.data.accounts || []).map(a => {
const balance = Object.values(a.balances || {}).reduce((x, y) => x + y, 0);
return [
esc(short(a.email || a.wallet || a.account_id, 24)),
esc(short(accountDisplayName(a), 24)),
`<span class="pill">${esc(a.role)}</span>`,
String((a.api_keys || []).length),
`<span class="num ${balance > 0 ? "ok" : "bad"}">${usdt(balance)}</span>`,