2weekmail/api/controllers/StatsController.js
2025-03-19 19:56:57 -05:00

434 lines
15 KiB
JavaScript

const path = require('path');
const { models } = require(path.resolve(process.env.ROOT_PATH, './db/db.js'));
const BaseController = require(path.resolve(process.env.ROOT_PATH, './controllers/BaseController.js'));
const { subDays } = require('date-fns');
class StatsController extends BaseController {
constructor() {
super();
}
/**
* Get overall system statistics
*
* @swagger
* /stats/system:
* get:
* summary: Get system statistics
* description: Retrieves overall system statistics including mailbox and domain counts
* tags: [Statistics]
* security:
* - bearerAuth: []
* responses:
* 200:
* description: System statistics
* content:
* application/json:
* schema:
* type: object
* properties:
* mailboxes:
* type: object
* properties:
* total:
* type: integer
* description: Total number of mailboxes
* active:
* type: integer
* description: Number of active mailboxes
* expiringToday:
* type: integer
* description: Number of mailboxes expiring today
* domains:
* type: object
* properties:
* total:
* type: integer
* description: Total number of domains
* active:
* type: integer
* description: Number of active domains
* creationStats:
* type: array
* description: Mailbox creation statistics for the last 7 days
* items:
* type: object
* properties:
* date:
* type: string
* format: date
* mailboxesCreated:
* type: integer
* 500:
* description: Server error
*/
async getSystemStats(req, res) {
try {
// Get total counts
const totalMailboxes = await models.Mailbox.query().count('* as count').first();
const activeMailboxes = await models.Mailbox.query()
.where('active', 1)
.count('* as count')
.first();
const expiringToday = await models.Mailbox.query()
.where('expires', '<', new Date(new Date().setHours(23, 59, 59, 999)).toISOString().slice(0, 19).replace('T', ' '))
.where('expires', '>', new Date().toISOString().slice(0, 19).replace('T', ' '))
.count('* as count')
.first();
const totalDomains = await models.Domain.query().count('* as count').first();
const activeDomains = await models.Domain.query()
.where('active', 1)
.count('* as count')
.first();
// Get creation stats for the last 7 days
const last7Days = [];
for (let i = 6; i >= 0; i--) {
const date = subDays(new Date(), i);
const formattedDate = date.toISOString().slice(0, 10);
// Get mailboxes created on this date
const mailboxesCreated = await models.Mailbox.query()
.where('created', 'like', `${formattedDate}%`)
.count('* as count')
.first();
last7Days.push({
date: formattedDate,
mailboxesCreated: parseInt(mailboxesCreated.count) || 0
});
}
return res.json({
mailboxes: {
total: parseInt(totalMailboxes.count) || 0,
active: parseInt(activeMailboxes.count) || 0,
expiringToday: parseInt(expiringToday.count) || 0
},
domains: {
total: parseInt(totalDomains.count) || 0,
active: parseInt(activeDomains.count) || 0
},
creationStats: last7Days
});
} catch (error) {
console.error('Error fetching system stats:', error);
return res.status(500).json({ error: 'Failed to fetch system statistics' });
}
}
/**
* Get mailbox statistics
*
* @swagger
* /stats/mailboxes:
* get:
* summary: Get mailbox statistics
* description: Retrieves detailed statistics about mailboxes
* tags: [Statistics]
* security:
* - bearerAuth: []
* responses:
* 200:
* description: Mailbox statistics
* content:
* application/json:
* schema:
* type: object
* properties:
* expiryStats:
* type: object
* properties:
* expired:
* type: integer
* description: Number of expired mailboxes
* expiringSoon:
* type: integer
* description: Number of mailboxes expiring within 3 days
* creationByDay:
* type: array
* description: Mailbox creation statistics for the last 30 days
* items:
* type: object
* properties:
* date:
* type: string
* format: date
* count:
* type: integer
* topDomains:
* type: array
* description: Top 10 domains by mailbox count
* items:
* type: object
* properties:
* domain:
* type: string
* count:
* type: integer
* 500:
* description: Server error
*/
async getMailboxStats(req, res) {
try {
// Get mailboxes by expiry status
const expired = await models.Mailbox.query()
.where('expires', '<', new Date().toISOString().slice(0, 19).replace('T', ' '))
.count('* as count')
.first();
const expiringSoon = await models.Mailbox.query()
.where('expires', '>', new Date().toISOString().slice(0, 19).replace('T', ' '))
.where('expires', '<', new Date(new Date().getTime() + 3 * 24 * 60 * 60 * 1000).toISOString().slice(0, 19).replace('T', ' '))
.count('* as count')
.first();
// Get mailboxes by creation date (last 30 days)
const creationByDay = [];
for (let i = 29; i >= 0; i--) {
const date = subDays(new Date(), i);
const formattedDate = date.toISOString().slice(0, 10);
const created = await models.Mailbox.query()
.where('created', 'like', `${formattedDate}%`)
.count('* as count')
.first();
creationByDay.push({
date: formattedDate,
count: parseInt(created.count) || 0
});
}
// Get top domains by mailbox count
const topDomains = await models.Mailbox.query()
.select('domain')
.count('* as count')
.groupBy('domain')
.orderBy('count', 'desc')
.limit(10);
return res.json({
expiryStats: {
expired: parseInt(expired.count) || 0,
expiringSoon: parseInt(expiringSoon.count) || 0
},
creationByDay,
topDomains: topDomains.map(d => ({
domain: d.domain,
count: parseInt(d.count) || 0
}))
});
} catch (error) {
console.error('Error fetching mailbox stats:', error);
return res.status(500).json({ error: 'Failed to fetch mailbox statistics' });
}
}
/**
* Get domain statistics
*
* @swagger
* /stats/domains:
* get:
* summary: Get domain statistics
* description: Retrieves detailed statistics about domains
* tags: [Statistics]
* security:
* - bearerAuth: []
* responses:
* 200:
* description: Domain statistics
* content:
* application/json:
* schema:
* type: object
* properties:
* domains:
* type: object
* properties:
* total:
* type: integer
* description: Total number of domains
* active:
* type: integer
* description: Number of active domains
* inactive:
* type: integer
* description: Number of inactive domains
* topDomains:
* type: array
* description: Top 10 domains by mailbox count
* items:
* type: object
* properties:
* domain:
* type: string
* mailboxCount:
* type: integer
* domainDetails:
* type: array
* description: Detailed information about all domains
* items:
* type: object
* properties:
* domain:
* type: string
* description:
* type: string
* active:
* type: boolean
* mailboxCount:
* type: integer
* 500:
* description: Server error
*/
async getDomainStats(req, res) {
try {
// Get all domains with mailbox counts
const domains = await models.Domain.query()
.select('domain.domain', 'domain.description', 'domain.active')
.leftJoin('mailbox', 'domain.domain', 'mailbox.domain')
.groupBy('domain.domain')
.count('mailbox.username as mailboxCount');
// Get active vs inactive domains
const activeDomains = domains.filter(d => d.active === 1).length;
const inactiveDomains = domains.filter(d => d.active === 0).length;
// Get domains with most mailboxes
const topDomains = [...domains]
.sort((a, b) => parseInt(b.mailboxCount) - parseInt(a.mailboxCount))
.slice(0, 10)
.map(d => ({
domain: d.domain,
mailboxCount: parseInt(d.mailboxCount) || 0
}));
return res.json({
domains: {
total: domains.length,
active: activeDomains,
inactive: inactiveDomains
},
topDomains,
domainDetails: domains.map(d => ({
domain: d.domain,
description: d.description,
active: d.active === 1,
mailboxCount: parseInt(d.mailboxCount) || 0
}))
});
} catch (error) {
console.error('Error fetching domain stats:', error);
return res.status(500).json({ error: 'Failed to fetch domain statistics' });
}
}
/**
* Get user statistics
*
* @swagger
* /stats/users:
* get:
* summary: Get user statistics
* description: Retrieves detailed statistics about users
* tags: [Statistics]
* security:
* - bearerAuth: []
* responses:
* 200:
* description: User statistics
* content:
* application/json:
* schema:
* type: object
* properties:
* users:
* type: object
* properties:
* total:
* type: integer
* description: Total number of users
* admin:
* type: integer
* description: Number of admin users
* active:
* type: integer
* description: Number of active users
* creationByDay:
* type: array
* description: User creation statistics for the last 30 days
* items:
* type: object
* properties:
* date:
* type: string
* format: date
* count:
* type: integer
* invites:
* type: object
* properties:
* active:
* type: integer
* description: Number of active invites
* 500:
* description: Server error
*/
async getUserStats(req, res) {
try {
// Get total users count
const totalUsers = await models.User.query().count('* as count').first();
const adminUsers = await models.User.query()
.where('is_admin', 1)
.count('* as count')
.first();
const activeUsers = await models.User.query()
.where('is_active', 1)
.count('* as count')
.first();
// Get users created in the last 30 days
const creationByDay = [];
for (let i = 29; i >= 0; i--) {
const date = subDays(new Date(), i);
const formattedDate = date.toISOString().slice(0, 10);
const created = await models.User.query()
.where('created', 'like', `${formattedDate}%`)
.count('* as count')
.first();
creationByDay.push({
date: formattedDate,
count: parseInt(created.count) || 0
});
}
// Get active invites count
const activeInvites = await models.Invite.query()
.where('expires', '>', new Date().toISOString().slice(0, 19).replace('T', ' '))
.count('* as count')
.first();
return res.json({
users: {
total: parseInt(totalUsers.count) || 0,
admin: parseInt(adminUsers.count) || 0,
active: parseInt(activeUsers.count) || 0
},
creationByDay,
invites: {
active: parseInt(activeInvites.count) || 0
}
});
} catch (error) {
console.error('Error fetching user stats:', error);
return res.status(500).json({ error: 'Failed to fetch user statistics' });
}
}
}
module.exports = new StatsController();