i18n subject in mailer

This commit is contained in:
Philipp Dormann 2024-12-11 18:09:16 +01:00
parent a5a8f42db6
commit 56ebf652b1
Signed by: philipp
GPG Key ID: 3BB9ADD52DCA4314
9 changed files with 15 additions and 12 deletions

View File

@ -34,7 +34,6 @@ curl -X POST http://localhost:3000/api/v1/email \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-d '{ -d '{
"to": "user@example.com", "to": "user@example.com",
"subject": "Welcome to Lauf für Kaya! 2025",
"templateName": "welcome", "templateName": "welcome",
"language": "en", "language": "en",
"data": { "data": {
@ -52,13 +51,10 @@ curl -X POST http://localhost:3000/api/v1/email \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-d '{ -d '{
"to": "user@example.com", "to": "user@example.com",
"subject": "Password Reset Request",
"templateName": "password-reset", "templateName": "password-reset",
"language": "de", "language": "de",
"data": { "data": {
"name": "John Doe", "token": "123465789"
"resetLink": "https://example.com/reset/123",
"expiresIn": "24 hours"
} }
}' }'
``` ```
@ -79,6 +75,7 @@ SMTP_PASS="secret.1"
EMAIL_FROM="noreply@lauf-fuer-kaya.de" EMAIL_FROM="noreply@lauf-fuer-kaya.de"
EMAIL_REPLYTO="info@lauf-fuer-kaya.de" EMAIL_REPLYTO="info@lauf-fuer-kaya.de"
REDIS_URL=redis://localhost:6379 REDIS_URL=redis://localhost:6379
FRONTEND_URL="https://run.lauf-fuer-kaya.de"
``` ```
## 🛠️ Development ## 🛠️ Development

View File

@ -14,6 +14,7 @@ services:
- SMTP_PASS="secret.1" - SMTP_PASS="secret.1"
- EMAIL_FROM="noreply@lauf-fuer-kaya.de" - EMAIL_FROM="noreply@lauf-fuer-kaya.de"
- EMAIL_REPLYTO="info@lauf-fuer-kaya.de" - EMAIL_REPLYTO="info@lauf-fuer-kaya.de"
- FRONTEND_URL="https://run.lauf-fuer-kaya.de"
depends_on: depends_on:
- redis - redis

View File

@ -11,7 +11,6 @@ const emailService = new EmailService()
const sendEmailSchema = z.object({ const sendEmailSchema = z.object({
to: z.string().email(), to: z.string().email(),
subject: z.string(),
templateName: z.string(), templateName: z.string(),
language: z.enum(['en', 'de']), language: z.enum(['en', 'de']),
data: z.record(z.any()) data: z.record(z.any())
@ -34,7 +33,7 @@ async function generateBarcodeDataURL(data) {
} }
emailRouter.post('/', zValidator('json', sendEmailSchema), async (c) => { emailRouter.post('/', zValidator('json', sendEmailSchema), async (c) => {
let { to, subject, templateName, language, data } = c.req.valid('json') let { to, templateName, language, data } = c.req.valid('json')
try { try {
const template = getEmailTemplate(templateName, language as Language) const template = getEmailTemplate(templateName, language as Language)
@ -56,7 +55,7 @@ emailRouter.post('/', zValidator('json', sendEmailSchema), async (c) => {
} }
await emailService.sendEmail({ await emailService.sendEmail({
to, to,
subject, subject: template.subject(data),
html: template.html(data), html: template.html(data),
text: template.text(data) text: template.text(data)
}) })

View File

@ -10,15 +10,12 @@ const routes = {
'application/json': { 'application/json': {
schema: { schema: {
type: 'object', type: 'object',
required: ['to', 'subject', 'templateName', 'language', 'data'], required: ['to', 'templateName', 'language', 'data'],
properties: { properties: {
to: { to: {
type: 'string', type: 'string',
format: 'email' format: 'email'
}, },
subject: {
type: 'string'
},
templateName: { templateName: {
type: 'string' type: 'string'
}, },

View File

@ -6,6 +6,7 @@ import { compile } from 'handlebars'
interface TemplateCache { interface TemplateCache {
[key: string]: { [key: string]: {
html: HandlebarsTemplateDelegate html: HandlebarsTemplateDelegate
subject: HandlebarsTemplateDelegate,
text: HandlebarsTemplateDelegate text: HandlebarsTemplateDelegate
} }
} }
@ -18,12 +19,15 @@ function loadTemplate(name: string, language: Language) {
if (!templateCache[cacheKey]) { if (!templateCache[cacheKey]) {
const htmlPath = join(process.cwd(), 'src', 'templates', name, `${language}.html`) const htmlPath = join(process.cwd(), 'src', 'templates', name, `${language}.html`)
const textPath = join(process.cwd(), 'src', 'templates', name, `${language}.txt`) const textPath = join(process.cwd(), 'src', 'templates', name, `${language}.txt`)
const subjectPath = join(process.cwd(), 'src', 'templates', name, `${language}.subject.txt`)
const htmlTemplate = readFileSync(htmlPath, 'utf-8') const htmlTemplate = readFileSync(htmlPath, 'utf-8')
const textTemplate = readFileSync(textPath, 'utf-8') const textTemplate = readFileSync(textPath, 'utf-8')
const subjectTemplate = readFileSync(subjectPath, 'utf-8')
templateCache[cacheKey] = { templateCache[cacheKey] = {
html: compile(htmlTemplate), html: compile(htmlTemplate),
subject: compile(subjectTemplate),
text: compile(textTemplate) text: compile(textTemplate)
} }
} }
@ -35,6 +39,7 @@ export function getEmailTemplate(name: string, language: Language) {
const template = loadTemplate(name, language) const template = loadTemplate(name, language)
return { return {
subject: (data: any) => template.subject(data),
html: (data: any) => template.html(data), html: (data: any) => template.html(data),
text: (data: any) => template.text(data) text: (data: any) => template.text(data)
} }

View File

@ -0,0 +1 @@
Lauf für Kaya! Passwort Reset

View File

@ -0,0 +1 @@
Lauf für Kaya! Password Reset

View File

@ -0,0 +1 @@
Willkommen beim Lauf für Kaya! 2025

View File

@ -0,0 +1 @@
Welcome to Lauf für Kaya! 2025