Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions backend/api/routes/admin_analytics.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,14 @@ async def _analytics_overview_payload() -> dict:
WHERE dm.status = 'active'
"""
),
"login_users_today": await _scalar(
"""
SELECT COUNT(DISTINCT user_id) FROM user_activity_events
WHERE user_id IS NOT NULL
AND event_name='auth.login'
AND date(created_at)=date('now','localtime')
"""
),
},
"devices": {
"bound": await _scalar("SELECT COUNT(DISTINCT mac) FROM device_memberships WHERE status='active'"),
Expand Down
12 changes: 11 additions & 1 deletion backend/static/console/console.css
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ th {
align-items: flex-end;
gap: 7px;
height: 142px;
padding-top: 14px;
padding-top: 26px;
}

.bar {
Expand All @@ -212,6 +212,16 @@ th {
position: relative;
}

.bar b {
position: absolute;
left: 50%;
top: -20px;
transform: translateX(-50%);
color: var(--ink);
font: 700 10px/1 ui-sans-serif, system-ui, sans-serif;
white-space: nowrap;
}

.bar span {
position: absolute;
left: 50%;
Expand Down
21 changes: 13 additions & 8 deletions backend/static/console/console.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const I18N = {
statusLoadFailed: "加载看板指标失败:{message}",
metricUsers: "用户",
metricDau: "日活",
metricLoginUsers: "今日登录用户",
metricActiveDevices: "今日活跃设备",
metricRenders: "今日渲染",
metricHeartbeats: "设备心跳",
Expand All @@ -46,6 +47,7 @@ const I18N = {
noData: "暂无数据",
usersNote: "今日 +{today} / 7日 +{week} / 30日 +{month}",
activeNote: "WAU {wau} / MAU {mau}",
loginUsersNote: "DAU {dau} / WAU {wau} / MAU {mau}",
devicesNote: "已绑定 {bound} / 7日活跃 {week}",
rendersNote: "7日 {week} / 今日平均耗时 {avg}ms",
heartbeatsNote: "今日设备心跳请求",
Expand Down Expand Up @@ -89,6 +91,7 @@ const I18N = {
statusLoadFailed: "Failed to load console metrics: {message}",
metricUsers: "Users",
metricDau: "DAU",
metricLoginUsers: "Login Users Today",
metricActiveDevices: "Active Devices Today",
metricRenders: "Renders Today",
metricHeartbeats: "Heartbeats",
Expand All @@ -113,6 +116,7 @@ const I18N = {
noData: "No data yet.",
usersNote: "+{today} today / +{week} 7d / +{month} 30d",
activeNote: "WAU {wau} / MAU {mau}",
loginUsersNote: "DAU {dau} / WAU {wau} / MAU {mau}",
devicesNote: "{bound} bound / {week} active 7d",
rendersNote: "{week} 7d / {avg}ms avg today",
heartbeatsNote: "device heartbeats today",
Expand Down Expand Up @@ -221,7 +225,7 @@ function renderBars(id, rows, key = "count") {
const value = Number(d[key] || 0);
const height = Math.max(8, Math.round((value / max) * 116));
const day = String(d.day || "");
return `<div class="bar" title="${escapeHtml(`${day}: ${value}`)}" style="height:${height}px"><span>${escapeHtml(day.slice(5))}</span></div>`;
return `<div class="bar" title="${escapeHtml(`${day}: ${value}`)}" style="height:${height}px"><b>${escapeHtml(fmt(value))}</b><span>${escapeHtml(day.slice(5))}</span></div>`;
}).join("");
}

Expand Down Expand Up @@ -250,10 +254,9 @@ function render(data) {
week: fmt(data.users.new_7d),
month: fmt(data.users.new_30d),
});
$("dau").textContent = fmt(data.users.dau);
$("activeNote").textContent = t("activeNote", {
wau: fmt(data.users.wau),
mau: fmt(data.users.mau),
$("deviceUsers").textContent = fmt(data.users.device_active_24h);
$("deviceUsersNote").textContent = t("deviceUsersNote", {
withDevice: fmt(data.users.with_device),
});
$("devicesActive").textContent = fmt(data.devices.active_today);
$("devicesNote").textContent = t("devicesNote", {
Expand All @@ -277,9 +280,11 @@ function render(data) {
shared: fmt(data.content.shared_modes),
byok: fmt(data.content.users_with_llm_config),
});
$("deviceUsers").textContent = fmt(data.users.device_active_24h);
$("deviceUsersNote").textContent = t("deviceUsersNote", {
withDevice: fmt(data.users.with_device),
$("loginUsersToday").textContent = fmt(data.users.login_users_today);
$("loginUsersNote").textContent = t("loginUsersNote", {
dau: fmt(data.users.dau),
wau: fmt(data.users.wau),
mau: fmt(data.users.mau),
});

renderBars("userBars", data.series.new_users);
Expand Down
4 changes: 2 additions & 2 deletions backend/static/console/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,13 @@ <h1 data-i18n="title">Backend pulse.</h1>

<section class="grid metrics" aria-live="polite">
<article class="card"><p class="metric-label" data-i18n="metricUsers">Users</p><p id="usersTotal" class="metric-value">--</p><p id="usersNote" class="metric-note">--</p></article>
<article class="card"><p class="metric-label" data-i18n="metricDau">DAU</p><p id="dau" class="metric-value">--</p><p id="activeNote" class="metric-note">--</p></article>
<article class="card"><p class="metric-label" data-i18n="metricDeviceUsers">Active Device Users Today</p><p id="deviceUsers" class="metric-value">--</p><p id="deviceUsersNote" class="metric-note">--</p></article>
<article class="card"><p class="metric-label" data-i18n="metricActiveDevices">Active Devices Today</p><p id="devicesActive" class="metric-value">--</p><p id="devicesNote" class="metric-note">--</p></article>
<article class="card"><p class="metric-label" data-i18n="metricRenders">Renders Today</p><p id="rendersToday" class="metric-value">--</p><p id="rendersNote" class="metric-note">--</p></article>
<article class="card"><p class="metric-label" data-i18n="metricHeartbeats">Heartbeats</p><p id="heartbeatsToday" class="metric-value">--</p><p id="heartbeatsNote" class="metric-note">--</p></article>
<article class="card"><p class="metric-label" data-i18n="metricRenderHealth">Render Health Today</p><p id="renderErrors" class="metric-value">--</p><p id="renderHealthNote" class="metric-note">--</p></article>
<article class="card"><p class="metric-label" data-i18n="metricContent">Content</p><p id="customModes" class="metric-value">--</p><p id="contentNote" class="metric-note">--</p></article>
<article class="card"><p class="metric-label" data-i18n="metricDeviceUsers">Active Device Users Today</p><p id="deviceUsers" class="metric-value">--</p><p id="deviceUsersNote" class="metric-note">--</p></article>
<article class="card"><p class="metric-label" data-i18n="metricLoginUsers">Login Users Today</p><p id="loginUsersToday" class="metric-value">--</p><p id="loginUsersNote" class="metric-note">--</p></article>
</section>

<section class="grid charts">
Expand Down
Loading