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:
130
server/api/notifications/templates/[id].delete.js
Normal file
130
server/api/notifications/templates/[id].delete.js
Normal file
@@ -0,0 +1,130 @@
|
||||
import prisma from "~/server/utils/prisma";
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
try {
|
||||
// Get template ID from route parameters
|
||||
const templateId = getRouterParam(event, "id");
|
||||
|
||||
if (!templateId) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: "Template ID is required",
|
||||
});
|
||||
}
|
||||
|
||||
console.log("Deleting template:", templateId);
|
||||
|
||||
// Get current user (assuming auth middleware provides this)
|
||||
const user = event.context.user;
|
||||
if (!user || !user.userID) {
|
||||
throw createError({
|
||||
statusCode: 401,
|
||||
statusMessage: "Authentication required",
|
||||
});
|
||||
}
|
||||
|
||||
// Check if template exists and user has permission
|
||||
const existingTemplate = await prisma.notification_templates.findFirst({
|
||||
where: {
|
||||
id: templateId,
|
||||
// Optionally restrict to user's own templates
|
||||
// created_by: user.userID.toString()
|
||||
}
|
||||
});
|
||||
|
||||
if (!existingTemplate) {
|
||||
throw createError({
|
||||
statusCode: 404,
|
||||
statusMessage: "Template not found or you don't have permission to delete it",
|
||||
});
|
||||
}
|
||||
|
||||
// Check if template is being used by any notifications
|
||||
const templatesInUse = await prisma.notifications.findFirst({
|
||||
where: {
|
||||
template_id: templateId
|
||||
}
|
||||
});
|
||||
|
||||
if (templatesInUse) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: "Cannot delete template that is currently being used by notifications. Please remove it from notifications first.",
|
||||
});
|
||||
}
|
||||
|
||||
console.log("Template found and can be deleted:", existingTemplate.name);
|
||||
|
||||
// Delete the template
|
||||
await prisma.notification_templates.delete({
|
||||
where: {
|
||||
id: templateId
|
||||
}
|
||||
});
|
||||
|
||||
console.log("Template deleted successfully:", templateId);
|
||||
|
||||
// Return success response
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
message: `Template "${existingTemplate.name}" has been deleted successfully`,
|
||||
deletedTemplate: {
|
||||
id: existingTemplate.id,
|
||||
name: existingTemplate.name,
|
||||
value: existingTemplate.value
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
console.error("Template deletion error:", error);
|
||||
console.error("Error details:", {
|
||||
message: error.message,
|
||||
stack: error.stack,
|
||||
cause: error.cause,
|
||||
code: error.code,
|
||||
statusCode: error.statusCode
|
||||
});
|
||||
|
||||
// Handle Prisma errors
|
||||
if (error.code && error.code.startsWith('P')) {
|
||||
console.error("Prisma error code:", error.code);
|
||||
|
||||
if (error.code === 'P2025') {
|
||||
throw createError({
|
||||
statusCode: 404,
|
||||
statusMessage: "Template not found",
|
||||
data: {
|
||||
error: "The template you're trying to delete does not exist.",
|
||||
code: error.code
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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 delete template",
|
||||
data: {
|
||||
error: error.message
|
||||
}
|
||||
});
|
||||
} finally {
|
||||
}
|
||||
});
|
||||
134
server/api/notifications/templates/[id].get.js
Normal file
134
server/api/notifications/templates/[id].get.js
Normal file
@@ -0,0 +1,134 @@
|
||||
import prisma from "~/server/utils/prisma";
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
try {
|
||||
// Get template ID from route parameters
|
||||
const templateId = getRouterParam(event, "id");
|
||||
|
||||
if (!templateId) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: "Template ID is required",
|
||||
});
|
||||
}
|
||||
|
||||
console.log("Fetching template:", templateId);
|
||||
|
||||
// Get current user (assuming auth middleware provides this)
|
||||
const user = event.context.user;
|
||||
if (!user || !user.userID) {
|
||||
throw createError({
|
||||
statusCode: 401,
|
||||
statusMessage: "Authentication required",
|
||||
});
|
||||
}
|
||||
|
||||
// Fetch the template
|
||||
const template = await prisma.notification_templates.findUnique({
|
||||
where: {
|
||||
id: templateId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!template) {
|
||||
throw createError({
|
||||
statusCode: 404,
|
||||
statusMessage: "Template not found",
|
||||
});
|
||||
}
|
||||
|
||||
console.log("Template found:", template.name);
|
||||
|
||||
// Format the response data to match frontend expectations
|
||||
const formattedTemplate = {
|
||||
id: template.id,
|
||||
title: template.name,
|
||||
value: template.value,
|
||||
description: template.description || "",
|
||||
subject: template.subject || "",
|
||||
preheader: template.preheader || "",
|
||||
category: template.category || "",
|
||||
channels: template.channels || [],
|
||||
status: template.status || "Draft",
|
||||
version: template.version || "1.0",
|
||||
content: template.email_content || "",
|
||||
tags: template.tags || "",
|
||||
isPersonal: template.is_personal || false,
|
||||
// Email specific settings
|
||||
fromName: template.from_name || "",
|
||||
replyTo: template.reply_to || "",
|
||||
trackOpens: template.track_opens !== false, // Default to true
|
||||
// Push notification specific settings
|
||||
pushTitle: template.push_title || "",
|
||||
pushIcon: template.push_icon || "",
|
||||
pushUrl: template.push_url || "",
|
||||
// SMS specific settings
|
||||
smsContent: template.sms_content || "",
|
||||
// Metadata
|
||||
isActive: template.is_active || false,
|
||||
variables: template.variables || null,
|
||||
createdBy: template.created_by,
|
||||
updatedBy: template.updated_by,
|
||||
createdAt: template.created_at,
|
||||
updatedAt: template.updated_at,
|
||||
};
|
||||
|
||||
// Return success response
|
||||
return {
|
||||
success: true,
|
||||
message: "wowowowow",
|
||||
data: {
|
||||
template: formattedTemplate,
|
||||
},
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Template fetch error:", error);
|
||||
console.error("Error details:", {
|
||||
message: error.message,
|
||||
stack: error.stack,
|
||||
cause: error.cause,
|
||||
code: error.code,
|
||||
statusCode: error.statusCode,
|
||||
});
|
||||
|
||||
// Handle Prisma errors
|
||||
if (error.code && error.code.startsWith("P")) {
|
||||
console.error("Prisma error code:", error.code);
|
||||
|
||||
if (error.code === "P2025") {
|
||||
throw createError({
|
||||
statusCode: 404,
|
||||
statusMessage: "Template not found",
|
||||
data: {
|
||||
error: "The template you're looking for does not exist.",
|
||||
code: error.code,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
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 fetch template",
|
||||
data: {
|
||||
error: error.message,
|
||||
},
|
||||
});
|
||||
} finally {
|
||||
}
|
||||
});
|
||||
308
server/api/notifications/templates/[id].put.js
Normal file
308
server/api/notifications/templates/[id].put.js
Normal file
@@ -0,0 +1,308 @@
|
||||
import { z } from "zod";
|
||||
import prisma from "~/server/utils/prisma";
|
||||
|
||||
// Input validation schema for updating templates
|
||||
const updateTemplateSchema = z.object({
|
||||
title: z.string().min(1, "Title is required").max(100, "Title must be less than 100 characters"),
|
||||
description: z.string().optional(),
|
||||
subject: z.string().min(1, "Subject is required").max(255, "Subject must be less than 255 characters"),
|
||||
preheader: z.string().max(255, "Preheader must be less than 255 characters").optional(),
|
||||
category: z.string().min(1, "Category is required"),
|
||||
channels: z.array(z.enum(["email", "sms", "push"])).min(1, "At least one channel is required"),
|
||||
status: z.enum(["Draft", "Active", "Archived"]).default("Draft"),
|
||||
version: z.string().default("1.0"),
|
||||
content: z.string().min(1, "Content is required"),
|
||||
tags: z.string().optional(),
|
||||
isPersonal: z.boolean().default(false),
|
||||
// Email specific settings
|
||||
fromName: z.string().optional(),
|
||||
replyTo: z.string().email("Invalid reply-to email format").optional().or(z.literal("")),
|
||||
trackOpens: z.boolean().default(true),
|
||||
// Push notification specific settings
|
||||
pushTitle: z.string().optional(),
|
||||
pushIcon: z.string().url("Invalid push icon URL").optional().or(z.literal("")),
|
||||
pushUrl: z.string().url("Invalid push URL").optional().or(z.literal("")),
|
||||
// SMS specific settings
|
||||
smsContent: z.string().max(160, "SMS content must be 160 characters or less").optional(),
|
||||
});
|
||||
|
||||
// Helper function to generate unique value from title
|
||||
function generateUniqueValue(title) {
|
||||
return title
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9\s]/g, '')
|
||||
.replace(/\s+/g, '_')
|
||||
.substring(0, 50);
|
||||
}
|
||||
|
||||
// Helper function to validate template content
|
||||
function validateTemplateContent(content, channels) {
|
||||
const errors = [];
|
||||
|
||||
// Check for balanced variable syntax
|
||||
const variableMatches = content.match(/\{\{[^}]*\}\}/g) || [];
|
||||
for (const match of variableMatches) {
|
||||
if (!match.endsWith('}}')) {
|
||||
errors.push(`Unmatched variable syntax: ${match}`);
|
||||
}
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
try {
|
||||
// Get template ID from route parameters
|
||||
const templateId = getRouterParam(event, "id");
|
||||
|
||||
if (!templateId) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: "Template ID is required",
|
||||
});
|
||||
}
|
||||
|
||||
// Read and validate request body
|
||||
const body = await readBody(event);
|
||||
|
||||
console.log("Template update request for ID:", templateId, body);
|
||||
|
||||
// Validate input data
|
||||
const validationResult = updateTemplateSchema.safeParse(body);
|
||||
if (!validationResult.success) {
|
||||
const errors = validationResult.error.errors.map(err =>
|
||||
`${err.path.join('.')}: ${err.message}`
|
||||
);
|
||||
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: "Validation failed",
|
||||
data: {
|
||||
errors: errors
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const validatedData = validationResult.data;
|
||||
|
||||
// Get current user (assuming auth middleware provides this)
|
||||
const user = event.context.user;
|
||||
if (!user || !user.userID) {
|
||||
throw createError({
|
||||
statusCode: 401,
|
||||
statusMessage: "Authentication required",
|
||||
});
|
||||
}
|
||||
|
||||
console.log("User authenticated:", user.userID);
|
||||
|
||||
// Check if template exists
|
||||
const existingTemplate = await prisma.notification_templates.findUnique({
|
||||
where: {
|
||||
id: templateId
|
||||
}
|
||||
});
|
||||
|
||||
if (!existingTemplate) {
|
||||
throw createError({
|
||||
statusCode: 404,
|
||||
statusMessage: "Template not found",
|
||||
});
|
||||
}
|
||||
|
||||
console.log("Template found for update:", existingTemplate.name);
|
||||
|
||||
// Validate template content
|
||||
const contentErrors = validateTemplateContent(validatedData.content, validatedData.channels);
|
||||
if (contentErrors.length > 0) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: "Content validation failed",
|
||||
data: {
|
||||
errors: contentErrors
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Generate unique value for the template if title changed
|
||||
let finalValue = existingTemplate.value;
|
||||
if (validatedData.title !== existingTemplate.name) {
|
||||
let uniqueValue = generateUniqueValue(validatedData.title);
|
||||
|
||||
// Check if value already exists and make it unique
|
||||
let counter = 1;
|
||||
finalValue = uniqueValue;
|
||||
while (true) {
|
||||
const existingValueTemplate = await prisma.notification_templates.findUnique({
|
||||
where: {
|
||||
value: finalValue,
|
||||
NOT: { id: templateId } // Exclude current template
|
||||
}
|
||||
});
|
||||
|
||||
if (!existingValueTemplate) {
|
||||
break;
|
||||
}
|
||||
|
||||
finalValue = `${uniqueValue}_${counter}`;
|
||||
counter++;
|
||||
|
||||
// Safety check to prevent infinite loop
|
||||
if (counter > 100) {
|
||||
finalValue = `${uniqueValue}_${Date.now()}`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare data for database update
|
||||
const templateData = {
|
||||
name: validatedData.title,
|
||||
value: finalValue,
|
||||
description: validatedData.description || null,
|
||||
subject: validatedData.subject,
|
||||
preheader: validatedData.preheader || null,
|
||||
email_content: validatedData.content,
|
||||
push_title: validatedData.pushTitle || null,
|
||||
push_body: validatedData.content ? validatedData.content.replace(/<[^>]*>/g, '').substring(0, 300) : null,
|
||||
push_icon: validatedData.pushIcon || null,
|
||||
push_url: validatedData.pushUrl || null,
|
||||
sms_content: validatedData.smsContent || null,
|
||||
category: validatedData.category,
|
||||
channels: validatedData.channels,
|
||||
status: validatedData.status,
|
||||
version: validatedData.version,
|
||||
tags: validatedData.tags || null,
|
||||
is_personal: validatedData.isPersonal,
|
||||
from_name: validatedData.fromName || null,
|
||||
reply_to: validatedData.replyTo || null,
|
||||
track_opens: validatedData.trackOpens,
|
||||
is_active: validatedData.status === "Active",
|
||||
updated_by: user.userID.toString(),
|
||||
updated_at: new Date(),
|
||||
};
|
||||
|
||||
console.log("Updating template with data:", templateData);
|
||||
|
||||
// Create version history entry before updating the template
|
||||
await prisma.notification_template_versions.create({
|
||||
data: {
|
||||
template_id: templateId,
|
||||
version: existingTemplate.version || "1.0",
|
||||
name: existingTemplate.name,
|
||||
description: existingTemplate.description,
|
||||
subject: existingTemplate.subject,
|
||||
preheader: existingTemplate.preheader,
|
||||
email_content: existingTemplate.email_content,
|
||||
push_title: existingTemplate.push_title,
|
||||
push_body: existingTemplate.push_body,
|
||||
push_icon: existingTemplate.push_icon,
|
||||
push_url: existingTemplate.push_url,
|
||||
sms_content: existingTemplate.sms_content,
|
||||
category: existingTemplate.category,
|
||||
channels: existingTemplate.channels,
|
||||
status: existingTemplate.status,
|
||||
tags: existingTemplate.tags,
|
||||
is_personal: existingTemplate.is_personal,
|
||||
from_name: existingTemplate.from_name,
|
||||
reply_to: existingTemplate.reply_to,
|
||||
track_opens: existingTemplate.track_opens,
|
||||
variables: existingTemplate.variables,
|
||||
is_active: existingTemplate.is_active,
|
||||
change_description: `Template updated - version ${existingTemplate.version}`,
|
||||
is_current: false,
|
||||
created_by: user.userID.toString(),
|
||||
}
|
||||
});
|
||||
|
||||
// Update the template
|
||||
const updatedTemplate = await prisma.notification_templates.update({
|
||||
where: {
|
||||
id: templateId
|
||||
},
|
||||
data: templateData
|
||||
});
|
||||
|
||||
console.log("Template updated successfully:", updatedTemplate.id);
|
||||
|
||||
// Return success response
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
id: updatedTemplate.id,
|
||||
message: "Template updated successfully",
|
||||
template: {
|
||||
id: updatedTemplate.id,
|
||||
name: updatedTemplate.name,
|
||||
value: updatedTemplate.value,
|
||||
status: updatedTemplate.status,
|
||||
category: updatedTemplate.category,
|
||||
channels: updatedTemplate.channels,
|
||||
version: updatedTemplate.version,
|
||||
updated_at: updatedTemplate.updated_at
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
console.error("Template update error:", error);
|
||||
console.error("Error details:", {
|
||||
message: error.message,
|
||||
stack: error.stack,
|
||||
cause: error.cause,
|
||||
code: error.code,
|
||||
statusCode: error.statusCode
|
||||
});
|
||||
|
||||
// Handle Prisma errors
|
||||
if (error.code && error.code.startsWith('P')) {
|
||||
console.error("Prisma error code:", error.code);
|
||||
|
||||
if (error.code === 'P2002') {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: "Template with this name already exists",
|
||||
data: {
|
||||
error: "A template with this name already exists. Please choose a different name.",
|
||||
code: error.code
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (error.code === 'P2025') {
|
||||
throw createError({
|
||||
statusCode: 404,
|
||||
statusMessage: "Template not found",
|
||||
data: {
|
||||
error: "The template you're trying to update does not exist.",
|
||||
code: error.code
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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 update template",
|
||||
data: {
|
||||
error: error.message
|
||||
}
|
||||
});
|
||||
} finally {
|
||||
}
|
||||
});
|
||||
185
server/api/notifications/templates/[id]/duplicate.post.js
Normal file
185
server/api/notifications/templates/[id]/duplicate.post.js
Normal file
@@ -0,0 +1,185 @@
|
||||
import prisma from "~/server/utils/prisma";
|
||||
|
||||
// Helper function to generate unique value from title
|
||||
function generateUniqueValue(title) {
|
||||
return title
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9\s]/g, '')
|
||||
.replace(/\s+/g, '_')
|
||||
.substring(0, 50);
|
||||
}
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
try {
|
||||
// Get template ID from route parameters
|
||||
const templateId = getRouterParam(event, "id");
|
||||
|
||||
if (!templateId) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: "Template ID is required",
|
||||
});
|
||||
}
|
||||
|
||||
console.log("Duplicating template:", templateId);
|
||||
|
||||
// Get current user (assuming auth middleware provides this)
|
||||
const user = event.context.user;
|
||||
if (!user || !user.userID) {
|
||||
throw createError({
|
||||
statusCode: 401,
|
||||
statusMessage: "Authentication required",
|
||||
});
|
||||
}
|
||||
|
||||
// Find the original template
|
||||
const originalTemplate = await prisma.notification_templates.findUnique({
|
||||
where: {
|
||||
id: templateId
|
||||
}
|
||||
});
|
||||
|
||||
if (!originalTemplate) {
|
||||
throw createError({
|
||||
statusCode: 404,
|
||||
statusMessage: "Template not found",
|
||||
});
|
||||
}
|
||||
|
||||
console.log("Original template found:", originalTemplate.name);
|
||||
|
||||
// Generate new name and value for the duplicate
|
||||
const newName = `${originalTemplate.name} (Copy)`;
|
||||
let uniqueValue = generateUniqueValue(newName);
|
||||
|
||||
// Check if value already exists and make it unique
|
||||
let counter = 1;
|
||||
let finalValue = uniqueValue;
|
||||
while (true) {
|
||||
const existingTemplate = await prisma.notification_templates.findUnique({
|
||||
where: { value: finalValue }
|
||||
});
|
||||
|
||||
if (!existingTemplate) {
|
||||
break;
|
||||
}
|
||||
|
||||
finalValue = `${uniqueValue}_copy_${counter}`;
|
||||
counter++;
|
||||
|
||||
// Safety check to prevent infinite loop
|
||||
if (counter > 100) {
|
||||
finalValue = `${uniqueValue}_copy_${Date.now()}`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare duplicate template data
|
||||
const duplicateData = {
|
||||
name: newName,
|
||||
value: finalValue,
|
||||
description: originalTemplate.description,
|
||||
subject: originalTemplate.subject,
|
||||
preheader: originalTemplate.preheader,
|
||||
email_content: originalTemplate.email_content,
|
||||
push_title: originalTemplate.push_title,
|
||||
push_body: originalTemplate.push_body,
|
||||
push_icon: originalTemplate.push_icon,
|
||||
push_url: originalTemplate.push_url,
|
||||
sms_content: originalTemplate.sms_content,
|
||||
category: originalTemplate.category,
|
||||
channels: originalTemplate.channels,
|
||||
status: "Draft", // Always start as draft
|
||||
version: "1.0", // Reset version for new template
|
||||
tags: originalTemplate.tags,
|
||||
is_personal: originalTemplate.is_personal,
|
||||
from_name: originalTemplate.from_name,
|
||||
reply_to: originalTemplate.reply_to,
|
||||
track_opens: originalTemplate.track_opens,
|
||||
variables: originalTemplate.variables,
|
||||
is_active: false, // Inactive by default
|
||||
created_by: user.userID.toString(),
|
||||
updated_by: user.userID.toString(),
|
||||
};
|
||||
|
||||
console.log("Creating duplicate with data:", duplicateData);
|
||||
|
||||
// Create the duplicate template
|
||||
const duplicateTemplate = await prisma.notification_templates.create({
|
||||
data: duplicateData
|
||||
});
|
||||
|
||||
console.log("Template duplicated successfully:", duplicateTemplate.id);
|
||||
|
||||
// Return success response
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
message: `Template "${originalTemplate.name}" has been duplicated successfully`,
|
||||
originalTemplate: {
|
||||
id: originalTemplate.id,
|
||||
name: originalTemplate.name,
|
||||
value: originalTemplate.value
|
||||
},
|
||||
duplicateTemplate: {
|
||||
id: duplicateTemplate.id,
|
||||
name: duplicateTemplate.name,
|
||||
value: duplicateTemplate.value,
|
||||
status: duplicateTemplate.status,
|
||||
version: duplicateTemplate.version,
|
||||
created_at: duplicateTemplate.created_at
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
console.error("Template duplication error:", error);
|
||||
console.error("Error details:", {
|
||||
message: error.message,
|
||||
stack: error.stack,
|
||||
cause: error.cause,
|
||||
code: error.code,
|
||||
statusCode: error.statusCode
|
||||
});
|
||||
|
||||
// Handle Prisma errors
|
||||
if (error.code && error.code.startsWith('P')) {
|
||||
console.error("Prisma error code:", error.code);
|
||||
|
||||
if (error.code === 'P2002') {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: "Duplicate value conflict",
|
||||
data: {
|
||||
error: "Unable to generate unique identifier for the duplicate template. Please try again.",
|
||||
code: error.code
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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 duplicate template",
|
||||
data: {
|
||||
error: error.message
|
||||
}
|
||||
});
|
||||
} finally {
|
||||
}
|
||||
});
|
||||
149
server/api/notifications/templates/[id]/versions.get.js
Normal file
149
server/api/notifications/templates/[id]/versions.get.js
Normal file
@@ -0,0 +1,149 @@
|
||||
import prisma from "~/server/utils/prisma";
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
try {
|
||||
// Get template ID from route parameters
|
||||
const templateId = getRouterParam(event, "id");
|
||||
|
||||
if (!templateId) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: "Template ID is required",
|
||||
});
|
||||
}
|
||||
|
||||
console.log("Fetching version history for template:", templateId);
|
||||
|
||||
// Get current user (assuming auth middleware provides this)
|
||||
const user = event.context.user;
|
||||
if (!user || !user.userID) {
|
||||
throw createError({
|
||||
statusCode: 401,
|
||||
statusMessage: "Authentication required",
|
||||
});
|
||||
}
|
||||
|
||||
// Check if template exists
|
||||
const template = await prisma.notification_templates.findUnique({
|
||||
where: { id: templateId }
|
||||
});
|
||||
|
||||
if (!template) {
|
||||
throw createError({
|
||||
statusCode: 404,
|
||||
statusMessage: "Template not found",
|
||||
});
|
||||
}
|
||||
|
||||
// Check if the version history table exists
|
||||
let versions = [];
|
||||
try {
|
||||
// Fetch version history for the template
|
||||
versions = await prisma.notification_template_versions.findMany({
|
||||
where: {
|
||||
template_id: templateId
|
||||
},
|
||||
orderBy: {
|
||||
created_at: 'desc'
|
||||
}
|
||||
});
|
||||
} catch (dbError) {
|
||||
console.error("Database error when fetching versions:", dbError);
|
||||
|
||||
// If table doesn't exist, return empty array
|
||||
if (dbError.code === 'P2021' || dbError.message.includes('doesn\'t exist')) {
|
||||
console.log("Version history table doesn't exist, returning empty array");
|
||||
versions = [];
|
||||
} else {
|
||||
throw dbError;
|
||||
}
|
||||
}
|
||||
|
||||
// Format the response data
|
||||
const formattedVersions = versions.map(version => ({
|
||||
id: version.id,
|
||||
version: version.version,
|
||||
name: version.name,
|
||||
description: version.description,
|
||||
subject: version.subject,
|
||||
content: version.email_content,
|
||||
changeDescription: version.change_description,
|
||||
isCurrent: version.is_current,
|
||||
status: version.status,
|
||||
createdBy: version.created_by,
|
||||
createdAt: version.created_at,
|
||||
formattedCreatedAt: version.created_at
|
||||
? new Date(version.created_at).toLocaleDateString("en-US", {
|
||||
year: "numeric",
|
||||
month: "2-digit",
|
||||
day: "2-digit",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
})
|
||||
: "",
|
||||
}));
|
||||
|
||||
console.log(`Found ${formattedVersions.length} versions for template ${templateId}`);
|
||||
|
||||
// Return success response
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
templateId,
|
||||
templateName: template.name,
|
||||
versions: formattedVersions,
|
||||
totalCount: formattedVersions.length
|
||||
}
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
console.error("Version history fetch error:", error);
|
||||
console.error("Error details:", {
|
||||
message: error.message,
|
||||
stack: error.stack,
|
||||
cause: error.cause,
|
||||
code: error.code,
|
||||
statusCode: error.statusCode
|
||||
});
|
||||
|
||||
// Handle Prisma errors
|
||||
if (error.code && error.code.startsWith('P')) {
|
||||
console.error("Prisma error code:", error.code);
|
||||
|
||||
if (error.code === 'P2025') {
|
||||
throw createError({
|
||||
statusCode: 404,
|
||||
statusMessage: "Template not found",
|
||||
data: {
|
||||
error: "The template you're looking for does not exist.",
|
||||
code: error.code
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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 fetch version history",
|
||||
data: {
|
||||
error: error.message
|
||||
}
|
||||
});
|
||||
} finally {
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,145 @@
|
||||
import prisma from "~/server/utils/prisma";
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
try {
|
||||
// Get template ID and version ID from route parameters
|
||||
const templateId = getRouterParam(event, "id");
|
||||
const versionId = getRouterParam(event, "versionId");
|
||||
|
||||
if (!templateId || !versionId) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: "Template ID and Version ID are required",
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`Deleting version ${versionId} for template ${templateId}`);
|
||||
|
||||
// Get current user (assuming auth middleware provides this)
|
||||
const user = event.context.user;
|
||||
if (!user || !user.userID) {
|
||||
throw createError({
|
||||
statusCode: 401,
|
||||
statusMessage: "Authentication required",
|
||||
});
|
||||
}
|
||||
|
||||
// Check if template exists
|
||||
const template = await prisma.notification_templates.findUnique({
|
||||
where: { id: templateId }
|
||||
});
|
||||
|
||||
if (!template) {
|
||||
throw createError({
|
||||
statusCode: 404,
|
||||
statusMessage: "Template not found",
|
||||
});
|
||||
}
|
||||
|
||||
// Check if version exists
|
||||
const version = await prisma.notification_template_versions.findUnique({
|
||||
where: { id: versionId }
|
||||
});
|
||||
|
||||
if (!version || version.template_id !== templateId) {
|
||||
throw createError({
|
||||
statusCode: 404,
|
||||
statusMessage: "Version not found",
|
||||
});
|
||||
}
|
||||
|
||||
// Check if this is the current version
|
||||
if (version.is_current) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: "Cannot delete current version",
|
||||
data: {
|
||||
error: "You cannot delete the current version of a template. Please restore a different version first."
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Check if there are other versions (prevent deletion of the only version)
|
||||
const versionCount = await prisma.notification_template_versions.count({
|
||||
where: { template_id: templateId }
|
||||
});
|
||||
|
||||
if (versionCount <= 1) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: "Cannot delete the only version",
|
||||
data: {
|
||||
error: "This is the only version of the template. At least one version must exist."
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Delete the version
|
||||
await prisma.notification_template_versions.delete({
|
||||
where: { id: versionId }
|
||||
});
|
||||
|
||||
console.log(`Version ${version.version} deleted successfully`);
|
||||
|
||||
// Return success response
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
message: `Version ${version.version} has been deleted successfully`,
|
||||
templateId,
|
||||
deletedVersion: version.version,
|
||||
deletedVersionId: versionId
|
||||
}
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
console.error("Version delete error:", error);
|
||||
console.error("Error details:", {
|
||||
message: error.message,
|
||||
stack: error.stack,
|
||||
cause: error.cause,
|
||||
code: error.code,
|
||||
statusCode: error.statusCode
|
||||
});
|
||||
|
||||
// Handle Prisma errors
|
||||
if (error.code && error.code.startsWith('P')) {
|
||||
console.error("Prisma error code:", error.code);
|
||||
|
||||
if (error.code === 'P2025') {
|
||||
throw createError({
|
||||
statusCode: 404,
|
||||
statusMessage: "Template or version not found",
|
||||
data: {
|
||||
error: "The template or version you're looking for does not exist.",
|
||||
code: error.code
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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 delete version",
|
||||
data: {
|
||||
error: error.message
|
||||
}
|
||||
});
|
||||
} finally {
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,231 @@
|
||||
import prisma from "~/server/utils/prisma";
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
try {
|
||||
// Get template ID and version ID from route parameters
|
||||
const templateId = getRouterParam(event, "id");
|
||||
const versionId = getRouterParam(event, "versionId");
|
||||
|
||||
if (!templateId || !versionId) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: "Template ID and Version ID are required",
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`Restoring version ${versionId} for template ${templateId}`);
|
||||
|
||||
// Get current user (assuming auth middleware provides this)
|
||||
const user = event.context.user;
|
||||
if (!user || !user.userID) {
|
||||
throw createError({
|
||||
statusCode: 401,
|
||||
statusMessage: "Authentication required",
|
||||
});
|
||||
}
|
||||
|
||||
// Check if template exists
|
||||
const template = await prisma.notification_templates.findUnique({
|
||||
where: { id: templateId }
|
||||
});
|
||||
|
||||
if (!template) {
|
||||
throw createError({
|
||||
statusCode: 404,
|
||||
statusMessage: "Template not found",
|
||||
});
|
||||
}
|
||||
|
||||
// Check if version exists
|
||||
const version = await prisma.notification_template_versions.findUnique({
|
||||
where: { id: versionId }
|
||||
});
|
||||
|
||||
if (!version || version.template_id !== templateId) {
|
||||
throw createError({
|
||||
statusCode: 404,
|
||||
statusMessage: "Version not found",
|
||||
});
|
||||
}
|
||||
|
||||
// Generate new version number (increment current version)
|
||||
const currentVersion = template.version || "1.0";
|
||||
const versionParts = currentVersion.split('.');
|
||||
const majorVersion = parseInt(versionParts[0]) || 1;
|
||||
const minorVersion = parseInt(versionParts[1]) || 0;
|
||||
const newVersion = `${majorVersion}.${minorVersion + 1}`;
|
||||
|
||||
// Create version history entry for current template state before restore
|
||||
await prisma.notification_template_versions.create({
|
||||
data: {
|
||||
template_id: templateId,
|
||||
version: currentVersion,
|
||||
name: template.name,
|
||||
description: template.description,
|
||||
subject: template.subject,
|
||||
preheader: template.preheader,
|
||||
email_content: template.email_content,
|
||||
push_title: template.push_title,
|
||||
push_body: template.push_body,
|
||||
push_icon: template.push_icon,
|
||||
push_url: template.push_url,
|
||||
sms_content: template.sms_content,
|
||||
category: template.category,
|
||||
channels: template.channels,
|
||||
status: template.status,
|
||||
tags: template.tags,
|
||||
is_personal: template.is_personal,
|
||||
from_name: template.from_name,
|
||||
reply_to: template.reply_to,
|
||||
track_opens: template.track_opens,
|
||||
variables: template.variables,
|
||||
is_active: template.is_active,
|
||||
change_description: `Automatic backup before restoring version ${version.version}`,
|
||||
is_current: false,
|
||||
created_by: user.userID.toString(),
|
||||
}
|
||||
});
|
||||
|
||||
// Update the current template with the version data
|
||||
const updatedTemplate = await prisma.notification_templates.update({
|
||||
where: { id: templateId },
|
||||
data: {
|
||||
name: version.name,
|
||||
description: version.description,
|
||||
subject: version.subject,
|
||||
preheader: version.preheader,
|
||||
email_content: version.email_content,
|
||||
push_title: version.push_title,
|
||||
push_body: version.push_body,
|
||||
push_icon: version.push_icon,
|
||||
push_url: version.push_url,
|
||||
sms_content: version.sms_content,
|
||||
category: version.category,
|
||||
channels: version.channels,
|
||||
status: version.status,
|
||||
tags: version.tags,
|
||||
is_personal: version.is_personal,
|
||||
from_name: version.from_name,
|
||||
reply_to: version.reply_to,
|
||||
track_opens: version.track_opens,
|
||||
variables: version.variables,
|
||||
is_active: version.is_active,
|
||||
version: newVersion,
|
||||
updated_by: user.userID.toString(),
|
||||
updated_at: new Date(),
|
||||
}
|
||||
});
|
||||
|
||||
// Create version history entry for the restored version
|
||||
await prisma.notification_template_versions.create({
|
||||
data: {
|
||||
template_id: templateId,
|
||||
version: newVersion,
|
||||
name: version.name,
|
||||
description: version.description,
|
||||
subject: version.subject,
|
||||
preheader: version.preheader,
|
||||
email_content: version.email_content,
|
||||
push_title: version.push_title,
|
||||
push_body: version.push_body,
|
||||
push_icon: version.push_icon,
|
||||
push_url: version.push_url,
|
||||
sms_content: version.sms_content,
|
||||
category: version.category,
|
||||
channels: version.channels,
|
||||
status: version.status,
|
||||
tags: version.tags,
|
||||
is_personal: version.is_personal,
|
||||
from_name: version.from_name,
|
||||
reply_to: version.reply_to,
|
||||
track_opens: version.track_opens,
|
||||
variables: version.variables,
|
||||
is_active: version.is_active,
|
||||
change_description: `Restored from version ${version.version}`,
|
||||
is_current: true,
|
||||
created_by: user.userID.toString(),
|
||||
}
|
||||
});
|
||||
|
||||
// Mark previous current version as not current
|
||||
await prisma.notification_template_versions.updateMany({
|
||||
where: {
|
||||
template_id: templateId,
|
||||
is_current: true,
|
||||
version: { not: newVersion }
|
||||
},
|
||||
data: {
|
||||
is_current: false
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`Version ${version.version} restored successfully as version ${newVersion}`);
|
||||
|
||||
// Return success response
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
message: `Version ${version.version} has been restored successfully as version ${newVersion}`,
|
||||
templateId,
|
||||
restoredVersion: version.version,
|
||||
newVersion,
|
||||
template: {
|
||||
id: updatedTemplate.id,
|
||||
title: updatedTemplate.name,
|
||||
version: updatedTemplate.version,
|
||||
updatedAt: updatedTemplate.updated_at
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
console.error("Version restore error:", error);
|
||||
console.error("Error details:", {
|
||||
message: error.message,
|
||||
stack: error.stack,
|
||||
cause: error.cause,
|
||||
code: error.code,
|
||||
statusCode: error.statusCode
|
||||
});
|
||||
|
||||
// Handle Prisma errors
|
||||
if (error.code && error.code.startsWith('P')) {
|
||||
console.error("Prisma error code:", error.code);
|
||||
|
||||
if (error.code === 'P2025') {
|
||||
throw createError({
|
||||
statusCode: 404,
|
||||
statusMessage: "Template or version not found",
|
||||
data: {
|
||||
error: "The template or version you're looking for does not exist.",
|
||||
code: error.code
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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 restore version",
|
||||
data: {
|
||||
error: error.message
|
||||
}
|
||||
});
|
||||
} finally {
|
||||
}
|
||||
});
|
||||
231
server/api/notifications/templates/create.post.js
Normal file
231
server/api/notifications/templates/create.post.js
Normal file
@@ -0,0 +1,231 @@
|
||||
import { z } from "zod";
|
||||
import prisma from "~/server/utils/prisma";
|
||||
|
||||
// Input validation schema
|
||||
const createTemplateSchema = z.object({
|
||||
title: z.string().min(1, "Title is required").max(100, "Title must be less than 100 characters"),
|
||||
description: z.string().optional(),
|
||||
subject: z.string().min(1, "Subject is required").max(255, "Subject must be less than 255 characters"),
|
||||
preheader: z.string().max(255, "Preheader must be less than 255 characters").optional(),
|
||||
category: z.string().min(1, "Category is required"),
|
||||
channels: z.array(z.enum(["email", "sms", "push"])).min(1, "At least one channel is required"),
|
||||
status: z.enum(["Draft", "Active", "Archived"]).default("Draft"),
|
||||
version: z.string().default("1.0"),
|
||||
content: z.string().min(1, "Content is required"),
|
||||
tags: z.string().optional(),
|
||||
isPersonal: z.boolean().default(false),
|
||||
// Email specific settings
|
||||
fromName: z.string().optional(),
|
||||
replyTo: z.string().email("Invalid reply-to email format").optional().or(z.literal("")),
|
||||
trackOpens: z.boolean().default(true),
|
||||
// Push notification specific settings
|
||||
pushTitle: z.string().optional(),
|
||||
pushIcon: z.string().url("Invalid push icon URL").optional().or(z.literal("")),
|
||||
pushUrl: z.string().url("Invalid push URL").optional().or(z.literal("")),
|
||||
// SMS specific settings
|
||||
smsContent: z.string().max(160, "SMS content must be 160 characters or less").optional(),
|
||||
});
|
||||
|
||||
// Helper function to generate unique value from title
|
||||
function generateUniqueValue(title) {
|
||||
return title
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9\s]/g, '')
|
||||
.replace(/\s+/g, '_')
|
||||
.substring(0, 50);
|
||||
}
|
||||
|
||||
// Helper function to validate template content
|
||||
function validateTemplateContent(content, channels) {
|
||||
const errors = [];
|
||||
|
||||
// Check for balanced variable syntax
|
||||
const variableMatches = content.match(/\{\{[^}]*\}\}/g) || [];
|
||||
for (const match of variableMatches) {
|
||||
if (!match.endsWith('}}')) {
|
||||
errors.push(`Unmatched variable syntax: ${match}`);
|
||||
}
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
try {
|
||||
// Read and validate request body
|
||||
const body = await readBody(event);
|
||||
|
||||
console.log("Template creation request:", body);
|
||||
|
||||
// Validate input data
|
||||
const validationResult = createTemplateSchema.safeParse(body);
|
||||
if (!validationResult.success) {
|
||||
const errors = validationResult.error.errors.map(err =>
|
||||
`${err.path.join('.')}: ${err.message}`
|
||||
);
|
||||
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: "Validation failed",
|
||||
data: {
|
||||
errors: errors
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const validatedData = validationResult.data;
|
||||
|
||||
// Get current user (assuming auth middleware provides this)
|
||||
const user = event.context.user;
|
||||
if (!user || !user.userID) {
|
||||
throw createError({
|
||||
statusCode: 401,
|
||||
statusMessage: "Authentication required",
|
||||
});
|
||||
}
|
||||
|
||||
console.log("User authenticated:", user.userID);
|
||||
|
||||
// Validate template content
|
||||
const contentErrors = validateTemplateContent(validatedData.content, validatedData.channels);
|
||||
if (contentErrors.length > 0) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: "Content validation failed",
|
||||
data: {
|
||||
errors: contentErrors
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Generate unique value for the template
|
||||
let uniqueValue = generateUniqueValue(validatedData.title);
|
||||
|
||||
// Check if value already exists and make it unique
|
||||
let counter = 1;
|
||||
let finalValue = uniqueValue;
|
||||
while (true) {
|
||||
const existingTemplate = await prisma.notification_templates.findUnique({
|
||||
where: { value: finalValue }
|
||||
});
|
||||
|
||||
if (!existingTemplate) {
|
||||
break;
|
||||
}
|
||||
|
||||
finalValue = `${uniqueValue}_${counter}`;
|
||||
counter++;
|
||||
|
||||
// Safety check to prevent infinite loop
|
||||
if (counter > 100) {
|
||||
finalValue = `${uniqueValue}_${Date.now()}`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare data for database insertion
|
||||
const templateData = {
|
||||
name: validatedData.title,
|
||||
value: finalValue,
|
||||
description: validatedData.description || null,
|
||||
subject: validatedData.subject,
|
||||
preheader: validatedData.preheader || null,
|
||||
email_content: validatedData.content,
|
||||
push_title: validatedData.pushTitle || null,
|
||||
push_body: validatedData.content ? validatedData.content.replace(/<[^>]*>/g, '').substring(0, 300) : null,
|
||||
push_icon: validatedData.pushIcon || null,
|
||||
push_url: validatedData.pushUrl || null,
|
||||
sms_content: validatedData.smsContent || null,
|
||||
category: validatedData.category,
|
||||
channels: validatedData.channels,
|
||||
status: validatedData.status,
|
||||
version: validatedData.version,
|
||||
tags: validatedData.tags || null,
|
||||
is_personal: validatedData.isPersonal,
|
||||
from_name: validatedData.fromName || null,
|
||||
reply_to: validatedData.replyTo || null,
|
||||
track_opens: validatedData.trackOpens,
|
||||
is_active: validatedData.status === "Active" ? 1 : validatedData.status === "Draft" ? 0 : 2,
|
||||
created_by: user.userID.toString(),
|
||||
updated_by: user.userID.toString(),
|
||||
};
|
||||
|
||||
console.log("Creating template with data:", templateData);
|
||||
|
||||
// Create the template
|
||||
const newTemplate = await prisma.notification_templates.create({
|
||||
data: templateData
|
||||
});
|
||||
|
||||
console.log("Template created successfully:", newTemplate.id);
|
||||
|
||||
// Return success response
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
id: newTemplate.id,
|
||||
message: "Template created successfully",
|
||||
template: {
|
||||
id: newTemplate.id,
|
||||
name: newTemplate.name,
|
||||
value: newTemplate.value,
|
||||
status: newTemplate.status,
|
||||
category: newTemplate.category,
|
||||
channels: newTemplate.channels,
|
||||
version: newTemplate.version,
|
||||
created_at: newTemplate.created_at
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
console.error("Template creation error:", error);
|
||||
console.error("Error details:", {
|
||||
message: error.message,
|
||||
stack: error.stack,
|
||||
cause: error.cause,
|
||||
code: error.code,
|
||||
statusCode: error.statusCode
|
||||
});
|
||||
|
||||
// Handle Prisma errors
|
||||
if (error.code && error.code.startsWith('P')) {
|
||||
console.error("Prisma error code:", error.code);
|
||||
|
||||
if (error.code === 'P2002') {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: "Template with this name already exists",
|
||||
data: {
|
||||
error: "A template with this name already exists. Please choose a different name.",
|
||||
code: error.code
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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 create template",
|
||||
data: {
|
||||
error: error.message
|
||||
}
|
||||
});
|
||||
} finally {
|
||||
}
|
||||
});
|
||||
64
server/api/notifications/templates/index.get.js
Normal file
64
server/api/notifications/templates/index.get.js
Normal file
@@ -0,0 +1,64 @@
|
||||
import prisma from "~/server/utils/prisma";
|
||||
|
||||
// Helper function to map is_active integer to status string
|
||||
function getStatusFromIsActive(isActive) {
|
||||
console.log("Converting is_active value:", isActive, "type:", typeof isActive);
|
||||
switch (isActive) {
|
||||
case 1:
|
||||
return "Active";
|
||||
case 0:
|
||||
return "Inactive";
|
||||
case 2:
|
||||
return "Draft";
|
||||
default:
|
||||
return "Draft";
|
||||
}
|
||||
}
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
try {
|
||||
// Use raw query to get the actual integer values for is_active
|
||||
const templates = await prisma.$queryRaw`
|
||||
SELECT
|
||||
id,
|
||||
name,
|
||||
value,
|
||||
category,
|
||||
is_active,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM notification_templates
|
||||
ORDER BY name ASC
|
||||
`;
|
||||
|
||||
console.log("RAW QUERY TEMPLATES:", templates);
|
||||
console.log("First template is_active:", templates[0]?.is_active, "type:", typeof templates[0]?.is_active);
|
||||
|
||||
// Format the response to match frontend expectations
|
||||
return {
|
||||
success: true,
|
||||
message: "Code: A1",
|
||||
data: {
|
||||
templates: templates.map((template) => ({
|
||||
id: template.id,
|
||||
title: template.name,
|
||||
value: template.value,
|
||||
category: template.category || "General",
|
||||
is_active: template.is_active,
|
||||
created_at: template.created_at || "",
|
||||
updated_at: template.updated_at || "",
|
||||
})),
|
||||
},
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error fetching templates:", error);
|
||||
return {
|
||||
success: false,
|
||||
data: {
|
||||
message: "Failed to fetch templates",
|
||||
error: error.message,
|
||||
},
|
||||
};
|
||||
} finally {
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user