225 lines
6.3 KiB
JavaScript
225 lines
6.3 KiB
JavaScript
import prisma from "~/server/utils/prisma";
|
|
|
|
export default defineEventHandler(async (event) => {
|
|
try {
|
|
// Get notification ID from route parameters
|
|
const notificationId = getRouterParam(event, "id");
|
|
|
|
if (!notificationId) {
|
|
throw createError({
|
|
statusCode: 400,
|
|
statusMessage: "Notification ID is required",
|
|
});
|
|
}
|
|
|
|
// Get current user (assuming auth middleware provides this)
|
|
const user = event.context.user;
|
|
if (!user) {
|
|
throw createError({
|
|
statusCode: 401,
|
|
statusMessage: "Authentication required",
|
|
});
|
|
}
|
|
|
|
// Fetch notification with all related data using Prisma
|
|
const notification = await prisma.notifications.findFirst({
|
|
where: {
|
|
id: notificationId,
|
|
created_by: user.id,
|
|
},
|
|
include: {
|
|
notification_categories: {
|
|
select: {
|
|
name: true,
|
|
value: true,
|
|
},
|
|
},
|
|
notification_templates: {
|
|
select: {
|
|
name: true,
|
|
subject: true,
|
|
email_content: true,
|
|
push_title: true,
|
|
push_body: true,
|
|
variables: true,
|
|
},
|
|
},
|
|
notification_channels: {
|
|
select: {
|
|
channel_type: true,
|
|
},
|
|
},
|
|
notification_user_segments: {
|
|
include: {
|
|
user_segments: {
|
|
select: {
|
|
value: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
notification_recipients: {
|
|
select: {
|
|
status: true,
|
|
opened_at: true,
|
|
clicked_at: true,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
if (!notification) {
|
|
throw createError({
|
|
statusCode: 404,
|
|
statusMessage:
|
|
"Notification not found or you do not have permission to access it",
|
|
});
|
|
}
|
|
|
|
// Calculate analytics
|
|
const totalRecipients = notification.notification_recipients.length;
|
|
const sentCount = notification.notification_recipients.filter(
|
|
(r) => r.status === "sent"
|
|
).length;
|
|
const deliveredCount = notification.notification_recipients.filter(
|
|
(r) => r.status === "delivered"
|
|
).length;
|
|
const failedCount = notification.notification_recipients.filter(
|
|
(r) => r.status === "failed"
|
|
).length;
|
|
const openedCount = notification.notification_recipients.filter(
|
|
(r) => r.opened_at !== null
|
|
).length;
|
|
const clickedCount = notification.notification_recipients.filter(
|
|
(r) => r.clicked_at !== null
|
|
).length;
|
|
|
|
// Calculate success rate
|
|
const successRate =
|
|
totalRecipients > 0
|
|
? Math.round((deliveredCount / totalRecipients) * 100)
|
|
: 0;
|
|
|
|
// Format the response
|
|
const formattedNotification = {
|
|
id: notification.id,
|
|
title: notification.title,
|
|
type: notification.type,
|
|
priority: notification.priority,
|
|
status: notification.status,
|
|
category: {
|
|
name: notification.notification_categories?.name || "Uncategorized",
|
|
value: notification.notification_categories?.value,
|
|
},
|
|
channels: notification.notification_channels.map((c) => c.channel_type),
|
|
deliveryType: notification.delivery_type,
|
|
scheduledAt: notification.scheduled_at,
|
|
timezone: notification.timezone,
|
|
expiresAt: notification.expires_at,
|
|
|
|
// A/B Testing
|
|
enableAbTesting: notification.enable_ab_testing,
|
|
abTestSplit: notification.ab_test_split,
|
|
abTestName: notification.ab_test_name,
|
|
|
|
// Tracking
|
|
enableTracking: notification.enable_tracking,
|
|
|
|
// Audience
|
|
audienceType: notification.audience_type,
|
|
specificUsers: notification.specific_users,
|
|
userSegments: notification.notification_user_segments.map(
|
|
(s) => s.user_segments.value
|
|
),
|
|
userStatus: notification.user_status,
|
|
registrationPeriod: notification.registration_period,
|
|
excludeUnsubscribed: notification.exclude_unsubscribed,
|
|
respectDoNotDisturb: notification.respect_do_not_disturb,
|
|
|
|
// Content
|
|
contentType: notification.content_type,
|
|
template: notification.notification_templates
|
|
? {
|
|
id: notification.template_id,
|
|
name: notification.notification_templates.name,
|
|
subject: notification.notification_templates.subject,
|
|
emailContent: notification.notification_templates.email_content,
|
|
pushTitle: notification.notification_templates.push_title,
|
|
pushBody: notification.notification_templates.push_body,
|
|
variables: notification.notification_templates.variables,
|
|
}
|
|
: null,
|
|
|
|
// Email Content
|
|
emailSubject: notification.email_subject,
|
|
emailContent: notification.email_content,
|
|
callToActionText: notification.call_to_action_text,
|
|
callToActionUrl: notification.call_to_action_url,
|
|
|
|
// Push Content
|
|
pushTitle: notification.push_title,
|
|
pushBody: notification.push_body,
|
|
pushImageUrl: notification.push_image_url,
|
|
|
|
// Analytics
|
|
analytics: {
|
|
estimatedReach: notification.estimated_reach,
|
|
actualSent: notification.actual_sent,
|
|
totalRecipients,
|
|
sentCount,
|
|
deliveredCount,
|
|
failedCount,
|
|
openedCount,
|
|
clickedCount,
|
|
successRate,
|
|
openRate:
|
|
totalRecipients > 0
|
|
? Math.round((openedCount / totalRecipients) * 100)
|
|
: 0,
|
|
clickRate:
|
|
totalRecipients > 0
|
|
? Math.round((clickedCount / totalRecipients) * 100)
|
|
: 0,
|
|
},
|
|
|
|
// Metadata
|
|
createdBy: notification.created_by,
|
|
createdAt: notification.created_at,
|
|
updatedAt: notification.updated_at,
|
|
sentAt: notification.sent_at,
|
|
};
|
|
|
|
return {
|
|
success: true,
|
|
data: formattedNotification,
|
|
};
|
|
} catch (error) {
|
|
console.error("Error fetching notification:", error);
|
|
|
|
if (error.code?.startsWith("P")) {
|
|
throw createError({
|
|
statusCode: 400,
|
|
statusMessage: "Database operation failed",
|
|
data: {
|
|
error: error.message,
|
|
code: error.code,
|
|
},
|
|
});
|
|
}
|
|
|
|
if (error.statusCode) {
|
|
throw error;
|
|
}
|
|
|
|
throw createError({
|
|
statusCode: 500,
|
|
statusMessage: "Failed to fetch notification",
|
|
data: {
|
|
error: error.message,
|
|
},
|
|
});
|
|
} finally {
|
|
await prisma.$disconnect();
|
|
}
|
|
});
|