434 lines
15 KiB
JavaScript
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();
|