fix(dashboard): fix chart crash, field name mismatches, and demo data injection
- overview.js: fix time-series chart crash (data is {series:[...]}, not array; field is 'time' not 'hour')
- monitoring.js: use fallback field names (total_tokens/tokens, duration_ms/duration) for WebSocket vs API compat
- monitoring.js: disable localhost demo data injection that mixed fake data with real
- websocket.js: fix duplicate condition and field name mismatches in dead-code handlers
- logging/mod.rs: add info! logs for successful DB insert and broadcast count for diagnostics
This commit is contained in:
@@ -2,7 +2,7 @@ use chrono::{DateTime, Utc};
|
|||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use sqlx::SqlitePool;
|
use sqlx::SqlitePool;
|
||||||
use tokio::sync::broadcast;
|
use tokio::sync::broadcast;
|
||||||
use tracing::warn;
|
use tracing::{info, warn};
|
||||||
|
|
||||||
use crate::errors::AppError;
|
use crate::errors::AppError;
|
||||||
|
|
||||||
@@ -42,14 +42,19 @@ impl RequestLogger {
|
|||||||
// Spawn async task to log without blocking response
|
// Spawn async task to log without blocking response
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
// Broadcast to dashboard
|
// Broadcast to dashboard
|
||||||
let _ = tx.send(serde_json::json!({
|
let broadcast_result = tx.send(serde_json::json!({
|
||||||
"type": "request",
|
"type": "request",
|
||||||
"channel": "requests",
|
"channel": "requests",
|
||||||
"payload": log
|
"payload": log
|
||||||
}));
|
}));
|
||||||
|
match broadcast_result {
|
||||||
|
Ok(receivers) => info!("Broadcast request log to {} dashboard listeners", receivers),
|
||||||
|
Err(_) => {} // No active WebSocket clients — expected when dashboard isn't open
|
||||||
|
}
|
||||||
|
|
||||||
if let Err(e) = Self::insert_log(&pool, log).await {
|
match Self::insert_log(&pool, log).await {
|
||||||
warn!("Failed to log request to database: {}", e);
|
Ok(()) => info!("Request logged to database successfully"),
|
||||||
|
Err(e) => warn!("Failed to log request to database: {}", e),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -403,7 +403,7 @@ class MonitoringPage {
|
|||||||
<strong>${request.client_id || 'Unknown'}</strong> →
|
<strong>${request.client_id || 'Unknown'}</strong> →
|
||||||
${request.provider || 'Unknown'} (${request.model || 'Unknown'})
|
${request.provider || 'Unknown'} (${request.model || 'Unknown'})
|
||||||
<div class="stream-entry-details">
|
<div class="stream-entry-details">
|
||||||
${request.tokens || 0} tokens • ${request.duration || 0}ms
|
${request.total_tokens || request.tokens || 0} tokens • ${request.duration_ms || request.duration || 0}ms
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
@@ -493,28 +493,7 @@ class MonitoringPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
startDemoUpdates() {
|
startDemoUpdates() {
|
||||||
// Simulate incoming requests for demo purposes
|
// Demo updates disabled — real data comes via WebSocket subscriptions
|
||||||
if (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1') {
|
|
||||||
setInterval(() => {
|
|
||||||
if (!this.isPaused && Math.random() > 0.3) { // 70% chance
|
|
||||||
this.simulateRequest();
|
|
||||||
}
|
|
||||||
}, 2000);
|
|
||||||
|
|
||||||
// Simulate logs
|
|
||||||
setInterval(() => {
|
|
||||||
if (!this.isPaused && Math.random() > 0.5) { // 50% chance
|
|
||||||
this.simulateLog();
|
|
||||||
}
|
|
||||||
}, 3000);
|
|
||||||
|
|
||||||
// Simulate metrics
|
|
||||||
setInterval(() => {
|
|
||||||
if (!this.isPaused) {
|
|
||||||
this.simulateMetric();
|
|
||||||
}
|
|
||||||
}, 5000);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
simulateRequest() {
|
simulateRequest() {
|
||||||
|
|||||||
@@ -129,13 +129,14 @@ class OverviewPage {
|
|||||||
async loadRequestsChart() {
|
async loadRequestsChart() {
|
||||||
try {
|
try {
|
||||||
const data = await window.api.get('/usage/time-series');
|
const data = await window.api.get('/usage/time-series');
|
||||||
|
const series = data.series || [];
|
||||||
|
|
||||||
const chartData = {
|
const chartData = {
|
||||||
labels: data.map(item => item.hour),
|
labels: series.map(item => item.time),
|
||||||
datasets: [
|
datasets: [
|
||||||
{
|
{
|
||||||
label: 'Requests',
|
label: 'Requests',
|
||||||
data: data.map(item => item.requests),
|
data: series.map(item => item.requests),
|
||||||
color: '#3b82f6',
|
color: '#3b82f6',
|
||||||
fill: true
|
fill: true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,8 +88,8 @@ class WebSocketManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleMessage(data) {
|
handleMessage(data) {
|
||||||
// Handle global events
|
// Handle request events
|
||||||
if (data.type === 'request' || data.type === 'request') {
|
if (data.type === 'request') {
|
||||||
this.notify('requests', data.payload);
|
this.notify('requests', data.payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -286,9 +286,9 @@ class WebSocketManager {
|
|||||||
|
|
||||||
// Update token counters
|
// Update token counters
|
||||||
const tokenCountElement = document.querySelector('[data-stat="total-tokens"]');
|
const tokenCountElement = document.querySelector('[data-stat="total-tokens"]');
|
||||||
if (tokenCountElement && request.tokens) {
|
if (tokenCountElement && (request.total_tokens || request.tokens)) {
|
||||||
const currentTokens = parseInt(tokenCountElement.textContent) || 0;
|
const currentTokens = parseInt(tokenCountElement.textContent) || 0;
|
||||||
tokenCountElement.textContent = currentTokens + request.tokens;
|
tokenCountElement.textContent = currentTokens + (request.total_tokens || request.tokens);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -312,7 +312,7 @@ class WebSocketManager {
|
|||||||
<td>${request.client_id || 'Unknown'}</td>
|
<td>${request.client_id || 'Unknown'}</td>
|
||||||
<td>${request.provider || 'Unknown'}</td>
|
<td>${request.provider || 'Unknown'}</td>
|
||||||
<td>${request.model || 'Unknown'}</td>
|
<td>${request.model || 'Unknown'}</td>
|
||||||
<td>${request.tokens || 0}</td>
|
<td>${(request.total_tokens || request.tokens || 0)}</td>
|
||||||
<td>
|
<td>
|
||||||
<span class="status-badge ${statusClass}">
|
<span class="status-badge ${statusClass}">
|
||||||
<i class="fas fa-${statusIcon}"></i>
|
<i class="fas fa-${statusIcon}"></i>
|
||||||
@@ -362,7 +362,7 @@ class WebSocketManager {
|
|||||||
<strong>${request.client_id || 'Unknown'}</strong> →
|
<strong>${request.client_id || 'Unknown'}</strong> →
|
||||||
${request.provider || 'Unknown'} (${request.model || 'Unknown'})
|
${request.provider || 'Unknown'} (${request.model || 'Unknown'})
|
||||||
<div class="stream-entry-details">
|
<div class="stream-entry-details">
|
||||||
${request.tokens || 0} tokens • ${request.duration || 0}ms
|
${(request.total_tokens || request.tokens || 0)} tokens • ${(request.duration_ms || request.duration || 0)}ms
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|||||||
Reference in New Issue
Block a user