From 16defb4da75bfd244613a654402f39cef2eb570d Mon Sep 17 00:00:00 2001 From: Priyanshu Date: Mon, 5 Aug 2024 15:29:07 +0530 Subject: [PATCH 1/2] Authentication completed --- app/(auth)/actions.ts | 15 +- app/(auth)/login/page.tsx | 82 ----------- .../signin/components/GoogleAuthButton.tsx | 4 +- app/(auth)/signin/components/LogInForm.tsx | 64 +++++++++ app/(auth)/signin/page.tsx | 21 +-- .../signup/components/GoogleAuthButton.tsx | 20 +++ .../components/SignInForm.tsx | 2 +- app/(auth)/signup/page.tsx | 28 ++++ app/components/Header/Header.tsx | 1 - app/globals.css | 22 +++ lib/auth.ts | 132 ++++++------------ .../20240804151601_new_changes/migration.sql | 13 ++ .../migration.sql | 2 + .../20240805092953_picture_null/migration.sql | 2 + prisma/schema.prisma | 6 +- 15 files changed, 217 insertions(+), 197 deletions(-) delete mode 100644 app/(auth)/login/page.tsx create mode 100644 app/(auth)/signin/components/LogInForm.tsx create mode 100644 app/(auth)/signup/components/GoogleAuthButton.tsx rename app/(auth)/{signin => signup}/components/SignInForm.tsx (99%) create mode 100644 app/(auth)/signup/page.tsx create mode 100644 prisma/migrations/20240804151601_new_changes/migration.sql create mode 100644 prisma/migrations/20240805092134_password_null/migration.sql create mode 100644 prisma/migrations/20240805092953_picture_null/migration.sql diff --git a/app/(auth)/actions.ts b/app/(auth)/actions.ts index 6c31485..4a9234d 100644 --- a/app/(auth)/actions.ts +++ b/app/(auth)/actions.ts @@ -26,12 +26,12 @@ const generateJWT = async (payload: JWTPayload) => { export const SigninAction = async (data: z.infer) => { try { // User validation - const isUserExist = await prisma.user.findFirst({ + const user = await prisma.user.findFirst({ where: { email: data.email } }) - if (isUserExist) return { + if (user && user.password) return { success: false, error: "Looks like you already have an account", }; @@ -48,8 +48,7 @@ export const SigninAction = async (data: z.infer) => { username: data.username, email: data.email, password: hashedPassword, - isVerified: true, - verifyToken: authToken, + isVerified: false, } }) @@ -85,14 +84,6 @@ export const LoginAction = async (data: z.infer) => { const authToken = await generateJWT({ email: data.email }); - await prisma.user.update({ - where: { id: user.id }, - data: { - isVerified: true, - verifyToken: authToken - } - }) - // Setting the cookie cookies().set('token', authToken, { httpOnly: true }); diff --git a/app/(auth)/login/page.tsx b/app/(auth)/login/page.tsx deleted file mode 100644 index 98dbaf7..0000000 --- a/app/(auth)/login/page.tsx +++ /dev/null @@ -1,82 +0,0 @@ -"use client" - -import Link from "next/link" -import { useRouter } from "next/navigation" -import { useForm } from 'react-hook-form' -import { z } from 'zod' - -import { Button } from "@/components/ui/button" -import { Input } from "@/components/ui/input" -import { Label } from "@/components/ui/label" - -import { LoginAction } from "../actions" -import { loginSchema } from '../zodSchema' -import { toast } from "sonner" - -export default function Login() { - const router = useRouter() - - const { register, handleSubmit } = useForm>(); - - const submitForm = async (data: z.infer) => { - const parsedData = loginSchema.parse({ - email: data.email, - password: data.password - }) - const response = await LoginAction(parsedData); - if(response.success){ - toast.success("login completed") - router.push('/') - }else{ - toast.error(response.error) - } - }; - - return ( - <> -
-

Login

-

- Enter your email below to login to your account -

-
-
-
- - -
-
-
- - - Forgot your password? - -
- -
- - -
-
- Don't have an account?{" "} - - Sign up - -
- - ) -} diff --git a/app/(auth)/signin/components/GoogleAuthButton.tsx b/app/(auth)/signin/components/GoogleAuthButton.tsx index 080f517..dbd8a0c 100644 --- a/app/(auth)/signin/components/GoogleAuthButton.tsx +++ b/app/(auth)/signin/components/GoogleAuthButton.tsx @@ -11,10 +11,10 @@ export default function GoogleAuthButton() { ) } diff --git a/app/(auth)/signin/components/LogInForm.tsx b/app/(auth)/signin/components/LogInForm.tsx new file mode 100644 index 0000000..4d632d1 --- /dev/null +++ b/app/(auth)/signin/components/LogInForm.tsx @@ -0,0 +1,64 @@ +import Link from "next/link" +import { useRouter } from "next/navigation" +import { useForm } from 'react-hook-form' +import { z } from 'zod' + +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { Label } from "@/components/ui/label" + +import { LoginAction } from "../../actions" +import { loginSchema } from '../../zodSchema' +import { toast } from "sonner" + + +export default function LogInForm() { + const router = useRouter() + + const { register, handleSubmit } = useForm>(); + + const submitForm = async (data: z.infer) => { + const parsedData = loginSchema.parse({ + email: data.email, + password: data.password + }) + const response = await LoginAction(parsedData); + if (response.success) { + toast.success("login completed") + router.push('/') + } else { + toast.error(response.error) + } + }; + + return ( +
+
+ + +
+
+
+ + + Forgot your password? + +
+ +
+ +
+ ) +} diff --git a/app/(auth)/signin/page.tsx b/app/(auth)/signin/page.tsx index 5846e6e..cc0cc73 100644 --- a/app/(auth)/signin/page.tsx +++ b/app/(auth)/signin/page.tsx @@ -1,25 +1,28 @@ +"use client" + import Link from "next/link" -import SignInForm from "./components/SignInForm" +import LogInForm from "./components/LogInForm" import GoogleAuthButton from "./components/GoogleAuthButton" -export default function Signup() { +export default function Login() { + return ( <>
-

Signin

+

Sign in

- Enter your credentials below to login to your account + Enter your email below to login to your account

- -

OR

+ +

OR

- Already have an account?{" "} - - Login + Don't have an account?{" "} + + Sign up

diff --git a/app/(auth)/signup/components/GoogleAuthButton.tsx b/app/(auth)/signup/components/GoogleAuthButton.tsx new file mode 100644 index 0000000..3620f53 --- /dev/null +++ b/app/(auth)/signup/components/GoogleAuthButton.tsx @@ -0,0 +1,20 @@ +"use client" + +import Image from "next/image"; +import { signIn } from "next-auth/react"; + +import Google from '@/public/google_icon.svg' +import { Button } from "@/components/ui/button"; + +export default function GoogleAuthButton() { + return ( + + ) +} diff --git a/app/(auth)/signin/components/SignInForm.tsx b/app/(auth)/signup/components/SignInForm.tsx similarity index 99% rename from app/(auth)/signin/components/SignInForm.tsx rename to app/(auth)/signup/components/SignInForm.tsx index c08e83f..cdb39e0 100644 --- a/app/(auth)/signin/components/SignInForm.tsx +++ b/app/(auth)/signup/components/SignInForm.tsx @@ -68,7 +68,7 @@ export default function CredentialsForm() { /> ) diff --git a/app/(auth)/signup/page.tsx b/app/(auth)/signup/page.tsx new file mode 100644 index 0000000..bbbc8e5 --- /dev/null +++ b/app/(auth)/signup/page.tsx @@ -0,0 +1,28 @@ +import Link from "next/link" + +import SignInForm from "./components/SignInForm" +import GoogleAuthButton from "./components/GoogleAuthButton" + +export default function Signup() { + return ( + <> +
+

Sign up

+

+ Enter your credentials below to login to your account +

+
+
+ +

OR

+ +

+ Already have an account?{" "} + + Sign in + +

+
+ + ) +} diff --git a/app/components/Header/Header.tsx b/app/components/Header/Header.tsx index 2f6be1d..b543f3c 100644 --- a/app/components/Header/Header.tsx +++ b/app/components/Header/Header.tsx @@ -49,7 +49,6 @@ export default function Header() { return (
- {JSON.stringify(session.data?.user)}
logo

Docx

diff --git a/app/globals.css b/app/globals.css index 8abdb15..a2077be 100644 --- a/app/globals.css +++ b/app/globals.css @@ -74,3 +74,25 @@ @apply bg-background text-foreground; } } + +.divider { + display: flex; + align-items: center; + text-align: center; + width: 100%; +} + +.divider::before, +.divider::after { + content: ""; + flex: 1; + border-bottom: 0.5px solid #808080; +} + +.divider:not(:empty)::before { + margin-right: 0.25em; +} + +.divider:not(:empty)::after { + margin-left: 0.25em; +} diff --git a/lib/auth.ts b/lib/auth.ts index 8980a97..db57558 100644 --- a/lib/auth.ts +++ b/lib/auth.ts @@ -1,83 +1,11 @@ -// import { cookies } from "next/headers" +import { redirect } from "next/navigation"; import { NextAuthOptions } from "next-auth"; -// import CredentialsProvider from "next-auth/providers/credentials"; import GoogleProvider from "next-auth/providers/google"; -import { JWTPayload, SignJWT, importJWK } from 'jose'; -// @ts-ignore -// import bcrypt from 'bcryptjs'; - -// import prisma from "@/prisma/prismaClient"; - -// const generateJWT = async (payload: JWTPayload) => { -// const secret = process.env.JWT_SECRET || 'secret'; -// -// const jwk = await importJWK({ k: secret, alg: 'HS256', kty: 'oct' }); -// -// const jwt = await new SignJWT(payload) -// .setProtectedHeader({ alg: 'HS256' }) -// .setIssuedAt() -// .setExpirationTime('365d') -// .sign(jwk); -// -// return jwt; -// }; +import prisma from "@/prisma/prismaClient"; export const authOptions: NextAuthOptions = { providers: [ - // CredentialsProvider({ - // name: 'Credentails', - // credentials: { - // username: { label: 'Username', type: 'text', placeholder: 'JohnDoe' }, - // name: { label: 'Name', type: 'text', placeholder: 'John Doe' }, - // email: { label: 'Email', type: 'email', placeholder: 'johndoe@email.com' }, - // password: { label: 'Password', type: 'password' }, - // }, - // async authorize(credentials: any) { - // if (!credentials || !credentials.email || !credentials.password) return null; - // - // try { - // const user = await prisma.user.findFirst({ - // where: { - // email: credentials.email - // }, - // select: { - // password: true, - // id: true, - // name: true - // } - // }) - // - // const isCorrectPassword = await bcrypt.compare(credentials.password, user?.password); - // if (user && user.password && isCorrectPassword) { - // const authToken = await generateJWT({ id: user.id }); - // - // await prisma.user.update({ - // where: { id: user.id }, - // data: { - // isVerified: true, - // verifyToken: authToken - // } - // }) - // - // // Setting the cookie - // // cookies().set('token', authToken, { httpOnly: true }); - // - // return { - // id: user.id, - // name: user.name, - // email: credentials.email, - // token: authToken, - // }; - // }; - // - // return null; - // } catch (e) { - // console.log(e); - // return null; - // } - // }, - // }), GoogleProvider({ clientId: process.env.GOOGLE_ID as string, clientSecret: process.env.GOOGLE_SECRET as string, @@ -88,23 +16,53 @@ export const authOptions: NextAuthOptions = { access_type: 'offline', response_type: 'code', } - } + }, }) ], secret: process.env.NEXTAUTH_SECRET, callbacks: { - // async jwt({ token, account }: any) { - // if (account) { - // token.accessToken = account.access_token - // } - // return token - // }, - // async session({ session, token, user }: any) { - // if (session.user) { - // session.user.id = token.uid - // } - // return session - // }, + async signIn({ account, profile }: any) { + if (account?.provider === "google") { + const { + email, + email_verified, + picture, + name, + given_name, + family_name + } = profile; + + if (!email_verified) return false; + + try { + const username = given_name + family_name; + + await prisma.user.upsert({ + where: { email }, + update: { + isVerified: true, + }, + create: { + name: name, + username: username, + email: email, + password: null, + picture, + isVerified: true, + } + }) + + return true; + } catch (e) { + console.log(e); + return false; + } + } + return true; + }, + async redirect() { + redirect("/"); + } }, cookies: { sessionToken: { diff --git a/prisma/migrations/20240804151601_new_changes/migration.sql b/prisma/migrations/20240804151601_new_changes/migration.sql new file mode 100644 index 0000000..5074792 --- /dev/null +++ b/prisma/migrations/20240804151601_new_changes/migration.sql @@ -0,0 +1,13 @@ +/* + Warnings: + + - You are about to drop the column `verifyToken` on the `User` table. All the data in the column will be lost. + - Added the required column `picture` to the `User` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "Document" ALTER COLUMN "updatedAt" SET DEFAULT CURRENT_TIMESTAMP; + +-- AlterTable +ALTER TABLE "User" DROP COLUMN "verifyToken", +ADD COLUMN "picture" TEXT NOT NULL; diff --git a/prisma/migrations/20240805092134_password_null/migration.sql b/prisma/migrations/20240805092134_password_null/migration.sql new file mode 100644 index 0000000..0b600a7 --- /dev/null +++ b/prisma/migrations/20240805092134_password_null/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "User" ALTER COLUMN "password" DROP NOT NULL; diff --git a/prisma/migrations/20240805092953_picture_null/migration.sql b/prisma/migrations/20240805092953_picture_null/migration.sql new file mode 100644 index 0000000..3f384b8 --- /dev/null +++ b/prisma/migrations/20240805092953_picture_null/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "User" ALTER COLUMN "picture" DROP NOT NULL; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 29a3e28..49ab903 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -18,9 +18,9 @@ model User { username String email String @unique name String - password String + password String? + picture String? isVerified Boolean @default(false) - verifyToken String joinedAt DateTime @default(now()) createdDocs Document[] @relation("CreatedDocuments") @@ -32,7 +32,7 @@ model Document { name String @default("Untitled Document") data String createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + updatedAt DateTime @default(now()) userId String createdBy User @relation(fields: [userId], references: [id], name: "CreatedDocuments") thumbnail String? From db5060cc95262fd27358e7f21dedaf6a96bc61b0 Mon Sep 17 00:00:00 2001 From: Priyanshu Date: Wed, 7 Aug 2024 14:58:08 +0530 Subject: [PATCH 2/2] Fixed hardcoded email address --- app/(auth)/actions.ts | 33 +++-- app/(auth)/signin/components/LogInForm.tsx | 14 +- app/(auth)/signup/components/SignInForm.tsx | 14 +- app/(auth)/zodSchema.ts | 4 +- app/actions.ts | 28 +++- app/components/Card/Card.tsx | 45 +++++-- app/components/Card/components/Options.tsx | 31 +++-- app/components/Header/Header.tsx | 14 +- app/page.tsx | 7 +- app/writer/[id]/actions.ts | 13 +- app/writer/[id]/page.tsx | 6 +- lib/auth.ts | 37 ++++-- lib/customHooks/action.ts | 12 ++ lib/customHooks/getServerSession.ts | 26 ++++ lib/customHooks/useClientSession.tsx | 48 +++++++ lib/zodSchema.ts | 18 --- package-lock.json | 134 ++------------------ package.json | 3 +- yarn.lock | 92 +------------- 19 files changed, 278 insertions(+), 301 deletions(-) create mode 100644 lib/customHooks/action.ts create mode 100644 lib/customHooks/getServerSession.ts create mode 100644 lib/customHooks/useClientSession.tsx delete mode 100644 lib/zodSchema.ts diff --git a/app/(auth)/actions.ts b/app/(auth)/actions.ts index 4a9234d..f2b1993 100644 --- a/app/(auth)/actions.ts +++ b/app/(auth)/actions.ts @@ -7,7 +7,7 @@ import { JWTPayload, SignJWT, importJWK } from 'jose'; import bcrypt from 'bcryptjs'; import prisma from "@/prisma/prismaClient"; -import { loginSchema, signinSchema } from './zodSchema'; +import { signinSchema, signupSchema } from './zodSchema'; const generateJWT = async (payload: JWTPayload) => { const secret = process.env.JWT_SECRET || 'secret'; @@ -23,7 +23,7 @@ const generateJWT = async (payload: JWTPayload) => { return jwt; }; -export const SigninAction = async (data: z.infer) => { +export const SignupAction = async (data: z.infer) => { try { // User validation const user = await prisma.user.findFirst({ @@ -40,15 +40,24 @@ export const SigninAction = async (data: z.infer) => { const salt = await bcrypt.genSalt(Number(process.env.SALT) || 10); const hashedPassword = await bcrypt.hash(data.password, salt); - const authToken = await generateJWT({ email: data.email }); - - await prisma.user.create({ - data: { + const jwtPayload = { + id: user?.id, + email: data.email, + name: user?.name, + picture: user?.picture + } + const authToken = await generateJWT(jwtPayload); + + await prisma.user.upsert({ + where: { email: data.email }, + update: { + password: hashedPassword + }, + create: { name: data.name, username: data.username, email: data.email, password: hashedPassword, - isVerified: false, } }) @@ -62,7 +71,7 @@ export const SigninAction = async (data: z.infer) => { } } -export const LoginAction = async (data: z.infer) => { +export const SigninAction = async (data: z.infer) => { try { // User validation const user = await prisma.user.findFirst({ @@ -82,7 +91,13 @@ export const LoginAction = async (data: z.infer) => { error: "Invalid credentials", } - const authToken = await generateJWT({ email: data.email }); + const jwtPayload = { + id: user?.id, + email: data.email, + name: user?.name, + picture: user?.picture + } + const authToken = await generateJWT(jwtPayload); // Setting the cookie cookies().set('token', authToken, { httpOnly: true }); diff --git a/app/(auth)/signin/components/LogInForm.tsx b/app/(auth)/signin/components/LogInForm.tsx index 4d632d1..e66c2df 100644 --- a/app/(auth)/signin/components/LogInForm.tsx +++ b/app/(auth)/signin/components/LogInForm.tsx @@ -7,22 +7,22 @@ import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" -import { LoginAction } from "../../actions" -import { loginSchema } from '../../zodSchema' +import { SigninAction } from "../../actions" +import { signinSchema } from '../../zodSchema' import { toast } from "sonner" export default function LogInForm() { const router = useRouter() - const { register, handleSubmit } = useForm>(); + const { register, handleSubmit } = useForm>(); - const submitForm = async (data: z.infer) => { - const parsedData = loginSchema.parse({ + const submitForm = async (data: z.infer) => { + const parsedData = signinSchema.parse({ email: data.email, password: data.password }) - const response = await LoginAction(parsedData); + const response = await SigninAction(parsedData); if (response.success) { toast.success("login completed") router.push('/') @@ -57,7 +57,7 @@ export default function LogInForm() { />
) diff --git a/app/(auth)/signup/components/SignInForm.tsx b/app/(auth)/signup/components/SignInForm.tsx index cdb39e0..f60e8b7 100644 --- a/app/(auth)/signup/components/SignInForm.tsx +++ b/app/(auth)/signup/components/SignInForm.tsx @@ -9,23 +9,23 @@ import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { z } from 'zod' -import { signinSchema } from '../../zodSchema' -import { SigninAction } from '../../actions' +import { signupSchema } from '../../zodSchema' +import { SignupAction } from '../../actions' export default function CredentialsForm() { const router = useRouter(); - const { register, handleSubmit } = useForm>(); + const { register, handleSubmit } = useForm>(); - const submitForm = async (data: z.infer) => { - const parsedData = signinSchema.parse({ + const submitForm = async (data: z.infer) => { + const parsedData = signupSchema.parse({ name: data.name, username: data.username, email: data.email, password: data.password }) - const response = await SigninAction(parsedData); + const response = await SignupAction(parsedData); if (response.success) { toast.success("Signin completed") router.push('/') @@ -68,7 +68,7 @@ export default function CredentialsForm() { />
) diff --git a/app/(auth)/zodSchema.ts b/app/(auth)/zodSchema.ts index 7196dc4..7fce95a 100644 --- a/app/(auth)/zodSchema.ts +++ b/app/(auth)/zodSchema.ts @@ -5,14 +5,14 @@ const passwordValidation = new RegExp( /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/ ); -export const signinSchema = z.object({ +export const signupSchema = z.object({ name: z.string().min(1).max(50), username: z.string().min(1).max(20), email: z.string().email(), password: z.string().min(1).regex(passwordValidation) }) -export const loginSchema = z.object({ +export const signinSchema = z.object({ email: z.string().email(), password: z.string() }) diff --git a/app/actions.ts b/app/actions.ts index 494c6d2..0663b01 100644 --- a/app/actions.ts +++ b/app/actions.ts @@ -3,6 +3,32 @@ import prisma from "@/prisma/prismaClient" export const GetAllDocs = async () => { - return await prisma.document.findMany({ orderBy: { updatedAt: 'desc' } }); + try { + const response = await prisma.document.findMany( + { + select: { + id: true, + thumbnail: true, + name: true, + updatedAt: true, + users: { + select: { + user: { + select: { + name: true, + picture: true + } + } + } + }, + }, + orderBy: { updatedAt: 'desc' } + } + ); + return response; + } catch (e) { + console.log(e); + return null; + } } diff --git a/app/components/Card/Card.tsx b/app/components/Card/Card.tsx index aec567f..96bd4a5 100644 --- a/app/components/Card/Card.tsx +++ b/app/components/Card/Card.tsx @@ -1,6 +1,6 @@ 'use client' -import { useCallback, useEffect, useMemo, useRef, useState } from "react" +import { useCallback, useMemo, useRef, useState } from "react" import { useRouter } from "next/navigation" import { @@ -15,27 +15,47 @@ import prettifyDate from '@/helpers/prettifyDates' import CardOptions from "./components/Options" import { debounce } from "lodash" import { RenameDocument } from "./actions" +import useClientSession from "@/lib/customHooks/useClientSession" type DocCardPropType = { docId: string; thumbnail: string | null; title: string; - createdAt: Date + updatedAt: Date + users: { + user: + { + name: string, + picture: string | null + } + }[] } -export default function DocCard({ docId, thumbnail, title, createdAt }: DocCardPropType) { +export default function DocCard({ docId, thumbnail, title, updatedAt, users }: DocCardPropType) { const router = useRouter(); + const session = useClientSession(); + const inputRef = useRef(null); const [name, setName] = useState(title) const saveName = useCallback(async () => { if (!inputRef.current) return; - await RenameDocument(docId, "bartwalpriyanshu@gmail.com", inputRef.current.value); + const email = session.email; + if (!email) return; + + await RenameDocument(docId, email, inputRef.current.value); }, []) const debounceSaveName = useMemo(() => debounce(saveName, 2000), [saveName]) + const getInitials = (name: string) => { + let initials = name.split(" "); + + if (initials.length > 2) return initials[0][0] + initials[1][0]; + return initials[0][0]; + } + return (
- - - CN - -

{prettifyDate(String(createdAt))}

+ {users.map((e, index) => { + return ( + + { + e.user.picture + ? + : {getInitials(e.user.name)} + } + + ) + })} +

{prettifyDate(String(updatedAt))}

diff --git a/app/components/Card/components/Options.tsx b/app/components/Card/components/Options.tsx index 489830f..917384a 100644 --- a/app/components/Card/components/Options.tsx +++ b/app/components/Card/components/Options.tsx @@ -24,6 +24,7 @@ import { import { Button } from "@/components/ui/button" import { DeleteDocument } from "../actions" +import useClientSession from "@/lib/customHooks/useClientSession" type CardOptionsPropType = { docId: string, @@ -33,6 +34,8 @@ type CardOptionsPropType = { export default function CardOptions({ docId, inputRef }: CardOptionsPropType) { const router = useRouter(); + const session = useClientSession(); + const docOptions = [ { icon: Type, color: "#60b7c3", title: "Rename", onClick: () => renameDocument() }, { icon: FilePenLine, color: "#48acf9", title: "Edit", onClick: () => editDocument() }, @@ -57,6 +60,17 @@ export default function CardOptions({ docId, inputRef }: CardOptionsPropType) { setIsOptionsOpen(false); setIsOpen(true); } + + const confirmDeleteDocument = async () => { + const email = session.email; + if (!email) return; + const response = await DeleteDocument(email, docId); + if (response.success) { + toast.success(response.data) + } else { + toast.error(response.error) + } + } return ( <> @@ -69,7 +83,7 @@ export default function CardOptions({ docId, inputRef }: CardOptionsPropType) { > {docOptions.map((item) => { return ( - @@ -88,14 +102,13 @@ export default function CardOptions({ docId, inputRef }: CardOptionsPropType) {
- +
diff --git a/app/components/Header/Header.tsx b/app/components/Header/Header.tsx index b543f3c..2edd739 100644 --- a/app/components/Header/Header.tsx +++ b/app/components/Header/Header.tsx @@ -1,9 +1,9 @@ "use client" + import { useState } from "react" import { useRouter } from "next/navigation" import Image from "next/image" import { toast } from "sonner" -import { signOut, useSession } from 'next-auth/react' import { CloudUpload, LogOut, @@ -13,19 +13,23 @@ import { import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" -import logo from "@/public/doc.svg" import { CreateNewDocument, LogoutAction } from "./actions" +import logo from "@/public/doc.svg" +import useClientSession from "@/lib/customHooks/useClientSession" export default function Header() { const router = useRouter(); - const session = useSession(); + const session = useClientSession(); const [isLoading, setIsLoading] = useState(false); const createDocument = async () => { setIsLoading(true); - const email = "bartwalpriyanshu@gmail.com" + + const email = session.email; + if(!email) return; + const response = await CreateNewDocument(email) if (response.success) { setIsLoading(false); @@ -41,7 +45,7 @@ export default function Header() { const response = await LogoutAction(); if (response.success) { toast.success("Successfully logged out") - router.push('/api/auth/signin') + router.push('/signup') } else { toast.error(response.error) } diff --git a/app/page.tsx b/app/page.tsx index 0a9ba36..32346bb 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -11,14 +11,15 @@ export default async function Home() {
- {data && data.map((doc) => { + {data && data.map((doc, index) => { return ( ) })} diff --git a/app/writer/[id]/actions.ts b/app/writer/[id]/actions.ts index 27367f3..31221f7 100644 --- a/app/writer/[id]/actions.ts +++ b/app/writer/[id]/actions.ts @@ -25,9 +25,16 @@ export const UpdateDocData = async (id: any, data: string) => { error: "Document does not exist", } - await prisma.document.update({ where: { id }, data: { data: data } }) - - return { success: true, data: "Saved"} + await prisma.document.update( + { + where: { id }, + data: { + data: data, + updatedAt: Date(), + } + }) + + return { success: true, data: "Saved" } } catch (e) { console.log(e) return { success: false, error: "Internal server error" } diff --git a/app/writer/[id]/page.tsx b/app/writer/[id]/page.tsx index 0f525bd..64e31cd 100644 --- a/app/writer/[id]/page.tsx +++ b/app/writer/[id]/page.tsx @@ -16,7 +16,6 @@ import Tabs from "./components/Tabs" import Loading from './components/EditorLoading' import { extensions, props } from './editor/editorConfig' -// import { editor } from './editor'; import { GetDocDetails, UpdateDocData, UpdateThumbnail } from './actions' import { toast } from 'sonner' @@ -31,8 +30,9 @@ export default function Dashboard() { queryFn: async () => { const response = await GetDocDetails(params.id); if (response.success) { - response.data?.data && + if (response.data?.data) { setDocData(JSON.parse(response.data?.data)); + } return response.data; } else { return null; @@ -109,7 +109,7 @@ export default function Dashboard() {
{Options[option]} - {!data?.data ? : + {!data ? : } diff --git a/lib/auth.ts b/lib/auth.ts index db57558..609a101 100644 --- a/lib/auth.ts +++ b/lib/auth.ts @@ -1,4 +1,3 @@ -import { redirect } from "next/navigation"; import { NextAuthOptions } from "next-auth"; import GoogleProvider from "next-auth/providers/google"; @@ -60,21 +59,33 @@ export const authOptions: NextAuthOptions = { } return true; }, - async redirect() { - redirect("/"); - } - }, - cookies: { - sessionToken: { - name: 'token', - options: { - httpOnly: true, - sameSite: 'lax', - path: '/', - secure: true + redirect({ baseUrl }) { + return baseUrl; + }, + jwt: ({ user, token }: any) => { + if (user) { + token.uid = user.id; + } + return token; + }, + session: ({ session, token }: any) => { + if (session.user) { + session.user.id = token.uid } + return session } }, + // cookies: { + // sessionToken: { + // name: 'token', + // options: { + // httpOnly: true, + // sameSite: 'lax', + // path: '/', + // secure: true + // } + // } + // }, pages: { signIn: "/signin", }, diff --git a/lib/customHooks/action.ts b/lib/customHooks/action.ts new file mode 100644 index 0000000..7996685 --- /dev/null +++ b/lib/customHooks/action.ts @@ -0,0 +1,12 @@ +"use server" + +import { jwtDecode } from "jwt-decode"; +import { cookies } from "next/headers"; + +export async function GetUserDetails() { + const token = cookies().get('token')?.value; + + if (!token) return; + const decoded = jwtDecode(token!); + return decoded; +} diff --git a/lib/customHooks/getServerSession.ts b/lib/customHooks/getServerSession.ts new file mode 100644 index 0000000..3f8d08b --- /dev/null +++ b/lib/customHooks/getServerSession.ts @@ -0,0 +1,26 @@ +// @ts-nocheck +import { getServerSession as nextAuthSession } from "next-auth"; + +import { ReturnType } from "./useClientSession"; +import { GetUserDetails } from "./action"; + +export default async function getServerSession(): Promise { + const session = await nextAuthSession(); + + if (session) return { + id: "", + name: session.user?.name, + email: session.user?.email, + image: session.user?.image + }; + + console.log("........................here...............................................") + const userDetails = await GetUserDetails(); + return { + id: userDetails?.id, + name: userDetails?.name, + email: userDetails?.email, + image: userDetails?.picture, + }; + +} diff --git a/lib/customHooks/useClientSession.tsx b/lib/customHooks/useClientSession.tsx new file mode 100644 index 0000000..706350b --- /dev/null +++ b/lib/customHooks/useClientSession.tsx @@ -0,0 +1,48 @@ +// @ts-nocheck +"use client" + +import { useEffect, useState } from 'react' +import { useSession } from "next-auth/react" + +import { GetUserDetails } from './action'; + +export type ReturnType = { + id: string | null | undefined, + name: string | null | undefined, + email: string | null | undefined, + image: string | null | undefined +} +export default function useClientSession(): ReturnType { + const [user, setUser] = useState(null); + + const { data: session } = useSession(); + + useEffect(() => { + (async () => { + if (!session) { + const userDetails = await GetUserDetails(); + setUser({ + id: userDetails?.id, + name: userDetails?.name, + email: userDetails?.email, + image: userDetails?.picture, + }); + }; + })(); + }, [session]) + + if (session) return { + id: "", + name: session.user?.name, + email: session.user?.email, + image: session.user?.image + }; + console.log("Credentials") + + return { + id: user?.id, + name: user?.name, + email: user?.email, + image: user?.image, + }; +} diff --git a/lib/zodSchema.ts b/lib/zodSchema.ts deleted file mode 100644 index 2900913..0000000 --- a/lib/zodSchema.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { z } from 'zod' - -// Minimum 8 characters, at least one uppercase letter, one lowercase letter, one number and one special character -const passwordValidation = new RegExp( - /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/ -); - -export const signupSchema = z.object({ - name: z.string().min(1).max(50), - username: z.string().min(1).max(20), - email: z.string().email(), - password: z.string().min(1).regex(passwordValidation) -}) - -export const loginSchema = z.object({ - email: z.string().email(), - password: z.string() -}) diff --git a/package-lock.json b/package-lock.json index bb4c4ae..fd9bd4c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,14 +32,13 @@ "@tiptap/pm": "^2.4.0", "@tiptap/react": "^2.4.0", "@tiptap/starter-kit": "^2.4.0", - "@types/jsonwebtoken": "^9.0.6", "axios": "^1.7.2", "bcryptjs": "^2.4.3", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "html2canvas": "^1.4.1", "jose": "^5.6.3", - "jsonwebtoken": "^9.0.2", + "jwt-decode": "^4.0.0", "lodash": "^4.17.21", "lucide-react": "^0.400.0", "next": "14.2.4", @@ -1943,15 +1942,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/jsonwebtoken": { - "version": "9.0.6", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.6.tgz", - "integrity": "sha512-/5hndP5dCjloafCXns6SZyESp3Ldq7YjH3zwzwczYnjxIT0Fqzk5ROSYVGfFyczIue7IUEj8hkvLbPoLQ18vQw==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/lodash": { "version": "4.17.6", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.6.tgz", @@ -1962,6 +1952,7 @@ "version": "20.14.9", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.9.tgz", "integrity": "sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==", + "dev": true, "license": "MIT", "dependencies": { "undici-types": "~5.26.4" @@ -2655,12 +2646,6 @@ "node": ">=8" } }, - "node_modules/buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", - "license": "BSD-3-Clause" - }, "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -3133,15 +3118,6 @@ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "license": "MIT" }, - "node_modules/ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "license": "Apache-2.0", - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -4934,28 +4910,6 @@ "json5": "lib/cli.js" } }, - "node_modules/jsonwebtoken": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", - "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", - "license": "MIT", - "dependencies": { - "jws": "^3.2.2", - "lodash.includes": "^4.3.0", - "lodash.isboolean": "^3.0.3", - "lodash.isinteger": "^4.0.4", - "lodash.isnumber": "^3.0.3", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.once": "^4.0.0", - "ms": "^2.1.1", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=12", - "npm": ">=6" - } - }, "node_modules/jsx-ast-utils": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", @@ -4972,25 +4926,12 @@ "node": ">=4.0" } }, - "node_modules/jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", - "license": "MIT", - "dependencies": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "license": "MIT", - "dependencies": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" + "node_modules/jwt-decode": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz", + "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==", + "engines": { + "node": ">=18" } }, "node_modules/keyv": { @@ -5089,40 +5030,11 @@ "dev": true, "license": "MIT" }, - "node_modules/lodash.includes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", - "license": "MIT" - }, - "node_modules/lodash.isboolean": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", - "license": "MIT" - }, - "node_modules/lodash.isinteger": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", - "license": "MIT" - }, - "node_modules/lodash.isnumber": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", - "license": "MIT" - }, "node_modules/lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", - "license": "MIT" - }, - "node_modules/lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "dev": true, "license": "MIT" }, "node_modules/lodash.merge": { @@ -5132,12 +5044,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", - "license": "MIT" - }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -6641,26 +6547,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "node_modules/safe-regex-test": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", @@ -6692,6 +6578,7 @@ "version": "7.6.2", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -7428,6 +7315,7 @@ "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true, "license": "MIT" }, "node_modules/uri-js": { diff --git a/package.json b/package.json index ada2b4a..ab4e3d7 100644 --- a/package.json +++ b/package.json @@ -37,14 +37,13 @@ "@tiptap/pm": "^2.4.0", "@tiptap/react": "^2.4.0", "@tiptap/starter-kit": "^2.4.0", - "@types/jsonwebtoken": "^9.0.6", "axios": "^1.7.2", "bcryptjs": "^2.4.3", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "html2canvas": "^1.4.1", "jose": "^5.6.3", - "jsonwebtoken": "^9.0.2", + "jwt-decode": "^4.0.0", "lodash": "^4.17.21", "lucide-react": "^0.400.0", "next": "14.2.4", diff --git a/yarn.lock b/yarn.lock index e2dd211..5d1122d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -844,19 +844,12 @@ resolved "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== -"@types/jsonwebtoken@^9.0.6": - version "9.0.6" - resolved "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.6.tgz" - integrity sha512-/5hndP5dCjloafCXns6SZyESp3Ldq7YjH3zwzwczYnjxIT0Fqzk5ROSYVGfFyczIue7IUEj8hkvLbPoLQ18vQw== - dependencies: - "@types/node" "*" - "@types/lodash@^4.17.6": version "4.17.6" resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.6.tgz" integrity sha512-OpXEVoCKSS3lQqjx9GGGOapBeuW5eUboYHRlHP9urXPX25IKZ6AnP5ZRxtVf63iieUbsHxLn8NQ5Nlftc6yzAA== -"@types/node@*", "@types/node@^20": +"@types/node@^20": version "20.14.9" resolved "https://registry.npmjs.org/@types/node/-/node-20.14.9.tgz" integrity sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg== @@ -1242,11 +1235,6 @@ braces@^3.0.3, braces@~3.0.2: dependencies: fill-range "^7.1.1" -buffer-equal-constant-time@1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz" - integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA== - busboy@1.6.0: version "1.6.0" resolved "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz" @@ -1539,13 +1527,6 @@ eastasianwidth@^0.2.0: resolved "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz" integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== -ecdsa-sig-formatter@1.0.11: - version "1.0.11" - resolved "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz" - integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== - dependencies: - safe-buffer "^5.0.1" - emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" @@ -2530,22 +2511,6 @@ json5@^1.0.2: dependencies: minimist "^1.2.0" -jsonwebtoken@^9.0.2: - version "9.0.2" - resolved "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz" - integrity sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ== - dependencies: - jws "^3.2.2" - lodash.includes "^4.3.0" - lodash.isboolean "^3.0.3" - lodash.isinteger "^4.0.4" - lodash.isnumber "^3.0.3" - lodash.isplainobject "^4.0.6" - lodash.isstring "^4.0.1" - lodash.once "^4.0.0" - ms "^2.1.1" - semver "^7.5.4" - "jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.5: version "3.3.5" resolved "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz" @@ -2556,22 +2521,10 @@ jsonwebtoken@^9.0.2: object.assign "^4.1.4" object.values "^1.1.6" -jwa@^1.4.1: - version "1.4.1" - resolved "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz" - integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== - dependencies: - buffer-equal-constant-time "1.0.1" - ecdsa-sig-formatter "1.0.11" - safe-buffer "^5.0.1" - -jws@^3.2.2: - version "3.2.2" - resolved "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz" - integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== - dependencies: - jwa "^1.4.1" - safe-buffer "^5.0.1" +jwt-decode@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz" + integrity sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA== keyv@^4.5.3: version "4.5.4" @@ -2634,46 +2587,16 @@ lodash.castarray@^4.4.0: resolved "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz" integrity sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q== -lodash.includes@^4.3.0: - version "4.3.0" - resolved "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz" - integrity sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w== - -lodash.isboolean@^3.0.3: - version "3.0.3" - resolved "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz" - integrity sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg== - -lodash.isinteger@^4.0.4: - version "4.0.4" - resolved "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz" - integrity sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA== - -lodash.isnumber@^3.0.3: - version "3.0.3" - resolved "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz" - integrity sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw== - lodash.isplainobject@^4.0.6: version "4.0.6" resolved "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz" integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== -lodash.isstring@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz" - integrity sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw== - lodash.merge@^4.6.2: version "4.6.2" resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lodash.once@^4.0.0: - version "4.1.1" - resolved "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz" - integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg== - lodash@^4.17.21: version "4.17.21" resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" @@ -3515,11 +3438,6 @@ safe-array-concat@^1.1.2: has-symbols "^1.0.3" isarray "^2.0.5" -safe-buffer@^5.0.1: - version "5.2.1" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - safe-regex-test@^1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz"