* WIP close.com app * Removing leaked dev key (now invalid) * Misspelled env variable * Making progress still WIP * Progress + tests * Final touches * More unit tests * Finished up tests * Merge main * Removing unneeded stuff + submodules * Removing static props, fields fix * Removing unneeded stuff p2 * Commenting * Refactoring Close.com Calendar Service + initial structure * Progress con CloseComService * Standarizing APIs * Zodifying * Expanding sync services * Sendgrid Sync Service * using own request for sendgrid + debug logs * Making get last booking work for console * Helpscout dynamic app API * Standarizing calls + adding call from booking creation * Strategy change for last booking * Strategy change for last booking on help scout api * Fixing failing build * Implementing user deletion * Fix linting + slight cleaning * Undoing eslint disable * Removing more unsupported eslint properties * Closecom as non-standard sync service * Finishing closecom lead operations * Fixing lint * Guarding app from sync services * Reverting submodules * Applying PR feedback * Reverting API to be plain handler * Cleaning notes Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> Co-authored-by: zomars <zomars@me.com>
90 lines
2.4 KiB
TypeScript
90 lines
2.4 KiB
TypeScript
import { createHmac } from "crypto";
|
|
import type { NextApiRequest, NextApiResponse } from "next";
|
|
import getRawBody from "raw-body";
|
|
import z from "zod";
|
|
|
|
import { default as webPrisma } from "@calcom/prisma";
|
|
|
|
export const config = {
|
|
api: {
|
|
bodyParser: false,
|
|
},
|
|
};
|
|
|
|
const helpscoutRequestBodySchema = z.object({
|
|
customer: z.object({
|
|
email: z.string().email(),
|
|
}),
|
|
});
|
|
|
|
/**
|
|
* API for Helpscout to retrieve key information about a user from a ticket
|
|
* Note: HelpScout expects a JSON with a `html` prop to show its content as HTML
|
|
*/
|
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|
if (req.method !== "POST") return res.status(405).json({ message: "Method not allowed" });
|
|
|
|
const hsSignature = req.headers["x-helpscout-signature"];
|
|
if (!hsSignature) return res.status(400).end();
|
|
|
|
if (!process.env.CALENDSO_ENCRYPTION_KEY) return res.status(500).end();
|
|
|
|
const rawBody = await getRawBody(req);
|
|
const parsedBody = helpscoutRequestBodySchema.safeParse(JSON.parse(rawBody.toString()));
|
|
|
|
if (!parsedBody.success) return res.status(400).end();
|
|
|
|
const calculatedSig = createHmac("sha1", process.env.CALENDSO_ENCRYPTION_KEY)
|
|
.update(rawBody)
|
|
.digest("base64");
|
|
|
|
if (req.headers["x-helpscout-signature"] !== calculatedSig) return res.status(400).end();
|
|
|
|
const user = await webPrisma.user.findFirst({
|
|
where: {
|
|
email: parsedBody.data.customer.email,
|
|
},
|
|
select: {
|
|
username: true,
|
|
id: true,
|
|
plan: true,
|
|
createdDate: true,
|
|
},
|
|
});
|
|
|
|
if (!user) return res.status(200).json({ html: "User not found" });
|
|
|
|
const lastBooking = await webPrisma.attendee.findFirst({
|
|
where: {
|
|
email: parsedBody.data.customer.email,
|
|
},
|
|
select: {
|
|
booking: {
|
|
select: {
|
|
createdAt: true,
|
|
},
|
|
},
|
|
},
|
|
orderBy: {
|
|
booking: {
|
|
createdAt: "desc",
|
|
},
|
|
},
|
|
});
|
|
|
|
return res.status(200).json({
|
|
html: `
|
|
<ul>
|
|
<li><b>Username:</b> ${user.username}</li>
|
|
<li><b>Last booking:</b> ${
|
|
lastBooking && lastBooking.booking
|
|
? new Date(lastBooking.booking.createdAt).toLocaleDateString("en-US")
|
|
: "No info"
|
|
}</li>
|
|
<li><b>Plan:</b> ${user.plan}</li>
|
|
<li><b>Account created:</b> ${new Date(user.createdDate).toLocaleDateString("en-US")}</li>
|
|
</ul>
|
|
`,
|
|
});
|
|
}
|