mirror of
https://github.com/Crazyco-xyz/48hr.email.git
synced 2026-01-09 11:19:36 +01:00
[Chore]: Stats patches
This commit is contained in:
parent
e012b772c8
commit
3fdf5bf61b
3 changed files with 33 additions and 99 deletions
|
|
@ -83,8 +83,15 @@ class ClientNotification extends EventEmitter {
|
||||||
this.pendingNotifications.set(address, prev + 1);
|
this.pendingNotifications.set(address, prev + 1);
|
||||||
debug(`No listeners for ${address}, queued notification (${prev + 1} pending)`);
|
debug(`No listeners for ${address}, queued notification (${prev + 1} pending)`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Also emit a global stats-update event for stats page
|
||||||
|
if (this.io) {
|
||||||
|
this.io.emit('stats-update');
|
||||||
|
debug('Emitted stats-update to all connected clients');
|
||||||
|
}
|
||||||
|
|
||||||
return hadListeners;
|
return hadListeners;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = ClientNotification
|
module.exports = ClientNotification
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,48 @@
|
||||||
/**
|
/**
|
||||||
* Statistics page functionality
|
* Statistics page functionality
|
||||||
* Handles Chart.js initialization and auto-refresh of statistics data
|
* Handles Chart.js initialization and real-time updates via Socket.IO
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Initialize stats chart if on stats page
|
// Initialize stats chart if on stats page
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
const chartCanvas = document.getElementById('statsChart');
|
const chartCanvas = document.getElementById('statsChart');
|
||||||
if (!chartCanvas) return; // Not on stats page
|
if (!chartCanvas) return; // Not on stats page
|
||||||
|
|
||||||
// Get initial data from global variable (set by template)
|
// Get initial data from global variable (set by template)
|
||||||
if (typeof window.initialStatsData === 'undefined') {
|
if (typeof window.initialStatsData === 'undefined') {
|
||||||
console.error('Initial stats data not found');
|
console.error('Initial stats data not found');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialData = window.initialStatsData;
|
const initialData = window.initialStatsData;
|
||||||
|
|
||||||
|
// Set up Socket.IO connection for real-time updates
|
||||||
|
if (typeof io !== 'undefined') {
|
||||||
|
const socket = io();
|
||||||
|
|
||||||
|
// Listen for stats updates (any email event: receive, delete, forward)
|
||||||
|
socket.on('stats-update', () => {
|
||||||
|
console.log('Stats update received, reloading page...');
|
||||||
|
location.reload();
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('reconnect', () => {
|
||||||
|
console.log('Reconnected to server');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Prepare chart data
|
// Prepare chart data
|
||||||
const labels = initialData.map(d => {
|
const labels = initialData.map(d => {
|
||||||
const date = new Date(d.timestamp);
|
const date = new Date(d.timestamp);
|
||||||
return date.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' });
|
return date.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' });
|
||||||
});
|
});
|
||||||
|
|
||||||
const ctx = chartCanvas.getContext('2d');
|
const ctx = chartCanvas.getContext('2d');
|
||||||
const chart = new Chart(ctx, {
|
const chart = new Chart(ctx, {
|
||||||
type: 'line',
|
type: 'line',
|
||||||
data: {
|
data: {
|
||||||
labels: labels,
|
labels: labels,
|
||||||
datasets: [
|
datasets: [{
|
||||||
{
|
|
||||||
label: 'Received',
|
label: 'Received',
|
||||||
data: initialData.map(d => d.receives),
|
data: initialData.map(d => d.receives),
|
||||||
borderColor: '#9b4dca',
|
borderColor: '#9b4dca',
|
||||||
|
|
@ -95,32 +109,4 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Auto-refresh stats every 30 seconds
|
|
||||||
setInterval(async () => {
|
|
||||||
try {
|
|
||||||
const response = await fetch('/stats/api');
|
|
||||||
const data = await response.json();
|
|
||||||
|
|
||||||
// Update stat cards
|
|
||||||
document.getElementById('currentCount').textContent = data.currentCount;
|
|
||||||
document.getElementById('historicalTotal').textContent = data.historicalTotal;
|
|
||||||
document.getElementById('receives24h').textContent = data.last24Hours.receives;
|
|
||||||
document.getElementById('deletes24h').textContent = data.last24Hours.deletes;
|
|
||||||
document.getElementById('forwards24h').textContent = data.last24Hours.forwards;
|
|
||||||
|
|
||||||
// Update chart
|
|
||||||
const timeline = data.last24Hours.timeline;
|
|
||||||
chart.data.labels = timeline.map(d => {
|
|
||||||
const date = new Date(d.timestamp);
|
|
||||||
return date.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' });
|
|
||||||
});
|
|
||||||
chart.data.datasets[0].data = timeline.map(d => d.receives);
|
|
||||||
chart.data.datasets[1].data = timeline.map(d => d.deletes);
|
|
||||||
chart.data.datasets[2].data = timeline.map(d => d.forwards);
|
|
||||||
chart.update('none'); // Update without animation
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to refresh stats:', error);
|
|
||||||
}
|
|
||||||
}, 30000);
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,12 @@ router.get('/', async(req, res) => {
|
||||||
const statisticsStore = req.app.get('statisticsStore')
|
const statisticsStore = req.app.get('statisticsStore')
|
||||||
const Helper = require('../../../application/helper')
|
const Helper = require('../../../application/helper')
|
||||||
const helper = new Helper()
|
const helper = new Helper()
|
||||||
|
|
||||||
const stats = statisticsStore.getStats()
|
const stats = statisticsStore.getStats()
|
||||||
const purgeTime = helper.purgeTimeElemetBuilder()
|
const purgeTime = helper.purgeTimeElemetBuilder()
|
||||||
|
|
||||||
debug(`Stats page requested: ${stats.currentCount} current, ${stats.historicalTotal} historical`)
|
debug(`Stats page requested: ${stats.currentCount} current, ${stats.historicalTotal} historical`)
|
||||||
|
|
||||||
res.render('stats', {
|
res.render('stats', {
|
||||||
title: `Statistics | ${config.http.branding[0]}`,
|
title: `Statistics | ${config.http.branding[0]}`,
|
||||||
branding: config.http.branding,
|
branding: config.http.branding,
|
||||||
|
|
@ -35,7 +35,7 @@ router.get('/api', async(req, res) => {
|
||||||
try {
|
try {
|
||||||
const statisticsStore = req.app.get('statisticsStore')
|
const statisticsStore = req.app.get('statisticsStore')
|
||||||
const stats = statisticsStore.getStats()
|
const stats = statisticsStore.getStats()
|
||||||
|
|
||||||
res.json(stats)
|
res.json(stats)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
debug(`Error fetching stats API: ${error.message}`)
|
debug(`Error fetching stats API: ${error.message}`)
|
||||||
|
|
@ -43,63 +43,4 @@ router.get('/api', async(req, res) => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// GET /statsdemo - Demo page with fake data for testing
|
|
||||||
router.get('/demo', async(req, res) => {
|
|
||||||
try {
|
|
||||||
const config = req.app.get('config')
|
|
||||||
const Helper = require('../../../application/helper')
|
|
||||||
const helper = new Helper()
|
|
||||||
const purgeTime = helper.purgeTimeElemetBuilder()
|
|
||||||
|
|
||||||
// Generate fake 24-hour timeline data
|
|
||||||
const now = Date.now()
|
|
||||||
const timeline = []
|
|
||||||
|
|
||||||
for (let i = 23; i >= 0; i--) {
|
|
||||||
const timestamp = now - (i * 60 * 60 * 1000) // Hourly data points
|
|
||||||
const receives = Math.floor(Math.random() * 100) + 200 // 200-300 receives per hour (~6k/day)
|
|
||||||
const deletes = Math.floor(receives * 0.85) + Math.floor(Math.random() * 10) // ~85% deletion rate
|
|
||||||
const forwards = Math.floor(receives * 0.01) + (Math.random() < 0.3 ? 1 : 0) // ~1% forward rate
|
|
||||||
|
|
||||||
timeline.push({
|
|
||||||
timestamp,
|
|
||||||
receives,
|
|
||||||
deletes,
|
|
||||||
forwards
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate totals
|
|
||||||
const totalReceives = timeline.reduce((sum, d) => sum + d.receives, 0)
|
|
||||||
const totalDeletes = timeline.reduce((sum, d) => sum + d.deletes, 0)
|
|
||||||
const totalForwards = timeline.reduce((sum, d) => sum + d.forwards, 0)
|
|
||||||
|
|
||||||
const fakeStats = {
|
|
||||||
currentCount: 6500,
|
|
||||||
historicalTotal: 124893,
|
|
||||||
last24Hours: {
|
|
||||||
receives: totalReceives,
|
|
||||||
deletes: totalDeletes,
|
|
||||||
forwards: totalForwards,
|
|
||||||
timeline: timeline
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
debug(`Stats demo page requested with fake data`)
|
|
||||||
|
|
||||||
res.render('stats', {
|
|
||||||
title: `Statistics Demo | ${config.http.branding[0]}`,
|
|
||||||
branding: config.http.branding,
|
|
||||||
purgeTime: purgeTime,
|
|
||||||
stats: fakeStats,
|
|
||||||
authEnabled: config.user.authEnabled,
|
|
||||||
currentUser: req.session && req.session.username
|
|
||||||
})
|
|
||||||
} catch (error) {
|
|
||||||
debug(`Error loading stats demo page: ${error.message}`)
|
|
||||||
console.error('Error while loading stats demo page', error)
|
|
||||||
res.status(500).send('Error loading statistics demo')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
module.exports = router
|
module.exports = router
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue