You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
793 lines
31 KiB
793 lines
31 KiB
{% extends "base.html" %}
|
|
|
|
{% block title %}Analytics Dashboard - Plutus Payment System{% endblock %}
|
|
|
|
{% block head %}
|
|
<style>
|
|
/* Background styling */
|
|
body {
|
|
background-color: #3a3a3a !important;
|
|
background-image: url("{{ url_for('static', filename='images/plutus3.JPG') }}") !important;
|
|
background-size: cover;
|
|
background-position: center;
|
|
background-repeat: no-repeat;
|
|
background-attachment: fixed;
|
|
position: relative;
|
|
}
|
|
|
|
body::before {
|
|
content: '';
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background-color: rgba(58, 58, 58, 0.85);
|
|
z-index: -1;
|
|
}
|
|
|
|
.container-fluid, .container {
|
|
position: relative;
|
|
z-index: 1;
|
|
}
|
|
|
|
/* Ensure navbar and footer are above background */
|
|
.navbar, .footer, main {
|
|
position: relative;
|
|
z-index: 1;
|
|
}
|
|
|
|
/* Page title and breadcrumb styling */
|
|
h1, h2, h3, h4, h5, h6 {
|
|
color: #faf8f0 !important;
|
|
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8);
|
|
}
|
|
|
|
.breadcrumb-item a {
|
|
color: #d4af37 !important;
|
|
}
|
|
|
|
.breadcrumb-item.active {
|
|
color: #faf8f0 !important;
|
|
}
|
|
|
|
.card {
|
|
background-color: rgba(250, 248, 240, 0.98) !important;
|
|
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.3);
|
|
}
|
|
|
|
.metric-card {
|
|
background: white;
|
|
border-radius: 8px;
|
|
padding: 20px;
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
margin-bottom: 20px;
|
|
text-align: center;
|
|
}
|
|
|
|
.metric-value {
|
|
font-size: 2rem;
|
|
font-weight: bold;
|
|
margin-bottom: 5px;
|
|
}
|
|
|
|
.metric-label {
|
|
color: #666;
|
|
font-size: 0.9rem;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.5px;
|
|
}
|
|
|
|
.health-score {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 100px;
|
|
height: 100px;
|
|
border-radius: 50%;
|
|
margin: 0 auto 15px;
|
|
font-size: 1.5rem;
|
|
font-weight: bold;
|
|
color: white;
|
|
}
|
|
|
|
.health-excellent { background: linear-gradient(135deg, #48c774, #00d1b2); }
|
|
.health-good { background: linear-gradient(135deg, #48c774, #ffdd57); }
|
|
.health-warning { background: linear-gradient(135deg, #ffdd57, #ff9800); }
|
|
.health-critical { background: linear-gradient(135deg, #ff9800, #f14668); }
|
|
|
|
.tab-content {
|
|
margin-top: 20px;
|
|
min-height: 400px;
|
|
}
|
|
|
|
.log-entry {
|
|
background: #f8f9fa;
|
|
border-left: 4px solid #0d6efd;
|
|
padding: 12px;
|
|
margin-bottom: 10px;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.loading {
|
|
opacity: 0.6;
|
|
pointer-events: none;
|
|
}
|
|
</style>
|
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container-fluid py-4">
|
|
<div class="d-flex justify-content-between align-items-start mb-4">
|
|
<div>
|
|
<h1 class="h2 mb-1">
|
|
<i class="fas fa-chart-line"></i> Analytics Dashboard
|
|
</h1>
|
|
</div>
|
|
<div class="d-flex gap-2">
|
|
<button class="btn btn-light" onclick="refreshDashboard()">
|
|
<i class="fas fa-sync-alt"></i> Refresh
|
|
</button>
|
|
<button class="btn btn-primary" onclick="exportReport()">
|
|
<i class="fas fa-download"></i> Export
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- System Health Overview -->
|
|
<div class="row">
|
|
<div class="col-md-3">
|
|
<div class="metric-card">
|
|
<div id="healthScore" class="health-score">
|
|
<span id="healthValue">--</span>
|
|
</div>
|
|
<div class="metric-label">System Health Score</div>
|
|
<p class="small text-muted">Overall system performance</p>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="metric-card">
|
|
<div id="paymentSuccessRate" class="metric-value text-success">--%</div>
|
|
<div class="metric-label">Payment Success Rate</div>
|
|
<p class="small text-muted">Last 24 hours</p>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="metric-card">
|
|
<div id="errorRate" class="metric-value text-warning">--%</div>
|
|
<div class="metric-label">Error Rate</div>
|
|
<p class="small text-muted">System errors in logs</p>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="metric-card">
|
|
<div id="totalPayments" class="metric-value text-info">--</div>
|
|
<div class="metric-label">Total Payments</div>
|
|
<p class="small text-muted">Recent activity</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Tabs -->
|
|
<ul class="nav nav-tabs justify-content-center" id="analyticsTabs" role="tablist">
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link active" id="performance-tab" data-bs-toggle="tab" data-bs-target="#performance-content" type="button" role="tab" data-tab="performance">
|
|
<i class="fas fa-tachometer-alt"></i> Performance
|
|
</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link" id="payments-tab" data-bs-toggle="tab" data-bs-target="#payments-content" type="button" role="tab" data-tab="payments">
|
|
<i class="fas fa-credit-card"></i> Payments
|
|
</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link" id="security-tab" data-bs-toggle="tab" data-bs-target="#security-content" type="button" role="tab" data-tab="security">
|
|
<i class="fas fa-shield-alt"></i> Security
|
|
</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link" id="logs-tab" data-bs-toggle="tab" data-bs-target="#logs-content" type="button" role="tab" data-tab="logs">
|
|
<i class="fas fa-list-alt"></i> Logs
|
|
</button>
|
|
</li>
|
|
</ul>
|
|
|
|
<!-- Tab Content -->
|
|
<div class="tab-content">
|
|
<!-- Performance Tab -->
|
|
<div class="tab-pane fade show active" id="performance-content" role="tabpanel">
|
|
<div class="card shadow mt-3">
|
|
<div class="card-body">
|
|
<h4 class="h5 mb-3">
|
|
<i class="fas fa-clock"></i> System Performance
|
|
</h4>
|
|
<div id="performanceMetrics">
|
|
<div class="text-center">
|
|
<div class="spinner-border text-primary" role="status">
|
|
<span class="visually-hidden">Loading...</span>
|
|
</div>
|
|
<p class="mt-3">Loading performance metrics...</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Payments Tab -->
|
|
<div class="tab-pane fade" id="payments-content" role="tabpanel">
|
|
<div class="card shadow mt-3">
|
|
<div class="card-body">
|
|
<h4 class="h5 mb-3">
|
|
<i class="fas fa-chart-line"></i> Payment Analytics
|
|
</h4>
|
|
<div id="paymentMetrics">
|
|
<div class="text-center">
|
|
<div class="spinner-border text-primary" role="status">
|
|
<span class="visually-hidden">Loading...</span>
|
|
</div>
|
|
<p class="mt-3">Loading payment analytics...</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Security Tab -->
|
|
<div class="tab-pane fade" id="security-content" role="tabpanel">
|
|
<div class="row mt-3">
|
|
<div class="col-md-4">
|
|
<div class="metric-card">
|
|
<div id="securityEvents" class="metric-value text-success">--</div>
|
|
<div class="metric-label">Security Events</div>
|
|
<p class="small text-muted">Last 7 days</p>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="metric-card">
|
|
<div id="failedLogins" class="metric-value text-warning">--</div>
|
|
<div class="metric-label">Failed Logins</div>
|
|
<p class="small text-muted">Authentication failures</p>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="metric-card">
|
|
<div id="blockedRequests" class="metric-value text-danger">--</div>
|
|
<div class="metric-label">Blocked Requests</div>
|
|
<p class="small text-muted">Suspicious activity</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="card shadow">
|
|
<div class="card-body">
|
|
<h4 class="h5 mb-3">
|
|
<i class="fas fa-shield-alt"></i> Security Events
|
|
</h4>
|
|
<div id="securityEventsList">
|
|
<div class="text-center">
|
|
<div class="spinner-border text-primary" role="status">
|
|
<span class="visually-hidden">Loading...</span>
|
|
</div>
|
|
<p class="mt-3">Loading security events...</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Logs Tab -->
|
|
<div class="tab-pane fade" id="logs-content" role="tabpanel">
|
|
<div class="card shadow mt-3">
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
<h4 class="h5 mb-0">
|
|
<i class="fas fa-search"></i> Log Search
|
|
</h4>
|
|
<div class="d-flex gap-2">
|
|
<input type="text" id="logSearch" class="form-control form-control-sm" placeholder="Search logs..." style="max-width: 200px;">
|
|
<select id="logAction" class="form-select form-select-sm" style="max-width: 200px;">
|
|
<option value="">All Actions</option>
|
|
<option value="LOGIN_SUCCESS">Login Success</option>
|
|
<option value="LOGIN_FAILED">Login Failed</option>
|
|
<option value="PAYMENT_PROCESSED">Payment Processed</option>
|
|
<option value="BATCH_CREATED">Batch Created</option>
|
|
</select>
|
|
<button class="btn btn-sm btn-primary" onclick="searchLogs()">
|
|
<i class="fas fa-search"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div id="logResults">
|
|
<div class="text-center">
|
|
<div class="spinner-border text-primary" role="status">
|
|
<span class="visually-hidden">Loading...</span>
|
|
</div>
|
|
<p class="mt-3">Loading recent logs...</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
$(document).ready(function() {
|
|
// Initialize dashboard
|
|
refreshSystemHealth();
|
|
|
|
// Load the default active tab content (Performance)
|
|
loadTabContent('performance');
|
|
|
|
// Tab switching
|
|
$('button[data-bs-toggle="tab"]').on('shown.bs.tab', function (e) {
|
|
const tab = $(e.target).data('tab');
|
|
loadTabContent(tab);
|
|
});
|
|
|
|
// Auto-refresh every 30 seconds
|
|
setInterval(refreshSystemHealth, 30000);
|
|
});
|
|
|
|
function refreshSystemHealth() {
|
|
fetch('/analytics/api/system-health')
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.error) {
|
|
console.error('Error loading system health:', data.error);
|
|
if (data.debug_error) {
|
|
console.error('Debug error:', data.debug_error);
|
|
}
|
|
}
|
|
|
|
updateHealthScore(data.health_score);
|
|
$('#paymentSuccessRate').text(data.payment_success_rate + '%');
|
|
$('#errorRate').text(data.error_rate + '%');
|
|
$('#totalPayments').text((data.metrics.total_payments || 0).toLocaleString());
|
|
|
|
// Update health score color
|
|
const healthElement = $('#healthScore');
|
|
healthElement.removeClass('health-excellent health-good health-warning health-critical');
|
|
|
|
if (data.health_score >= 90) {
|
|
healthElement.addClass('health-excellent');
|
|
} else if (data.health_score >= 75) {
|
|
healthElement.addClass('health-good');
|
|
} else if (data.health_score >= 60) {
|
|
healthElement.addClass('health-warning');
|
|
} else {
|
|
healthElement.addClass('health-critical');
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error loading system health:', error);
|
|
// Show mock data
|
|
updateHealthScore(85);
|
|
$('#paymentSuccessRate').text('95%');
|
|
$('#errorRate').text('2.5%');
|
|
$('#totalPayments').text('45');
|
|
});
|
|
}
|
|
|
|
function updateHealthScore(score) {
|
|
$('#healthValue').text(Math.round(score || 0));
|
|
}
|
|
|
|
function loadTabContent(tab) {
|
|
switch(tab) {
|
|
case 'performance':
|
|
loadPerformanceMetrics();
|
|
break;
|
|
case 'payments':
|
|
loadPaymentAnalytics();
|
|
break;
|
|
case 'security':
|
|
loadSecurityEvents();
|
|
break;
|
|
case 'logs':
|
|
searchLogs();
|
|
break;
|
|
}
|
|
}
|
|
|
|
function loadPerformanceMetrics() {
|
|
console.log('Loading performance metrics...');
|
|
$('#performanceMetrics').html(`
|
|
<div class="text-center">
|
|
<div class="spinner-border text-primary" role="status">
|
|
<span class="visually-hidden">Loading...</span>
|
|
</div>
|
|
<p class="mt-3">Loading performance metrics...</p>
|
|
</div>
|
|
`);
|
|
|
|
// Add timeout to the fetch
|
|
const controller = new AbortController();
|
|
const timeoutId = setTimeout(() => controller.abort(), 10000); // 10 second timeout
|
|
|
|
fetch('/analytics/api/performance-metrics', {
|
|
signal: controller.signal,
|
|
headers: {
|
|
'Accept': 'application/json',
|
|
'Content-Type': 'application/json'
|
|
}
|
|
})
|
|
.then(response => {
|
|
clearTimeout(timeoutId);
|
|
console.log('Performance metrics response:', response.status);
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
}
|
|
return response.json();
|
|
})
|
|
.then(data => {
|
|
console.log('Performance metrics data:', data);
|
|
if (data.error) {
|
|
$('#performanceMetrics').html(`
|
|
<div class="alert alert-info">
|
|
<h5 class="h5">
|
|
<i class="fas fa-chart-line"></i> Performance Monitoring
|
|
</h5>
|
|
<p><strong>Status:</strong> ${data.message || data.error}</p>
|
|
<p>The system is actively collecting performance data. Check back after some activity.</p>
|
|
${data.debug_error ? `<details><summary>Debug Info</summary><pre>${data.debug_error}</pre></details>` : ''}
|
|
</div>
|
|
`);
|
|
return;
|
|
}
|
|
|
|
let html = '<div>';
|
|
|
|
// System overview
|
|
if (data.system_info) {
|
|
html += '<div class="row">';
|
|
html += '<div class="col-md-4">';
|
|
html += '<div class="card text-center">';
|
|
html += '<div class="card-body">';
|
|
html += '<p class="text-muted text-uppercase small">Monitoring Status</p>';
|
|
html += `<p class="h4 ${data.system_info.monitoring_active ? 'text-success' : 'text-warning'}">`;
|
|
html += `<i class="fas ${data.system_info.monitoring_active ? 'fa-check-circle' : 'fa-exclamation-triangle'}"></i> `;
|
|
html += `${data.system_info.monitoring_active ? 'Active' : 'Inactive'}`;
|
|
html += '</p>';
|
|
html += '</div></div></div>';
|
|
|
|
html += '<div class="col-md-4">';
|
|
html += '<div class="card text-center">';
|
|
html += '<div class="card-body">';
|
|
html += '<p class="text-muted text-uppercase small">Log Files</p>';
|
|
html += `<p class="h4 text-info">${data.system_info.log_files_found || 0}</p>`;
|
|
html += '</div></div></div>';
|
|
|
|
html += '<div class="col-md-4">';
|
|
html += '<div class="card text-center">';
|
|
html += '<div class="card-body">';
|
|
html += '<p class="text-muted text-uppercase small">Collection Period</p>';
|
|
html += `<p class="h4 text-muted">${data.system_info.data_collection_period || '7 days'}</p>`;
|
|
html += '</div></div></div>';
|
|
html += '</div>';
|
|
}
|
|
|
|
// Performance summary
|
|
if (data.summary) {
|
|
html += '<h5 class="h5 mt-4">Performance Summary</h5>';
|
|
html += '<div class="row">';
|
|
html += '<div class="col-md-3">';
|
|
html += '<div class="card text-center">';
|
|
html += '<div class="card-body">';
|
|
html += '<p class="text-muted text-uppercase small">Slow Requests</p>';
|
|
html += `<p class="h4 ${data.summary.slow_request_count > 0 ? 'text-warning' : 'text-success'}">${data.summary.slow_request_count || 0}</p>`;
|
|
html += '<p class="small text-muted">Requests > 1 second</p>';
|
|
html += '</div></div></div>';
|
|
|
|
html += '<div class="col-md-3">';
|
|
html += '<div class="card text-center">';
|
|
html += '<div class="card-body">';
|
|
html += '<p class="text-muted text-uppercase small">Slow Queries</p>';
|
|
html += `<p class="h4 ${data.summary.database_queries > 0 ? 'text-warning' : 'text-success'}">${data.summary.database_queries || 0}</p>`;
|
|
html += '<p class="small text-muted">DB queries > 100ms</p>';
|
|
html += '</div></div></div>';
|
|
|
|
html += '<div class="col-md-3">';
|
|
html += '<div class="card text-center">';
|
|
html += '<div class="card-body">';
|
|
html += '<p class="text-muted text-uppercase small">Total Requests</p>';
|
|
html += `<p class="h4 text-info">${data.summary.total_requests || 'N/A'}</p>`;
|
|
html += '<p class="small text-muted">Recent requests</p>';
|
|
html += '</div></div></div>';
|
|
|
|
html += '<div class="col-md-3">';
|
|
html += '<div class="card text-center">';
|
|
html += '<div class="card-body">';
|
|
html += '<p class="text-muted text-uppercase small">Avg Response</p>';
|
|
html += `<p class="h4 text-info">${data.summary.avg_response_time || 'N/A'}</p>`;
|
|
html += '<p class="small text-muted">Milliseconds</p>';
|
|
html += '</div></div></div>';
|
|
html += '</div>';
|
|
}
|
|
|
|
// Slow requests table
|
|
if (data.slow_requests && data.slow_requests.length > 0) {
|
|
html += '<h5 class="h5 mt-4">Recent Slow Requests</h5>';
|
|
html += '<div class="table-responsive">';
|
|
html += '<table class="table table-striped">';
|
|
html += '<thead><tr><th>Time</th><th>Endpoint</th><th>Duration</th><th>Status</th></tr></thead>';
|
|
html += '<tbody>';
|
|
data.slow_requests.slice(0, 10).forEach(req => {
|
|
html += '<tr>';
|
|
html += `<td>${new Date(req.timestamp).toLocaleString()}</td>`;
|
|
html += `<td><code>${req.endpoint}</code></td>`;
|
|
html += `<td><span class="badge bg-warning">${Math.round(req.duration_ms)}ms</span></td>`;
|
|
html += `<td><span class="badge ${req.status_code < 400 ? 'bg-success' : 'bg-danger'}">${req.status_code}</span></td>`;
|
|
html += '</tr>';
|
|
});
|
|
html += '</tbody></table>';
|
|
html += '</div>';
|
|
} else {
|
|
html += '<div class="alert alert-success mt-3">';
|
|
html += '<i class="fas fa-check"></i> ';
|
|
html += '<strong>Good Performance!</strong> No slow requests detected recently.';
|
|
html += '</div>';
|
|
}
|
|
|
|
// Slow queries table
|
|
if (data.slow_queries && data.slow_queries.length > 0) {
|
|
html += '<h5 class="h5 mt-4">Recent Slow Database Queries</h5>';
|
|
html += '<div class="table-responsive">';
|
|
html += '<table class="table table-striped">';
|
|
html += '<thead><tr><th>Time</th><th>Table</th><th>Type</th><th>Duration</th></tr></thead>';
|
|
html += '<tbody>';
|
|
data.slow_queries.slice(0, 10).forEach(query => {
|
|
html += '<tr>';
|
|
html += `<td>${new Date(query.timestamp).toLocaleString()}</td>`;
|
|
html += `<td><code>${query.table}</code></td>`;
|
|
html += `<td>${query.query_type}</td>`;
|
|
html += `<td><span class="badge bg-warning">${Math.round(query.duration_ms)}ms</span></td>`;
|
|
html += '</tr>';
|
|
});
|
|
html += '</tbody></table>';
|
|
html += '</div>';
|
|
}
|
|
|
|
html += '</div>';
|
|
$('#performanceMetrics').html(html);
|
|
})
|
|
.catch(error => {
|
|
clearTimeout(timeoutId);
|
|
console.error('Error loading performance metrics:', error);
|
|
|
|
let errorMessage = 'Unable to load performance metrics at this time.';
|
|
if (error.name === 'AbortError') {
|
|
errorMessage = 'Request timed out after 10 seconds.';
|
|
} else if (error.message.includes('HTTP')) {
|
|
errorMessage = `Server error: ${error.message}`;
|
|
}
|
|
|
|
$('#performanceMetrics').html(`
|
|
<div class="alert alert-warning">
|
|
<h5 class="h5">Performance Data Loading Error</h5>
|
|
<p><strong>Error:</strong> ${errorMessage}</p>
|
|
<p>The system may be initializing or there may be a connectivity issue.</p>
|
|
<button class="btn btn-sm btn-primary" onclick="loadPerformanceMetrics()">Try Again</button>
|
|
<details class="mt-2">
|
|
<summary>Debug Information</summary>
|
|
<pre>${error.stack || error.message}</pre>
|
|
</details>
|
|
</div>
|
|
`);
|
|
});
|
|
}
|
|
|
|
function loadPaymentAnalytics() {
|
|
$('#paymentMetrics').html(`
|
|
<div class="text-center">
|
|
<div class="spinner-border text-primary" role="status">
|
|
<span class="visually-hidden">Loading...</span>
|
|
</div>
|
|
<p class="mt-3">Loading payment analytics...</p>
|
|
</div>
|
|
`);
|
|
|
|
fetch('/analytics/api/payment-analytics')
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.error) {
|
|
$('#paymentMetrics').html(`
|
|
<div class="alert alert-info">
|
|
<p><strong>Payment analytics:</strong> No recent payment data available.</p>
|
|
<p>Analytics will appear after payment processing activity.</p>
|
|
</div>
|
|
`);
|
|
return;
|
|
}
|
|
|
|
let html = '<div>';
|
|
html += '<h5>Payment Analytics Overview</h5>';
|
|
html += '<p>Payment analytics are being collected. Detailed metrics will appear with payment activity.</p>';
|
|
html += '</div>';
|
|
|
|
$('#paymentMetrics').html(html);
|
|
})
|
|
.catch(error => {
|
|
console.error('Error loading payment analytics:', error);
|
|
$('#paymentMetrics').html(`
|
|
<div class="alert alert-info">
|
|
<p>Payment analytics will be available after payment processing activity.</p>
|
|
</div>
|
|
`);
|
|
});
|
|
}
|
|
|
|
function loadSecurityEvents() {
|
|
$('#securityEventsList').html(`
|
|
<div class="text-center">
|
|
<div class="spinner-border text-primary" role="status">
|
|
<span class="visually-hidden">Loading...</span>
|
|
</div>
|
|
<p class="mt-3">Loading security events...</p>
|
|
</div>
|
|
`);
|
|
|
|
fetch('/analytics/api/security-events')
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.error) {
|
|
$('#securityEventsList').html(`
|
|
<div class="alert alert-success">
|
|
<p><strong>Security status:</strong> All clear - no security events detected.</p>
|
|
<p>The system is actively monitoring for security threats.</p>
|
|
</div>
|
|
`);
|
|
$('#securityEvents').text('0');
|
|
$('#failedLogins').text('0');
|
|
$('#blockedRequests').text('0');
|
|
return;
|
|
}
|
|
|
|
$('#securityEvents').text(data.summary.total_events);
|
|
$('#failedLogins').text(data.summary.failed_logins);
|
|
$('#blockedRequests').text(data.summary.blocked_requests);
|
|
|
|
if (data.summary.total_events === 0) {
|
|
$('#securityEventsList').html(`
|
|
<div class="alert alert-success">
|
|
<i class="fas fa-shield-alt"></i>
|
|
<strong>All clear!</strong> No security events detected.
|
|
</div>
|
|
`);
|
|
} else {
|
|
$('#securityEventsList').html('<p>Security events detected. Review logs for details.</p>');
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error loading security events:', error);
|
|
$('#securityEventsList').html(`
|
|
<div class="alert alert-success">
|
|
<i class="fas fa-shield-alt"></i>
|
|
Security monitoring is active.
|
|
</div>
|
|
`);
|
|
$('#securityEvents').text('0');
|
|
$('#failedLogins').text('0');
|
|
$('#blockedRequests').text('0');
|
|
});
|
|
}
|
|
|
|
function searchLogs() {
|
|
const query = $('#logSearch').val();
|
|
const action = $('#logAction').val();
|
|
|
|
$('#logResults').html(`
|
|
<div class="text-center">
|
|
<div class="spinner-border text-primary" role="status">
|
|
<span class="visually-hidden">Loading...</span>
|
|
</div>
|
|
<p class="mt-3">Searching logs...</p>
|
|
</div>
|
|
`);
|
|
|
|
const params = new URLSearchParams({ page: 1, per_page: 20 });
|
|
if (query) params.append('q', query);
|
|
if (action) params.append('action', action);
|
|
|
|
fetch(`/analytics/api/logs/search?${params}`)
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.error) {
|
|
$('#logResults').html(`
|
|
<div class="alert alert-warning">
|
|
<p><strong>Log search error:</strong> ${data.error}</p>
|
|
</div>
|
|
`);
|
|
return;
|
|
}
|
|
|
|
if (!data.logs || data.logs.length === 0) {
|
|
$('#logResults').html(`
|
|
<div class="alert alert-info">
|
|
<p>No logs found matching your search criteria.</p>
|
|
</div>
|
|
`);
|
|
return;
|
|
}
|
|
|
|
let html = '';
|
|
data.logs.forEach(log => {
|
|
const logClass = log.action.includes('ERROR') || log.action.includes('FAILED') ? 'bg-danger bg-opacity-10' :
|
|
log.action.includes('WARNING') ? 'bg-warning bg-opacity-10' :
|
|
log.action.includes('SUCCESS') ? 'bg-success bg-opacity-10' : '';
|
|
|
|
html += `<div class="log-entry ${logClass}">
|
|
<div class="d-flex justify-content-between">
|
|
<div>
|
|
<strong>${log.action}</strong>
|
|
</div>
|
|
<div>
|
|
<small class="text-muted">${new Date(log.timestamp).toLocaleString()}</small>
|
|
</div>
|
|
</div>
|
|
<div>${log.message || 'No message'}</div>
|
|
<small class="text-muted">
|
|
${log.entity_type} ${log.entity_id || ''} | User ID: ${log.user_id || 'System'} | IP: ${log.ip_address || 'Unknown'}
|
|
</small>
|
|
</div>`;
|
|
});
|
|
|
|
$('#logResults').html(html);
|
|
})
|
|
.catch(error => {
|
|
console.error('Error searching logs:', error);
|
|
$('#logResults').html(`
|
|
<div class="alert alert-warning">
|
|
<p>Error searching logs. Please try again.</p>
|
|
</div>
|
|
`);
|
|
});
|
|
}
|
|
|
|
function refreshDashboard() {
|
|
document.body.classList.add('loading');
|
|
|
|
refreshSystemHealth();
|
|
|
|
// Refresh current tab content
|
|
const activeTabButton = document.querySelector('.nav-link.active');
|
|
const activeTab = activeTabButton ? activeTabButton.getAttribute('data-tab') : null;
|
|
if (activeTab) {
|
|
loadTabContent(activeTab);
|
|
}
|
|
|
|
setTimeout(() => {
|
|
document.body.classList.remove('loading');
|
|
}, 1000);
|
|
}
|
|
|
|
function exportReport() {
|
|
const activeTabButton = document.querySelector('.nav-link.active');
|
|
const activeTab = activeTabButton ? activeTabButton.getAttribute('data-tab') : 'system';
|
|
|
|
fetch(`/analytics/api/generate-report?type=${activeTab}&days=7`)
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.error) {
|
|
alert('Error generating report: ' + data.error);
|
|
return;
|
|
}
|
|
|
|
// Create and download JSON report
|
|
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
|
|
const url = URL.createObjectURL(blob);
|
|
const a = document.createElement('a');
|
|
a.href = url;
|
|
a.download = `plutus_${activeTab}_report_${new Date().toISOString().split('T')[0]}.json`;
|
|
document.body.appendChild(a);
|
|
a.click();
|
|
document.body.removeChild(a);
|
|
URL.revokeObjectURL(url);
|
|
})
|
|
.catch(error => {
|
|
console.error('Error generating report:', error);
|
|
alert('Error generating report');
|
|
});
|
|
}
|
|
</script>
|
|
{% endblock %}
|
|
|