From a7d4584e07e1ad5bb7c2c54d5d96cc6febd8a0ab Mon Sep 17 00:00:00 2001 From: Caner <s86215@bht-berlin.de> Date: Tue, 4 Jul 2023 10:22:31 +0200 Subject: [PATCH] ES FUNKTIONIERT --- app/api/verification/[token]/route.ts | 61 ++++++++++++++++++++++++ app/api/{email => verifyEmail}/route.ts | 35 ++++++++++---- components/user-auth-form.tsx | 31 +----------- lib/validations/sendVerificationEmail.ts | 26 ++++++++++ prisma/schema.prisma | 16 ++++++- 5 files changed, 129 insertions(+), 40 deletions(-) create mode 100644 app/api/verification/[token]/route.ts rename app/api/{email => verifyEmail}/route.ts (50%) create mode 100644 lib/validations/sendVerificationEmail.ts diff --git a/app/api/verification/[token]/route.ts b/app/api/verification/[token]/route.ts new file mode 100644 index 0000000..9b397d6 --- /dev/null +++ b/app/api/verification/[token]/route.ts @@ -0,0 +1,61 @@ +import { db } from '@/lib/db' +import { randomUUID } from 'crypto'; +import { redirect } from 'next/navigation' +import { NextRequest } from 'next/server' + + +export async function GET( + _request: NextRequest, + { + params, + }: { + params: { token: string } + } +) { + const { token } = params + + const user = await db.user.findFirst({ + where: { + ActivationToken: { + some: { + AND: [ + { + activationDate: null, + }, + { + createdAt: { + gt: new Date(Date.now() - 24 * 60 * 60 * 1000), // 24 hours ago + }, + }, + { + token + }, + ], + }, + }, + }, + }) + + if (!user) { + throw new Error('Token is invalid or expired') + } + + await db.user.update({ + where: { + id: user.id, + }, + data: { + emailVerified: true, + }, + }) + + await db.activationToken.update({ + where: { + token, + }, + data: { + activationDate: new Date(), + }, + }) + redirect('/login') +} \ No newline at end of file diff --git a/app/api/email/route.ts b/app/api/verifyEmail/route.ts similarity index 50% rename from app/api/email/route.ts rename to app/api/verifyEmail/route.ts index 9a5f682..b333390 100644 --- a/app/api/email/route.ts +++ b/app/api/verifyEmail/route.ts @@ -1,9 +1,12 @@ import nodemailer from "nodemailer"; import { NextResponse } from "next/server"; +import { db } from "@/lib/db"; +import { randomUUID } from "crypto"; +import getURL from "@/lib/utils"; export async function POST(req: Request) { - const { email, subject, html } = await req.json(); + const { email} = await req.json(); const transporter = nodemailer.createTransport({ service: 'gmail', host: 'smtp.gmail.com', @@ -12,26 +15,38 @@ export async function POST(req: Request) { pass: process.env.NODEMAIL_PW, }, }); - + + const user = await db.user.findFirst({ + where: { + email: email + } + }); + + const token = await db.activationToken.create({ + data: { + token: `${randomUUID()}${randomUUID()}`.replace(/-/g, ''), + userId: user?.id! + }, + }); + const mailData = { - from: email, + from: process.env.NODEMAIL_MAIL, to: email, - subject: `${subject}`, - text: `${subject} | Sent from: ${email}`, - html: `${html}`, + subject: ` 'Email Verification for your GameUnity Account'`, + html: `Hello ${user?.name} Please follow the Link: ${getURL(`/api/verification/${token.token}`)} and verify your email address.`, }; let emailRes; try { emailRes = await transporter.sendMail(mailData); - + console.log("Message sent", emailRes.messageId); } catch (err) { - + console.log(err); - + console.error("Error email could not be send"); } - console.log(emailRes?.messageId) + console.log(emailRes?.messageId) return NextResponse.json({ success: true, messageId: emailRes?.messageId }); } \ No newline at end of file diff --git a/components/user-auth-form.tsx b/components/user-auth-form.tsx index 0326ca6..6b79d54 100644 --- a/components/user-auth-form.tsx +++ b/components/user-auth-form.tsx @@ -15,6 +15,7 @@ import { ToastAction } from "@/components/ui/toast" import { toast } from "@/components/ui/use-toast" import { cn } from '@/lib/utils' import { userAuthSchema } from "@/lib/validations/auth" +import { sendVerificationEmail } from "@/lib/validations/sendVerificationEmail" interface UserAuthFormProps extends HTMLAttributes<HTMLDivElement> { type: "login" | "signup" @@ -22,6 +23,7 @@ interface UserAuthFormProps extends HTMLAttributes<HTMLDivElement> { type FormData = z.infer<typeof userAuthSchema> + export function UserAuthForm({ type, className, ...props }: UserAuthFormProps) { const { register, @@ -35,36 +37,7 @@ export function UserAuthForm({ type, className, ...props }: UserAuthFormProps) { const [isGitHubLoading, setIsGitHubLoading] = useState<boolean>(false) const router = useRouter(); const searchParams = useSearchParams() - //muss noch exportiert werden - async function sendVerificationEmail(email: string) { - try { - const res = await fetch('/api/email', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - email, - subject: 'Email Verification', - html: 'Please verify your email address.', - }), - }); - - const body = await res.json(); - - if (res.ok) { - alert(`${body.message} 🚀`); - } - - if (res.status === 400) { - alert(`${body.message} 😢`); - } - } catch (err) { - console.log('Something went wrong: ', err); - } - - } async function onSubmit(data: FormData) { setIsLoading(true) diff --git a/lib/validations/sendVerificationEmail.ts b/lib/validations/sendVerificationEmail.ts new file mode 100644 index 0000000..c60337b --- /dev/null +++ b/lib/validations/sendVerificationEmail.ts @@ -0,0 +1,26 @@ + +export async function sendVerificationEmail(email: string) { + + try { + + const res = await fetch('/api/verifyEmail', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + email, + }), + }); + + if (res.ok) { + alert(`Verification Email was send 🚀,/n Please Verify your Account!`); + } + + if (res.status === 400) { + alert(`Something went wrong! Verification Email could not be send 😢`); + } + } catch (err) { + console.log('Something went wrong: ', err); + } +} \ No newline at end of file diff --git a/prisma/schema.prisma b/prisma/schema.prisma index aa9abd8..4dd200f 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -58,7 +58,7 @@ model User { name String username String? @unique email String? @unique - emailVerified DateTime? @map("email_verified") + emailVerified Boolean @default(false) password String? image String? banner String? @@ -84,9 +84,23 @@ model User { following Follows[] @relation("following") followers Follows[] @relation("follower") + ActivationToken ActivationToken[] + @@map("users") } +model ActivationToken{ + id String @id @default(cuid()) + token String @unique + activationDate DateTime? + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + userId String +} + model Follows { followerId String followingId String -- GitLab