Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
326b241dab
|
|||
|
a7a50ff59d
|
|||
|
4257686f35
|
|||
|
2f25937a94
|
|||
|
658b8d4dd8
|
|||
|
df6381fd5e
|
|||
|
a11e5f2f3e
|
|||
|
7a8e484632
|
|||
|
1f771fb73f
|
|||
|
34c8f03571
|
|||
|
dfac4c0fe9
|
|||
|
9860014420
|
|||
|
bfce0c22c0
|
|||
|
828290d9ba
|
@@ -6,7 +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/"
|
||||
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
|
||||
28
.gitea/workflows/dev.yaml
Normal file
28
.gitea/workflows/dev.yaml
Normal file
@@ -0,0 +1,28 @@
|
||||
name: Build Latest image
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
|
||||
jobs:
|
||||
build-container:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Login to registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: registry.odit.services
|
||||
username: ${{ vars.REGISTRY_USERNAME }}
|
||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
push: true
|
||||
tags: |
|
||||
${{ vars.REGISTRY }}/lfk/mailer:dev
|
||||
${{ vars.REGISTRY }}/lfk/mailer:latest
|
||||
platforms: linux/amd64,linux/arm64
|
||||
27
.gitea/workflows/release.yaml
Normal file
27
.gitea/workflows/release.yaml
Normal file
@@ -0,0 +1,27 @@
|
||||
name: Build release images
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "*.*.*"
|
||||
|
||||
jobs:
|
||||
build-container:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Login to registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: registry.odit.services
|
||||
username: ${{ vars.REGISTRY_USERNAME }}
|
||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
push: true
|
||||
tags: |
|
||||
${{ vars.REGISTRY }}/lfk/mailer:${{ github.ref_name }}
|
||||
platforms: linux/amd64,linux/arm64
|
||||
@@ -1,33 +0,0 @@
|
||||
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:
|
||||
repo: registry.odit.services/lfk/mailer
|
||||
tags:
|
||||
- dev
|
||||
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: dev
|
||||
when:
|
||||
event: push
|
||||
@@ -1,17 +0,0 @@
|
||||
steps:
|
||||
- name: build tag
|
||||
image: woodpeckerci/plugin-docker-buildx
|
||||
settings:
|
||||
repo: registry.odit.services/lfk/mailer
|
||||
tags:
|
||||
- "${CI_COMMIT_TAG}"
|
||||
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:
|
||||
event:
|
||||
- tag
|
||||
16
Dockerfile
16
Dockerfile
@@ -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"]
|
||||
10
package.json
10
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@odit/lfk-mailer",
|
||||
"version": "1.1.0",
|
||||
"version": "1.2.3",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
@@ -11,10 +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",
|
||||
"@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"
|
||||
@@ -22,6 +22,6 @@
|
||||
"devDependencies": {
|
||||
"@types/node": "22.10.2",
|
||||
"@types/nodemailer": "6.4.17",
|
||||
"bun-types": "1.1.38"
|
||||
"bun-types": "1.1.40"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
},
|
||||
{
|
||||
|
||||
@@ -5,6 +5,7 @@ import { z } from 'zod'
|
||||
import { EmailService } from '../services/email'
|
||||
import { getEmailTemplate } from '../templates'
|
||||
import { Language } from '../types'
|
||||
import { Attachment } from 'nodemailer/lib/mailer'
|
||||
|
||||
const emailRouter = new Hono()
|
||||
const emailService = new EmailService()
|
||||
@@ -22,7 +23,7 @@ async function generateBarcodeDataURL(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") {
|
||||
@@ -32,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\nBEGIN:VEVENT\nSUMMARY:Lauf für Kaya! 2025\nDTSTART:20250523T110000Z\nDTEND:20250523T160000Z\nDTSTAMP:20250318T230306Z\nUID:lfk2025-xhJP3h\nDESCRIPTION:Der Lauf für Kaya! 2025 findet am 23.05.2025 auf dem Sportplatz des Gymnasium Herzogenaurach statt - Bürgerlauf von 14 bis 18 Uhr - Firmenlauf von 13:30 bis 18 Uhr.\nLOCATION:Sportplatz Gymnasium Herzogenaurach\nORGANIZER:info@lauf-fuer-kaya.de\nSTATUS:CONFIRMED\nPRIORITY:5\nEND:VEVENT\nEND:VCALENDAR`,
|
||||
contentType: 'text/calendar; method=REQUEST',
|
||||
}
|
||||
attachments.push(attachment)
|
||||
}
|
||||
if (templateName === "password-reset") {
|
||||
if (data.token) {
|
||||
@@ -45,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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -175,8 +175,11 @@
|
||||
-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>Bürgerlauf</b> findet von
|
||||
<b>14:00 bis 18:00 Uhr</b> statt.<br />Der
|
||||
<b>Firmenlauf</b> beginnt um <b>13:30 Uhr</b>.
|
||||
</p>
|
||||
<table
|
||||
align="center"
|
||||
@@ -223,20 +226,68 @@
|
||||
"
|
||||
>
|
||||
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
|
||||
>    </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
|
||||
>    ​</i
|
||||
><!
|
||||
[endif]--></span
|
||||
></a
|
||||
>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p
|
||||
style="
|
||||
font-size: 15px;
|
||||
|
||||
@@ -3,6 +3,9 @@ Hallo {{name}} 👋
|
||||
vielen Dank für deine Registrierung beim {{event_name}}
|
||||
|
||||
Am Lauftag ({{event_date}}) musst du nur noch deinen Barcode vorzeigen, damit erhältst du deine Läuferkarte.
|
||||
Der Bürgerlauf findet von 14:00 bis 18:00 Uhr statt.
|
||||
Der Firmenlauf beginnt um 13:30 Uhr.
|
||||
|
||||
Deinen Registrierungs-Code, Rundenzeiten und weitere Infos kannst du jederzeit im Lauf für Kaya! Selfservice unter {{link}} einsehen.
|
||||
|
||||
Wir freuen uns schon auf dich und einen erfolgreichen Lauf für Kaya!
|
||||
|
||||
@@ -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,11 @@
|
||||
-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
|
||||
<b>Citizens' Run</b> takes place from
|
||||
<b>2:00 PM to 6:00 PM</b>.<br />The <b>Corporate Run</b> begins at
|
||||
<b>1:30 PM</b>.
|
||||
</p>
|
||||
<table
|
||||
align="center"
|
||||
@@ -221,18 +225,83 @@
|
||||
-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"
|
||||
style="
|
||||
color: #111827;
|
||||
text-decoration: underline;
|
||||
font-weight: 500;
|
||||
"
|
||||
target="_blank"
|
||||
>Lauf für Kaya! Selfservice</a
|
||||
>
|
||||
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
|
||||
>    </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
|
||||
>    ​</i
|
||||
><!
|
||||
[endif]--></span
|
||||
></a
|
||||
>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p
|
||||
style="
|
||||
font-size: 15px;
|
||||
line-height: 24px;
|
||||
margin: 16px 0;
|
||||
text-align: left;
|
||||
margin-bottom: 20px;
|
||||
margin-top: 0px;
|
||||
color: #374151;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
"
|
||||
>
|
||||
We look forward to seeing you and to a successful Lauf für Kaya!
|
||||
</p>
|
||||
<p
|
||||
style="
|
||||
@@ -247,22 +316,8 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
"
|
||||
>
|
||||
We look forward to seeing you and to a successful Lauf für Kaya!
|
||||
</p>
|
||||
<p
|
||||
style="
|
||||
font-size: 15px;
|
||||
line-height: 24px;
|
||||
margin: 16px 0;
|
||||
text-align: left;
|
||||
margin-bottom: 20px;
|
||||
margin-top: 0px;
|
||||
color: #374151;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-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 +347,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="
|
||||
|
||||
@@ -3,6 +3,9 @@ Hello {{name}} 👋
|
||||
Thank you for registering for the {{event_name}}
|
||||
|
||||
On the day of the run ({{event_date}}) you only have to show your barcode to receive your runner's card.
|
||||
The Citizens' Run takes place from 2:00 PM to 6:00 PM.
|
||||
The Corporate Run begins at 1:30 PM.
|
||||
|
||||
You can view your registration code, lap times and further information at any time from the Lauf für Kaya! Selfservice at {{link}}.
|
||||
|
||||
We look forward to seeing you and to a successful Lauf für Kaya!
|
||||
|
||||
Reference in New Issue
Block a user