added reminder emails for opt-in bookings
parent
a2bf242c9e
commit
f5516ed427
|
@ -32,3 +32,5 @@ EMAIL_SERVER_PORT=587
|
|||
EMAIL_SERVER_USER='<office365_emailAddress>'
|
||||
# Keep in mind that if you have 2FA enabled, you will need to provision an App Password.
|
||||
EMAIL_SERVER_PASSWORD='<office365_password>'
|
||||
# ApiKey for cronjobs
|
||||
CRON_API_KEY='0cc0e6c35519bba620c9360cfe3e68d0'
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
import dayjs, { Dayjs } from "dayjs";
|
||||
|
||||
import utc from "dayjs/plugin/utc";
|
||||
import timezone from "dayjs/plugin/timezone";
|
||||
import toArray from "dayjs/plugin/toArray";
|
||||
import localizedFormat from "dayjs/plugin/localizedFormat";
|
||||
import EventOrganizerRequestMail from "@lib/emails/EventOrganizerRequestMail";
|
||||
|
||||
dayjs.extend(utc);
|
||||
dayjs.extend(timezone);
|
||||
dayjs.extend(toArray);
|
||||
dayjs.extend(localizedFormat);
|
||||
|
||||
export default class EventOrganizerRequestReminderMail extends EventOrganizerRequestMail {
|
||||
protected getBodyHeader(): string {
|
||||
return "An event is still waiting for your approval.";
|
||||
}
|
||||
|
||||
protected getSubject(): string {
|
||||
const organizerStart: Dayjs = <Dayjs>dayjs(this.calEvent.startTime).tz(this.calEvent.organizer.timeZone);
|
||||
return `Event request is still waiting: ${this.calEvent.attendees[0].name} - ${organizerStart.format(
|
||||
"LT dddd, LL"
|
||||
)} - ${this.calEvent.type}`;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
import prisma from "@lib/prisma";
|
||||
import dayjs from "dayjs";
|
||||
import { ReminderType } from "@prisma/client";
|
||||
import EventOrganizerRequestReminderMail from "@lib/emails/EventOrganizerRequestReminderMail";
|
||||
import { CalendarEvent } from "@lib/calendarClient";
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const apiKey = req.query.apiKey;
|
||||
if (process.env.CRON_API_KEY != apiKey) {
|
||||
return res.status(401).json({ message: "Not authenticated" });
|
||||
}
|
||||
|
||||
if (req.method == "POST") {
|
||||
const reminderIntervalMinutes = [48 * 60, 24 * 60, 3 * 60];
|
||||
let notificationsSent = 0;
|
||||
for (const interval of reminderIntervalMinutes) {
|
||||
const bookings = await prisma.booking.findMany({
|
||||
where: {
|
||||
confirmed: false,
|
||||
rejected: false,
|
||||
createdAt: {
|
||||
lte: dayjs().add(-interval, "minutes").toDate(),
|
||||
},
|
||||
},
|
||||
select: {
|
||||
title: true,
|
||||
description: true,
|
||||
startTime: true,
|
||||
endTime: true,
|
||||
attendees: true,
|
||||
user: true,
|
||||
id: true,
|
||||
uid: true,
|
||||
},
|
||||
});
|
||||
|
||||
const reminders = await prisma.reminderMail.findMany({
|
||||
where: {
|
||||
reminderType: ReminderType.PENDING_BOOKING_CONFIRMATION,
|
||||
referenceId: {
|
||||
in: bookings.map((b) => b.id),
|
||||
},
|
||||
elapsedMinutes: {
|
||||
gte: interval,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
for (const booking of bookings.filter((b) => !reminders.some((r) => r.referenceId == b.id))) {
|
||||
const evt: CalendarEvent = {
|
||||
type: booking.title,
|
||||
title: booking.title,
|
||||
description: booking.description,
|
||||
startTime: booking.startTime.toISOString(),
|
||||
endTime: booking.endTime.toISOString(),
|
||||
organizer: { email: booking.user.email, name: booking.user.name, timeZone: booking.user.timeZone },
|
||||
attendees: booking.attendees,
|
||||
};
|
||||
|
||||
await new EventOrganizerRequestReminderMail(evt, booking.uid).sendEmail();
|
||||
await prisma.reminderMail.create({
|
||||
data: {
|
||||
referenceId: booking.id,
|
||||
reminderType: ReminderType.PENDING_BOOKING_CONFIRMATION,
|
||||
elapsedMinutes: interval,
|
||||
},
|
||||
});
|
||||
notificationsSent++;
|
||||
}
|
||||
}
|
||||
res.status(200).json({ notificationsSent });
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
-- CreateEnum
|
||||
CREATE TYPE "ReminderType" AS ENUM ('PENDING_BOOKING_CONFIRMATION');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "ReminderMail" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"referenceId" INTEGER NOT NULL,
|
||||
"reminderType" "ReminderType" NOT NULL,
|
||||
"elapsedMinutes" INTEGER NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
PRIMARY KEY ("id")
|
||||
);
|
|
@ -176,3 +176,15 @@ model ResetPasswordRequest {
|
|||
email String
|
||||
expires DateTime
|
||||
}
|
||||
|
||||
enum ReminderType {
|
||||
PENDING_BOOKING_CONFIRMATION
|
||||
}
|
||||
|
||||
model ReminderMail {
|
||||
id Int @id @default(autoincrement())
|
||||
referenceId Int
|
||||
reminderType ReminderType
|
||||
elapsedMinutes Int
|
||||
createdAt DateTime @default(now())
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue