talk2me/admin/templates/dashboard.html
Adolfo Delorenzo fa951c3141 Add comprehensive database integration, authentication, and admin dashboard
This commit introduces major enhancements to Talk2Me:

## Database Integration
- PostgreSQL support with SQLAlchemy ORM
- Redis integration for caching and real-time analytics
- Automated database initialization scripts
- Migration support infrastructure

## User Authentication System
- JWT-based API authentication
- Session-based web authentication
- API key authentication for programmatic access
- User roles and permissions (admin/user)
- Login history and session tracking
- Rate limiting per user with customizable limits

## Admin Dashboard
- Real-time analytics and monitoring
- User management interface (create, edit, delete users)
- System health monitoring
- Request/error tracking
- Language pair usage statistics
- Performance metrics visualization

## Key Features
- Dual authentication support (token + user accounts)
- Graceful fallback for missing services
- Non-blocking analytics middleware
- Comprehensive error handling
- Session management with security features

## Bug Fixes
- Fixed rate limiting bypass for admin routes
- Added missing email validation method
- Improved error handling for missing database tables
- Fixed session-based authentication for API endpoints

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-03 18:21:56 -06:00

277 lines
9.5 KiB
HTML

{% extends "base.html" %}
{% block title %}Dashboard - Talk2Me Admin{% endblock %}
{% block content %}
<!-- Quick Actions -->
<div class="row mb-4">
<div class="col-12">
<div class="card bg-light">
<div class="card-body">
<h5 class="card-title">Quick Actions</h5>
<div class="btn-group" role="group">
<a href="{{ url_for('admin.users') }}" class="btn btn-primary">
<i class="fas fa-users"></i> Manage Users
</a>
<button onclick="exportData('all')" class="btn btn-secondary">
<i class="fas fa-download"></i> Export Data
</button>
<button onclick="clearCache()" class="btn btn-warning">
<i class="fas fa-trash"></i> Clear Cache
</button>
</div>
</div>
</div>
</div>
</div>
<!-- Overview Cards -->
<div class="row mb-4">
<div class="col-md-3 mb-3">
<div class="card text-white bg-primary">
<div class="card-body">
<h5 class="card-title">Total Requests</h5>
<h2 class="card-text" id="total-requests">
<div class="spinner-border spinner-border-sm" role="status"></div>
</h2>
<p class="card-text"><small>Today: <span id="today-requests">-</span></small></p>
</div>
</div>
</div>
<div class="col-md-3 mb-3">
<div class="card text-white bg-success">
<div class="card-body">
<h5 class="card-title">Active Sessions</h5>
<h2 class="card-text" id="active-sessions">
<div class="spinner-border spinner-border-sm" role="status"></div>
</h2>
<p class="card-text"><small>Live users</small></p>
</div>
</div>
</div>
<div class="col-md-3 mb-3">
<div class="card text-white bg-warning">
<div class="card-body">
<h5 class="card-title">Error Rate</h5>
<h2 class="card-text" id="error-rate">
<div class="spinner-border spinner-border-sm" role="status"></div>
</h2>
<p class="card-text"><small>Last 24 hours</small></p>
</div>
</div>
</div>
<div class="col-md-3 mb-3">
<div class="card text-white bg-info">
<div class="card-body">
<h5 class="card-title">Cache Hit Rate</h5>
<h2 class="card-text" id="cache-hit-rate">
<div class="spinner-border spinner-border-sm" role="status"></div>
</h2>
<p class="card-text"><small>Performance metric</small></p>
</div>
</div>
</div>
</div>
<!-- System Health Status -->
<div class="row mb-4">
<div class="col-12">
<div class="card">
<div class="card-header">
<h5 class="mb-0"><i class="fas fa-heartbeat"></i> System Health</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-4">
<div class="d-flex align-items-center">
<i class="fas fa-database fa-2x me-3"></i>
<div>
<h6 class="mb-0">Redis</h6>
<span class="badge" id="redis-status">Checking...</span>
</div>
</div>
</div>
<div class="col-md-4">
<div class="d-flex align-items-center">
<i class="fas fa-server fa-2x me-3"></i>
<div>
<h6 class="mb-0">PostgreSQL</h6>
<span class="badge" id="postgresql-status">Checking...</span>
</div>
</div>
</div>
<div class="col-md-4">
<div class="d-flex align-items-center">
<i class="fas fa-microphone fa-2x me-3"></i>
<div>
<h6 class="mb-0">Whisper/TTS</h6>
<span class="badge" id="ml-status">Checking...</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Charts Row 1 -->
<div class="row mb-4">
<div class="col-md-8">
<div class="card">
<div class="card-header">
<h5 class="mb-0">Request Volume</h5>
<div class="btn-group btn-group-sm float-end" role="group">
<button type="button" class="btn btn-outline-primary active" onclick="updateRequestChart('minute')">Minute</button>
<button type="button" class="btn btn-outline-primary" onclick="updateRequestChart('hour')">Hour</button>
<button type="button" class="btn btn-outline-primary" onclick="updateRequestChart('day')">Day</button>
</div>
</div>
<div class="card-body">
<canvas id="requestChart" height="100"></canvas>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card">
<div class="card-header">
<h5 class="mb-0">Language Pairs</h5>
</div>
<div class="card-body">
<canvas id="languageChart" height="200"></canvas>
</div>
</div>
</div>
</div>
<!-- Charts Row 2 -->
<div class="row mb-4">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h5 class="mb-0">Operations</h5>
</div>
<div class="card-body">
<canvas id="operationsChart" height="120"></canvas>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h5 class="mb-0">Response Times (ms)</h5>
</div>
<div class="card-body">
<canvas id="responseTimeChart" height="120"></canvas>
</div>
</div>
</div>
</div>
<!-- Error Analysis -->
<div class="row mb-4">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h5 class="mb-0">Error Types</h5>
</div>
<div class="card-body">
<canvas id="errorTypeChart" height="150"></canvas>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h5 class="mb-0">Recent Errors</h5>
</div>
<div class="card-body" style="max-height: 300px; overflow-y: auto;">
<div id="recent-errors-list">
<div class="text-center">
<div class="spinner-border" role="status"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Performance Metrics -->
<div class="row mb-4">
<div class="col-12">
<div class="card">
<div class="card-header">
<h5 class="mb-0">Performance Metrics</h5>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>Operation</th>
<th>Average (ms)</th>
<th>95th Percentile (ms)</th>
<th>99th Percentile (ms)</th>
</tr>
</thead>
<tbody id="performance-table">
<tr>
<td colspan="4" class="text-center">
<div class="spinner-border" role="status"></div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- Real-time Updates Status -->
<div class="position-fixed bottom-0 end-0 p-3" style="z-index: 1000">
<div class="toast" id="update-toast" role="alert">
<div class="toast-header">
<i class="fas fa-sync-alt me-2"></i>
<strong class="me-auto">Real-time Updates</strong>
<small id="last-update">Just now</small>
<button type="button" class="btn-close" data-bs-dismiss="toast"></button>
</div>
<div class="toast-body">
<span id="update-status">Connected</span>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script>
// Initialize dashboard
$(document).ready(function() {
initializeDashboard();
// Start real-time updates
startRealtimeUpdates();
// Load initial data
loadOverviewStats();
loadRequestChart('minute');
loadOperationStats();
loadErrorStats();
loadPerformanceStats();
// Refresh data periodically
setInterval(loadOverviewStats, 10000); // Every 10 seconds
setInterval(function() {
loadRequestChart(currentTimeframe);
}, 30000); // Every 30 seconds
});
</script>
{% endblock %}