mirror of
https://github.com/tomru/pfadi-bussle.git
synced 2026-03-03 14:37:13 +01:00
It also brings the problem of consolidating bookers over multiple bookings. The amount of data is not justifying having it in an own entity
217 lines
5.1 KiB
TypeScript
217 lines
5.1 KiB
TypeScript
import { Booking } from '../db/booking'
|
|
import { getBaseURL } from '../helpers/url'
|
|
import { daysFormatFrontend } from './date'
|
|
import { generateCalendarEntry } from './ical'
|
|
import { retryWithDelay } from './retryWithDelay'
|
|
|
|
const SENDGRID_API_KEY = process.env.SENDGRID_API_KEY
|
|
const ADMIN_EMAIL = process.env.ADMIN_EMAIL
|
|
const FROM_EMAIL = process.env.FROM_EMAIL
|
|
const SENDGRID_URL = 'https://api.sendgrid.com/v3/mail/send'
|
|
|
|
if (!SENDGRID_API_KEY) {
|
|
throw new Error('NO SENDGRID_API_KEY set!')
|
|
}
|
|
|
|
if (!ADMIN_EMAIL) {
|
|
throw new Error('No ADMIN_EMAIL set!')
|
|
}
|
|
|
|
if (!FROM_EMAIL) {
|
|
throw new Error('No FROM_EMAIL set!')
|
|
}
|
|
|
|
const footer = `
|
|
|
|
Viele Grüße
|
|
|
|
Thomas Ruoff
|
|
Pfadi Bussle Wart
|
|
|
|
Tel. 0151/212 253 62
|
|
`
|
|
|
|
function getReceivedBookingBookerText(booking: Booking): string {
|
|
return `Hallo liebe/r ${booking.name},
|
|
|
|
Vielen Dank für Deine Buchungsanfrage zum ${daysFormatFrontend(booking.days)}!
|
|
|
|
Nach Prüfung bestätigen wir die Buchung bald per E-Mail!
|
|
|
|
Du kannst sie jederzeit unter
|
|
|
|
${getBaseURL()}/booking/${booking.uuid}
|
|
|
|
einsehen und auch stornieren.
|
|
|
|
${footer}
|
|
`
|
|
}
|
|
|
|
function getBookingConfirmedText(booking: Booking): string {
|
|
return `Hallo liebe/r ${booking.name},
|
|
|
|
deine Buchunganfrage zum ${daysFormatFrontend(
|
|
booking.days
|
|
)} bestätigen wir gerne!
|
|
|
|
Bitte melde dich spätestens 7 Tage vor dem Buchungstermin per E-Mail oder Telefon
|
|
um eine Schlüsselübergabe zu vereinbaren.
|
|
|
|
Du kannst deine Buchung weiterhin unter ${getBaseURL()}/booking/${booking.uuid}
|
|
einsehen und stornieren.
|
|
|
|
${footer}
|
|
`
|
|
}
|
|
function getBookingRejectedText(booking: Booking): string {
|
|
return `Hallo liebe/r ${booking.name},
|
|
|
|
es tut uns leid aber deine Buchungsanfrage zum ${daysFormatFrontend(
|
|
booking.days
|
|
)} konnten wir leider nicht bestätigen.
|
|
|
|
Willst du das Bussle an einem anderen Termin buchen? Dann stelle bitte nochmal
|
|
eine Buchungsanfrage auf ${getBaseURL()}.
|
|
|
|
${footer}
|
|
`
|
|
}
|
|
|
|
function getReceivedBookingAdminText(booking: { uuid: string }): string {
|
|
return `Hallo lieber Admin,
|
|
|
|
es ging folgende Buchung ein: ${getBaseURL()}/admin/booking/${booking.uuid}
|
|
|
|
MfG`
|
|
}
|
|
|
|
export async function sendReceivedBookingAdminMail(
|
|
booking: Booking
|
|
): Promise<void> {
|
|
try {
|
|
await sendMail({
|
|
to: [{ email: ADMIN_EMAIL }],
|
|
from: { email: FROM_EMAIL, name: 'Pfadi-Bussle Wart' },
|
|
subject: `Buchung für ${booking.days} eingegangen!`,
|
|
textPlainContent: getReceivedBookingAdminText(booking),
|
|
})
|
|
} catch (error) {
|
|
console.error(
|
|
`Failed in sendReceivedBookingMail for ${booking.uuid}`,
|
|
error
|
|
)
|
|
}
|
|
}
|
|
|
|
export async function sendReceivedBookingBookerMail(
|
|
booking: Booking
|
|
): Promise<void> {
|
|
try {
|
|
await sendMail({
|
|
to: [{ email: booking.email, name: booking.name }],
|
|
from: { email: FROM_EMAIL, name: 'Pfadi-Bussle Wart' },
|
|
subject: `Deine Pfadi-Bussle Buchung ist eingegangen!`,
|
|
textPlainContent: getReceivedBookingBookerText(booking),
|
|
})
|
|
} catch (error) {
|
|
console.error(
|
|
`Failed in sendReceivedBookingMail for ${booking.uuid}`,
|
|
error
|
|
)
|
|
}
|
|
}
|
|
|
|
export async function sendBookingConfirmed(booking: Booking): Promise<void> {
|
|
try {
|
|
await sendMail({
|
|
to: [{ email: booking.email, name: booking.name }],
|
|
from: { email: FROM_EMAIL, name: 'Pfadi-Bussle Wart' },
|
|
subject: `Deine Pfadi-Bussle Buchung wurde bestätigt!`,
|
|
textPlainContent: getBookingConfirmedText(booking),
|
|
attachments: [
|
|
{
|
|
content: Buffer.from(generateCalendarEntry(booking)).toString(
|
|
'base64'
|
|
),
|
|
type: 'text/calendar',
|
|
filename: 'pfadibussle-buchung.ics',
|
|
},
|
|
],
|
|
})
|
|
} catch (error) {
|
|
console.error(
|
|
`Failed in sendBookingConfirmedMail for ${booking.uuid}`,
|
|
error
|
|
)
|
|
}
|
|
}
|
|
|
|
export async function sendBookingRejected(booking: Booking): Promise<void> {
|
|
try {
|
|
await sendMail({
|
|
to: [{ email: booking.email, name: booking.name }],
|
|
from: { email: FROM_EMAIL, name: 'Pfadi-Bussle Wart' },
|
|
subject: `Deine Pfadi-Bussle Buchung wurde abgelehnt!`,
|
|
textPlainContent: getBookingRejectedText(booking),
|
|
})
|
|
} catch (error) {
|
|
console.error(
|
|
`Failed in sendBookingRejectedMail for ${booking.uuid}`,
|
|
error
|
|
)
|
|
}
|
|
}
|
|
|
|
async function sendMail({
|
|
to,
|
|
from,
|
|
subject,
|
|
textPlainContent,
|
|
attachments,
|
|
}: {
|
|
to: { email: string; name?: string }[]
|
|
from: { email: string; name?: string }
|
|
subject: string
|
|
textPlainContent: string
|
|
attachments?: {
|
|
content: string
|
|
type?: string
|
|
filename: string
|
|
}[]
|
|
}): Promise<void> {
|
|
const data = {
|
|
personalizations: [
|
|
{
|
|
to,
|
|
},
|
|
],
|
|
from,
|
|
subject,
|
|
content: [{ type: 'text/plain', value: textPlainContent }],
|
|
attachments,
|
|
}
|
|
|
|
const fetchOptions = {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
Authorization: `Bearer ${SENDGRID_API_KEY}`,
|
|
},
|
|
body: JSON.stringify(data),
|
|
}
|
|
|
|
return retryWithDelay({
|
|
run: async () => {
|
|
const resp = await fetch(SENDGRID_URL, fetchOptions)
|
|
const bodyText = await resp.text()
|
|
|
|
if (!resp.ok) {
|
|
throw new Error(
|
|
`Failed to send mail with status ${resp.status}: ${bodyText}`
|
|
)
|
|
}
|
|
},
|
|
})
|
|
}
|