171 lines
4.4 KiB
JavaScript
171 lines
4.4 KiB
JavaScript
import prisma from "~/server/utils/prisma";
|
|
|
|
export default defineEventHandler(async (event) => {
|
|
try {
|
|
const now = new Date();
|
|
const last24Hours = new Date(now.getTime() - 24 * 60 * 60 * 1000);
|
|
const last7Days = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
|
|
const last30Days = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
|
|
|
|
// Fetch comprehensive stats
|
|
const [
|
|
totalNotifications,
|
|
totalSent,
|
|
totalScheduled,
|
|
totalDraft,
|
|
sentLast24h,
|
|
sentLast7Days,
|
|
totalRecipients,
|
|
successfulDeliveries,
|
|
failedDeliveries,
|
|
queuedJobs,
|
|
channelStats,
|
|
categoryStats,
|
|
] = await Promise.all([
|
|
// Total notifications
|
|
prisma.notifications.count(),
|
|
|
|
// Total sent notifications
|
|
prisma.notifications.count({
|
|
where: { status: "sending" }
|
|
}),
|
|
|
|
// Total scheduled
|
|
prisma.notifications.count({
|
|
where: { status: "scheduled" }
|
|
}),
|
|
|
|
// Total drafts
|
|
prisma.notifications.count({
|
|
where: { status: "draft" }
|
|
}),
|
|
|
|
// Sent in last 24 hours
|
|
prisma.notifications.count({
|
|
where: {
|
|
status: "sending",
|
|
sent_at: { gte: last24Hours }
|
|
}
|
|
}),
|
|
|
|
// Sent in last 7 days
|
|
prisma.notifications.count({
|
|
where: {
|
|
status: "sending",
|
|
sent_at: { gte: last7Days }
|
|
}
|
|
}),
|
|
|
|
// Total recipients
|
|
prisma.notification_recipients.count(),
|
|
|
|
// Successful deliveries
|
|
prisma.notification_recipients.count({
|
|
where: { status: "sent" }
|
|
}),
|
|
|
|
// Failed deliveries
|
|
prisma.notification_recipients.count({
|
|
where: { status: "failed" }
|
|
}),
|
|
|
|
// Queued jobs
|
|
prisma.notification_queue.count({
|
|
where: { status: "queued" }
|
|
}),
|
|
|
|
// Channel distribution
|
|
prisma.notification_channels.groupBy({
|
|
by: ['channel_type'],
|
|
_count: {
|
|
channel_type: true
|
|
}
|
|
}),
|
|
|
|
// Category distribution
|
|
prisma.notifications.groupBy({
|
|
by: ['category_id'],
|
|
_count: {
|
|
category_id: true
|
|
},
|
|
take: 5,
|
|
orderBy: {
|
|
_count: {
|
|
category_id: 'desc'
|
|
}
|
|
}
|
|
}),
|
|
]);
|
|
|
|
// Calculate delivery rate
|
|
const totalDeliveryAttempts = successfulDeliveries + failedDeliveries;
|
|
const deliveryRate = totalDeliveryAttempts > 0
|
|
? ((successfulDeliveries / totalDeliveryAttempts) * 100).toFixed(1)
|
|
: 100;
|
|
|
|
// Calculate growth rate (last 7 days vs previous 7 days)
|
|
const previous7Days = new Date(last7Days.getTime() - 7 * 24 * 60 * 60 * 1000);
|
|
const sentPrevious7Days = await prisma.notifications.count({
|
|
where: {
|
|
status: "sending",
|
|
sent_at: {
|
|
gte: previous7Days,
|
|
lt: last7Days
|
|
}
|
|
}
|
|
});
|
|
|
|
const growthRate = sentPrevious7Days > 0
|
|
? (((sentLast7Days - sentPrevious7Days) / sentPrevious7Days) * 100).toFixed(1)
|
|
: 0;
|
|
|
|
// Get category names
|
|
const categoryIds = categoryStats.map(c => c.category_id).filter(Boolean);
|
|
const categories = await prisma.notification_categories.findMany({
|
|
where: { id: { in: categoryIds } },
|
|
select: { id: true, name: true }
|
|
});
|
|
|
|
const categoryMap = Object.fromEntries(categories.map(c => [c.id, c.name]));
|
|
|
|
return {
|
|
success: true,
|
|
data: {
|
|
overview: {
|
|
total: totalNotifications,
|
|
sent: totalSent,
|
|
scheduled: totalScheduled,
|
|
draft: totalDraft,
|
|
sentLast24h,
|
|
sentLast7Days,
|
|
growthRate: parseFloat(growthRate),
|
|
},
|
|
delivery: {
|
|
totalRecipients,
|
|
successful: successfulDeliveries,
|
|
failed: failedDeliveries,
|
|
deliveryRate: parseFloat(deliveryRate),
|
|
queued: queuedJobs,
|
|
},
|
|
channels: channelStats.map(ch => ({
|
|
channel: ch.channel_type,
|
|
count: ch._count.channel_type,
|
|
})),
|
|
topCategories: categoryStats.map(cat => ({
|
|
categoryId: cat.category_id,
|
|
categoryName: categoryMap[cat.category_id] || 'Unknown',
|
|
count: cat._count.category_id,
|
|
})),
|
|
},
|
|
};
|
|
} catch (error) {
|
|
console.error("Error fetching dashboard overview:", error);
|
|
throw createError({
|
|
statusCode: 500,
|
|
statusMessage: "Failed to fetch dashboard data",
|
|
data: { error: error.message },
|
|
});
|
|
} finally {
|
|
}
|
|
});
|