calcom/pages/api/cancel.ts
Lola adee3fd211
Daily video calls (#542)
* ⬆️ Bump tailwindcss from 2.2.14 to 2.2.15

Bumps [tailwindcss](https://github.com/tailwindlabs/tailwindcss) from 2.2.14 to 2.2.15.
- [Release notes](https://github.com/tailwindlabs/tailwindcss/releases)
- [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/master/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/tailwindcss/compare/v2.2.14...v2.2.15)

---
updated-dependencies:
- dependency-name: tailwindcss
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* updating cal will provide a zoom meeting url

* updating cal will provide a zoom meeting url

* modifying how daily emails send

* modifying how daily emails send

* daily table

* migration updates

* daily table

* rebasing updates

* updating Daily references to a new table

* updating internal notes

* merge updates, adding Daily references to book/events.ts

* updated video email templates to remove Daily specific references

* updating the events.ts and refactoring in the event manager

* removing the package-lock

* changing calendso video powered by Daily.co to cal video powered by Daily.co

* updating some of the internal Daily notes

* added a modal for when the call/ link is invalid

* removing handle errors raw from the Daily video client

* prettier formatting fixes

* Added the Daily location to calendar events and updated Cal video references to Daily.co video

* updating references to create in event manager to check for Daily video

* fixing spacing on the cancel booking modal and adding Daily references in the event manager

* formatting fixes

* updating the readme file

* adding a daily interface in the event manager

* adding daily to the location labels

* added a note to cal event parser

* resolving yarn merge conflicts

* updating dailyReturn to DailyReturnType

* removing prettier auto and refactoring integrations: daily in the event manager

* removing changes to estlintrc.json

* updating read me formatting

* indent space for Daily ReadMe section

* resolving the merge conflicts in the yarn file

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Lola-Ojabowale <lola.ojabowale@gmail.com>
2021-10-07 17:12:39 +01:00

180 lines
5.3 KiB
TypeScript

import { BookingStatus } from "@prisma/client";
import async from "async";
import { refund } from "@ee/lib/stripe/server";
import { asStringOrNull } from "@lib/asStringOrNull";
import { getSession } from "@lib/auth";
import { CalendarEvent, deleteEvent } from "@lib/calendarClient";
import prisma from "@lib/prisma";
import { deleteMeeting } from "@lib/videoClient";
import sendPayload from "@lib/webhooks/sendPayload";
import getSubscriberUrls from "@lib/webhooks/subscriberUrls";
import { dailyDeleteMeeting } from "../../lib/dailyVideoClient";
export default async function handler(req, res) {
// just bail if it not a DELETE
if (req.method !== "DELETE" && req.method !== "POST") {
return res.status(405).end();
}
const uid = asStringOrNull(req.body.uid) || "";
const session = await getSession({ req: req });
const bookingToDelete = await prisma.booking.findUnique({
where: {
uid,
},
select: {
id: true,
userId: true,
user: {
select: {
id: true,
credentials: true,
email: true,
timeZone: true,
name: true,
},
},
attendees: true,
location: true,
references: {
select: {
uid: true,
type: true,
},
},
payment: true,
paid: true,
location: true,
title: true,
description: true,
startTime: true,
endTime: true,
uid: true,
eventTypeId: true,
},
});
if (!bookingToDelete || !bookingToDelete.user) {
return res.status(404).end();
}
if ((!session || session.user?.id != bookingToDelete.user?.id) && bookingToDelete.startTime < new Date()) {
return res.status(403).json({ message: "Cannot cancel past events" });
}
const organizer = await prisma.user.findFirst({
where: {
id: bookingToDelete.userId as number,
},
select: {
name: true,
email: true,
timeZone: true,
},
});
const evt: CalendarEvent = {
type: bookingToDelete?.title,
title: bookingToDelete?.title,
description: bookingToDelete?.description || "",
startTime: bookingToDelete?.startTime.toString(),
endTime: bookingToDelete?.endTime.toString(),
organizer: organizer,
attendees: bookingToDelete?.attendees.map((attendee) => {
const retObj = { name: attendee.name, email: attendee.email, timeZone: attendee.timeZone };
return retObj;
}),
};
// Hook up the webhook logic here
const eventTrigger = "BOOKING_CANCELLED";
// Send Webhook call if hooked to BOOKING.CANCELLED
const subscriberUrls = await getSubscriberUrls(bookingToDelete.userId, eventTrigger);
const promises = subscriberUrls.map((url) =>
sendPayload(eventTrigger, new Date().toISOString(), url, evt).catch((e) => {
console.error(`Error executing webhook for event: ${eventTrigger}, URL: ${url}`, e);
})
);
await Promise.all(promises);
// by cancelling first, and blocking whilst doing so; we can ensure a cancel
// action always succeeds even if subsequent integrations fail cancellation.
await prisma.booking.update({
where: {
uid,
},
data: {
status: BookingStatus.CANCELLED,
},
});
const apiDeletes = async.mapLimit(bookingToDelete.user.credentials, 5, async (credential) => {
const bookingRefUid = bookingToDelete.references.filter((ref) => ref.type === credential.type)[0]?.uid;
if (bookingRefUid) {
if (credential.type.endsWith("_calendar")) {
return await deleteEvent(credential, bookingRefUid);
} else if (credential.type.endsWith("_video")) {
return await deleteMeeting(credential, bookingRefUid);
}
}
//deleting a Daily meeting
const isDaily = bookingToDelete.location === "integrations:daily";
const bookingUID = bookingToDelete.references.filter((ref) => ref.type === "daily")[0]?.uid;
if (isDaily) {
return await dailyDeleteMeeting(credential, bookingUID);
}
});
if (bookingToDelete && bookingToDelete.paid) {
const evt: CalendarEvent = {
type: bookingToDelete.title,
title: bookingToDelete.title,
description: bookingToDelete.description ?? "",
startTime: bookingToDelete.startTime.toISOString(),
endTime: bookingToDelete.endTime.toISOString(),
organizer: {
email: bookingToDelete.user?.email ?? "dev@calendso.com",
name: bookingToDelete.user?.name ?? "no user",
timeZone: bookingToDelete.user?.timeZone ?? "",
},
attendees: bookingToDelete.attendees,
location: bookingToDelete.location ?? "",
};
await refund(bookingToDelete, evt);
await prisma.booking.update({
where: {
id: bookingToDelete.id,
},
data: {
rejected: true,
},
});
// We skip the deletion of the event, because that would also delete the payment reference, which we should keep
await apiDeletes;
return res.status(200).json({ message: "Booking successfully deleted." });
}
const attendeeDeletes = prisma.attendee.deleteMany({
where: {
bookingId: bookingToDelete.id,
},
});
const bookingReferenceDeletes = prisma.bookingReference.deleteMany({
where: {
bookingId: bookingToDelete.id,
},
});
await Promise.all([apiDeletes, attendeeDeletes, bookingReferenceDeletes]);
//TODO Perhaps send emails to user and client to tell about the cancellation
res.status(204).end();
}