From 9ee0626e8bfa8432543f50c5ff97c66ef1b1699a Mon Sep 17 00:00:00 2001 From: "Calum H." Date: Wed, 8 Oct 2025 20:05:23 +0100 Subject: [PATCH] feat: dynamic email template using markdown (#4515) * feat: markdown dynamic email template * fix: lint and remove debug statements * fix: lint issues --- .../_internal/templates/email/[template].ts | 8 +++++ .../_internal/templates/email/dynamic.post.ts | 29 +++++++++++++++++++ .../emails/dynamic/MarkdownDynamicEmail.vue | 20 +++++++++++++ 3 files changed, 57 insertions(+) create mode 100644 apps/frontend/src/server/routes/_internal/templates/email/dynamic.post.ts create mode 100644 apps/frontend/src/templates/emails/dynamic/MarkdownDynamicEmail.vue diff --git a/apps/frontend/src/server/routes/_internal/templates/email/[template].ts b/apps/frontend/src/server/routes/_internal/templates/email/[template].ts index 0614de9f..ceae7df2 100644 --- a/apps/frontend/src/server/routes/_internal/templates/email/[template].ts +++ b/apps/frontend/src/server/routes/_internal/templates/email/[template].ts @@ -5,6 +5,14 @@ import emails from '~/templates/emails' export default defineEventHandler(async (event) => { const template = event.context.params?.template as string + + if (template === 'dynamic') { + throw createError({ + statusCode: 404, + message: 'Email template not found', + }) + } + try { const component = (await emails[template]()).default as Component | undefined diff --git a/apps/frontend/src/server/routes/_internal/templates/email/dynamic.post.ts b/apps/frontend/src/server/routes/_internal/templates/email/dynamic.post.ts new file mode 100644 index 00000000..547eeee9 --- /dev/null +++ b/apps/frontend/src/server/routes/_internal/templates/email/dynamic.post.ts @@ -0,0 +1,29 @@ +import { render } from '@vue-email/render' + +import MarkdownDynamicEmail from '~/templates/emails/dynamic/MarkdownDynamicEmail.vue' + +export default defineEventHandler(async (event) => { + try { + const body = await readBody<{ title: string; body: string }>(event) + + if (!body.title || !body.body) { + throw createError({ + statusCode: 400, + message: 'Missing required fields: title and body', + }) + } + + const html = await render(MarkdownDynamicEmail, { + title: body.title, + body: body.body, + }) + + return html + } catch (error) { + console.error('Error rendering dynamic email template:', error) + throw createError({ + statusCode: 500, + message: 'Failed to render dynamic email template', + }) + } +}) diff --git a/apps/frontend/src/templates/emails/dynamic/MarkdownDynamicEmail.vue b/apps/frontend/src/templates/emails/dynamic/MarkdownDynamicEmail.vue new file mode 100644 index 00000000..e0e52834 --- /dev/null +++ b/apps/frontend/src/templates/emails/dynamic/MarkdownDynamicEmail.vue @@ -0,0 +1,20 @@ + + +