i18n subject in mailer
This commit is contained in:
parent
a5a8f42db6
commit
56ebf652b1
@ -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
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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)
|
||||||
})
|
})
|
||||||
|
@ -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'
|
||||||
},
|
},
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
1
src/templates/password-reset/de.subject.txt
Normal file
1
src/templates/password-reset/de.subject.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
Lauf für Kaya! Passwort Reset
|
1
src/templates/password-reset/en.subject.txt
Normal file
1
src/templates/password-reset/en.subject.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
Lauf für Kaya! Password Reset
|
1
src/templates/welcome/de.subject.txt
Normal file
1
src/templates/welcome/de.subject.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
Willkommen beim Lauf für Kaya! 2025
|
1
src/templates/welcome/en.subject.txt
Normal file
1
src/templates/welcome/en.subject.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
Welcome to Lauf für Kaya! 2025
|
Loading…
x
Reference in New Issue
Block a user