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:
235
server/api/notifications/draft.post.js
Normal file
235
server/api/notifications/draft.post.js
Normal file
@@ -0,0 +1,235 @@
|
||||
import prisma from "~/server/utils/prisma";
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
try {
|
||||
// Read request body
|
||||
const body = await readBody(event);
|
||||
|
||||
// Get current user
|
||||
const user = event.context.user;
|
||||
if (!user || !user.userID) {
|
||||
throw createError({
|
||||
statusCode: 401,
|
||||
statusMessage: 'Authentication required'
|
||||
});
|
||||
}
|
||||
|
||||
// Prepare data for saving
|
||||
const draftData = {
|
||||
title: body.title || 'Untitled Draft',
|
||||
type: body.type || 'single',
|
||||
priority: body.priority || 'medium',
|
||||
category: body.category,
|
||||
channels: Array.isArray(body.channels) ? body.channels : [],
|
||||
emailSubject: body.emailSubject || null,
|
||||
deliveryType: body.deliveryType || 'immediate',
|
||||
scheduledAt: body.scheduledAt || null,
|
||||
timezone: body.timezone || 'UTC',
|
||||
audienceType: body.audienceType || 'all',
|
||||
specificUsers: body.specificUsers || null,
|
||||
userSegments: Array.isArray(body.userSegments) ? body.userSegments : [],
|
||||
excludeUnsubscribed: body.excludeUnsubscribed !== false, // default true
|
||||
contentType: body.contentType || 'new',
|
||||
selectedTemplate: body.selectedTemplate || null,
|
||||
emailContent: body.emailContent || null,
|
||||
callToActionText: body.callToActionText || null,
|
||||
callToActionUrl: body.callToActionUrl || null,
|
||||
pushTitle: body.pushTitle || null,
|
||||
pushBody: body.pushBody || null,
|
||||
draftId: body.draftId // For updating existing drafts
|
||||
};
|
||||
|
||||
// Use Prisma transaction
|
||||
const result = await prisma.$transaction(async (tx) => {
|
||||
let notificationId = draftData.draftId;
|
||||
|
||||
// Get category if provided
|
||||
let categoryId = null;
|
||||
if (draftData.category) {
|
||||
const category = await tx.notification_categories.findFirst({
|
||||
where: { value: draftData.category }
|
||||
});
|
||||
|
||||
if (category) {
|
||||
categoryId = category.id;
|
||||
}
|
||||
}
|
||||
|
||||
// Get template if provided
|
||||
let templateId = null;
|
||||
if (draftData.contentType === 'template' && draftData.selectedTemplate) {
|
||||
const template = await tx.notification_templates.findFirst({
|
||||
where: {
|
||||
value: draftData.selectedTemplate,
|
||||
is_active: true
|
||||
}
|
||||
});
|
||||
|
||||
if (template) {
|
||||
templateId = template.id;
|
||||
}
|
||||
}
|
||||
|
||||
if (notificationId) {
|
||||
// Check if the draft exists and belongs to the user
|
||||
const existingDraft = await tx.notifications.findFirst({
|
||||
where: {
|
||||
id: notificationId,
|
||||
created_by: user.userID.toString(),
|
||||
status: 'draft'
|
||||
}
|
||||
});
|
||||
|
||||
if (!existingDraft) {
|
||||
throw createError({
|
||||
statusCode: 404,
|
||||
statusMessage: 'Draft not found or you do not have permission to update it'
|
||||
});
|
||||
}
|
||||
|
||||
// Update existing draft
|
||||
const updatedDraft = await tx.notifications.update({
|
||||
where: { id: notificationId },
|
||||
data: {
|
||||
title: draftData.title,
|
||||
type: draftData.type,
|
||||
priority: draftData.priority,
|
||||
category_id: categoryId,
|
||||
delivery_type: draftData.deliveryType,
|
||||
scheduled_at: draftData.scheduledAt ? new Date(draftData.scheduledAt) : null,
|
||||
timezone: draftData.timezone,
|
||||
audience_type: draftData.audienceType,
|
||||
specific_users: draftData.specificUsers,
|
||||
exclude_unsubscribed: draftData.excludeUnsubscribed,
|
||||
content_type: draftData.contentType,
|
||||
template_id: templateId,
|
||||
email_subject: draftData.emailSubject,
|
||||
email_content: draftData.emailContent,
|
||||
call_to_action_text: draftData.callToActionText,
|
||||
call_to_action_url: draftData.callToActionUrl,
|
||||
push_title: draftData.pushTitle,
|
||||
push_body: draftData.pushBody,
|
||||
updated_at: new Date()
|
||||
}
|
||||
});
|
||||
|
||||
notificationId = updatedDraft.id;
|
||||
} else {
|
||||
// Create new draft
|
||||
const newDraft = await tx.notifications.create({
|
||||
data: {
|
||||
title: draftData.title,
|
||||
type: draftData.type,
|
||||
priority: draftData.priority,
|
||||
category_id: categoryId,
|
||||
delivery_type: draftData.deliveryType,
|
||||
scheduled_at: draftData.scheduledAt ? new Date(draftData.scheduledAt) : null,
|
||||
timezone: draftData.timezone,
|
||||
audience_type: draftData.audienceType,
|
||||
specific_users: draftData.specificUsers,
|
||||
exclude_unsubscribed: draftData.excludeUnsubscribed,
|
||||
respect_do_not_disturb: true,
|
||||
enable_tracking: true,
|
||||
content_type: draftData.contentType,
|
||||
template_id: templateId,
|
||||
email_subject: draftData.emailSubject,
|
||||
email_content: draftData.emailContent,
|
||||
call_to_action_text: draftData.callToActionText,
|
||||
call_to_action_url: draftData.callToActionUrl,
|
||||
push_title: draftData.pushTitle,
|
||||
push_body: draftData.pushBody,
|
||||
created_by: user.userID.toString(),
|
||||
status: 'draft'
|
||||
}
|
||||
});
|
||||
|
||||
notificationId = newDraft.id;
|
||||
}
|
||||
|
||||
// Update channels
|
||||
if (draftData.channels && draftData.channels.length > 0) {
|
||||
// Delete existing channels
|
||||
await tx.notification_channels.deleteMany({
|
||||
where: { notification_id: notificationId }
|
||||
});
|
||||
|
||||
// Insert new channels
|
||||
await tx.notification_channels.createMany({
|
||||
data: draftData.channels.map(channel => ({
|
||||
notification_id: notificationId,
|
||||
channel_type: channel
|
||||
}))
|
||||
});
|
||||
}
|
||||
|
||||
// Update segments
|
||||
if (draftData.userSegments && draftData.userSegments.length > 0) {
|
||||
// Delete existing segments
|
||||
await tx.notification_user_segments.deleteMany({
|
||||
where: { notification_id: notificationId }
|
||||
});
|
||||
|
||||
// Insert new segments
|
||||
for (const segment of draftData.userSegments) {
|
||||
const segmentData = await tx.user_segments.findFirst({
|
||||
where: {
|
||||
value: segment,
|
||||
is_active: true
|
||||
}
|
||||
});
|
||||
|
||||
if (segmentData) {
|
||||
await tx.notification_user_segments.create({
|
||||
data: {
|
||||
notification_id: notificationId,
|
||||
segment_id: segmentData.id
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
id: notificationId
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
id: result.id,
|
||||
message: 'Draft saved successfully'
|
||||
}
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error saving draft:', error);
|
||||
|
||||
// Handle Prisma errors
|
||||
if (error.code && error.code.startsWith('P')) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: 'Database operation failed',
|
||||
data: {
|
||||
error: error.message,
|
||||
code: error.code
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Handle known errors with status codes
|
||||
if (error.statusCode) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Generic server error
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
statusMessage: 'Failed to save draft',
|
||||
data: {
|
||||
error: error.message
|
||||
}
|
||||
});
|
||||
} finally {
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user