Update various configuration files, components, and assets; enhance notification system and API endpoints; improve documentation and styles across the application.
This commit is contained in:
234
server/api/notifications/logs/index.get.js
Normal file
234
server/api/notifications/logs/index.get.js
Normal file
@@ -0,0 +1,234 @@
|
||||
import prisma from '~/server/utils/prisma'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
try {
|
||||
// Check authentication
|
||||
const user = event.context.user;
|
||||
if (!user || !user.userID) {
|
||||
return {
|
||||
statusCode: 401,
|
||||
body: { success: false, message: 'Unauthorized' }
|
||||
}
|
||||
}
|
||||
|
||||
const query = getQuery(event)
|
||||
let logs = [];
|
||||
let totalLogs = 0;
|
||||
let failedDeliveries = 0;
|
||||
let successfulDeliveries = 0;
|
||||
let total = 0;
|
||||
|
||||
try {
|
||||
// Define filters
|
||||
const filters = {}
|
||||
|
||||
// Date range filter
|
||||
if (query.startDate) {
|
||||
filters.created_at = {
|
||||
...filters.created_at,
|
||||
gte: new Date(query.startDate)
|
||||
}
|
||||
}
|
||||
|
||||
if (query.endDate) {
|
||||
filters.created_at = {
|
||||
...filters.created_at,
|
||||
lte: new Date(query.endDate)
|
||||
}
|
||||
}
|
||||
|
||||
// Action filter
|
||||
if (query.action) {
|
||||
filters.action = query.action
|
||||
}
|
||||
|
||||
// Channel filter
|
||||
if (query.channel) {
|
||||
filters.channel_type = query.channel
|
||||
}
|
||||
|
||||
// Status filter
|
||||
if (query.status) {
|
||||
filters.status = query.status
|
||||
}
|
||||
|
||||
// Actor/user filter
|
||||
if (query.actor) {
|
||||
filters.actor = {
|
||||
contains: query.actor
|
||||
}
|
||||
}
|
||||
|
||||
// Notification ID filter
|
||||
if (query.notificationId) {
|
||||
filters.notification_id = query.notificationId
|
||||
}
|
||||
|
||||
// Keyword search in details or error_message
|
||||
if (query.keyword) {
|
||||
filters.OR = [
|
||||
{ details: { contains: query.keyword } },
|
||||
{ error_message: { contains: query.keyword } }
|
||||
]
|
||||
}
|
||||
|
||||
// Pagination
|
||||
const page = parseInt(query.page) || 1
|
||||
const limit = parseInt(query.limit) || 10
|
||||
const skip = (page - 1) * limit
|
||||
|
||||
try {
|
||||
console.log("Attempting to query notification_logs table...");
|
||||
|
||||
// First check if the table exists
|
||||
let tableExists = true;
|
||||
try {
|
||||
await prisma.$queryRaw`SELECT 1 FROM notification_logs LIMIT 1`;
|
||||
console.log("notification_logs table exists!");
|
||||
} catch (tableCheckError) {
|
||||
console.error("Table check error:", tableCheckError.message);
|
||||
console.error("Table likely doesn't exist - you need to run the migration!");
|
||||
tableExists = false;
|
||||
throw new Error("notification_logs table does not exist");
|
||||
}
|
||||
|
||||
if (tableExists) {
|
||||
// Get total count for pagination
|
||||
total = await prisma.notification_logs.count({
|
||||
where: filters
|
||||
})
|
||||
|
||||
// Get logs with pagination and sorting
|
||||
logs = await prisma.notification_logs.findMany({
|
||||
where: filters,
|
||||
orderBy: {
|
||||
created_at: 'desc'
|
||||
},
|
||||
skip,
|
||||
take: limit
|
||||
})
|
||||
|
||||
// Get summary stats
|
||||
totalLogs = await prisma.notification_logs.count()
|
||||
failedDeliveries = await prisma.notification_logs.count({
|
||||
where: {
|
||||
status: 'Failed'
|
||||
}
|
||||
})
|
||||
successfulDeliveries = await prisma.notification_logs.count({
|
||||
where: {
|
||||
status: 'Sent'
|
||||
}
|
||||
})
|
||||
|
||||
console.log(`Successfully fetched ${logs.length} logs from database`);
|
||||
}
|
||||
} catch (dbError) {
|
||||
console.error("Database query error:", dbError.message);
|
||||
console.error("Stack trace:", dbError.stack);
|
||||
|
||||
// Uncommenting the mock data code below will revert to using mock data
|
||||
// If you want to see real errors, keep this commented out
|
||||
|
||||
/*
|
||||
// If the database table doesn't exist or there's another error, use mock data
|
||||
logs = generateMockLogs(limit);
|
||||
total = 25; // Mock total count
|
||||
totalLogs = 25;
|
||||
failedDeliveries = 3;
|
||||
successfulDeliveries = 20;
|
||||
*/
|
||||
|
||||
throw dbError; // Re-throw to see actual error in response
|
||||
}
|
||||
} catch (prismaError) {
|
||||
console.error("Prisma error:", prismaError.message);
|
||||
console.error("Stack trace:", prismaError.stack);
|
||||
|
||||
// Uncommenting the mock data code below will revert to using mock data
|
||||
// If you want to see real errors, keep this commented out
|
||||
|
||||
/*
|
||||
// If there's an issue with Prisma itself, use mock data
|
||||
logs = generateMockLogs(10);
|
||||
total = 25; // Mock total count
|
||||
totalLogs = 25;
|
||||
failedDeliveries = 3;
|
||||
successfulDeliveries = 20;
|
||||
*/
|
||||
|
||||
throw prismaError; // Re-throw to see actual error in response
|
||||
}
|
||||
|
||||
// Calculate success rate
|
||||
const successRate = totalLogs > 0
|
||||
? Math.round((successfulDeliveries / totalLogs) * 100)
|
||||
: 0
|
||||
|
||||
const page = parseInt(query.page) || 1
|
||||
const limit = parseInt(query.limit) || 10
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
body: {
|
||||
success: true,
|
||||
data: {
|
||||
logs,
|
||||
pagination: {
|
||||
page,
|
||||
limit,
|
||||
total,
|
||||
pages: Math.ceil(total / limit)
|
||||
},
|
||||
summary: {
|
||||
totalLogs,
|
||||
failedDeliveries,
|
||||
successfulDeliveries,
|
||||
successRate
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching notification logs:', error)
|
||||
return {
|
||||
statusCode: 500,
|
||||
body: {
|
||||
success: false,
|
||||
message: 'Failed to fetch notification logs',
|
||||
error: error.message,
|
||||
stack: error.stack
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Helper function to generate mock logs for testing
|
||||
function generateMockLogs(count = 10) {
|
||||
const actions = ['Notification Created', 'Notification Sent', 'Delivery Attempted', 'Notification Opened'];
|
||||
const statuses = ['Sent', 'Failed', 'Opened', 'Queued'];
|
||||
const channels = ['Email', 'SMS', 'Push Notification', 'Webhook'];
|
||||
const actors = ['System', 'Admin', 'API', 'Scheduler'];
|
||||
|
||||
return Array.from({ length: count }, (_, i) => {
|
||||
const status = statuses[Math.floor(Math.random() * statuses.length)];
|
||||
const action = actions[Math.floor(Math.random() * actions.length)];
|
||||
const created_at = new Date();
|
||||
created_at.setHours(created_at.getHours() - Math.floor(Math.random() * 72)); // Random time in last 72 hours
|
||||
|
||||
return {
|
||||
id: `mock-${i+1}-${Date.now()}`.substring(0, 36),
|
||||
notification_id: `notif-${i+1}-${Date.now()}`.substring(0, 36),
|
||||
action,
|
||||
actor: actors[Math.floor(Math.random() * actors.length)],
|
||||
actor_id: `user-${i+1}`,
|
||||
channel_type: channels[Math.floor(Math.random() * channels.length)],
|
||||
status,
|
||||
details: `${action} via ${channels[Math.floor(Math.random() * channels.length)]}`,
|
||||
source_ip: `192.168.1.${i+1}`,
|
||||
error_code: status === 'Failed' ? 'ERR_DELIVERY_FAILED' : null,
|
||||
error_message: status === 'Failed' ? 'Failed to deliver notification' : null,
|
||||
created_at
|
||||
};
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user