* feat:show old and new email in confirmation dialog * fix: update i18n * chore: update styles * wip: change email upon verification * fix: email being changed w/o need to logout * chore: tidy up - remove update sessison (WIP) will try do this serverside * wip: try new approach of serverside call to nextjs then client update * fix: tests * fix: update without logout * i18n: use i18n in toast * fix: locale ready toast * fix: restore email api template * fix: revert changes from wrong branch * fix: restore yarn.lock * feat: happy e2e path * fix: verification disabled e2e tests * fix:move toast * chore: cleanup early return * fix: await input selector * Update apps/web/pages/auth/verify-email-change.tsx * fix: feedback * fix: update the way we update session * fix: reset password -> email verified * fix: email change toast failure * tests: add tests for error path --------- Co-authored-by: Keith Williams <keithwillcode@gmail.com> Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
98 lines
2.4 KiB
TypeScript
98 lines
2.4 KiB
TypeScript
import type { NextApiRequest, NextApiResponse } from "next";
|
|
import { z } from "zod";
|
|
|
|
import dayjs from "@calcom/dayjs";
|
|
import { WEBAPP_URL } from "@calcom/lib/constants";
|
|
import { prisma } from "@calcom/prisma";
|
|
import { userMetadata } from "@calcom/prisma/zod-utils";
|
|
|
|
const verifySchema = z.object({
|
|
token: z.string(),
|
|
});
|
|
|
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|
const { token } = verifySchema.parse(req.query);
|
|
|
|
const foundToken = await prisma.verificationToken.findFirst({
|
|
where: {
|
|
token,
|
|
},
|
|
});
|
|
|
|
if (!foundToken) {
|
|
return res.status(401).json({ message: "No token found" });
|
|
}
|
|
|
|
if (dayjs(foundToken?.expires).isBefore(dayjs())) {
|
|
return res.status(401).json({ message: "Token expired" });
|
|
}
|
|
|
|
const user = await prisma.user.findFirst({
|
|
where: {
|
|
email: foundToken?.identifier,
|
|
},
|
|
});
|
|
|
|
if (!user) {
|
|
return res.status(401).json({ message: "Cannot find a user attached to this token" });
|
|
}
|
|
|
|
const userMetadataParsed = userMetadata.parse(user.metadata);
|
|
// Attach the new email and verify
|
|
if (userMetadataParsed?.emailChangeWaitingForVerification) {
|
|
// Ensure this email isnt in use
|
|
const existingUser = await prisma.user.findUnique({
|
|
where: { email: userMetadataParsed?.emailChangeWaitingForVerification },
|
|
select: {
|
|
id: true,
|
|
},
|
|
});
|
|
|
|
if (existingUser) {
|
|
return res.status(401).json({ message: "A User already exists with this email" });
|
|
}
|
|
|
|
const updatedEmail = userMetadataParsed.emailChangeWaitingForVerification;
|
|
delete userMetadataParsed.emailChangeWaitingForVerification;
|
|
|
|
// Update and re-verify
|
|
await prisma.user.update({
|
|
where: {
|
|
id: user.id,
|
|
},
|
|
data: {
|
|
email: updatedEmail,
|
|
metadata: userMetadataParsed,
|
|
},
|
|
});
|
|
|
|
await cleanUpVerificationTokens(foundToken.id);
|
|
|
|
return res.status(200).json({
|
|
updatedEmail,
|
|
});
|
|
}
|
|
|
|
await prisma.user.update({
|
|
where: {
|
|
id: user.id,
|
|
},
|
|
data: {
|
|
emailVerified: new Date(),
|
|
},
|
|
});
|
|
|
|
const hasCompletedOnboarding = user.completedOnboarding;
|
|
|
|
return res.redirect(`${WEBAPP_URL}/${hasCompletedOnboarding ? "/event-types" : "/getting-started"}`);
|
|
}
|
|
|
|
async function cleanUpVerificationTokens(id: number) {
|
|
// Delete token from DB after it has been used
|
|
await prisma.verificationToken.delete({
|
|
where: {
|
|
id,
|
|
},
|
|
});
|
|
}
|