const page = document.body.dataset.page const $ = (selector) => document.querySelector(selector) function escapeHtml(value) { const node = document.createElement('span') node.textContent = String(value ?? '') return node.innerHTML } function number(value, digits = 0) { if (value === null || value === undefined || Number.isNaN(Number(value))) return '—' return Number(value).toLocaleString('zh-CN', { maximumFractionDigits: digits, minimumFractionDigits: digits }) } function usd(value, digits = 3) { const numeric = Number(value) if (value === null || value === undefined || !Number.isFinite(numeric)) return '' const formatter = new Intl.NumberFormat('zh-CN', { maximumFractionDigits: digits, minimumFractionDigits: digits }) const parts = formatter.formatToParts(numeric) const whole = parts.filter(({ type }) => ['minusSign', 'plusSign', 'integer', 'group'].includes(type)).map(({ value: part }) => part).join('') const point = parts.find(({ type }) => type === 'decimal')?.value ?? '.' const fraction = parts.find(({ type }) => type === 'fraction')?.value ?? ''.padEnd(digits, '0') const label = `$${formatter.format(numeric)}` return `` } function compact(value) { if (value === null || value === undefined) return '—' return new Intl.NumberFormat('zh-CN', { notation: 'compact', maximumFractionDigits: 2 }).format(Number(value)) } function percent(value) { return value === null || value === undefined ? '—' : `${number(Number(value) * 100, 1)}%` } function time(value) { if (!value) return '—' return new Intl.DateTimeFormat('zh-CN', { timeZone: 'Asia/Shanghai', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false, }).format(new Date(value)) } async function requestJson(url, options = {}, timeoutMs = 20000) { const controller = new AbortController() const timer = setTimeout(() => controller.abort(), timeoutMs) try { const response = await fetch(url, { ...options, signal: controller.signal, headers: { 'content-type': 'application/json', ...(options.headers ?? {}) } }) const data = await response.json().catch(() => null) if (response.status === 401 && page !== 'login') { location.assign('/login') throw new Error('登录状态已失效') } if (!response.ok || !data?.ok) throw new Error(data?.error ?? `HTTP ${response.status}`) return data } finally { clearTimeout(timer) } } function shell() { const mount = $('[data-shell]') if (!mount) return const links = [ ['scores', '/scores', '账号评分'], ['ranking', '/ranking', '用户用量'], ['lottery', '/lottery', '额度抽奖'], ] mount.innerHTML = `
ASApiStateSub2API Operations
PK01
` $('#logout').addEventListener('click', async () => { await requestJson('/api/logout', { method: 'POST', body: '{}' }).catch(() => null) location.assign('/login') }) } async function loginPage() { const form = $('#login-form') form.addEventListener('submit', async (event) => { event.preventDefault() const button = form.querySelector('button') const error = $('#login-error') button.disabled = true error.textContent = '' try { await requestJson('/api/login', { method: 'POST', body: JSON.stringify({ username: $('#username').value, password: $('#password').value }), }) location.assign('/scores') } catch (cause) { error.textContent = cause instanceof Error ? cause.message : String(cause) } finally { button.disabled = false } }) } let scoreRows = [] let scoreRefreshedAt = null let scoreNextRefreshAt = null function gradeClass(value) { const grade = String(value ?? '').toLowerCase() if (grade === 'a' || grade === 'b' || grade === 'excellent' || grade === 'good') return 'grade-good' if (grade === 'd' || grade === 'e' || grade === 'poor' || grade === 'critical' || grade === 'insufficient') return 'grade-risk' return 'grade-mid' } function groupLabels(row) { const groups = Array.isArray(row.groupNames) && row.groupNames.length ? row.groupNames : [row.groupName].filter(Boolean) return `
${groups.map((group) => `${escapeHtml(group)}`).join('')}
` } function countdown(value) { if (!value) return '--:--' const remaining = Math.max(0, new Date(value).getTime() - Date.now()) if (remaining === 0) return '等待刷新' const totalSeconds = Math.ceil(remaining / 1000) const minutes = Math.floor(totalSeconds / 60) const seconds = totalSeconds % 60 return `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}` } function renderRefreshClock() { $('#score-updated-time').textContent = scoreRefreshedAt ? `北京时间 ${time(scoreRefreshedAt)}` : '尚无成功快照' $('#score-countdown').textContent = countdown(scoreNextRefreshAt) } function renderScoreRows() { const term = ($('#score-filter')?.value ?? '').trim().toLowerCase() const rows = scoreRows.filter((row) => `${row.accountName} ${row.groupName}`.toLowerCase().includes(term)) $('#score-body').innerHTML = rows.length ? rows.map((row) => { const usage = row.usage ?? {} return ` ${escapeHtml(row.accountName)}#${escapeHtml(row.accountId)} ${groupLabels(row)} ${number(row.priority)} ${number(row.score, 1)} ${escapeHtml(row.grade ?? '—')} ${escapeHtml(row.confidence ?? '—')} ${number(row.observedAttempts)} ${percent(row.failureRate)} ${row.ttftP95Ms == null ? '—' : `${number(row.ttftP95Ms)} ms`} ${compact(usage.requestCount)} ${compact(usage.tokenCount)} ${usd(usage.apiAmountUsd)} ${number(row.failoverRecovered)} / ${number(row.failoverRequests)} ${row.currentlyAvailable ? '可用' : '不可用'} ` }).join('') : '没有匹配的账号' } function renderScores(data) { scoreRows = data.accounts ?? [] $('#metric-accounts').textContent = number(scoreRows.length) $('#metric-groups').textContent = number((data.groups ?? []).length) $('#metric-good').textContent = number(scoreRows.filter((row) => Number(row.score) >= 80).length) $('#metric-risk').textContent = number(scoreRows.filter((row) => Number(row.score) < 60).length) $('#metric-window').textContent = data.window ?? '—' $('#score-state').textContent = ({ ready: '已更新', refreshing: '刷新中', stale: '使用旧快照', unavailable: '暂无快照' })[data.status] ?? data.status $('#score-state').dataset.state = data.status scoreRefreshedAt = data.refreshedAt ?? scoreRefreshedAt scoreNextRefreshAt = data.nextRefreshAt ?? scoreNextRefreshAt renderRefreshClock() renderScoreRows() } async function scoresPage() { $('#score-filter').addEventListener('input', renderScoreRows) $('#refresh-scores').addEventListener('click', async () => { const button = $('#refresh-scores') button.disabled = true $('#score-state').textContent = '刷新中' try { renderScores(await requestJson('/api/scores/refresh', { method: 'POST', body: '{}' }, 200000)) } catch (error) { $('#score-updated-time').textContent = error instanceof Error ? error.message : String(error) } finally { button.disabled = false } }) renderScores(await requestJson('/api/scores')) setInterval(renderRefreshClock, 1000) setInterval(async () => { if (!document.hidden) { const data = await requestJson('/api/scores').catch(() => null) if (data) renderScores(data) } }, 30000) } async function rankingPage() { const data = await requestJson('/api/ranking') const ranking = data.ranking $('#ranking-range').textContent = `${ranking.startDate} 至 ${ranking.endDate}` $('#ranking-cost').innerHTML = usd(ranking.totals.actualCost) $('#ranking-requests').textContent = compact(ranking.totals.requests) $('#ranking-tokens').textContent = compact(ranking.totals.tokens) $('#ranking-body').innerHTML = ranking.rows.length ? ranking.rows.map((row) => `${String(row.rank).padStart(2, '0')}${escapeHtml(row.displayName)}${usd(row.actualCost)}${compact(row.requests)}${compact(row.tokens)}`).join('') : '当前窗口暂无用量' } function creditLabel(status) { return ({ succeeded: '已充值', dry_run: '模拟充值', disabled: '充值未开启', pending: '充值待确认', failed: '充值失败' })[status] ?? status } function renderLottery(data) { $('#lottery-prize').textContent = number(data.prizeAmountUsd, 0) $('#lottery-remaining').textContent = number(data.remainingDraws) $('#lottery-eligible').textContent = number(data.eligibleUserCount) $('#lottery-next').textContent = time(data.nextGrantAt) $('#lottery-mode').textContent = data.automaticCredit?.enabled ? creditLabel(data.automaticCredit.mode) : '自动充值关闭' $('#draw-button').disabled = Number(data.remainingDraws) < 1 || Number(data.eligibleUserCount) < 1 $('#draw-status').textContent = Number(data.remainingDraws) < 1 ? '今天的机会已用完' : `${data.eligibleUserCount} 名候选用户已就绪` $('#record-list').innerHTML = data.records?.length ? data.records.map((record) => `
  • ${escapeHtml(record.winnerDisplayName)}$${number(record.prizeAmountUsd, 0)} · ${creditLabel(record.creditStatus)}
  • `).join('') : '
  • 暂无开奖记录
  • ' } async function lotteryPage() { let state = await requestJson('/api/lottery') renderLottery(state) $('#draw-button').addEventListener('click', async () => { const button = $('#draw-button') button.disabled = true $('#draw-status').textContent = '正在抽取活跃用户...' try { const data = await requestJson('/api/lottery/draw', { method: 'POST', body: '{}' }) $('#winner-name').textContent = data.record.winnerDisplayName $('#winner-prize').textContent = number(data.record.prizeAmountUsd, 0) $('#winner-meta').textContent = `${data.record.eligibleCount} 人等概率 · ${creditLabel(data.record.creditStatus)}` $('#winner-dialog').showModal() state = await requestJson('/api/lottery') renderLottery(state) } catch (error) { $('#draw-status').textContent = error instanceof Error ? error.message : String(error) button.disabled = false } }) $('#winner-close').addEventListener('click', () => $('#winner-dialog').close()) } async function boot() { if (page === 'login') return await loginPage() shell() if (page === 'scores') return await scoresPage() if (page === 'ranking') return await rankingPage() if (page === 'lottery') return await lotteryPage() } boot().catch((error) => { const target = $('.workspace') ?? $('main') if (target) target.insertAdjacentHTML('afterbegin', `
    ${escapeHtml(error instanceof Error ? error.message : String(error))}
    `) })