mirror of
https://github.com/Crazyco-xyz/48hr.email.git
synced 2026-01-08 10:49:35 +01:00
[Chore]: Misc. V2 patches
This commit is contained in:
parent
2f58eacfa7
commit
c56ec92ce5
7 changed files with 62 additions and 31 deletions
BIN
.github/assets/stats.png
vendored
Normal file
BIN
.github/assets/stats.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 250 KiB |
|
|
@ -42,9 +42,9 @@ All data is being removed 48hrs after they have reached the mail server.
|
|||
|
||||
## Screenshots
|
||||
|
||||
| Homepage | Account Panel |
|
||||
|:---:|:---:|
|
||||
| <img src=".github/assets/home.png" width="500px" height="300px" style="object-fit: fit;"> | <img src=".github/assets/account.png" width="500px" height="300px" style="object-fit: fit;"> |
|
||||
| Homepage | Account Panel | Stats Page |
|
||||
|:---:|:---:|:---:|
|
||||
| <img src=".github/assets/home.png" width="500px" height="300px" style="object-fit: fit;"> | <img src=".github/assets/account.png" width="500px" height="300px" style="object-fit: fit;"> | <img src=".github/assets/stats.png" width="500px" height="300px" style="object-fit: fit;"> |
|
||||
|
||||
| Inbox | Email using HTML and CSS | Attachments and Cryptographic Keys view |
|
||||
|:---:|:---:|:---:|
|
||||
|
|
|
|||
10
app.js
10
app.js
|
|
@ -5,7 +5,7 @@
|
|||
const config = require('./application/config')
|
||||
const debug = require('debug')('48hr-email:app')
|
||||
const Helper = require('./application/helper')
|
||||
|
||||
const helper = new(Helper)
|
||||
const { app, io, server } = require('./infrastructure/web/web')
|
||||
const ClientNotification = require('./infrastructure/web/client-notification')
|
||||
const ImapService = require('./application/imap-service')
|
||||
|
|
@ -95,10 +95,14 @@ const mailProcessingService = new MailProcessingService(
|
|||
debug('Mail processing service initialized')
|
||||
|
||||
// Initialize statistics with current count
|
||||
imapService.on(ImapService.EVENT_INITIAL_LOAD_DONE, () => {
|
||||
imapService.on(ImapService.EVENT_INITIAL_LOAD_DONE, async() => {
|
||||
const count = mailProcessingService.getCount()
|
||||
statisticsStore.initialize(count)
|
||||
debug(`Statistics initialized with ${count} emails`)
|
||||
|
||||
// Get and set the largest UID for all-time total
|
||||
const largestUid = await helper.getLargestUid(imapService)
|
||||
statisticsStore.updateLargestUid(largestUid)
|
||||
debug(`Statistics initialized with ${count} emails, largest UID: ${largestUid}`)
|
||||
})
|
||||
|
||||
// Set up timer sync broadcasting after IMAP is ready
|
||||
|
|
|
|||
|
|
@ -172,7 +172,8 @@ class Helper {
|
|||
}
|
||||
|
||||
async getLargestUid(imapService) {
|
||||
return await imapService.getLargestUid();
|
||||
const uid = await imapService.getLargestUid();
|
||||
return uid || 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -8,15 +8,15 @@ class StatisticsStore {
|
|||
constructor() {
|
||||
// Current totals
|
||||
this.currentCount = 0
|
||||
this.historicalTotal = 0
|
||||
|
||||
this.largestUid = 0
|
||||
|
||||
// 24-hour rolling data (one entry per minute = 1440 entries)
|
||||
this.hourlyData = []
|
||||
this.maxDataPoints = 24 * 60 // 24 hours * 60 minutes
|
||||
|
||||
|
||||
// Track last cleanup to avoid too frequent operations
|
||||
this.lastCleanup = Date.now()
|
||||
|
||||
|
||||
debug('Statistics store initialized')
|
||||
}
|
||||
|
||||
|
|
@ -26,18 +26,27 @@ class StatisticsStore {
|
|||
*/
|
||||
initialize(count) {
|
||||
this.currentCount = count
|
||||
this.historicalTotal = count
|
||||
debug(`Initialized with ${count} emails`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Update largest UID (all-time total emails processed)
|
||||
* @param {number} uid - Largest UID from mailbox (0 if no emails)
|
||||
*/
|
||||
updateLargestUid(uid) {
|
||||
if (uid >= 0 && uid > this.largestUid) {
|
||||
this.largestUid = uid
|
||||
debug(`Largest UID updated to ${uid}`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Record an email received event
|
||||
*/
|
||||
recordReceive() {
|
||||
this.currentCount++
|
||||
this.historicalTotal++
|
||||
this._addDataPoint('receive')
|
||||
debug(`Email received. Current: ${this.currentCount}, Historical: ${this.historicalTotal}`)
|
||||
this._addDataPoint('receive')
|
||||
debug(`Email received. Current: ${this.currentCount}`)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -79,12 +88,12 @@ class StatisticsStore {
|
|||
*/
|
||||
getStats() {
|
||||
this._cleanup()
|
||||
|
||||
|
||||
const last24h = this._getLast24Hours()
|
||||
|
||||
|
||||
return {
|
||||
currentCount: this.currentCount,
|
||||
historicalTotal: this.historicalTotal,
|
||||
allTimeTotal: this.largestUid,
|
||||
last24Hours: {
|
||||
receives: last24h.receives,
|
||||
deletes: last24h.deletes,
|
||||
|
|
@ -102,7 +111,7 @@ class StatisticsStore {
|
|||
_addDataPoint(type) {
|
||||
const now = Date.now()
|
||||
const minute = Math.floor(now / 60000) * 60000 // Round to minute
|
||||
|
||||
|
||||
// Find or create entry for this minute
|
||||
let entry = this.hourlyData.find(e => e.timestamp === minute)
|
||||
if (!entry) {
|
||||
|
|
@ -114,10 +123,10 @@ class StatisticsStore {
|
|||
}
|
||||
this.hourlyData.push(entry)
|
||||
}
|
||||
|
||||
|
||||
entry[type + 's']++
|
||||
|
||||
this._cleanup()
|
||||
|
||||
this._cleanup()
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -126,20 +135,20 @@ class StatisticsStore {
|
|||
*/
|
||||
_cleanup() {
|
||||
const now = Date.now()
|
||||
|
||||
|
||||
// Only cleanup every 5 minutes to avoid constant filtering
|
||||
if (now - this.lastCleanup < 5 * 60 * 1000) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
const cutoff = now - (24 * 60 * 60 * 1000)
|
||||
const beforeCount = this.hourlyData.length
|
||||
this.hourlyData = this.hourlyData.filter(entry => entry.timestamp >= cutoff)
|
||||
|
||||
|
||||
if (beforeCount !== this.hourlyData.length) {
|
||||
debug(`Cleaned up ${beforeCount - this.hourlyData.length} old data points`)
|
||||
}
|
||||
|
||||
|
||||
this.lastCleanup = now
|
||||
}
|
||||
|
||||
|
|
@ -151,7 +160,7 @@ class StatisticsStore {
|
|||
_getLast24Hours() {
|
||||
const cutoff = Date.now() - (24 * 60 * 60 * 1000)
|
||||
const recent = this.hourlyData.filter(e => e.timestamp >= cutoff)
|
||||
|
||||
|
||||
return {
|
||||
receives: recent.reduce((sum, e) => sum + e.receives, 0),
|
||||
deletes: recent.reduce((sum, e) => sum + e.deletes, 0),
|
||||
|
|
@ -168,7 +177,7 @@ class StatisticsStore {
|
|||
const now = Date.now()
|
||||
const cutoff = now - (24 * 60 * 60 * 1000)
|
||||
const hourly = {}
|
||||
|
||||
|
||||
// Aggregate by hour
|
||||
this.hourlyData
|
||||
.filter(e => e.timestamp >= cutoff)
|
||||
|
|
@ -181,7 +190,7 @@ class StatisticsStore {
|
|||
hourly[hour].deletes += entry.deletes
|
||||
hourly[hour].forwards += entry.forwards
|
||||
})
|
||||
|
||||
|
||||
// Convert to sorted array
|
||||
return Object.values(hourly).sort((a, b) => a.timestamp - b.timestamp)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,13 +7,20 @@ router.get('/', async(req, res) => {
|
|||
try {
|
||||
const config = req.app.get('config')
|
||||
const statisticsStore = req.app.get('statisticsStore')
|
||||
const imapService = req.app.get('imapService')
|
||||
const Helper = require('../../../application/helper')
|
||||
const helper = new Helper()
|
||||
|
||||
// Update largest UID before getting stats (if IMAP is ready)
|
||||
if (imapService) {
|
||||
const largestUid = await helper.getLargestUid(imapService)
|
||||
statisticsStore.updateLargestUid(largestUid)
|
||||
}
|
||||
|
||||
const stats = statisticsStore.getStats()
|
||||
const purgeTime = helper.purgeTimeElemetBuilder()
|
||||
|
||||
debug(`Stats page requested: ${stats.currentCount} current, ${stats.historicalTotal} historical`)
|
||||
debug(`Stats page requested: ${stats.currentCount} current, ${stats.allTimeTotal} all-time total`)
|
||||
|
||||
res.render('stats', {
|
||||
title: `Statistics | ${config.http.branding[0]}`,
|
||||
|
|
@ -34,6 +41,16 @@ router.get('/', async(req, res) => {
|
|||
router.get('/api', async(req, res) => {
|
||||
try {
|
||||
const statisticsStore = req.app.get('statisticsStore')
|
||||
const imapService = req.app.get('imapService')
|
||||
const Helper = require('../../../application/helper')
|
||||
const helper = new Helper()
|
||||
|
||||
// Update largest UID before getting stats (if IMAP is ready)
|
||||
if (imapService) {
|
||||
const largestUid = await helper.getLargestUid(imapService)
|
||||
statisticsStore.updateLargestUid(largestUid)
|
||||
}
|
||||
|
||||
const stats = statisticsStore.getStats()
|
||||
|
||||
res.json(stats)
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@
|
|||
|
||||
<!-- Historical Total -->
|
||||
<div class="stat-card">
|
||||
<div class="stat-value" id="historicalTotal">{{ stats.historicalTotal }}</div>
|
||||
<div class="stat-value" id="historicalTotal">{{ stats.allTimeTotal }}</div>
|
||||
<div class="stat-label">All Time Total</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue