2021-09-22 19:52:38 +00:00
|
|
|
import { randomBytes } from "crypto";
|
2021-08-19 12:27:01 +00:00
|
|
|
import type { NextApiRequest, NextApiResponse } from "next";
|
2021-09-22 19:52:38 +00:00
|
|
|
|
2021-09-03 20:51:21 +00:00
|
|
|
import { getSession } from "@lib/auth";
|
2021-11-26 11:03:43 +00:00
|
|
|
import { BASE_URL } from "@lib/config/constants";
|
|
|
|
import { sendTeamInviteEmail } from "@lib/emails/email-manager";
|
|
|
|
import { TeamInvite } from "@lib/emails/templates/team-invite-email";
|
2021-10-25 13:05:21 +00:00
|
|
|
import prisma from "@lib/prisma";
|
2021-09-22 19:52:38 +00:00
|
|
|
|
2021-10-25 13:05:21 +00:00
|
|
|
import { getTranslation } from "@server/lib/i18n";
|
2021-06-05 22:53:33 +00:00
|
|
|
|
|
|
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
2021-10-25 13:05:21 +00:00
|
|
|
const t = await getTranslation(req.body.language ?? "en", "common");
|
|
|
|
|
2021-06-05 22:53:33 +00:00
|
|
|
if (req.method !== "POST") {
|
|
|
|
return res.status(400).json({ message: "Bad request" });
|
|
|
|
}
|
|
|
|
|
2021-08-19 12:27:01 +00:00
|
|
|
const session = await getSession({ req: req });
|
2021-06-05 22:53:33 +00:00
|
|
|
if (!session) {
|
2021-08-19 12:27:01 +00:00
|
|
|
return res.status(401).json({ message: "Not authenticated" });
|
2021-06-05 22:53:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const team = await prisma.team.findFirst({
|
|
|
|
where: {
|
2021-10-25 13:05:21 +00:00
|
|
|
id: parseInt(req.query.team as string),
|
2021-08-19 12:27:01 +00:00
|
|
|
},
|
2021-06-05 22:53:33 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
if (!team) {
|
2021-08-19 12:27:01 +00:00
|
|
|
return res.status(404).json({ message: "Invalid team" });
|
2021-06-05 22:53:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const invitee = await prisma.user.findFirst({
|
|
|
|
where: {
|
2021-08-19 12:27:01 +00:00
|
|
|
OR: [{ username: req.body.usernameOrEmail }, { email: req.body.usernameOrEmail }],
|
|
|
|
},
|
2021-06-05 22:53:33 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
if (!invitee) {
|
2021-06-09 21:29:31 +00:00
|
|
|
// liberal email match
|
|
|
|
const isEmail = (str: string) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(str);
|
|
|
|
|
|
|
|
if (!isEmail(req.body.usernameOrEmail)) {
|
|
|
|
return res.status(400).json({
|
2021-08-19 12:27:01 +00:00
|
|
|
message: `Invite failed because there is no corresponding user for ${req.body.usernameOrEmail}`,
|
2021-06-09 21:29:31 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
// valid email given, create User
|
2021-08-19 12:27:01 +00:00
|
|
|
await prisma.user
|
|
|
|
.create({
|
2021-06-09 21:29:31 +00:00
|
|
|
data: {
|
|
|
|
email: req.body.usernameOrEmail,
|
2021-08-19 12:27:01 +00:00
|
|
|
},
|
2021-06-09 21:29:31 +00:00
|
|
|
})
|
2021-08-19 12:27:01 +00:00
|
|
|
.then((invitee) =>
|
|
|
|
prisma.membership.create({
|
2021-06-09 21:29:31 +00:00
|
|
|
data: {
|
2021-08-19 12:27:01 +00:00
|
|
|
teamId: parseInt(req.query.team as string),
|
2021-06-09 21:29:31 +00:00
|
|
|
userId: invitee.id,
|
|
|
|
role: req.body.role,
|
|
|
|
},
|
2021-08-19 12:27:01 +00:00
|
|
|
})
|
|
|
|
);
|
2021-06-09 21:29:31 +00:00
|
|
|
|
|
|
|
const token: string = randomBytes(32).toString("hex");
|
|
|
|
|
2021-08-19 12:27:01 +00:00
|
|
|
await prisma.verificationRequest.create({
|
2021-06-09 21:29:31 +00:00
|
|
|
data: {
|
|
|
|
identifier: req.body.usernameOrEmail,
|
|
|
|
token,
|
2021-08-19 12:27:01 +00:00
|
|
|
expires: new Date(new Date().setHours(168)), // +1 week
|
|
|
|
},
|
2021-06-09 21:29:31 +00:00
|
|
|
});
|
|
|
|
|
2021-10-25 13:05:21 +00:00
|
|
|
if (session?.user?.name && team?.name) {
|
2021-11-26 11:03:43 +00:00
|
|
|
const teamInviteEvent: TeamInvite = {
|
2021-10-25 13:05:21 +00:00
|
|
|
language: t,
|
|
|
|
from: session.user.name,
|
2021-11-26 11:03:43 +00:00
|
|
|
to: req.body.usernameOrEmail,
|
2021-10-25 13:05:21 +00:00
|
|
|
teamName: team.name,
|
2021-11-26 11:03:43 +00:00
|
|
|
joinLink: `${BASE_URL}/auth/signup?token=${token}&callbackUrl=${BASE_URL + "/settings/teams"}`,
|
|
|
|
};
|
|
|
|
|
|
|
|
await sendTeamInviteEmail(teamInviteEvent);
|
2021-10-25 13:05:21 +00:00
|
|
|
}
|
2021-06-09 21:29:31 +00:00
|
|
|
|
|
|
|
return res.status(201).json({});
|
2021-06-05 22:53:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// create provisional membership
|
2021-06-07 15:12:00 +00:00
|
|
|
try {
|
2021-08-19 12:27:01 +00:00
|
|
|
await prisma.membership.create({
|
2021-06-07 15:12:00 +00:00
|
|
|
data: {
|
2021-08-19 12:27:01 +00:00
|
|
|
teamId: parseInt(req.query.team as string),
|
2021-06-07 15:12:00 +00:00
|
|
|
userId: invitee.id,
|
|
|
|
role: req.body.role,
|
|
|
|
},
|
|
|
|
});
|
2021-10-25 13:05:21 +00:00
|
|
|
} catch (err: any) {
|
2021-08-19 12:27:01 +00:00
|
|
|
if (err.code === "P2002") {
|
|
|
|
// unique constraint violation
|
2021-06-07 15:12:00 +00:00
|
|
|
return res.status(409).json({
|
2021-08-19 12:27:01 +00:00
|
|
|
message: "This user is a member of this team / has a pending invitation.",
|
2021-06-07 15:12:00 +00:00
|
|
|
});
|
|
|
|
} else {
|
|
|
|
throw err; // rethrow
|
|
|
|
}
|
2021-08-19 12:27:01 +00:00
|
|
|
}
|
2021-06-05 22:53:33 +00:00
|
|
|
|
|
|
|
// inform user of membership by email
|
2021-10-25 13:05:21 +00:00
|
|
|
if (req.body.sendEmailInvitation && session?.user?.name && team?.name) {
|
2021-11-26 11:03:43 +00:00
|
|
|
const teamInviteEvent: TeamInvite = {
|
2021-10-25 13:05:21 +00:00
|
|
|
language: t,
|
2021-06-05 22:53:33 +00:00
|
|
|
from: session.user.name,
|
2021-11-26 11:03:43 +00:00
|
|
|
to: req.body.usernameOrEmail,
|
2021-08-19 12:27:01 +00:00
|
|
|
teamName: team.name,
|
2021-11-26 11:03:43 +00:00
|
|
|
joinLink: BASE_URL + "/settings/teams",
|
|
|
|
};
|
|
|
|
|
|
|
|
await sendTeamInviteEmail(teamInviteEvent);
|
2021-06-05 22:53:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
res.status(201).json({});
|
|
|
|
}
|