feat: add templates for transactional emails (#103)

* add template migration & router

* template CRUD

* templated transactional emails API

* zod schema fix & rearranging template columns
This commit is contained in:
Ganapathy S
2025-03-07 00:50:25 +05:30
committed by KM Koushik
parent 1c2417df2f
commit 38314a35dc
16 changed files with 981 additions and 8 deletions

View File

@@ -3,6 +3,7 @@ import { db } from "../db";
import { UnsendApiError } from "~/server/public-api/api-error";
import { EmailQueueService } from "./email-queue-service";
import { validateDomainFromEmail } from "./domain-service";
import { EmailRenderer } from "@unsend/email-editor/src/renderer";
async function checkIfValidEmail(emailId: string) {
const email = await db.email.findUnique({
@@ -30,6 +31,14 @@ async function checkIfValidEmail(emailId: string) {
return { email, domain };
}
export const replaceVariables = (text: string, variables: Record<string, string>) => {
return Object.keys(variables).reduce((accum, key) => {
const re = new RegExp(`{{${key}}}`, 'g');
const returnTxt = accum.replace(re, variables[key] as string);
return returnTxt;
}, text);
};
/**
Send transactional email
*/
@@ -39,9 +48,11 @@ export async function sendEmail(
const {
to,
from,
subject,
subject: subjectFromApiCall,
templateId,
variables,
text,
html,
html: htmlFromApiCall,
teamId,
attachments,
replyTo,
@@ -49,9 +60,28 @@ export async function sendEmail(
bcc,
scheduledAt,
} = emailContent;
let subject = subjectFromApiCall;
let html = htmlFromApiCall;
const domain = await validateDomainFromEmail(from, teamId);
if (templateId) {
const template = await db.template.findUnique({
where: { id: templateId },
});
if (template) {
const jsonContent = JSON.parse(template.content || "{}");
const renderer = new EmailRenderer(jsonContent);
subject = replaceVariables(template.subject || '', variables || {});
html = await renderer.render({
shouldReplaceVariableValues: true,
variableValues: variables,
});
}
}
const scheduledAtDate = scheduledAt ? new Date(scheduledAt) : undefined;
const delay = scheduledAtDate
? Math.max(0, scheduledAtDate.getTime() - Date.now())
@@ -61,7 +91,7 @@ export async function sendEmail(
data: {
to: Array.isArray(to) ? to : [to],
from,
subject,
subject: subject as string,
replyTo: replyTo
? Array.isArray(replyTo)
? replyTo