cal.pub0.org/apps/web/pages/api/teams/[team]/invite.ts

133 lines
3.4 KiB
TypeScript
Raw Normal View History

import { MembershipRole } from "@prisma/client";
import { randomBytes } from "crypto";
import type { NextApiRequest, NextApiResponse } from "next";
import { sendTeamInviteEmail } from "@calcom/emails";
import { getSession } from "@lib/auth";
import { BASE_URL } from "@lib/config/constants";
import prisma from "@lib/prisma";
import slugify from "@lib/slugify";
import { getTranslation } from "@server/lib/i18n";
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const t = await getTranslation(req.body.language ?? "en", "common");
if (req.method !== "POST") {
return res.status(400).json({ message: "Bad request" });
}
const session = await getSession({ req });
if (!session) {
return res.status(401).json({ message: "Not authenticated" });
}
const team = await prisma.team.findFirst({
where: {
id: parseInt(req.query.team as string),
},
});
if (!team) {
return res.status(404).json({ message: "Invalid team" });
}
const reqBody = req.body as {
usernameOrEmail: string;
role: MembershipRole;
sendEmailInvitation: boolean;
};
const { role, sendEmailInvitation } = reqBody;
// liberal email match
const isEmail = (str: string) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(str);
const usernameOrEmail = isEmail(reqBody.usernameOrEmail)
? reqBody.usernameOrEmail.toLowerCase()
: slugify(reqBody.usernameOrEmail);
const invitee = await prisma.user.findFirst({
where: {
OR: [{ username: usernameOrEmail }, { email: usernameOrEmail }],
},
});
if (!invitee) {
const email = isEmail(usernameOrEmail) ? usernameOrEmail : undefined;
if (!email) {
return res.status(400).json({
message: `Invite failed because there is no corresponding user for ${usernameOrEmail}`,
});
}
await prisma.user.create({
data: {
email,
teams: {
create: {
team: {
connect: {
id: parseInt(req.query.team as string),
},
},
role,
},
},
},
});
const token: string = randomBytes(32).toString("hex");
Feature: Verify login on signup with magic link. (#2122) * manual migration to rename verificationtoken, maybe it could be dropped and create a new table instead if we're not using it, will consult @zomars * feat: rename verificationRequest --> verificationToken in schema.prisma * fix: rename verificationRequest -> verificationToken in the codebase * feat: add default cookies for next-auth * fix: moves @lib/serverConfig to @calcom/lib so it can be called by website too * fix: make self-certificate work in dev env by not rejecting tls in serverConfig * fix verificationTokenToken typo Co-authored-by: Omar López <zomars@me.com> * Adds domain: .cal.com if not dev env in cookies * Adds default-cookies to apps/web, and nextauth_domain to turbo website build deps"a * update NEXTAUTH_DOMAIN to NEXTAUTH_COOKIE_DOMAIN * Updates website submodule * Removes deprecated env vars * Consolidates auth logic in one place * Updates website module * Signup fixes * Build fixes * Updates example * Updates example * Fixes * Fix Email Verification * fix: move csrf-token cookiePrefix from __Host -> __Secure * Removes console log * Fixes link in email template * Removed irrelevant coment * Testing with a 32 bit secret * Fixes for cookien in E2E * E2E fixes * Fixes Stripe tests locally * Temp fix for E2E Co-authored-by: Agusti Fernandez Pardo <git@agusti.me> Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> Co-authored-by: Omar López <zomars@me.com> Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
2022-04-21 20:32:25 +00:00
await prisma.verificationToken.create({
data: {
identifier: usernameOrEmail,
token,
expires: new Date(new Date().setHours(168)), // +1 week
},
});
if (session?.user?.name && team?.name) {
2022-06-06 18:59:57 +00:00
await sendTeamInviteEmail({
language: t,
from: session.user.name,
to: usernameOrEmail,
teamName: team.name,
joinLink: `${BASE_URL}/auth/signup?token=${token}&callbackUrl=/settings/teams}`,
2022-06-06 18:59:57 +00:00
});
}
return res.status(201).json({});
}
// create provisional membership
try {
await prisma.membership.create({
data: {
teamId: parseInt(req.query.team as string),
userId: invitee.id,
role,
},
});
} catch (err: any) {
if (err.code === "P2002") {
// unique constraint violation
return res.status(409).json({
message: "This user is a member of this team / has a pending invitation.",
});
} else {
throw err; // rethrow
}
}
// inform user of membership by email
if (sendEmailInvitation && session?.user?.name && team?.name) {
2022-06-06 18:59:57 +00:00
await sendTeamInviteEmail({
language: t,
from: session.user.name,
to: usernameOrEmail,
teamName: team.name,
joinLink: BASE_URL + "/settings/teams",
2022-06-06 18:59:57 +00:00
});
}
res.status(201).json({});
}