feat(webdemo): add database-backed usage dashboard
This commit is contained in:
@@ -322,11 +322,15 @@ func (a *App) usageDashboardResponse() ManagementResponse {
|
||||
}
|
||||
users := make([]map[string]any, 0, len(dashboard.Users))
|
||||
for _, user := range dashboard.Users {
|
||||
users = append(users, map[string]any{"key_id": user.KeyID, "key_alias": user.KeyAlias, "today": usageSummaryItem(user.Today), "last_used_at": user.LastUsedAt})
|
||||
userDays := make([]map[string]any, 0, len(user.Days))
|
||||
for _, day := range user.Days {
|
||||
userDays = append(userDays, map[string]any{"date": day.Date.Format("2006-01-02"), "total_tokens": day.TotalTokens, "cost_usd": float64(day.CostMicros) / 1_000_000})
|
||||
}
|
||||
users = append(users, map[string]any{"key_id": user.KeyID, "key_alias": user.KeyAlias, "today": usageSummaryItem(user.Today), "days": userDays, "last_used_at": user.LastUsedAt})
|
||||
}
|
||||
days := make([]map[string]any, 0, len(dashboard.Days))
|
||||
for _, day := range dashboard.Days {
|
||||
days = append(days, map[string]any{"date": day.Date.Format("2006-01-02"), "total_tokens": day.TotalTokens})
|
||||
days = append(days, map[string]any{"date": day.Date.Format("2006-01-02"), "total_tokens": day.TotalTokens, "cost_usd": float64(day.CostMicros) / 1_000_000})
|
||||
}
|
||||
return jsonManagementResponse(http.StatusOK, map[string]any{"today": usageSummaryItem(dashboard.Today), "users": users, "days": days})
|
||||
}
|
||||
|
||||
@@ -562,14 +562,19 @@ func TestUsageManagementResponseContainsDisplayFields(t *testing.T) {
|
||||
func TestUsageResourceServesFeatureModules(t *testing.T) {
|
||||
pageResponse := managementCall(t, NewApp(), http.MethodGet, resourceBase+resourceUI)
|
||||
page := string(pageResponse.Body)
|
||||
if pageResponse.StatusCode != http.StatusOK || !strings.Contains(page, "请求明细") {
|
||||
if pageResponse.StatusCode != http.StatusOK || !strings.Contains(page, `data-view="logs">日志`) {
|
||||
t.Fatalf("unexpected UI response: status=%d", pageResponse.StatusCode)
|
||||
}
|
||||
for _, feature := range []string{`<body class="read-only">`, `id="page-buttons"`, `id="user-usage"`, `id="usage-filter-panel" class="usage-filter-panel"`, `id="editor-quota"`, `id="billing-ledger"`, `id="key-management" class="surface admin-only"`, `class="surface price-panel price-layout"`, `src="./ui-config.js"`, `type="module" src="./app/main.js"`, `href="./styles/base.css"`, `href="./styles/keys.css"`, `href="./styles/usage.css"`, `href="./styles/pricing.css"`} {
|
||||
for _, feature := range []string{`<body class="read-only">`, `id="demo-perspective" class="demo-perspective hidden"`, `data-perspective="admin">管理员示教`, `data-perspective="user">普通用户视角`, `id="quota-chart"`, `id="page-buttons"`, `id="user-usage"`, `id="usage-filter-panel" class="usage-filter-panel"`, `id="editor-quota"`, `id="billing-ledger"`, `id="key-management" class="surface admin-only"`, `class="surface price-panel price-layout"`, `src="./ui-config.js"`, `type="module" src="./app/main.js"`, `href="./styles/base.css"`, `href="./styles/keys.css"`, `href="./styles/usage.css"`, `href="./styles/pricing.css"`} {
|
||||
if !strings.Contains(page, feature) {
|
||||
t.Fatalf("UI does not contain HTML feature %q", feature)
|
||||
}
|
||||
}
|
||||
for _, removed := range []string{`<h2>Day</h2>`, `<h2>Week</h2>`} {
|
||||
if strings.Contains(page, removed) {
|
||||
t.Fatalf("UI still contains removed card title %q", removed)
|
||||
}
|
||||
}
|
||||
if strings.Index(page, `id="page-buttons"`) > strings.Index(page, `id="headers"`) {
|
||||
t.Fatal("UI pagination controls are not above the usage table")
|
||||
}
|
||||
@@ -589,7 +594,7 @@ func TestUsageResourceServesFeatureModules(t *testing.T) {
|
||||
}
|
||||
javascript.Write(response.Body)
|
||||
}
|
||||
for _, feature := range []string{"initializeRuntime", "dataFetch", "isManagementAuthorized", "pageSize = 100", `row.setAttribute("role", "button")`, "syncLongSectionVisibility", `return "compact"`, "isCompactEndpoint(record.endpoint)", "maskedSecret(key.secret)", `return "自由选择"`, `document.body.classList.toggle("read-only"`, `删除 ${model} 的真实价格配置`, `发现 ${changed.length} 个已关联价格发生变化`} {
|
||||
for _, feature := range []string{"initializeRuntime", "dataFetch", "isManagementAuthorized", "pageSize = 100", `row.setAttribute("role", "button")`, "syncLongSectionVisibility", `return "compact"`, "isCompactEndpoint(record.endpoint)", "maskedSecret(key.secret)", `return "自由选择"`, "剩余额度", `document.body.classList.toggle("read-only"`, `删除 ${model} 的真实价格配置`, `发现 ${changed.length} 个已关联价格发生变化`} {
|
||||
if !strings.Contains(javascript.String(), feature) {
|
||||
t.Fatalf("UI modules do not contain feature %q", feature)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user