13 Commits
1.0.2 ... 1.2.2

Author SHA1 Message Date
2f25937a94 feat: add ical 2025-03-18 23:57:01 +01:00
658b8d4dd8 chore: 1.2.1 2025-03-18 23:38:08 +01:00
df6381fd5e feat: new email templates 2025-03-18 23:37:28 +01:00
a11e5f2f3e ci: drop main branch
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2024-12-18 18:01:12 +01:00
7a8e484632 chore: 1.2.0
Some checks failed
ci/woodpecker/push/build Pipeline failed
ci/woodpecker/tag/release Pipeline was successful
2024-12-18 18:00:06 +01:00
1f771fb73f feat: NODE_ENV=production 2024-12-18 17:59:40 +01:00
34c8f03571 refactor: dockerfile 2024-12-18 17:59:30 +01:00
dfac4c0fe9 chore(deps): bun:1.1.40 2024-12-18 17:59:24 +01:00
9860014420 chore(deps): bump all
Some checks failed
ci/woodpecker/push/build Pipeline failed
2024-12-18 17:59:10 +01:00
bfce0c22c0 chore: 1.1.1
All checks were successful
ci/woodpecker/push/build Pipeline was successful
ci/woodpecker/tag/release Pipeline was successful
2024-12-12 18:55:05 +01:00
828290d9ba fix(env): no trailing slash for DOCUMENT_SERVER_URL 2024-12-12 18:54:53 +01:00
96e3be543b chore: 1.1.0
All checks were successful
ci/woodpecker/push/build Pipeline was successful
ci/woodpecker/tag/release Pipeline was successful
2024-12-12 18:37:43 +01:00
c8781e3a3d refactor: move barcode generation to document server 2024-12-12 18:37:25 +01:00
10 changed files with 227 additions and 82 deletions

View File

@@ -6,6 +6,8 @@ EMAIL_FROM="noreply@lauf-fuer-kaya.de"
EMAIL_REPLYTO="info@lauf-fuer-kaya.de"
REDIS_URL=redis://localhost:6379
FRONTEND_URL="https://run.lauf-fuer-kaya.de"
DOCUMENT_SERVER_URL="https://documents.run.lauf-fuer-kaya.de"
AUTHKEY=""
EVENT_DATE="23.05.2025"
EVENT_NAME="Lauf für Kaya! 2025"
NODE_ENV=production

View File

@@ -1,19 +1,4 @@
steps:
- name: build latest
image: woodpeckerci/plugin-docker-buildx
settings:
repo: registry.odit.services/lfk/mailer
tags:
- latest
registry: registry.odit.services
platforms: linux/amd64,linux/arm64
cache_from: registry.odit.services/lfk/mailer:dev
username:
from_secret: odit-registry-builder-username
password:
from_secret: odit-registry-builder-password
when:
branch: main
- name: build dev
image: woodpeckerci/plugin-docker-buildx
settings:

View File

@@ -1,21 +1,7 @@
FROM oven/bun:1.1.36-slim
# FROM oven/bun:1.0.25
FROM oven/bun:1.1.40-slim
WORKDIR /app
# Copy package files
# Install dependencies
COPY package.json .
RUN bun i
# COPY package.json bun.lockb ./
# RUN bun install --frozen-lockfile
# Copy source code
COPY . .
# Expose the application port
EXPOSE 3000
# Start the application
CMD ["bun", "run", "start"]

BIN
bun.lockb Normal file → Executable file

Binary file not shown.

View File

@@ -1,6 +1,6 @@
{
"name": "@odit/lfk-mailer",
"version": "1.0.2",
"version": "1.2.1",
"private": true,
"type": "module",
"scripts": {
@@ -11,11 +11,10 @@
"@hono/node-server": "1.13.7",
"@hono/swagger-ui": "0.5.0",
"@hono/zod-openapi": "0.18.3",
"@hono/zod-validator": "0.4.1",
"bullmq": "5.34.0",
"bwip-js": "4.5.1",
"@hono/zod-validator": "0.4.2",
"bullmq": "5.34.3",
"handlebars": "4.7.8",
"hono": "4.6.13",
"hono": "4.6.14",
"ioredis": "5.4.1",
"nodemailer": "6.9.16",
"zod": "3.24.1"
@@ -23,6 +22,6 @@
"devDependencies": {
"@types/node": "22.10.2",
"@types/nodemailer": "6.4.17",
"bun-types": "1.1.38"
"bun-types": "1.1.40"
}
}

View File

@@ -2,11 +2,13 @@ import { createTransport } from "nodemailer";
import { Queue, Worker, QueueEvents } from "bullmq";
import { config } from "../config/env";
import Redis from "ioredis";
import { Attachment } from "nodemailer/lib/mailer";
interface EmailJob {
to: string;
subject: string;
html: string;
attachments: Attachment[];
text: string;
}
@@ -40,12 +42,13 @@ const worker = new Worker<EmailJob>(
QUEUE_NAME,
async (job) => {
await transporter.sendMail({
from: config.email.from,
from: { address: config.email.from, name: "Lauf für Kaya!" },
replyTo: config.email.replyTo,
to: job.data.to,
subject: job.data.subject,
text: job.data.text,
html: job.data.html,
attachments: job.data.attachments,
});
},
{

View File

@@ -5,7 +5,7 @@ import { z } from 'zod'
import { EmailService } from '../services/email'
import { getEmailTemplate } from '../templates'
import { Language } from '../types'
import { toBuffer } from 'bwip-js/node'
import { Attachment } from 'nodemailer/lib/mailer'
const emailRouter = new Hono()
const emailService = new EmailService()
@@ -18,25 +18,12 @@ const sendEmailSchema = z.object({
})
async function generateBarcodeDataURL(data) {
return `https://barcodeapi.org/api/128/${data}`
const buffer = await toBuffer({
bcid: 'code128',
text: data,
scale: 3,
height: 10,
includetext: true,
textxalign: 'center',
});
const base64Data = buffer.toString('base64');
const dataURL = `data:image/png;base64,${base64Data}`;
return dataURL;
return `${process.env.DOCUMENT_SERVER_URL}/v1/barcodes/code128/${data}`
}
emailRouter.post('/', bearerAuth({ token: process.env.AUTHKEY }), zValidator('json', sendEmailSchema), async (c) => {
let { to, templateName, language, data } = c.req.valid('json')
const attachments: Attachment[] = []
try {
const template = getEmailTemplate(templateName, language as Language)
if (templateName === "welcome") {
@@ -46,6 +33,12 @@ emailRouter.post('/', bearerAuth({ token: process.env.AUTHKEY }), zValidator('js
} else {
return c.json({ success: false, error: "required params 'data.name', 'data.link', 'data.barcode_content' not provided" }, 406)
}
const attachment: Attachment = {
filename: 'invite.ics',
content: `BEGIN:VCALENDAR\nVERSION:2.0\nPRODID:-//ICS Generator//NONSGML ICS Generator//DE\nBEGIN:VEVENT\nUID:1742337822408-5ghrzyi@icsgenerator.local\nDTSTAMP:20250318T224342Z\nSUMMARY:Lauf für Kaya! 2025\nDTSTART:20250523T110000Z\nDTEND:20250523T160000Z\nDESCRIPTION:Der Lauf für Kaya! 2025 findet am 23.05.2025 auf dem Sportplatz des Gymnasium Herzogenaurach statt. Zur Anmeldung einfach zum Infozelt kommen.\nLOCATION:Sportplatz Gymnasium Herzogenaurach\nBEGIN:VALARM\nACTION:DISPLAY\nDESCRIPTION:Erinnerung: Lauf für Kaya! 2025\nTRIGGER:-PT1440M\nEND:VALARM\nEND:VEVENT\nEND:VCALENDAR`,
contentType: 'text/calendar; method=REQUEST',
}
attachments.push(attachment)
}
if (templateName === "password-reset") {
if (data.token) {
@@ -59,6 +52,7 @@ emailRouter.post('/', bearerAuth({ token: process.env.AUTHKEY }), zValidator('js
data.event_name = process.env.EVENT_NAME
await emailService.sendEmail({
to,
attachments,
subject: template.subject(data),
html: template.html(data),
text: template.text(data)

View File

@@ -1,10 +1,12 @@
import { emailQueue } from '../queues/email.queue'
import { config } from '../config/env'
import { Attachment } from 'nodemailer/lib/mailer'
interface EmailOptions {
to: string
subject: string
html: string
attachments: Attachment[]
text: string
}

View File

@@ -175,8 +175,10 @@
-moz-osx-font-smoothing: grayscale;
"
>
Am Lauftag (<strong>{{event_date}}</strong>) musst du nur noch diesen
Barcode vorzeigen, damit erhältst du deine Läuferkarte.
Am Lauftag (<strong>{{event_date}}</strong>) musst du nur noch
diesen Barcode vorzeigen, damit erhältst du deine Läuferkarte.<br />Der
Bürger- & Firmenlauf findet von
<strong>13:00 bis 18:00 Uhr</strong> statt.
</p>
<table
align="center"
@@ -223,20 +225,103 @@
"
>
Deinen Registrierungs-Code, Rundenzeiten und weitere Infos kannst
du jederzeit im
<a
href="{{link}}"
rel="noopener noreferrer nofollow"
style="
color: #111827;
text-decoration: underline;
font-weight: 500;
"
target="_blank"
>Lauf für Kaya! Selfservice</a
>
einsehen.
du jederzeit im Lauf für Kaya! Selfservice einsehen.
</p>
<table
align="center"
width="100%"
border="0"
cellpadding="0"
cellspacing="0"
role="presentation"
style="max-width: 100%; text-align: center; margin-bottom: 20px"
>
<tbody>
<tr style="width: 100%">
<td>
<a
href="{{link}}"
style="
line-height: 100%;
text-decoration: none;
display: inline-block;
max-width: 100%;
mso-padding-alt: 0px;
color: #ffffff;
background-color: #3a4bdd;
border-color: #3a4bdd;
border-width: 2px;
border-style: solid;
font-size: 14px;
font-weight: 500;
border-radius: 0px;
padding: 12px 32px 12px 32px;
"
target="_blank"
><span
><!--[if mso
]><i
style="mso-font-width: 400%; mso-text-raise: 18"
hidden
>&#8202;&#8202;&#8202;&#8202;</i
><!
[endif]--></span
><span
style="
max-width: 100%;
display: inline-block;
line-height: 120%;
mso-padding-alt: 0px;
mso-text-raise: 9px;
"
>Zum Selfservice Portal</span
><span
><!--[if mso
]><i style="mso-font-width: 400%" hidden
>&#8202;&#8202;&#8202;&#8202;&#8203;</i
><!
[endif]--></span
></a
>
</td>
</tr>
</tbody>
</table>
<p
style="
font-size: 15px;
line-height: 26.25px;
margin: 0 0 20px 0;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: #374151;
"
>
Wenn der Button nicht funktioniert, kannst du diesen Link in
deinen Browser kopieren:
<code
style="
background-color: #efefef;
color: #111827;
padding: 2px 4px;
border-radius: 6px;
font-family: SFMono-Regular, Menlo, Monaco, Consolas,
'Liberation Mono', 'Courier New', monospace;
font-weight: 400;
letter-spacing: 0;
"
>{{link}}</code
>
</p>
<hr
style="
width: 100%;
border: none;
border-top: 1px solid #eaeaea;
margin-top: 32px;
margin-bottom: 32px;
"
/>
<p
style="
font-size: 15px;

View File

@@ -160,7 +160,7 @@
-moz-osx-font-smoothing: grayscale;
"
>
Thank you for registering for the {{event_name}}
Thank you for registering for the {{event_name}}
</p>
<p
style="
@@ -175,7 +175,10 @@
-moz-osx-font-smoothing: grayscale;
"
>
On the day of the run (<strong>{{event_date}}</strong>) you only have to show your barcode to receive your runner's card.
On the day of the run (<strong>{{event_date}}</strong>) you only
have to show your barcode to receive your runner's card.<br />The
Citizens' & Company Run will take place from
<strong> 1:00 p.m. to 6:00 p.m.</strong>
</p>
<table
align="center"
@@ -221,19 +224,104 @@
-moz-osx-font-smoothing: grayscale;
"
>
You can view your registration code, lap times and further information at any time from the
<a
href="{{link}}"
rel="noopener noreferrer nofollow"
You can view your registration code, lap times and further
information at any time from the Lauf für Kaya! Selfservice
</p>
<table
align="center"
width="100%"
border="0"
cellpadding="0"
cellspacing="0"
role="presentation"
style="max-width: 100%; text-align: center; margin-bottom: 20px"
>
<tbody>
<tr style="width: 100%">
<td>
<a
href="{{link}}"
style="
line-height: 100%;
text-decoration: none;
display: inline-block;
max-width: 100%;
mso-padding-alt: 0px;
color: #ffffff;
background-color: #3a4bdd;
border-color: #3a4bdd;
border-width: 2px;
border-style: solid;
font-size: 14px;
font-weight: 500;
border-radius: 0px;
padding: 12px 32px 12px 32px;
"
target="_blank"
><span
><!--[if mso
]><i
style="mso-font-width: 400%; mso-text-raise: 18"
hidden
>&#8202;&#8202;&#8202;&#8202;</i
><!
[endif]--></span
><span
style="
max-width: 100%;
display: inline-block;
line-height: 120%;
mso-padding-alt: 0px;
mso-text-raise: 9px;
"
>To the self-service portal</span
><span
><!--[if mso
]><i style="mso-font-width: 400%" hidden
>&#8202;&#8202;&#8202;&#8202;&#8203;</i
><!
[endif]--></span
></a
>
</td>
</tr>
</tbody>
</table>
<p
style="
font-size: 15px;
line-height: 26.25px;
margin: 0 0 20px 0;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: #374151;
"
>
If the button doesn't work, you can copy this link into your
browser:
<code
style="
background-color: #efefef;
color: #111827;
text-decoration: underline;
font-weight: 500;
padding: 2px 4px;
border-radius: 6px;
font-family: SFMono-Regular, Menlo, Monaco, Consolas,
'Liberation Mono', 'Courier New', monospace;
font-weight: 400;
letter-spacing: 0;
"
target="_blank"
>Lauf für Kaya! Selfservice</a
>{{link}}</code
>
</p>
<hr
style="
width: 100%;
border: none;
border-top: 1px solid #eaeaea;
margin-top: 32px;
margin-bottom: 32px;
"
/>
<p
style="
font-size: 15px;
@@ -247,7 +335,7 @@
-moz-osx-font-smoothing: grayscale;
"
>
We look forward to seeing you and to a successful Lauf für Kaya!
We look forward to seeing you and to a successful Lauf für Kaya!
</p>
<p
style="
@@ -262,7 +350,8 @@
-moz-osx-font-smoothing: grayscale;
"
>
If you have any questions, please reply to this e-mail at any time or write to
If you have any questions, please reply to this e-mail at any time
or write to
<u
><strong
><a
@@ -292,7 +381,7 @@
-moz-osx-font-smoothing: grayscale;
"
>
Sporty Greetings 🏃‍♂️<br />Your Team Lauf für Kaya!
Sporty Greetings 🏃‍♂️<br />Your Team Lauf für Kaya!
</p>
<hr
style="