├── .DS_Store ├── .gitignore ├── LICENSE ├── README.md ├── nextauth-node.png ├── nextauth ├── .env.development ├── .eslintrc.json ├── README.md ├── jsconfig.json ├── next.config.js ├── package-lock.json ├── package.json ├── public │ ├── avatar3.png │ ├── bg.jpg │ ├── logo-sm.png │ ├── next copy.svg │ ├── next.svg │ ├── post1.jpg │ ├── post10.jpg │ ├── post2.jpg │ ├── post3.jpg │ ├── post4.jpg │ ├── post5.jpg │ ├── post6.jpg │ ├── post7.jpg │ ├── post8.jpg │ ├── post9.jpg │ ├── vercel copy.svg │ └── vercel.svg └── src │ ├── app │ ├── (site) │ │ ├── articles │ │ │ ├── error.js │ │ │ └── page.js │ │ ├── auth │ │ │ └── error │ │ │ │ └── page.js │ │ ├── blog │ │ │ ├── error.js │ │ │ └── page.js │ │ ├── create-post │ │ │ └── page.js │ │ ├── dashboard │ │ │ ├── error.js │ │ │ └── page.js │ │ ├── posts │ │ │ ├── error.js │ │ │ └── page.js │ │ ├── profile │ │ │ ├── error.js │ │ │ └── page.js │ │ └── users │ │ │ ├── error.js │ │ │ └── page.js │ ├── api │ │ ├── auth │ │ │ ├── [...nextauth] │ │ │ │ └── route.js │ │ │ └── authOptions.js │ │ ├── posts │ │ │ └── route.js │ │ └── users │ │ │ └── route.js │ ├── favicon.ico │ ├── globals.css │ ├── layout.js │ ├── page.js │ └── page.module.css │ ├── components │ ├── atoms │ │ ├── MainNavbar.js │ │ ├── PostCard.js │ │ ├── SiteNavbar.js │ │ ├── UsersTable.js │ │ └── index.js │ └── pages │ │ ├── AdminDashboard.js │ │ ├── ArticlePage.js │ │ ├── CreatePostPage.js │ │ ├── DashboardPage.js │ │ ├── HomePage.js │ │ ├── ProfilePage.js │ │ ├── UserDashboard.js │ │ ├── UsersPage.js │ │ └── index.js │ ├── config │ └── index.js │ ├── db │ └── index.js │ ├── hooks │ └── index.js │ ├── lib │ ├── post.js │ └── user.js │ ├── middleware.js │ ├── models │ ├── account.js │ ├── index.js │ ├── post.js │ └── user.js │ ├── providers │ └── index.js │ ├── schema │ └── index.js │ ├── services │ ├── index.js │ ├── post.js │ └── user.js │ ├── utils │ └── index.js │ └── validators │ └── index.js └── node-server ├── .env ├── controllers ├── index.mjs └── post.mjs ├── db └── index.mjs ├── index.mjs ├── middleware └── index.mjs ├── models ├── index.mjs ├── post.mjs └── user.mjs ├── package-lock.json ├── package.json ├── routes ├── index.mjs └── post.mjs ├── services ├── index.mjs └── post.mjs └── utils └── index.mjs /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torver213/nextjs14-nextauth-nodeapi-yt/9a4fb7475526c40364187b14428d9b97e07e74f6/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /nextauth/node_modules 5 | /nextauth/.pnp 6 | /nextauth/.pnp.js 7 | /nextauth/.yarn/install-state.gz 8 | 9 | # testing 10 | /nextauth/coverage 11 | 12 | # next.js 13 | /nextauth/.next/ 14 | /nextauth/out/ 15 | 16 | # production 17 | /nextauth/build 18 | 19 | # misc 20 | /nextauth/.DS_Store 21 | /nextauth/*.pem 22 | 23 | # debug 24 | /nextauth/npm-debug.log* 25 | /nextauth/yarn-debug.log* 26 | /nextauth/yarn-error.log* 27 | 28 | # local env files 29 | /nextauth/.env*.local 30 | 31 | # vercel 32 | /nextauth/.vercel 33 | 34 | # typescript 35 | /nextauth/*.tsbuildinfo 36 | /nextauth/next-env.d.ts 37 | 38 | /node-server/node_modules -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Peter Kelvin Torver 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Nextjs 14 Authentication and Role-Based Authorization Using NextAuth with Mongodb Adapter](./nextauth-node.png) 2 | 3 | ## 🚀 Nextjs 14 Authentication and Role-Based Authorization Using NextAuth with Mongodb Adapter 4 | 5 | 🔥 Key Highlights: 6 | 7 | - Seamlessly integrate NextAuth into your NextJS 14 application 8 | - Harness the power of MongoDB as your authentication backend 9 | - Implement role-based authorization to control user access 10 | - Elevate your NextJS development skills to the next level 11 | - Connect Nextjs App with Node server 12 | - Implement Access and refresh token functionality 13 | - Implement nextauth jwt & database strategy with database adapter 14 | 15 | 🎯 Target Audience: 16 | 17 | - Developers seeking to enhance their NextJS authentication skills 18 | - Aspiring web security specialists 19 | - Enthusiasts eager to explore MongoDB's authentication capabilities 20 | 21 | 🔒🚦 Embrace the Future of Secure NextJS Development 22 | 23 | Embark on a comprehensive journey with our exclusive guide to NextJS 14 Authentication & Role-Based Authorization! This tutorial unpacks the power of NextAuth and MongoDb Adapter, Json web token, empowering you to seamlessly implement authentication and robust role-based authorization within your NextJS 14 applications. 24 | 25 | 🔐 Learn Effortless User Authentication: 26 | Discover how to set up secure user authentication effortlessly. 27 | 28 | 🔑 Flawless Role-Based Access Control: 29 | Implement role-based access control to manage user permissions seamlessly. 30 | 31 | 🛠️ Harness NextJS 14, NextAuth, and MongoDb Adapter: 32 | Leverage their potential for a smooth, secure, and scalable user authentication experience. 33 | 34 | Whether you're a beginner or an experienced developer, this video equips you with the tools and knowledge to effortlessly implement advanced authentication and authorization features in your NextJS projects! Level up your NextJS game and don't miss this opportunity to enhance your skills! 📈💥 35 | 36 | 🎬🔓 **Watch now and unlock the world of seamless authentication and role-based authorization in NextJS 14!** Don't forget to like, subscribe, and share this knowledge with fellow developers! 🌟🚀 -------------------------------------------------------------------------------- /nextauth-node.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torver213/nextjs14-nextauth-nodeapi-yt/9a4fb7475526c40364187b14428d9b97e07e74f6/nextauth-node.png -------------------------------------------------------------------------------- /nextauth/.env.development: -------------------------------------------------------------------------------- 1 | NEXT_PUBLIC_BASE_URL = "http://localhost:3000/api" 2 | 3 | NEXT_PUBLIC_BASE_NODE_URL = "http://localhost:5001/api" 4 | 5 | NEXTAUTH_URL = "http://localhost:3000" 6 | 7 | NEXTAUTH_SECRET = "ZXwBUbYq8uw7Psg5BZrlAndbNvrn1l1Cgin08nJ9TmM=" 8 | 9 | MONGODB_URI = "" 10 | 11 | GITHUB_ID = "" 12 | 13 | GITHUB_SECRET = "" 14 | 15 | GOOGLE_ID = "" 16 | 17 | GOOOGLE_SECRET = "" 18 | 19 | CRYPTO_ENCRYPTION_KEY = "e883993939ijjdiuduuehyrhuuer" 20 | 21 | CRYPTO_IV_KEY = "8289eudjjdue88e9e0e" 22 | 23 | ACCESS_TOKEN_PRIVATE_KEY = "ek8qvPCLavjSOlSjR/nHGS/iJV6fOhg/MftYpOMMIDbMKC+U0grPFtu6q8jg/dJBJEjTL2dafb+0ADqeSjsF5w0EzpY5PIWIXvP52anY1hpH3FjYeeFe2PS2jmukwb1J 24 | d7zhlh9i7MA55QU+7LH7PYupK1k23KMP6sZheblAD17HTg==" -------------------------------------------------------------------------------- /nextauth/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /nextauth/README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | # or 12 | pnpm dev 13 | # or 14 | bun dev 15 | ``` 16 | 17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 18 | 19 | You can start editing the page by modifying `app/page.js`. The page auto-updates as you edit the file. 20 | 21 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. 22 | 23 | ## Learn More 24 | 25 | To learn more about Next.js, take a look at the following resources: 26 | 27 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 28 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 29 | 30 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 31 | 32 | ## Deploy on Vercel 33 | 34 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 35 | 36 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 37 | -------------------------------------------------------------------------------- /nextauth/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "paths": { 4 | "@/*": ["./src/*"] 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /nextauth/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {} 3 | 4 | module.exports = nextConfig 5 | -------------------------------------------------------------------------------- /nextauth/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nextauth", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@auth/mongodb-adapter": "^2.0.4", 13 | "@emotion/react": "^11.11.1", 14 | "@emotion/styled": "^11.11.0", 15 | "@fontsource/roboto": "^5.0.8", 16 | "@mui/icons-material": "^5.14.18", 17 | "@mui/material": "^5.14.18", 18 | "axios": "^1.6.2", 19 | "bcrypt": "^5.1.1", 20 | "crypto-js": "^4.2.0", 21 | "jose": "^5.1.1", 22 | "mongodb": "^6.2.0", 23 | "mongoose": "^8.0.1", 24 | "nanoid": "^5.0.3", 25 | "next": "14.0.2", 26 | "next-auth": "^4.24.5", 27 | "react": "^18", 28 | "react-dom": "^18", 29 | "slugify": "^1.6.6", 30 | "swr": "^2.2.4", 31 | "zod": "^3.22.4" 32 | }, 33 | "devDependencies": { 34 | "eslint": "^8", 35 | "eslint-config-next": "14.0.2" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /nextauth/public/avatar3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torver213/nextjs14-nextauth-nodeapi-yt/9a4fb7475526c40364187b14428d9b97e07e74f6/nextauth/public/avatar3.png -------------------------------------------------------------------------------- /nextauth/public/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torver213/nextjs14-nextauth-nodeapi-yt/9a4fb7475526c40364187b14428d9b97e07e74f6/nextauth/public/bg.jpg -------------------------------------------------------------------------------- /nextauth/public/logo-sm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torver213/nextjs14-nextauth-nodeapi-yt/9a4fb7475526c40364187b14428d9b97e07e74f6/nextauth/public/logo-sm.png -------------------------------------------------------------------------------- /nextauth/public/next copy.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /nextauth/public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /nextauth/public/post1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torver213/nextjs14-nextauth-nodeapi-yt/9a4fb7475526c40364187b14428d9b97e07e74f6/nextauth/public/post1.jpg -------------------------------------------------------------------------------- /nextauth/public/post10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torver213/nextjs14-nextauth-nodeapi-yt/9a4fb7475526c40364187b14428d9b97e07e74f6/nextauth/public/post10.jpg -------------------------------------------------------------------------------- /nextauth/public/post2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torver213/nextjs14-nextauth-nodeapi-yt/9a4fb7475526c40364187b14428d9b97e07e74f6/nextauth/public/post2.jpg -------------------------------------------------------------------------------- /nextauth/public/post3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torver213/nextjs14-nextauth-nodeapi-yt/9a4fb7475526c40364187b14428d9b97e07e74f6/nextauth/public/post3.jpg -------------------------------------------------------------------------------- /nextauth/public/post4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torver213/nextjs14-nextauth-nodeapi-yt/9a4fb7475526c40364187b14428d9b97e07e74f6/nextauth/public/post4.jpg -------------------------------------------------------------------------------- /nextauth/public/post5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torver213/nextjs14-nextauth-nodeapi-yt/9a4fb7475526c40364187b14428d9b97e07e74f6/nextauth/public/post5.jpg -------------------------------------------------------------------------------- /nextauth/public/post6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torver213/nextjs14-nextauth-nodeapi-yt/9a4fb7475526c40364187b14428d9b97e07e74f6/nextauth/public/post6.jpg -------------------------------------------------------------------------------- /nextauth/public/post7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torver213/nextjs14-nextauth-nodeapi-yt/9a4fb7475526c40364187b14428d9b97e07e74f6/nextauth/public/post7.jpg -------------------------------------------------------------------------------- /nextauth/public/post8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torver213/nextjs14-nextauth-nodeapi-yt/9a4fb7475526c40364187b14428d9b97e07e74f6/nextauth/public/post8.jpg -------------------------------------------------------------------------------- /nextauth/public/post9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torver213/nextjs14-nextauth-nodeapi-yt/9a4fb7475526c40364187b14428d9b97e07e74f6/nextauth/public/post9.jpg -------------------------------------------------------------------------------- /nextauth/public/vercel copy.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /nextauth/public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /nextauth/src/app/(site)/articles/error.js: -------------------------------------------------------------------------------- 1 | 'use client' 2 | import { getErrorMessage } from '@/utils' 3 | import { Box, Button, Container, Stack, Typography } from '@mui/material' 4 | import Link from 'next/link' 5 | import React, { Fragment } from 'react' 6 | 7 | const Error = ({error, reset}) => { 8 | return ( 9 | 10 | 11 | 12 | Oops something went wrong! 13 | 14 | {getErrorMessage(error)} 15 | 16 | 17 | 18 | 21 | 24 | 25 | 26 | 27 | 28 | 29 | ) 30 | } 31 | 32 | export default Error -------------------------------------------------------------------------------- /nextauth/src/app/(site)/articles/page.js: -------------------------------------------------------------------------------- 1 | import { ArticlePage } from "@/components/pages"; 2 | import { getPublicPosts } from "@/lib/post"; 3 | 4 | export default async function Home() { 5 | const posts = await getPublicPosts() 6 | return 7 | } 8 | -------------------------------------------------------------------------------- /nextauth/src/app/(site)/auth/error/page.js: -------------------------------------------------------------------------------- 1 | 'use client' 2 | import { Box, Container, Typography } from '@mui/material' 3 | import React, { Fragment } from 'react' 4 | 5 | const Page = ({params, searchParams}) => { 6 | const {error} = searchParams 7 | return ( 8 | 9 | 10 | 11 | Oops something went wrong! 12 | 13 | {error} 14 | 15 | 16 | 17 | 18 | ) 19 | } 20 | 21 | export default Page -------------------------------------------------------------------------------- /nextauth/src/app/(site)/blog/error.js: -------------------------------------------------------------------------------- 1 | 'use client' 2 | import { getErrorMessage } from '@/utils' 3 | import { Box, Button, Container, Stack, Typography } from '@mui/material' 4 | import Link from 'next/link' 5 | import React, { Fragment } from 'react' 6 | 7 | const Error = ({error, reset}) => { 8 | return ( 9 | 10 | 11 | 12 | Oops something went wrong! 13 | 14 | {getErrorMessage(error)} 15 | 16 | 17 | 18 | 21 | 24 | 25 | 26 | 27 | 28 | 29 | ) 30 | } 31 | 32 | export default Error -------------------------------------------------------------------------------- /nextauth/src/app/(site)/blog/page.js: -------------------------------------------------------------------------------- 1 | 'use client' 2 | import { ArticlePage } from "@/components/pages"; 3 | import { useAuthUser } from "@/hooks"; 4 | import { getAllPosts } from "@/lib/post"; 5 | import { getErrorMessage } from "@/utils"; 6 | import { Box, CircularProgress, Typography } from "@mui/material"; 7 | import { redirect } from "next/navigation"; 8 | import useSWR from "swr"; 9 | 10 | export default function Page() { 11 | const {session, isLoading: isSessionLoading, isUnAuthenticated} = useAuthUser() 12 | 13 | const {data: posts, isLoading, error} = useSWR(["/posts", session?.accessToken], ([url, token]) => getAllPosts(token)) 14 | 15 | if(isLoading || isSessionLoading){ 16 | return () 17 | } 18 | 19 | if(isUnAuthenticated) return redirect("/api/auth/signin?callbackUrl=/blog") 20 | 21 | if(error){ 22 | return ( 23 | 24 | 25 | {getErrorMessage(error)} 26 | 27 | ) 28 | } 29 | return 30 | } 31 | -------------------------------------------------------------------------------- /nextauth/src/app/(site)/create-post/page.js: -------------------------------------------------------------------------------- 1 | import authOptions from "@/app/api/auth/authOptions"; 2 | import { CreatePostPage } from "@/components/pages"; 3 | import { getServerSession } from "next-auth"; 4 | import { redirect } from "next/navigation"; 5 | 6 | export default async function Home() { 7 | const session = await getServerSession(authOptions) 8 | if(!session) return redirect("/api/auth/signin?callbackUrl=/create-post") 9 | return 10 | } 11 | -------------------------------------------------------------------------------- /nextauth/src/app/(site)/dashboard/error.js: -------------------------------------------------------------------------------- 1 | 'use client' 2 | import { getErrorMessage } from '@/utils' 3 | import { Box, Button, Container, Stack, Typography } from '@mui/material' 4 | import Link from 'next/link' 5 | import React, { Fragment } from 'react' 6 | 7 | const Error = ({error, reset}) => { 8 | return ( 9 | 10 | 11 | 12 | Oops something went wrong! 13 | 14 | {getErrorMessage(error)} 15 | 16 | 17 | 18 | 21 | 24 | 25 | 26 | 27 | 28 | 29 | ) 30 | } 31 | 32 | export default Error -------------------------------------------------------------------------------- /nextauth/src/app/(site)/dashboard/page.js: -------------------------------------------------------------------------------- 1 | import authOptions from "@/app/api/auth/authOptions"; 2 | import { DashboardPage } from "@/components/pages"; 3 | import { getServerSession } from "next-auth"; 4 | import { redirect } from "next/navigation"; 5 | 6 | export default async function Home() { 7 | const session = await getServerSession(authOptions) 8 | if(!session) return redirect("/api/auth/signin?callbackUrl=/dashboard") 9 | return 10 | } 11 | -------------------------------------------------------------------------------- /nextauth/src/app/(site)/posts/error.js: -------------------------------------------------------------------------------- 1 | 'use client' 2 | import { getErrorMessage } from '@/utils' 3 | import { Box, Button, Container, Stack, Typography } from '@mui/material' 4 | import Link from 'next/link' 5 | import React, { Fragment } from 'react' 6 | 7 | const Error = ({error, reset}) => { 8 | return ( 9 | 10 | 11 | 12 | Oops something went wrong! 13 | 14 | {getErrorMessage(error)} 15 | 16 | 17 | 18 | 21 | 24 | 25 | 26 | 27 | 28 | 29 | ) 30 | } 31 | 32 | export default Error -------------------------------------------------------------------------------- /nextauth/src/app/(site)/posts/page.js: -------------------------------------------------------------------------------- 1 | import authOptions from "@/app/api/auth/authOptions"; 2 | import { ArticlePage } from "@/components/pages"; 3 | import { getAllNodeAPIPosts } from "@/lib/post"; 4 | import { getServerSession } from "next-auth"; 5 | import { redirect } from "next/navigation"; 6 | 7 | export default async function Page() { 8 | const session = await getServerSession(authOptions) 9 | if(!session) return redirect("/api/auth/signin?callbackUrl=/posts") 10 | const posts = await getAllNodeAPIPosts(session?.accessToken) 11 | console.log("Node api posts ", posts) 12 | return 13 | } 14 | -------------------------------------------------------------------------------- /nextauth/src/app/(site)/profile/error.js: -------------------------------------------------------------------------------- 1 | 'use client' 2 | import { getErrorMessage } from '@/utils' 3 | import { Box, Button, Container, Stack, Typography } from '@mui/material' 4 | import Link from 'next/link' 5 | import React, { Fragment } from 'react' 6 | 7 | const Error = ({error, reset}) => { 8 | return ( 9 | 10 | 11 | 12 | Oops something went wrong! 13 | 14 | {getErrorMessage(error)} 15 | 16 | 17 | 18 | 21 | 24 | 25 | 26 | 27 | 28 | 29 | ) 30 | } 31 | 32 | export default Error -------------------------------------------------------------------------------- /nextauth/src/app/(site)/profile/page.js: -------------------------------------------------------------------------------- 1 | import authOptions from "@/app/api/auth/authOptions"; 2 | import { ProfilePage } from "@/components/pages"; 3 | import { getServerSession } from "next-auth"; 4 | import { redirect } from "next/navigation"; 5 | 6 | export default async function Home() { 7 | const session = await getServerSession(authOptions) 8 | if(!session) return redirect("/api/auth/signin?callbackUrl=/dashboard") 9 | return 10 | } 11 | -------------------------------------------------------------------------------- /nextauth/src/app/(site)/users/error.js: -------------------------------------------------------------------------------- 1 | 'use client' 2 | import { getErrorMessage } from '@/utils' 3 | import { Box, Button, Container, Stack, Typography } from '@mui/material' 4 | import Link from 'next/link' 5 | import React, { Fragment } from 'react' 6 | 7 | const Error = ({error, reset}) => { 8 | return ( 9 | 10 | 11 | 12 | Oops something went wrong! 13 | 14 | {getErrorMessage(error)} 15 | 16 | 17 | 18 | 21 | 24 | 25 | 26 | 27 | 28 | 29 | ) 30 | } 31 | 32 | export default Error -------------------------------------------------------------------------------- /nextauth/src/app/(site)/users/page.js: -------------------------------------------------------------------------------- 1 | import authOptions from "@/app/api/auth/authOptions"; 2 | import { UsersPage } from "@/components/pages"; 3 | import { getAllUsers } from "@/lib/user"; 4 | import { getServerSession } from "next-auth"; 5 | import { redirect } from "next/navigation"; 6 | 7 | export default async function Home() { 8 | const session = await getServerSession(authOptions) 9 | if(!session) return redirect("/api/auth/signin?callbackUrl=/dashboard") 10 | const users = await getAllUsers(session?.accessToken) 11 | console.log(users) 12 | return 13 | 14 | 15 | 16 | } 17 | -------------------------------------------------------------------------------- /nextauth/src/app/api/auth/[...nextauth]/route.js: -------------------------------------------------------------------------------- 1 | import NextAuth from "next-auth" 2 | import authOptions from "../authOptions" 3 | 4 | const handler = NextAuth(authOptions) 5 | 6 | export { handler as GET, handler as POST } -------------------------------------------------------------------------------- /nextauth/src/app/api/auth/authOptions.js: -------------------------------------------------------------------------------- 1 | import GithubProvider from 'next-auth/providers/github' 2 | import GoogleProvider from 'next-auth/providers/google' 3 | import CrendentialsProvider from 'next-auth/providers/credentials' 4 | import { MongoDBAdapter } from '@auth/mongodb-adapter' 5 | import { nanoid } from 'nanoid' 6 | import clientPromise from '@/db' 7 | import { validateZodInput } from '@/validators' 8 | import { UserZodSchema } from '@/schema' 9 | import { UserService } from '@/services' 10 | import { encryptString, signJwtToken, verifyJwtToken } from '@/utils' 11 | 12 | const generateAccessToken = async({accessToken, user, isRefresh}) => { 13 | try { 14 | const verifAccessToken = await verifyJwtToken(accessToken) 15 | if(!isRefresh && !accessToken){ 16 | const encryptData = encryptString(JSON.stringify(user)) 17 | const signToken = await signJwtToken({user: encryptData}) 18 | return signToken 19 | } 20 | if(isRefresh && verifAccessToken.isError){ 21 | // make a db call 22 | const result = await UserService.getCurrentUser(user.id) 23 | if(result.isError) throw new Error(result.message) 24 | const encryptData = encryptString(JSON.stringify(result.data)) 25 | const signToken = await signJwtToken({user: encryptData}) 26 | return signToken 27 | } 28 | return accessToken 29 | } catch (error) { 30 | throw new Error(error.message) 31 | } 32 | } 33 | 34 | 35 | const authOptions = { 36 | adapter: MongoDBAdapter(clientPromise), 37 | session: { 38 | strategy: "jwt", 39 | maxAge: 30 * 24 * 60 * 60, // 30 days 40 | updateAge: 24 * 60 * 60, 41 | generateSessionToken: () => { 42 | return nanoid(32) 43 | } 44 | }, 45 | providers: [ 46 | CrendentialsProvider({ 47 | name: 'crendentials', 48 | credentials: { 49 | email: { 50 | label: "Email", 51 | type: "email", 52 | placeholder: "Enter your email e.g. me@example.com" 53 | }, 54 | password: { 55 | label: "Password", 56 | type: "password", 57 | placeholder: "Enter your password" 58 | } 59 | }, 60 | async authorize(credentials) { 61 | try { 62 | // validate the inputs 63 | const parsedResult = validateZodInput(credentials, UserZodSchema) 64 | if(parsedResult.isError) throw new Error(parsedResult.message) 65 | // make db calls her 66 | const result = await UserService.createUser(parsedResult.data) 67 | if(result.isError) throw new Error(result.message) 68 | return result.data 69 | } catch (error) { 70 | throw new Error(error.message); 71 | } 72 | } 73 | }), 74 | GithubProvider({ 75 | clientId: process.env.GITHUB_ID, 76 | clientSecret: process.env.GITHUB_SECRET, 77 | profile: async(user) => { 78 | const isBlocked = user?.hasOwnProperty("isBlocked") ? user.isBlocked : false; 79 | const role = user?.hasOwnProperty("role") ? user.role : "user"; 80 | const isVerifiedEmail = user?.hasOwnProperty("emailVerified") ? user?.emailVerified : true 81 | const avatar = user?.avatar_url 82 | const createdAt = user?.hasOwnProperty("createdAt") ? user?.createdAt : new Date().toISOString() 83 | const updatedAt = user?.hasOwnProperty("updatedAt") ? user?.updatedAt : new Date().toISOString() 84 | return {id: user?.id, name: user?.name, email: user?.email, role, isBlocked, avatar, isVerifiedEmail,createdAt,updatedAt} 85 | } 86 | }), 87 | GoogleProvider({ 88 | clientId: process.env.GOOGLE_ID, 89 | clientSecret: process.env.GOOOGLE_SECRET, 90 | profile: async(user) => { 91 | const isBlocked = user?.hasOwnProperty("isBlocked") ? user.isBlocked : false; 92 | const role = user?.hasOwnProperty("role") ? user.role : "user"; 93 | const isVerifiedEmail = user?.hasOwnProperty("email_verified") ? user?.email_verified : true 94 | const avatar = user?.picture 95 | const createdAt = user?.hasOwnProperty("createdAt") ? user?.createdAt : new Date().toISOString() 96 | const updatedAt = user?.hasOwnProperty("updatedAt") ? user?.updatedAt : new Date().toISOString() 97 | const id = user?.id ? user?.id : user?.sub 98 | return {id, name: user?.name, email: user?.email, role, isBlocked, isVerifiedEmail, avatar, createdAt, updatedAt } 99 | } 100 | }) 101 | ], 102 | callbacks: { 103 | async signIn({user, account, profile, email, crendentials}) { 104 | return true 105 | }, 106 | async redirect({url, baseUrl}) { 107 | if(url.startsWith('/')) return url 108 | return baseUrl 109 | }, 110 | async session({session, token, user}) { 111 | if(token){ 112 | session.user = token?.user 113 | session.accessToken = token?.accessToken 114 | } 115 | return session 116 | }, 117 | async jwt({token, user, account, profile, isNewUser}) { 118 | 119 | if(token && user ){ 120 | const _user = { 121 | id: user.id, 122 | email: user.email, 123 | role: user.role, 124 | name: user.name, 125 | avatar: user.avatar 126 | } 127 | // create a fn generate access token 128 | const accessToken = await generateAccessToken({ 129 | accessToken: token?.accessToken, 130 | user: _user, 131 | isRefresh: false 132 | }) 133 | token.user = _user 134 | token.accessToken = accessToken 135 | } 136 | if(token && !user){ 137 | const _user = token?.user 138 | // create a fn generate access token 139 | const accessToken = await generateAccessToken({ 140 | accessToken: token?.accessToken, 141 | user: _user, 142 | isRefresh: true 143 | }) 144 | token.user = _user 145 | token.accessToken = accessToken 146 | } 147 | return token 148 | } 149 | }, 150 | pages: { 151 | error: "/auth/error" 152 | } 153 | } 154 | 155 | export default authOptions 156 | -------------------------------------------------------------------------------- /nextauth/src/app/api/posts/route.js: -------------------------------------------------------------------------------- 1 | import { PostZodSchema } from "@/schema"; 2 | import { PostService } from "@/services"; 3 | import { checkUserRole } from "@/utils"; 4 | import { validateZodInput } from "@/validators"; 5 | 6 | export async function POST(req) { 7 | const body = await req.json(); 8 | const requestHeaders = new Headers(req.headers) 9 | 10 | const user = JSON.parse(requestHeaders.get('user')); 11 | 12 | if(!user || !checkUserRole(user?.role).isAdminRole){ 13 | return Response.json("You are not authorized to access this resource", { status: 403 }) 14 | } 15 | 16 | const parsedResult = validateZodInput(body, PostZodSchema); 17 | 18 | 19 | if(parsedResult.isError) return Response.json(parsedResult.message, {status: 400}); 20 | 21 | // create a new post 22 | 23 | const result = await PostService.createNewPost({...parsedResult.data, user: user?.id}) 24 | 25 | if(result.isError) return Response.json(result.message, {status: 400}); 26 | 27 | return Response.json(result.data); 28 | 29 | } 30 | 31 | export async function GET(request) { 32 | const result = await PostService.getAllPosts(); 33 | if(result.isError) return Response.json(result.message, {status: 500}); 34 | return Response.json(result.data) 35 | } -------------------------------------------------------------------------------- /nextauth/src/app/api/users/route.js: -------------------------------------------------------------------------------- 1 | import { UserService } from "@/services"; 2 | 3 | export async function GET(request) { 4 | const result = await UserService.getAllUsers(); 5 | if(result.isError) return Response.json(result.message, {status: 500}); 6 | return Response.json(result.data) 7 | } -------------------------------------------------------------------------------- /nextauth/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torver213/nextjs14-nextauth-nodeapi-yt/9a4fb7475526c40364187b14428d9b97e07e74f6/nextauth/src/app/favicon.ico -------------------------------------------------------------------------------- /nextauth/src/app/globals.css: -------------------------------------------------------------------------------- 1 | @import url("https://fonts.googleapis.com/icon?family=Material+Icons"); 2 | 3 | * { 4 | box-sizing: border-box; 5 | padding: 0; 6 | margin: 0; 7 | } 8 | 9 | html, 10 | body { 11 | max-width: 100vw; 12 | overflow-x: hidden; 13 | } 14 | 15 | -------------------------------------------------------------------------------- /nextauth/src/app/layout.js: -------------------------------------------------------------------------------- 1 | import "@fontsource/roboto/300.css"; 2 | import "@fontsource/roboto/400.css"; 3 | import "@fontsource/roboto/500.css"; 4 | import "@fontsource/roboto/700.css"; 5 | import "./globals.css"; 6 | import { MainNavbar } from "@/components/atoms"; 7 | import { NextAuthSessionProvider } from "@/providers"; 8 | 9 | export const metadata = { 10 | title: "Create Next App", 11 | description: "Generated by create next app", 12 | }; 13 | 14 | export default function RootLayout({ children }) { 15 | return ( 16 | 17 | 18 | 19 | 20 |
{children}
21 | 22 |
23 | 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /nextauth/src/app/page.js: -------------------------------------------------------------------------------- 1 | import { HomePage } from "@/components/pages"; 2 | 3 | export default function Home() { 4 | return 5 | } 6 | -------------------------------------------------------------------------------- /nextauth/src/app/page.module.css: -------------------------------------------------------------------------------- 1 | .main { 2 | display: flex; 3 | flex-direction: column; 4 | justify-content: space-between; 5 | align-items: center; 6 | padding: 6rem; 7 | min-height: 100vh; 8 | } 9 | 10 | .description { 11 | display: inherit; 12 | justify-content: inherit; 13 | align-items: inherit; 14 | font-size: 0.85rem; 15 | max-width: var(--max-width); 16 | width: 100%; 17 | z-index: 2; 18 | font-family: var(--font-mono); 19 | } 20 | 21 | .description a { 22 | display: flex; 23 | justify-content: center; 24 | align-items: center; 25 | gap: 0.5rem; 26 | } 27 | 28 | .description p { 29 | position: relative; 30 | margin: 0; 31 | padding: 1rem; 32 | background-color: rgba(var(--callout-rgb), 0.5); 33 | border: 1px solid rgba(var(--callout-border-rgb), 0.3); 34 | border-radius: var(--border-radius); 35 | } 36 | 37 | .code { 38 | font-weight: 700; 39 | font-family: var(--font-mono); 40 | } 41 | 42 | .grid { 43 | display: grid; 44 | grid-template-columns: repeat(4, minmax(25%, auto)); 45 | max-width: 100%; 46 | width: var(--max-width); 47 | } 48 | 49 | .card { 50 | padding: 1rem 1.2rem; 51 | border-radius: var(--border-radius); 52 | background: rgba(var(--card-rgb), 0); 53 | border: 1px solid rgba(var(--card-border-rgb), 0); 54 | transition: background 200ms, border 200ms; 55 | } 56 | 57 | .card span { 58 | display: inline-block; 59 | transition: transform 200ms; 60 | } 61 | 62 | .card h2 { 63 | font-weight: 600; 64 | margin-bottom: 0.7rem; 65 | } 66 | 67 | .card p { 68 | margin: 0; 69 | opacity: 0.6; 70 | font-size: 0.9rem; 71 | line-height: 1.5; 72 | max-width: 30ch; 73 | } 74 | 75 | .center { 76 | display: flex; 77 | justify-content: center; 78 | align-items: center; 79 | position: relative; 80 | padding: 4rem 0; 81 | } 82 | 83 | .center::before { 84 | background: var(--secondary-glow); 85 | border-radius: 50%; 86 | width: 480px; 87 | height: 360px; 88 | margin-left: -400px; 89 | } 90 | 91 | .center::after { 92 | background: var(--primary-glow); 93 | width: 240px; 94 | height: 180px; 95 | z-index: -1; 96 | } 97 | 98 | .center::before, 99 | .center::after { 100 | content: ''; 101 | left: 50%; 102 | position: absolute; 103 | filter: blur(45px); 104 | transform: translateZ(0); 105 | } 106 | 107 | .logo { 108 | position: relative; 109 | } 110 | /* Enable hover only on non-touch devices */ 111 | @media (hover: hover) and (pointer: fine) { 112 | .card:hover { 113 | background: rgba(var(--card-rgb), 0.1); 114 | border: 1px solid rgba(var(--card-border-rgb), 0.15); 115 | } 116 | 117 | .card:hover span { 118 | transform: translateX(4px); 119 | } 120 | } 121 | 122 | @media (prefers-reduced-motion) { 123 | .card:hover span { 124 | transform: none; 125 | } 126 | } 127 | 128 | /* Mobile */ 129 | @media (max-width: 700px) { 130 | .content { 131 | padding: 4rem; 132 | } 133 | 134 | .grid { 135 | grid-template-columns: 1fr; 136 | margin-bottom: 120px; 137 | max-width: 320px; 138 | text-align: center; 139 | } 140 | 141 | .card { 142 | padding: 1rem 2.5rem; 143 | } 144 | 145 | .card h2 { 146 | margin-bottom: 0.5rem; 147 | } 148 | 149 | .center { 150 | padding: 8rem 0 6rem; 151 | } 152 | 153 | .center::before { 154 | transform: none; 155 | height: 300px; 156 | } 157 | 158 | .description { 159 | font-size: 0.8rem; 160 | } 161 | 162 | .description a { 163 | padding: 1rem; 164 | } 165 | 166 | .description p, 167 | .description div { 168 | display: flex; 169 | justify-content: center; 170 | position: fixed; 171 | width: 100%; 172 | } 173 | 174 | .description p { 175 | align-items: center; 176 | inset: 0 0 auto; 177 | padding: 2rem 1rem 1.4rem; 178 | border-radius: 0; 179 | border: none; 180 | border-bottom: 1px solid rgba(var(--callout-border-rgb), 0.25); 181 | background: linear-gradient( 182 | to bottom, 183 | rgba(var(--background-start-rgb), 1), 184 | rgba(var(--callout-rgb), 0.5) 185 | ); 186 | background-clip: padding-box; 187 | backdrop-filter: blur(24px); 188 | } 189 | 190 | .description div { 191 | align-items: flex-end; 192 | pointer-events: none; 193 | inset: auto 0 0; 194 | padding: 2rem; 195 | height: 200px; 196 | background: linear-gradient( 197 | to bottom, 198 | transparent 0%, 199 | rgb(var(--background-end-rgb)) 40% 200 | ); 201 | z-index: 1; 202 | } 203 | } 204 | 205 | /* Tablet and Smaller Desktop */ 206 | @media (min-width: 701px) and (max-width: 1120px) { 207 | .grid { 208 | grid-template-columns: repeat(2, 50%); 209 | } 210 | } 211 | 212 | @media (prefers-color-scheme: dark) { 213 | .vercelLogo { 214 | filter: invert(1); 215 | } 216 | 217 | .logo { 218 | filter: invert(1) drop-shadow(0 0 0.3rem #ffffff70); 219 | } 220 | } 221 | 222 | @keyframes rotate { 223 | from { 224 | transform: rotate(360deg); 225 | } 226 | to { 227 | transform: rotate(0deg); 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /nextauth/src/components/atoms/MainNavbar.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import SiteNavbar from './SiteNavbar' 3 | import { getServerSession } from 'next-auth' 4 | import authOptions from '@/app/api/auth/authOptions' 5 | 6 | const MainNavbar = async() => { 7 | const session = await getServerSession(authOptions) 8 | return () 9 | } 10 | 11 | export default MainNavbar -------------------------------------------------------------------------------- /nextauth/src/components/atoms/PostCard.js: -------------------------------------------------------------------------------- 1 | 'use client' 2 | import { formatDate } from '@/utils' 3 | import { Avatar, Box, Paper, Stack, Typography } from '@mui/material' 4 | import Image from 'next/image' 5 | import Link from 'next/link' 6 | import React from 'react' 7 | 8 | const PostCard = ({post}) => { 9 | return ( 10 | 11 | 12 | 15 | 16 | 17 | 18 | 20 | {post.title} 21 | 22 | {post.description} 23 | 24 | 25 | Posted on: 26 | {formatDate(post.createdAt)} 27 | 28 | 29 | 30 | 31 | 32 | {post?.user?.name} 33 | 34 | 35 | 36 | 37 | 38 | ) 39 | } 40 | 41 | export default PostCard -------------------------------------------------------------------------------- /nextauth/src/components/atoms/SiteNavbar.js: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import * as React from "react"; 3 | import AppBar from "@mui/material/AppBar"; 4 | import Box from "@mui/material/Box"; 5 | import Toolbar from "@mui/material/Toolbar"; 6 | import IconButton from "@mui/material/IconButton"; 7 | import Typography from "@mui/material/Typography"; 8 | import Menu from "@mui/material/Menu"; 9 | import MenuIcon from "@mui/icons-material/Menu"; 10 | import Container from "@mui/material/Container"; 11 | import Avatar from "@mui/material/Avatar"; 12 | import Button from "@mui/material/Button"; 13 | import Tooltip from "@mui/material/Tooltip"; 14 | import MenuItem from "@mui/material/MenuItem"; 15 | import AdbIcon from "@mui/icons-material/Adb"; 16 | import { checkUserRole } from "@/utils"; 17 | import Link from "next/link"; 18 | import { Person } from "@mui/icons-material"; 19 | import { signIn, signOut } from "next-auth/react"; 20 | import { usePathname } from "next/navigation"; 21 | 22 | const publicPages = [{ title: "Articles", url: "/articles" }]; 23 | 24 | const adminPages = [ 25 | { title: "Dashboard", url: "/dashboard" }, 26 | { title: "Server Articles", url: "/articles" }, 27 | { title: "Client Blog", url: "/blog" }, 28 | { title: "Node API Posts", url: "/posts" }, 29 | { title: "All Users", url: "/users" }, 30 | ]; 31 | 32 | const adminNavItems = [ 33 | { title: "Dashboard", url: "/dashboard" }, 34 | { title: "Profile", url: "/profile" }, 35 | { title: "Create Post", url: "/create-post" }, 36 | { title: "Logout", url: "/api/auth/signout" }, 37 | ]; 38 | 39 | const userPages = [ 40 | { title: "Dashboard", url: "/dashboard" }, 41 | { title: "Server Articles", url: "/articles" }, 42 | { title: "Client Blog", url: "/blog" }, 43 | { title: "Node API Posts", url: "/posts" }, 44 | ]; 45 | 46 | const userMenuItems = [ 47 | { title: "Dashboard", url: "/dashboard" }, 48 | { title: "Profile", url: "/profile" }, 49 | { title: "Logout", url: "/api/auth/signout" }, 50 | ]; 51 | 52 | function SiteNavbar({ session }) { 53 | const [anchorElNav, setAnchorElNav] = React.useState(null); 54 | const [anchorElUser, setAnchorElUser] = React.useState(null); 55 | const pathname = usePathname() 56 | 57 | 58 | const handleOpenNavMenu = (event) => { 59 | setAnchorElNav(event.currentTarget); 60 | }; 61 | const handleOpenUserMenu = (event) => { 62 | setAnchorElUser(event.currentTarget); 63 | }; 64 | 65 | const handleCloseNavMenu = () => { 66 | setAnchorElNav(null); 67 | }; 68 | 69 | const handleCloseUserMenu = () => { 70 | setAnchorElUser(null); 71 | }; 72 | 73 | 74 | const checkRole = checkUserRole(session?.user?.role); 75 | 76 | const pages = checkRole.isAdminRole 77 | ? adminPages 78 | : checkRole.isUserRole 79 | ? userPages 80 | : publicPages; 81 | 82 | const settings = checkRole.isAdminRole 83 | ? adminNavItems 84 | : checkRole.isUserRole 85 | ? userMenuItems 86 | : []; 87 | return ( 88 | 89 | 90 | 91 | 92 | 108 | LOGO 109 | 110 | 111 | 112 | 120 | 121 | 122 | 140 | {pages.map((page) => ( 141 | 142 | 145 | 146 | ))} 147 | 148 | 149 | 150 | 167 | LOGO 168 | 169 | 170 | {pages.map((page) => ( 171 | 180 | ))} 181 | 182 | 183 | {!session ? ( 184 | 185 | 186 | signIn()} sx={{ p: 0 }}> 187 | 188 | 189 | 190 | 191 | 192 | 193 | ) : ( 194 | 195 | 196 | 197 | 198 | 199 | 200 | 216 | {settings.map((setting) => ( 217 | handleCloseUserMenu()}> 218 | 219 | 220 | ))} 221 | 222 | 223 | )} 224 | 225 | 226 | 227 | ); 228 | } 229 | export default SiteNavbar; 230 | -------------------------------------------------------------------------------- /nextauth/src/components/atoms/UsersTable.js: -------------------------------------------------------------------------------- 1 | 'use client' 2 | import * as React from 'react'; 3 | import Table from '@mui/material/Table'; 4 | import TableBody from '@mui/material/TableBody'; 5 | import TableCell from '@mui/material/TableCell'; 6 | import TableContainer from '@mui/material/TableContainer'; 7 | import TableHead from '@mui/material/TableHead'; 8 | import TableRow from '@mui/material/TableRow'; 9 | import Paper from '@mui/material/Paper'; 10 | import { Avatar } from '@mui/material'; 11 | 12 | 13 | export default function UsersTable({users}) { 14 | return ( 15 | 16 | 17 | 18 | 19 | ID 20 | Avatar 21 | Name 22 | Email 23 | Role 24 | 25 | 26 | 27 | {users.map((row) => ( 28 | 32 | 33 | {row.id} 34 | 35 | 36 | 37 | 38 | {row.name} 39 | {row.email} 40 | {row.role} 41 | 42 | ))} 43 | 44 |
45 |
46 | ); 47 | } 48 | -------------------------------------------------------------------------------- /nextauth/src/components/atoms/index.js: -------------------------------------------------------------------------------- 1 | export {default as MainNavbar } from './MainNavbar' -------------------------------------------------------------------------------- /nextauth/src/components/pages/AdminDashboard.js: -------------------------------------------------------------------------------- 1 | 'use client' 2 | import { Box, Container, Grid, Paper, Stack, Typography } from '@mui/material' 3 | import React from 'react' 4 | 5 | const items = [ 6 | {title: "Users", total: 10000}, 7 | {title: "Articles", total: 200}, 8 | {title: "Visitors", total: 2000}, 9 | {title: "Authors", total: 700}, 10 | {title: "Comments", total: 1500}, 11 | {title: "Reactions", total: 1200}, 12 | {title: "Videos", total: 150}, 13 | {title: "Ads", total: 77}, 14 | ] 15 | 16 | const AdminDashboard = ({session}) => { 17 | return ( 18 | theme.palette.grey[300], minHeight: "100vh"}}> 19 | 20 | 21 | 22 | Admin Dashboard 23 | 24 | 25 | Welcome {session?.user?.name}, {session?.user?.role} 26 | 27 | 28 | 29 | { 30 | items.map(item => ( 31 | 32 | 33 | 34 | {item.title} 35 | {item.total} 36 | 37 | 38 | 39 | )) 40 | } 41 | 42 | 43 | 44 | 45 | ) 46 | } 47 | 48 | export default AdminDashboard -------------------------------------------------------------------------------- /nextauth/src/components/pages/ArticlePage.js: -------------------------------------------------------------------------------- 1 | import { Box, Container, Grid, Typography } from '@mui/material' 2 | import React from 'react' 3 | import PostCard from '../atoms/PostCard' 4 | 5 | const ArticlePage = ({title, posts}) => { 6 | return ( 7 | 8 | 9 | {title} 10 | 11 | { 12 | posts.map(post =>( 13 | 14 | 15 | )) 16 | } 17 | 18 | 19 | 20 | ) 21 | } 22 | 23 | export default ArticlePage -------------------------------------------------------------------------------- /nextauth/src/components/pages/CreatePostPage.js: -------------------------------------------------------------------------------- 1 | 'use client' 2 | import { createNewPost } from '@/lib/post' 3 | import { PostZodSchema } from '@/schema' 4 | import { validateZodInput } from '@/validators' 5 | import { Box, Button, CircularProgress, Container, Grid, Paper, TextField, Typography } from '@mui/material' 6 | import React, { useState } from 'react' 7 | 8 | 9 | const photos = ["/post1.jpg", "/post2.jpg", "/post3.jpg", "/post4.jpg", "/post5.jpg", "/post6.jpg", "/post7.jpg", "/post8.jpg", "/post9.jpg", "/post10.jpg"] 10 | 11 | const getPhoto = () => { 12 | const randomNumber = Math.round(Math.random() * (photos.length - 1)) 13 | return photos[randomNumber] 14 | } 15 | 16 | const initialState = { body: {title: "", description: "", content: ""}, message: "", isLoading: false} 17 | 18 | const CreatePostPage = ({session}) => { 19 | const [state, setState] = useState(initialState) 20 | 21 | const handleInputChange = event => { 22 | setState(prev => ({...prev, message: "", body: {...prev.body, [event.target.name]: event.target.value}})) 23 | } 24 | 25 | const handleSubmit = async(event) => { 26 | event.preventDefault() 27 | try { 28 | const photo = getPhoto() 29 | const parsedResult = validateZodInput({...state.body, photo}, PostZodSchema ) 30 | if(parsedResult.isError) return setState(prev => ({...prev, message: parsedResult.message, })) 31 | setState(prev => ({...prev, isLoading: true})) 32 | const result = await createNewPost(parsedResult.data, session?.accessToken) 33 | if(!result.isError) return setState(() => ({...initialState, isLoading: false, message: result.message})) 34 | setState(prev => ({...prev, isLoading: false, message: result.message})) 35 | } catch (error) { 36 | setState(prev => ({...prev, isLoading: false, message: error.message})) 37 | 38 | } 39 | } 40 | 41 | 42 | return ( 43 | theme.palette.grey[300], minHeight: "100vh", pb: 4}}> 44 | 45 | 46 | Create New Post 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | {state.message && state.message} 63 | 64 | 65 | 66 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | ) 83 | } 84 | 85 | export default CreatePostPage -------------------------------------------------------------------------------- /nextauth/src/components/pages/DashboardPage.js: -------------------------------------------------------------------------------- 1 | import { checkUserRole } from '@/utils' 2 | import React from 'react' 3 | import AdminDashboard from './AdminDashboard' 4 | import UserDashboard from './UserDashboard' 5 | 6 | const DashboardPage = ({session}) => { 7 | const checkRole = checkUserRole(session?.user?.role) 8 | if(checkRole.isAdminRole) return 9 | if(checkRole.isUserRole) return 10 | return null 11 | } 12 | 13 | export default DashboardPage -------------------------------------------------------------------------------- /nextauth/src/components/pages/HomePage.js: -------------------------------------------------------------------------------- 1 | 'use client' 2 | import { Box, Typography } from '@mui/material' 3 | import Image from 'next/image' 4 | import React from 'react' 5 | 6 | const HomePage = () => { 7 | return ( 8 | 15 | 16 | 17 | 18 | 19 | Logo 20 | 21 | theme.palette.common.white, p: 4, fontSize: {lg: "3.75rem", md: 20, sm: 20, xs: 20}}}> 22 | NextJs 14 Authentication & Role Based Authorisation Using NextAuth with Mongodb Adapter 23 | 24 | 25 | 26 | 27 | 28 | ) 29 | } 30 | 31 | export default HomePage -------------------------------------------------------------------------------- /nextauth/src/components/pages/ProfilePage.js: -------------------------------------------------------------------------------- 1 | 'use client' 2 | import { Box, Container, Paper, Stack, Typography } from '@mui/material' 3 | import React from 'react' 4 | 5 | const ProfilePage = ({session}) => { 6 | return ( 7 | theme.palette.grey[300], height: "100vh"}}> 8 | 9 | My Profile 10 | 11 | 12 | Name: 13 | {session?.user?.name} 14 | 15 | 16 | 17 | Email: 18 | {session?.user?.email} 19 | 20 | 21 | 22 | Role: 23 | {session?.user?.role} 24 | 25 | 26 | 27 | 28 | 29 | ) 30 | } 31 | 32 | export default ProfilePage -------------------------------------------------------------------------------- /nextauth/src/components/pages/UserDashboard.js: -------------------------------------------------------------------------------- 1 | 'use client' 2 | import { Box, Container, Grid, Paper, Stack, Typography } from '@mui/material' 3 | import React from 'react' 4 | 5 | const items = [ 6 | {title: "Followers", total: 10000}, 7 | {title: "Following", total: 1400}, 8 | {title: "Comments", total: 760}, 9 | {title: "Likes", total: 1350} 10 | ] 11 | 12 | const UserDashboard = ({session}) => { 13 | return ( 14 | theme.palette.grey[300], height: "100vh"}}> 15 | 16 | 17 | 18 | User Dashboard 19 | 20 | 21 | Welcome {session?.user?.name}, {session?.user?.role} 22 | 23 | 24 | 25 | { 26 | items.map(item => ( 27 | 28 | 29 | 30 | {item.title} 31 | {item.total} 32 | 33 | 34 | 35 | )) 36 | } 37 | 38 | 39 | 40 | 41 | ) 42 | } 43 | 44 | export default UserDashboard -------------------------------------------------------------------------------- /nextauth/src/components/pages/UsersPage.js: -------------------------------------------------------------------------------- 1 | 'use client' 2 | import { Box, Container, Paper, Stack, Typography } from '@mui/material' 3 | import React from 'react' 4 | import UsersTable from '../atoms/UsersTable' 5 | 6 | 7 | 8 | const UsersPage = ({users}) => { 9 | return ( 10 | theme.palette.grey[300], height: "100vh", pt: 8}}> 11 | 12 | All Users 13 | 14 | 15 | 16 | ) 17 | } 18 | 19 | export default UsersPage -------------------------------------------------------------------------------- /nextauth/src/components/pages/index.js: -------------------------------------------------------------------------------- 1 | export { default as HomePage } from './HomePage' 2 | export { default as CreatePostPage } from './CreatePostPage' 3 | export { default as ArticlePage } from './ArticlePage' 4 | export { default as DashboardPage } from './DashboardPage' 5 | export { default as ProfilePage } from './ProfilePage' 6 | export { default as UsersPage } from './UsersPage' 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /nextauth/src/config/index.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | 3 | export const axiosPublic = axios.create({baseURL: process.env.NEXT_PUBLIC_BASE_URL, withCredentials: true}) 4 | 5 | export const authAxios = axios.create({baseURL: process.env.NEXT_PUBLIC_BASE_URL, withCredentials: true}) 6 | 7 | // intercept the request 8 | authAxios.interceptors.request.use(async(config) => { 9 | const accessToken = authAxios.accessToken 10 | if(!accessToken) return Promise.reject(new Error("No access token provided")) 11 | config.headers = { 12 | "Authorization": "Bearer " + accessToken, 13 | "Accept": "application/json" 14 | } 15 | return config 16 | }, error => Promise.reject(new Error(error))) 17 | 18 | // intercept the response 19 | 20 | authAxios.interceptors.response.use(async(response) => { 21 | return response 22 | }, async function(error) { 23 | const originalRequest = error.config 24 | if(error?.response?.status === 403 && !originalRequest._retry){ 25 | try { 26 | originalRequest._retry = true 27 | const result = await refreshAccessToken() 28 | authAxios.accessToken = result?.accessToken 29 | return authAxios(originalRequest) 30 | } catch (err) { 31 | if(err?.response && err?.response?.data){ 32 | return Promise.reject(err?.response?.data) 33 | } 34 | return Promise.reject(err) 35 | } 36 | } 37 | return Promise.reject(error) 38 | }) 39 | 40 | 41 | 42 | export const authNodeAxios = axios.create({baseURL: process.env.NEXT_PUBLIC_BASE_NODE_URL, withCredentials: true}) 43 | 44 | // intercept the request 45 | authNodeAxios.interceptors.request.use(async(config) => { 46 | const accessToken = authNodeAxios.accessToken 47 | if(!accessToken) return Promise.reject(new Error("No access token provided")) 48 | config.headers = { 49 | "Authorization": "Bearer " + accessToken, 50 | "Accept": "application/json" 51 | } 52 | return config 53 | }, error => Promise.reject(new Error(error))) 54 | 55 | // intercept the response 56 | 57 | authNodeAxios.interceptors.response.use(async(response) => { 58 | return response 59 | }, async function(error) { 60 | const originalRequest = error.config 61 | if(error?.response?.status === 403 && !originalRequest._retry){ 62 | try { 63 | originalRequest._retry = true 64 | const result = await refreshAccessToken() 65 | authNodeAxios.accessToken = result?.accessToken 66 | return authNodeAxios(originalRequest) 67 | } catch (err) { 68 | if(err?.response && err?.response?.data){ 69 | return Promise.reject(err?.response?.data) 70 | } 71 | return Promise.reject(err) 72 | } 73 | } 74 | return Promise.reject(error) 75 | }) 76 | 77 | 78 | const refreshAccessToken = async() => { 79 | try { 80 | const response = await axiosPublic.get("/auth/session") 81 | return response.data 82 | } catch (error) { 83 | throw error 84 | } 85 | } -------------------------------------------------------------------------------- /nextauth/src/db/index.js: -------------------------------------------------------------------------------- 1 | // This approach is taken from https://github.com/vercel/next.js/tree/canary/examples/with-mongodb 2 | import { MongoClient } from "mongodb" 3 | 4 | if (!process.env.MONGODB_URI) { 5 | throw new Error('Invalid/Missing environment variable: "MONGODB_URI"') 6 | } 7 | 8 | const uri = process.env.MONGODB_URI 9 | const options = {} 10 | 11 | let client 12 | let clientPromise 13 | 14 | if (process.env.NODE_ENV === "development") { 15 | // In development mode, use a global variable so that the value 16 | // is preserved across module reloads caused by HMR (Hot Module Replacement). 17 | if (!global._mongoClientPromise) { 18 | client = new MongoClient(uri, options) 19 | global._mongoClientPromise = client.connect() 20 | } 21 | clientPromise = global._mongoClientPromise 22 | } else { 23 | // In production mode, it's best to not use a global variable. 24 | client = new MongoClient(uri, options) 25 | clientPromise = client.connect() 26 | } 27 | 28 | // Export a module-scoped MongoClient promise. By doing this in a 29 | // separate module, the client can be shared across functions. 30 | export default clientPromise -------------------------------------------------------------------------------- /nextauth/src/hooks/index.js: -------------------------------------------------------------------------------- 1 | import { useSession } from "next-auth/react" 2 | 3 | export const useAuthUser = (url = "/") => { 4 | const {data: session, status} = useSession() 5 | const isLoading = status === "loading" 6 | const isAuthenticated = status === "authenticated" 7 | const isUnAuthenticated = status === "unauthenticated" 8 | 9 | return { session, isLoading, isAuthenticated, isUnAuthenticated} 10 | 11 | } -------------------------------------------------------------------------------- /nextauth/src/lib/post.js: -------------------------------------------------------------------------------- 1 | import { authAxios, authNodeAxios, axiosPublic } from "@/config" 2 | import { getErrorMessage } from "@/utils" 3 | 4 | export const createNewPost = async(body, accessToken) => { 5 | try { 6 | authAxios.accessToken = accessToken 7 | const result = await authAxios.post("/posts", body) 8 | return {isError: false, data: result.data, message: 'Post created successfully'} 9 | } catch (error) { 10 | return {isError: true, data:null, message: getErrorMessage(error)} 11 | } 12 | } 13 | 14 | export const getPublicPosts = async() => { 15 | try { 16 | const result = await axiosPublic.get("/posts?type=public") 17 | return result.data 18 | } catch (error) { 19 | throw new Error(error.message) 20 | } 21 | } 22 | 23 | export const getAllPosts = async(accessToken) => { 24 | try { 25 | authAxios.accessToken = accessToken 26 | const result = await authAxios.get("/posts") 27 | return result.data 28 | } catch (error) { 29 | throw new Error(error.message) 30 | } 31 | } 32 | 33 | export const getAllNodeAPIPosts = async(accessToken) => { 34 | try { 35 | authNodeAxios.accessToken = accessToken 36 | const result = await authNodeAxios.get("/posts") 37 | return result.data 38 | } catch (error) { 39 | throw new Error(error.message) 40 | } 41 | } -------------------------------------------------------------------------------- /nextauth/src/lib/user.js: -------------------------------------------------------------------------------- 1 | import { authAxios } from "@/config" 2 | 3 | export const getAllUsers = async(accessToken) => { 4 | try { 5 | authAxios.accessToken = accessToken 6 | const result = await authAxios.get("/users") 7 | return result.data 8 | } catch (error) { 9 | throw new Error(error.message) 10 | } 11 | } -------------------------------------------------------------------------------- /nextauth/src/middleware.js: -------------------------------------------------------------------------------- 1 | import {headers} from 'next/headers' 2 | import { decryptString, encryptString, verifyJwtToken } from './utils' 3 | import { NextResponse } from 'next/server' 4 | 5 | export const config = { 6 | matcher: ["/api/users", "/api/users/:path*", "/api/posts", "/api/posts/:path*"] 7 | } 8 | 9 | const publicPostsRegexPattern = /^\/api\/posts\?type=public$/; 10 | 11 | export async function middleware(request){ 12 | try { 13 | const nextUrl = request.nextUrl 14 | 15 | const pathname = nextUrl.pathname 16 | 17 | const search = nextUrl?.search 18 | 19 | const path = `${pathname}${search}` 20 | 21 | const httpMethod = request.method 22 | 23 | // /api/posts?type=public 24 | // public posts 25 | if(publicPostsRegexPattern.test(path) && httpMethod === "GET"){ 26 | request.headers.set("user", null) 27 | return NextResponse.next({request}) 28 | } 29 | 30 | // authenticate the request 31 | 32 | const headerList = headers() 33 | const authorization = headerList.get('Authorization') 34 | if(!authorization) return Response.json("You are not authenticated, please login to continue", {status: 401}) 35 | const accessToken = authorization.split("Bearer ")[1] 36 | 37 | if(!accessToken) return Response.json("You are not authenticated, please login to continue", {status: 401}) 38 | 39 | const result = await verifyJwtToken(accessToken) 40 | 41 | console.log("verifyJwtToken ", result) 42 | 43 | if(result.isError) return Response.json("You are not authorised to access this resource, please login to continue", {status: 403}) 44 | 45 | const decrypted = decryptString(result?.data?.user) 46 | 47 | if(!decrypted) return Response.json("You are not authorised to access this resource, please login to continue", {status: 403}) 48 | 49 | request.headers.set("user", JSON.stringify(decrypted)) 50 | 51 | return NextResponse.next({request}) 52 | } catch (error) { 53 | return Response.json(error.message, {status: 500}) 54 | } 55 | } -------------------------------------------------------------------------------- /nextauth/src/models/account.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | const { Schema } = mongoose 4 | 5 | const AccountSchema = new Schema({ 6 | id: {type: Schema.ObjectId}, 7 | provider: {type: String, default: "nextApp"}, 8 | type: {type: String, default: "credentials"}, 9 | providerAccountId: {type: String, default: "782389209ej"}, 10 | access_token: {type: String, default: "ehdhjdiuuiweuie"}, 11 | scope: {type: String, default: "all"}, 12 | userId: {type: Schema.ObjectId, ref: "User"} 13 | }) 14 | 15 | const AccountModel = mongoose.models.Account || mongoose.model("Account", AccountSchema) 16 | 17 | export default AccountModel 18 | -------------------------------------------------------------------------------- /nextauth/src/models/index.js: -------------------------------------------------------------------------------- 1 | export { default as UserModel } from './user' 2 | export { default as AccountModel } from './account' 3 | export { default as PostModel } from './post' 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /nextauth/src/models/post.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | const { Schema } = mongoose 4 | 5 | const PostSchema = new Schema({ 6 | id: {type: Schema.ObjectId}, 7 | title: String, 8 | slug: String, 9 | description: String, 10 | content: String, 11 | photo: String, 12 | user: {type: Schema.ObjectId, ref: "User"} 13 | }, {timestamps: true}) 14 | 15 | const PostModel = mongoose.models.Post || mongoose.model("Post", PostSchema) 16 | 17 | export default PostModel -------------------------------------------------------------------------------- /nextauth/src/models/user.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | const { Schema } = mongoose 4 | 5 | mongoose.connect(process.env.MONGODB_URI) 6 | 7 | mongoose.Promise = global.Promise 8 | 9 | const UserSchema = new Schema({ 10 | id: {type: Schema.ObjectId}, 11 | name: String, 12 | email: String, 13 | password: String, 14 | role: String, 15 | avatar: {type: String, default: "/avatar3.png"}, 16 | isVerifiedEmail: {type: Boolean, default: false}, 17 | isBlocked: {type: Boolean, default: false} 18 | }, {timestamps: true}) 19 | 20 | const UserModel = mongoose.models.User || mongoose.model("User", UserSchema) 21 | 22 | export default UserModel -------------------------------------------------------------------------------- /nextauth/src/providers/index.js: -------------------------------------------------------------------------------- 1 | 'use client' 2 | import { SessionProvider } from "next-auth/react" 3 | 4 | export const NextAuthSessionProvider = ({children}) => { 5 | return 6 | {children} 7 | 8 | } -------------------------------------------------------------------------------- /nextauth/src/schema/index.js: -------------------------------------------------------------------------------- 1 | import z from 'zod' 2 | 3 | export const UserZodSchema = z.object({ 4 | id: z.string({required_error: "User Id must be provided"}).optional(), 5 | 6 | email: z.string({required_error: "Email must be provided"}) 7 | .email(z.string({required_error: "Email must be a valid email address"})) 8 | .min(3, { message: "Enter a valid email address"}), 9 | 10 | password: z.string({required_error: "Password must be provided"}) 11 | .min(8, { message: "Password must be at least 8 characters"}). 12 | refine((pw) => /[0-9]/.test(pw), "Password must contain a number") 13 | 14 | }) 15 | 16 | export const PostZodSchema = z.object({ 17 | 18 | title: z.string({required_error: "Title cannot be empty"}) 19 | .min(10, { message: "Title must be at least 10 characters"}) 20 | .max(150,{ message: "Title is too long, Max is 150 characters"}), 21 | 22 | description: z.string({required_error: "Description cannot be empty"}) 23 | .min(10, { message: "Description must be at least 10 characters"}) 24 | .max(255,{ message: "Description is too long, Max is 255 characters"}), 25 | 26 | content: z.string({required_error: "Content cannot be empty"}) 27 | .min(10, { message: "Content must be at least 10 characters"}), 28 | 29 | photo: z.string({required_error: "Photo url cannot be empty"}) 30 | .min(2, { message: "Photo url must be provided"}) 31 | }) -------------------------------------------------------------------------------- /nextauth/src/services/index.js: -------------------------------------------------------------------------------- 1 | export { default as UserService } from './user' 2 | export { default as PostService } from './post' 3 | 4 | 5 | -------------------------------------------------------------------------------- /nextauth/src/services/post.js: -------------------------------------------------------------------------------- 1 | import { PostModel } from "@/models" 2 | import { nanoid } from "nanoid" 3 | import slugify from "slugify" 4 | 5 | const createNewPost = async(body) => { 6 | try { 7 | const title = `${body.title}-${nanoid(5)}` 8 | const slug = slugify(title, {replacement: '-'}) 9 | const result = await PostModel.create({...body, slug}) 10 | return {isError: false, data: result, message: "success" } 11 | } catch (error) { 12 | return {isError: true, data: null, message: error.message } 13 | 14 | } 15 | } 16 | 17 | const getAllPosts = async() =>{ 18 | try { 19 | const result = await PostModel.find({}).populate({path: "user", select: ["name", "email", "avatar", "_id"]}).lean().exec() 20 | const data = result?.map(post => ({...post, id: post._id?.toString(), user: {...post.user, id: post?.user?._id.toString()} })) 21 | return {isError: false, data, message: "success" } 22 | } catch (error) { 23 | return {isError: true, data: null, message: error.message } 24 | } 25 | } 26 | 27 | const PostService = { createNewPost, getAllPosts} 28 | 29 | export default PostService 30 | 31 | -------------------------------------------------------------------------------- /nextauth/src/services/user.js: -------------------------------------------------------------------------------- 1 | const { UserModel, AccountModel } = require("@/models"); 2 | import bcrypt from "bcrypt"; 3 | 4 | 5 | const createUser = async(body) => { 6 | try { 7 | const result = await UserModel.findOne({email: body.email}).lean().exec(); 8 | const name = body?.email?.split('@')[0] 9 | if(!result){ 10 | const hashPassword = await bcrypt.hash(body.password, 10) 11 | const newUser = await UserModel.create({...body, name, role: "user", password: hashPassword}) 12 | await AccountModel.create({userId: newUser._id}) 13 | return { isError: false, data: {id: newUser?._id?.toString(), name: newUser.name, role: newUser.role, email: newUser.email, avatar: newUser.avatar}, message: "Success"} 14 | } 15 | // if account is blocked 16 | if(result?.isBlocked) return {isError: true, data: null, message: "This account is suspended, please contact the support"} 17 | // check for password 18 | const isMatch = await bcrypt.compare(body.password, result?.password) 19 | if(!isMatch) return {isError: true, data: null, message: "Wrong Email/Password provided"} 20 | 21 | return {isError: false, data: {id: result?._id?.toString(), name: result.name, role: result.role, email: result.email, avatar: result.avatar}, message: "Success"} 22 | 23 | } catch (error) { 24 | return {isError: true, data: null, message: "An error occurred trying to process the request, please try again later"}; 25 | } 26 | } 27 | 28 | 29 | const getCurrentUser = async(id) => { 30 | try { 31 | const result = await UserModel.findOne({_id: id}).lean().exec(); 32 | if(!result) return {isError: true, data: null, message: "User does not exist"}; 33 | // if account is blocked 34 | if(result?.isBlocked) return {isError: true, data: null, message: "This account is suspended, please contact the support"} 35 | return {isError: false, data: {id: result?._id?.toString(), name: result.name, role: result.role, email: result.email, avatar: result.avatar}, message: "Success"} 36 | 37 | } catch (error) { 38 | return {isError: true, data: null, message: "An error occurred trying to process the request, please try again later"}; 39 | } 40 | } 41 | 42 | 43 | const getAllUsers = async() => { 44 | try { 45 | const results = await UserModel.find({}).lean().exec() 46 | const data = results?.map(user => ({id: user?._id?.toString(), name: user.name, email: user.email, role: user.role, avatar: user.avatar})) 47 | return {isError: false, data, message: "success"} 48 | } catch (error) { 49 | return {isError: true, data: null, message: "An error occurred trying to process the request, please try again later"}; 50 | } 51 | } 52 | 53 | 54 | const UserService = { createUser, getCurrentUser, getAllUsers } 55 | 56 | export default UserService -------------------------------------------------------------------------------- /nextauth/src/utils/index.js: -------------------------------------------------------------------------------- 1 | import * as joseJwt from "jose" 2 | import { nanoid } from "nanoid" 3 | import CryptoJS from "crypto-js" 4 | 5 | export const signJwtToken = async(payload) => { 6 | const privateKey = process.env.ACCESS_TOKEN_PRIVATE_KEY 7 | const expiresIn = "5m" 8 | const token = await new joseJwt.SignJWT(payload) 9 | .setProtectedHeader({alg: "HS256"}) 10 | .setJti(nanoid()) 11 | .setIssuedAt() 12 | .setExpirationTime(expiresIn) 13 | .sign(new TextEncoder().encode(privateKey)) 14 | return token 15 | } 16 | 17 | export const verifyJwtToken = async(token) => { 18 | try { 19 | const privateKey = process.env.ACCESS_TOKEN_PRIVATE_KEY 20 | const result = await joseJwt.jwtVerify(token, new TextEncoder().encode(privateKey)) 21 | return {data: result.payload, isError: false, message: "success" } 22 | } catch (error) { 23 | return { data: null, isError: true, message:error.message } 24 | } 25 | } 26 | 27 | export const decodeJwtToken = (token) => { 28 | try { 29 | const payload = joseJwt.decodeJwt(token) 30 | return {data: payload, isError: false, message: "success" } 31 | } catch (error) { 32 | return { data: null, isError: true, message:error.message } 33 | } 34 | } 35 | 36 | export const encryptString = (str) => { 37 | const secretKey = CryptoJS.enc.Utf8.parse(process.env.CRYPTO_ENCRYPTION_KEY) 38 | const iv = CryptoJS.enc.Utf8.parse(process.env.CRYPTO_IV_KEY) 39 | const encrypted = CryptoJS.AES.encrypt(str, secretKey, {iv}) 40 | const hexString = CryptoJS.enc.Hex.stringify(encrypted.ciphertext) 41 | return hexString 42 | } 43 | 44 | export const decryptString = (hexString) => { 45 | const secretKey = CryptoJS.enc.Utf8.parse(process.env.CRYPTO_ENCRYPTION_KEY) 46 | const iv = CryptoJS.enc.Utf8.parse(process.env.CRYPTO_IV_KEY) 47 | const ciphertext = CryptoJS.enc.Hex.parse(hexString) 48 | const decrypted = CryptoJS.AES.decrypt({ciphertext}, secretKey, {iv}).toString(CryptoJS.enc.Utf8) 49 | console.log("decrypted: ", decrypted) 50 | return JSON.parse(decrypted) 51 | } 52 | 53 | export const checkUserRole =(role) => { 54 | return { 55 | isAdminRole: role === 'admin', 56 | isUserRole: role === 'user', 57 | } 58 | } 59 | 60 | export const formatDate = (date) => { 61 | return new Intl.DateTimeFormat(undefined, { dateStyle: "short", timeStyle: "short", }).format(new Date(date)) 62 | } 63 | 64 | export const getErrorMessage = (error) => { 65 | let message = error.message 66 | if(error?.response?.data){ 67 | message = error.response.data 68 | } 69 | return message 70 | } -------------------------------------------------------------------------------- /nextauth/src/validators/index.js: -------------------------------------------------------------------------------- 1 | export const validateZodInput = (payload, schema, isArrayErrorResult = false) => { 2 | try { 3 | const parseResult = schema.parse(payload) 4 | return { isError: false, data: parseResult, message: "success" } 5 | } catch (error) { 6 | const errors = {} 7 | let issues = error.issues 8 | const message = issues?.map(issue => issue.message)?.join("\r\n") 9 | if(isArrayErrorResult){ 10 | issues = issues.map(issue => issue.message) 11 | return { isError: true, message, data: issues} 12 | } 13 | for (const issue of issues){ 14 | errors[issue?.path[0]] = issue.message 15 | } 16 | return {isError: true, message, data: errors} 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /node-server/.env: -------------------------------------------------------------------------------- 1 | CRYPTO_ENCRYPTION_KEY = "e883993939ijjdiuduuehyrhuuer" 2 | 3 | CRYPTO_IV_KEY = "8289eudjjdue88e9e0e" 4 | 5 | ACCESS_TOKEN_PRIVATE_KEY = "ek8qvPCLavjSOlSjR/nHGS/iJV6fOhg/MftYpOMMIDbMKC+U0grPFtu6q8jg/dJBJEjTL2dafb+0ADqeSjsF5w0EzpY5PIWIXvP52anY1hpH3FjYeeFe2PS2jmukwb1J 6 | d7zhlh9i7MA55QU+7LH7PYupK1k23KMP6sZheblAD17HTg==" 7 | 8 | MONGODB_URI = "" 9 | -------------------------------------------------------------------------------- /node-server/controllers/index.mjs: -------------------------------------------------------------------------------- 1 | export { default as PostController } from './post.mjs' -------------------------------------------------------------------------------- /node-server/controllers/post.mjs: -------------------------------------------------------------------------------- 1 | import PostService from "../services/post.mjs" 2 | 3 | export const getPostsController = async (req, res) => { 4 | const result = await PostService.getAllPosts() 5 | console.log("Node API",result) 6 | if(result.isError) return res.status(result.status).send(result.message) 7 | return res.status(result.status).json(result.data) 8 | } 9 | 10 | const PostController = { getPostsController } 11 | 12 | export default PostController -------------------------------------------------------------------------------- /node-server/db/index.mjs: -------------------------------------------------------------------------------- 1 | import mongoose from 'mongoose'; 2 | 3 | const connectDb = async() => { 4 | try { 5 | await mongoose.connect(process.env.MONGODB_URI) 6 | console.log("Mongodb database connection established") 7 | } catch (error) { 8 | console.log("Mongodb database connection error ", error.messsage) 9 | } 10 | } 11 | 12 | export default connectDb -------------------------------------------------------------------------------- /node-server/index.mjs: -------------------------------------------------------------------------------- 1 | import dotenv from 'dotenv' 2 | dotenv.config() 3 | import express from "express"; 4 | import http from "http"; 5 | import cors from "cors"; 6 | import compression from "compression"; 7 | import connectDb from './db/index.mjs'; 8 | import { postRoutes } from './routes/index.mjs'; 9 | 10 | const app = express(); 11 | 12 | app.use(cors({origin: true})) 13 | app.use(compression()); 14 | app.use(express.json()); 15 | app.use(express.urlencoded({ extended: false})); 16 | 17 | app.use("/api/", postRoutes) 18 | 19 | // connect db 20 | connectDb() 21 | 22 | 23 | app.use((req, res) => { 24 | res.send("Server is up & running") 25 | }) 26 | 27 | const server = http.createServer(app); 28 | 29 | server.listen(5001, () => { 30 | console.log(`Server running on port 5001 - http://localhost:5001`) 31 | }) -------------------------------------------------------------------------------- /node-server/middleware/index.mjs: -------------------------------------------------------------------------------- 1 | import { decryptString, verifyJwtToken } from "../utils/index.mjs"; 2 | 3 | export const authenticateUser = async (req, res, next) => { 4 | try { 5 | const authorization = req.headers["authorization"] 6 | if (!authorization){ 7 | return res.status(401).send("You are not authenticated, please login to continue"); 8 | } 9 | const accessToken = authorization.split("Bearer ")[1]; 10 | 11 | if (!accessToken){ 12 | return res.status(401).send("You are not authenticated, please login to continue"); 13 | } 14 | 15 | const result = await verifyJwtToken(accessToken); 16 | 17 | console.log("verifyJwtToken ", result); 18 | 19 | if (result.isError){ 20 | return res.status(403).send("You are not authorised to access this resource, please login to continue"); 21 | } 22 | 23 | const decrypted = decryptString(result?.data?.user); 24 | 25 | if (!decrypted){ 26 | return res.status(401).send("You are not authorised to access this resource, please login to continue"); 27 | } 28 | req.user = decrypted 29 | return next() 30 | } catch (error) { 31 | return res.status(500).send("Sorry, something went wrong. Please, try again later") 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /node-server/models/index.mjs: -------------------------------------------------------------------------------- 1 | export {default as PostModel } from './post.mjs' 2 | export {default as UserModel } from './user.mjs' 3 | -------------------------------------------------------------------------------- /node-server/models/post.mjs: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | const { Schema } = mongoose 4 | 5 | const PostSchema = new Schema({ 6 | id: {type: Schema.ObjectId}, 7 | title: String, 8 | slug: String, 9 | description: String, 10 | content: String, 11 | photo: String, 12 | user: {type: Schema.ObjectId, ref: "User"} 13 | }, {timestamps: true}) 14 | 15 | const PostModel = mongoose.model("Post", PostSchema) 16 | 17 | export default PostModel -------------------------------------------------------------------------------- /node-server/models/user.mjs: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | const { Schema } = mongoose 4 | 5 | const UserSchema = new Schema({ 6 | id: {type: Schema.Types.ObjectId}, 7 | name: String, 8 | email: String, 9 | password: String, 10 | role: String, 11 | avatar: {type: String, default: "/avatar3.png"}, 12 | isVerifiedEmail: {type: Boolean, default: false}, 13 | isBlocked: {type: Boolean, default: false} 14 | }, {timestamps: true}) 15 | 16 | const UserModel = mongoose.model("User", UserSchema) 17 | 18 | export default UserModel -------------------------------------------------------------------------------- /node-server/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "server", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "bcrypt": "^5.1.1", 13 | "compression": "^1.7.4", 14 | "cors": "^2.8.5", 15 | "crypto-js": "^4.2.0", 16 | "dotenv": "^16.3.1", 17 | "express": "^4.18.2", 18 | "jose": "^5.1.1", 19 | "mongoose": "^8.0.1", 20 | "nanoid": "^5.0.3" 21 | }, 22 | "devDependencies": { 23 | "nodemon": "^3.0.1" 24 | } 25 | }, 26 | "node_modules/@mapbox/node-pre-gyp": { 27 | "version": "1.0.11", 28 | "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", 29 | "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", 30 | "dependencies": { 31 | "detect-libc": "^2.0.0", 32 | "https-proxy-agent": "^5.0.0", 33 | "make-dir": "^3.1.0", 34 | "node-fetch": "^2.6.7", 35 | "nopt": "^5.0.0", 36 | "npmlog": "^5.0.1", 37 | "rimraf": "^3.0.2", 38 | "semver": "^7.3.5", 39 | "tar": "^6.1.11" 40 | }, 41 | "bin": { 42 | "node-pre-gyp": "bin/node-pre-gyp" 43 | } 44 | }, 45 | "node_modules/@mongodb-js/saslprep": { 46 | "version": "1.1.1", 47 | "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.1.tgz", 48 | "integrity": "sha512-t7c5K033joZZMspnHg/gWPE4kandgc2OxE74aYOtGKfgB9VPuVJPix0H6fhmm2erj5PBJ21mqcx34lpIGtUCsQ==", 49 | "dependencies": { 50 | "sparse-bitfield": "^3.0.3" 51 | } 52 | }, 53 | "node_modules/@types/node": { 54 | "version": "20.9.1", 55 | "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.1.tgz", 56 | "integrity": "sha512-HhmzZh5LSJNS5O8jQKpJ/3ZcrrlG6L70hpGqMIAoM9YVD0YBRNWYsfwcXq8VnSjlNpCpgLzMXdiPo+dxcvSmiA==", 57 | "dependencies": { 58 | "undici-types": "~5.26.4" 59 | } 60 | }, 61 | "node_modules/@types/webidl-conversions": { 62 | "version": "7.0.3", 63 | "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", 64 | "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==" 65 | }, 66 | "node_modules/@types/whatwg-url": { 67 | "version": "8.2.2", 68 | "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz", 69 | "integrity": "sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==", 70 | "dependencies": { 71 | "@types/node": "*", 72 | "@types/webidl-conversions": "*" 73 | } 74 | }, 75 | "node_modules/abbrev": { 76 | "version": "1.1.1", 77 | "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", 78 | "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" 79 | }, 80 | "node_modules/accepts": { 81 | "version": "1.3.8", 82 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", 83 | "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", 84 | "dependencies": { 85 | "mime-types": "~2.1.34", 86 | "negotiator": "0.6.3" 87 | }, 88 | "engines": { 89 | "node": ">= 0.6" 90 | } 91 | }, 92 | "node_modules/agent-base": { 93 | "version": "6.0.2", 94 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", 95 | "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", 96 | "dependencies": { 97 | "debug": "4" 98 | }, 99 | "engines": { 100 | "node": ">= 6.0.0" 101 | } 102 | }, 103 | "node_modules/agent-base/node_modules/debug": { 104 | "version": "4.3.4", 105 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 106 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 107 | "dependencies": { 108 | "ms": "2.1.2" 109 | }, 110 | "engines": { 111 | "node": ">=6.0" 112 | }, 113 | "peerDependenciesMeta": { 114 | "supports-color": { 115 | "optional": true 116 | } 117 | } 118 | }, 119 | "node_modules/agent-base/node_modules/ms": { 120 | "version": "2.1.2", 121 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 122 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 123 | }, 124 | "node_modules/ansi-regex": { 125 | "version": "5.0.1", 126 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 127 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 128 | "engines": { 129 | "node": ">=8" 130 | } 131 | }, 132 | "node_modules/anymatch": { 133 | "version": "3.1.3", 134 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", 135 | "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", 136 | "dev": true, 137 | "dependencies": { 138 | "normalize-path": "^3.0.0", 139 | "picomatch": "^2.0.4" 140 | }, 141 | "engines": { 142 | "node": ">= 8" 143 | } 144 | }, 145 | "node_modules/aproba": { 146 | "version": "2.0.0", 147 | "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", 148 | "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" 149 | }, 150 | "node_modules/are-we-there-yet": { 151 | "version": "2.0.0", 152 | "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", 153 | "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", 154 | "dependencies": { 155 | "delegates": "^1.0.0", 156 | "readable-stream": "^3.6.0" 157 | }, 158 | "engines": { 159 | "node": ">=10" 160 | } 161 | }, 162 | "node_modules/array-flatten": { 163 | "version": "1.1.1", 164 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 165 | "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" 166 | }, 167 | "node_modules/balanced-match": { 168 | "version": "1.0.2", 169 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 170 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" 171 | }, 172 | "node_modules/bcrypt": { 173 | "version": "5.1.1", 174 | "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", 175 | "integrity": "sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==", 176 | "hasInstallScript": true, 177 | "dependencies": { 178 | "@mapbox/node-pre-gyp": "^1.0.11", 179 | "node-addon-api": "^5.0.0" 180 | }, 181 | "engines": { 182 | "node": ">= 10.0.0" 183 | } 184 | }, 185 | "node_modules/binary-extensions": { 186 | "version": "2.2.0", 187 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 188 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", 189 | "dev": true, 190 | "engines": { 191 | "node": ">=8" 192 | } 193 | }, 194 | "node_modules/body-parser": { 195 | "version": "1.20.1", 196 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", 197 | "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", 198 | "dependencies": { 199 | "bytes": "3.1.2", 200 | "content-type": "~1.0.4", 201 | "debug": "2.6.9", 202 | "depd": "2.0.0", 203 | "destroy": "1.2.0", 204 | "http-errors": "2.0.0", 205 | "iconv-lite": "0.4.24", 206 | "on-finished": "2.4.1", 207 | "qs": "6.11.0", 208 | "raw-body": "2.5.1", 209 | "type-is": "~1.6.18", 210 | "unpipe": "1.0.0" 211 | }, 212 | "engines": { 213 | "node": ">= 0.8", 214 | "npm": "1.2.8000 || >= 1.4.16" 215 | } 216 | }, 217 | "node_modules/body-parser/node_modules/bytes": { 218 | "version": "3.1.2", 219 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", 220 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", 221 | "engines": { 222 | "node": ">= 0.8" 223 | } 224 | }, 225 | "node_modules/brace-expansion": { 226 | "version": "1.1.11", 227 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 228 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 229 | "dependencies": { 230 | "balanced-match": "^1.0.0", 231 | "concat-map": "0.0.1" 232 | } 233 | }, 234 | "node_modules/braces": { 235 | "version": "3.0.2", 236 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 237 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 238 | "dev": true, 239 | "dependencies": { 240 | "fill-range": "^7.0.1" 241 | }, 242 | "engines": { 243 | "node": ">=8" 244 | } 245 | }, 246 | "node_modules/bson": { 247 | "version": "6.2.0", 248 | "resolved": "https://registry.npmjs.org/bson/-/bson-6.2.0.tgz", 249 | "integrity": "sha512-ID1cI+7bazPDyL9wYy9GaQ8gEEohWvcUl/Yf0dIdutJxnmInEEyCsb4awy/OiBfall7zBA179Pahi3vCdFze3Q==", 250 | "engines": { 251 | "node": ">=16.20.1" 252 | } 253 | }, 254 | "node_modules/bytes": { 255 | "version": "3.0.0", 256 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", 257 | "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", 258 | "engines": { 259 | "node": ">= 0.8" 260 | } 261 | }, 262 | "node_modules/call-bind": { 263 | "version": "1.0.5", 264 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", 265 | "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", 266 | "dependencies": { 267 | "function-bind": "^1.1.2", 268 | "get-intrinsic": "^1.2.1", 269 | "set-function-length": "^1.1.1" 270 | }, 271 | "funding": { 272 | "url": "https://github.com/sponsors/ljharb" 273 | } 274 | }, 275 | "node_modules/chokidar": { 276 | "version": "3.5.3", 277 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", 278 | "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", 279 | "dev": true, 280 | "funding": [ 281 | { 282 | "type": "individual", 283 | "url": "https://paulmillr.com/funding/" 284 | } 285 | ], 286 | "dependencies": { 287 | "anymatch": "~3.1.2", 288 | "braces": "~3.0.2", 289 | "glob-parent": "~5.1.2", 290 | "is-binary-path": "~2.1.0", 291 | "is-glob": "~4.0.1", 292 | "normalize-path": "~3.0.0", 293 | "readdirp": "~3.6.0" 294 | }, 295 | "engines": { 296 | "node": ">= 8.10.0" 297 | }, 298 | "optionalDependencies": { 299 | "fsevents": "~2.3.2" 300 | } 301 | }, 302 | "node_modules/chownr": { 303 | "version": "2.0.0", 304 | "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", 305 | "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", 306 | "engines": { 307 | "node": ">=10" 308 | } 309 | }, 310 | "node_modules/color-support": { 311 | "version": "1.1.3", 312 | "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", 313 | "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", 314 | "bin": { 315 | "color-support": "bin.js" 316 | } 317 | }, 318 | "node_modules/compressible": { 319 | "version": "2.0.18", 320 | "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", 321 | "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", 322 | "dependencies": { 323 | "mime-db": ">= 1.43.0 < 2" 324 | }, 325 | "engines": { 326 | "node": ">= 0.6" 327 | } 328 | }, 329 | "node_modules/compression": { 330 | "version": "1.7.4", 331 | "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", 332 | "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", 333 | "dependencies": { 334 | "accepts": "~1.3.5", 335 | "bytes": "3.0.0", 336 | "compressible": "~2.0.16", 337 | "debug": "2.6.9", 338 | "on-headers": "~1.0.2", 339 | "safe-buffer": "5.1.2", 340 | "vary": "~1.1.2" 341 | }, 342 | "engines": { 343 | "node": ">= 0.8.0" 344 | } 345 | }, 346 | "node_modules/concat-map": { 347 | "version": "0.0.1", 348 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 349 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" 350 | }, 351 | "node_modules/console-control-strings": { 352 | "version": "1.1.0", 353 | "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", 354 | "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" 355 | }, 356 | "node_modules/content-disposition": { 357 | "version": "0.5.4", 358 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", 359 | "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", 360 | "dependencies": { 361 | "safe-buffer": "5.2.1" 362 | }, 363 | "engines": { 364 | "node": ">= 0.6" 365 | } 366 | }, 367 | "node_modules/content-disposition/node_modules/safe-buffer": { 368 | "version": "5.2.1", 369 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 370 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 371 | "funding": [ 372 | { 373 | "type": "github", 374 | "url": "https://github.com/sponsors/feross" 375 | }, 376 | { 377 | "type": "patreon", 378 | "url": "https://www.patreon.com/feross" 379 | }, 380 | { 381 | "type": "consulting", 382 | "url": "https://feross.org/support" 383 | } 384 | ] 385 | }, 386 | "node_modules/content-type": { 387 | "version": "1.0.5", 388 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", 389 | "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", 390 | "engines": { 391 | "node": ">= 0.6" 392 | } 393 | }, 394 | "node_modules/cookie": { 395 | "version": "0.5.0", 396 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", 397 | "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", 398 | "engines": { 399 | "node": ">= 0.6" 400 | } 401 | }, 402 | "node_modules/cookie-signature": { 403 | "version": "1.0.6", 404 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 405 | "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" 406 | }, 407 | "node_modules/cors": { 408 | "version": "2.8.5", 409 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", 410 | "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", 411 | "dependencies": { 412 | "object-assign": "^4", 413 | "vary": "^1" 414 | }, 415 | "engines": { 416 | "node": ">= 0.10" 417 | } 418 | }, 419 | "node_modules/crypto-js": { 420 | "version": "4.2.0", 421 | "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", 422 | "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" 423 | }, 424 | "node_modules/debug": { 425 | "version": "2.6.9", 426 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 427 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 428 | "dependencies": { 429 | "ms": "2.0.0" 430 | } 431 | }, 432 | "node_modules/define-data-property": { 433 | "version": "1.1.1", 434 | "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", 435 | "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", 436 | "dependencies": { 437 | "get-intrinsic": "^1.2.1", 438 | "gopd": "^1.0.1", 439 | "has-property-descriptors": "^1.0.0" 440 | }, 441 | "engines": { 442 | "node": ">= 0.4" 443 | } 444 | }, 445 | "node_modules/delegates": { 446 | "version": "1.0.0", 447 | "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", 448 | "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" 449 | }, 450 | "node_modules/depd": { 451 | "version": "2.0.0", 452 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", 453 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", 454 | "engines": { 455 | "node": ">= 0.8" 456 | } 457 | }, 458 | "node_modules/destroy": { 459 | "version": "1.2.0", 460 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", 461 | "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", 462 | "engines": { 463 | "node": ">= 0.8", 464 | "npm": "1.2.8000 || >= 1.4.16" 465 | } 466 | }, 467 | "node_modules/detect-libc": { 468 | "version": "2.0.2", 469 | "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", 470 | "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", 471 | "engines": { 472 | "node": ">=8" 473 | } 474 | }, 475 | "node_modules/dotenv": { 476 | "version": "16.3.1", 477 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", 478 | "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", 479 | "engines": { 480 | "node": ">=12" 481 | }, 482 | "funding": { 483 | "url": "https://github.com/motdotla/dotenv?sponsor=1" 484 | } 485 | }, 486 | "node_modules/ee-first": { 487 | "version": "1.1.1", 488 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 489 | "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" 490 | }, 491 | "node_modules/emoji-regex": { 492 | "version": "8.0.0", 493 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 494 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" 495 | }, 496 | "node_modules/encodeurl": { 497 | "version": "1.0.2", 498 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 499 | "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", 500 | "engines": { 501 | "node": ">= 0.8" 502 | } 503 | }, 504 | "node_modules/escape-html": { 505 | "version": "1.0.3", 506 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 507 | "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" 508 | }, 509 | "node_modules/etag": { 510 | "version": "1.8.1", 511 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 512 | "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", 513 | "engines": { 514 | "node": ">= 0.6" 515 | } 516 | }, 517 | "node_modules/express": { 518 | "version": "4.18.2", 519 | "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", 520 | "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", 521 | "dependencies": { 522 | "accepts": "~1.3.8", 523 | "array-flatten": "1.1.1", 524 | "body-parser": "1.20.1", 525 | "content-disposition": "0.5.4", 526 | "content-type": "~1.0.4", 527 | "cookie": "0.5.0", 528 | "cookie-signature": "1.0.6", 529 | "debug": "2.6.9", 530 | "depd": "2.0.0", 531 | "encodeurl": "~1.0.2", 532 | "escape-html": "~1.0.3", 533 | "etag": "~1.8.1", 534 | "finalhandler": "1.2.0", 535 | "fresh": "0.5.2", 536 | "http-errors": "2.0.0", 537 | "merge-descriptors": "1.0.1", 538 | "methods": "~1.1.2", 539 | "on-finished": "2.4.1", 540 | "parseurl": "~1.3.3", 541 | "path-to-regexp": "0.1.7", 542 | "proxy-addr": "~2.0.7", 543 | "qs": "6.11.0", 544 | "range-parser": "~1.2.1", 545 | "safe-buffer": "5.2.1", 546 | "send": "0.18.0", 547 | "serve-static": "1.15.0", 548 | "setprototypeof": "1.2.0", 549 | "statuses": "2.0.1", 550 | "type-is": "~1.6.18", 551 | "utils-merge": "1.0.1", 552 | "vary": "~1.1.2" 553 | }, 554 | "engines": { 555 | "node": ">= 0.10.0" 556 | } 557 | }, 558 | "node_modules/express/node_modules/safe-buffer": { 559 | "version": "5.2.1", 560 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 561 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 562 | "funding": [ 563 | { 564 | "type": "github", 565 | "url": "https://github.com/sponsors/feross" 566 | }, 567 | { 568 | "type": "patreon", 569 | "url": "https://www.patreon.com/feross" 570 | }, 571 | { 572 | "type": "consulting", 573 | "url": "https://feross.org/support" 574 | } 575 | ] 576 | }, 577 | "node_modules/fill-range": { 578 | "version": "7.0.1", 579 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 580 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 581 | "dev": true, 582 | "dependencies": { 583 | "to-regex-range": "^5.0.1" 584 | }, 585 | "engines": { 586 | "node": ">=8" 587 | } 588 | }, 589 | "node_modules/finalhandler": { 590 | "version": "1.2.0", 591 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", 592 | "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", 593 | "dependencies": { 594 | "debug": "2.6.9", 595 | "encodeurl": "~1.0.2", 596 | "escape-html": "~1.0.3", 597 | "on-finished": "2.4.1", 598 | "parseurl": "~1.3.3", 599 | "statuses": "2.0.1", 600 | "unpipe": "~1.0.0" 601 | }, 602 | "engines": { 603 | "node": ">= 0.8" 604 | } 605 | }, 606 | "node_modules/forwarded": { 607 | "version": "0.2.0", 608 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", 609 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", 610 | "engines": { 611 | "node": ">= 0.6" 612 | } 613 | }, 614 | "node_modules/fresh": { 615 | "version": "0.5.2", 616 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 617 | "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", 618 | "engines": { 619 | "node": ">= 0.6" 620 | } 621 | }, 622 | "node_modules/fs-minipass": { 623 | "version": "2.1.0", 624 | "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", 625 | "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", 626 | "dependencies": { 627 | "minipass": "^3.0.0" 628 | }, 629 | "engines": { 630 | "node": ">= 8" 631 | } 632 | }, 633 | "node_modules/fs-minipass/node_modules/minipass": { 634 | "version": "3.3.6", 635 | "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", 636 | "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", 637 | "dependencies": { 638 | "yallist": "^4.0.0" 639 | }, 640 | "engines": { 641 | "node": ">=8" 642 | } 643 | }, 644 | "node_modules/fs.realpath": { 645 | "version": "1.0.0", 646 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 647 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" 648 | }, 649 | "node_modules/fsevents": { 650 | "version": "2.3.3", 651 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", 652 | "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", 653 | "dev": true, 654 | "hasInstallScript": true, 655 | "optional": true, 656 | "os": [ 657 | "darwin" 658 | ], 659 | "engines": { 660 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 661 | } 662 | }, 663 | "node_modules/function-bind": { 664 | "version": "1.1.2", 665 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", 666 | "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", 667 | "funding": { 668 | "url": "https://github.com/sponsors/ljharb" 669 | } 670 | }, 671 | "node_modules/gauge": { 672 | "version": "3.0.2", 673 | "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", 674 | "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", 675 | "dependencies": { 676 | "aproba": "^1.0.3 || ^2.0.0", 677 | "color-support": "^1.1.2", 678 | "console-control-strings": "^1.0.0", 679 | "has-unicode": "^2.0.1", 680 | "object-assign": "^4.1.1", 681 | "signal-exit": "^3.0.0", 682 | "string-width": "^4.2.3", 683 | "strip-ansi": "^6.0.1", 684 | "wide-align": "^1.1.2" 685 | }, 686 | "engines": { 687 | "node": ">=10" 688 | } 689 | }, 690 | "node_modules/get-intrinsic": { 691 | "version": "1.2.2", 692 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", 693 | "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", 694 | "dependencies": { 695 | "function-bind": "^1.1.2", 696 | "has-proto": "^1.0.1", 697 | "has-symbols": "^1.0.3", 698 | "hasown": "^2.0.0" 699 | }, 700 | "funding": { 701 | "url": "https://github.com/sponsors/ljharb" 702 | } 703 | }, 704 | "node_modules/glob": { 705 | "version": "7.2.3", 706 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", 707 | "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", 708 | "dependencies": { 709 | "fs.realpath": "^1.0.0", 710 | "inflight": "^1.0.4", 711 | "inherits": "2", 712 | "minimatch": "^3.1.1", 713 | "once": "^1.3.0", 714 | "path-is-absolute": "^1.0.0" 715 | }, 716 | "engines": { 717 | "node": "*" 718 | }, 719 | "funding": { 720 | "url": "https://github.com/sponsors/isaacs" 721 | } 722 | }, 723 | "node_modules/glob-parent": { 724 | "version": "5.1.2", 725 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 726 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 727 | "dev": true, 728 | "dependencies": { 729 | "is-glob": "^4.0.1" 730 | }, 731 | "engines": { 732 | "node": ">= 6" 733 | } 734 | }, 735 | "node_modules/gopd": { 736 | "version": "1.0.1", 737 | "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", 738 | "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", 739 | "dependencies": { 740 | "get-intrinsic": "^1.1.3" 741 | }, 742 | "funding": { 743 | "url": "https://github.com/sponsors/ljharb" 744 | } 745 | }, 746 | "node_modules/has-flag": { 747 | "version": "3.0.0", 748 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 749 | "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", 750 | "dev": true, 751 | "engines": { 752 | "node": ">=4" 753 | } 754 | }, 755 | "node_modules/has-property-descriptors": { 756 | "version": "1.0.1", 757 | "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", 758 | "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", 759 | "dependencies": { 760 | "get-intrinsic": "^1.2.2" 761 | }, 762 | "funding": { 763 | "url": "https://github.com/sponsors/ljharb" 764 | } 765 | }, 766 | "node_modules/has-proto": { 767 | "version": "1.0.1", 768 | "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", 769 | "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", 770 | "engines": { 771 | "node": ">= 0.4" 772 | }, 773 | "funding": { 774 | "url": "https://github.com/sponsors/ljharb" 775 | } 776 | }, 777 | "node_modules/has-symbols": { 778 | "version": "1.0.3", 779 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", 780 | "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", 781 | "engines": { 782 | "node": ">= 0.4" 783 | }, 784 | "funding": { 785 | "url": "https://github.com/sponsors/ljharb" 786 | } 787 | }, 788 | "node_modules/has-unicode": { 789 | "version": "2.0.1", 790 | "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", 791 | "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" 792 | }, 793 | "node_modules/hasown": { 794 | "version": "2.0.0", 795 | "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", 796 | "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", 797 | "dependencies": { 798 | "function-bind": "^1.1.2" 799 | }, 800 | "engines": { 801 | "node": ">= 0.4" 802 | } 803 | }, 804 | "node_modules/http-errors": { 805 | "version": "2.0.0", 806 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", 807 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", 808 | "dependencies": { 809 | "depd": "2.0.0", 810 | "inherits": "2.0.4", 811 | "setprototypeof": "1.2.0", 812 | "statuses": "2.0.1", 813 | "toidentifier": "1.0.1" 814 | }, 815 | "engines": { 816 | "node": ">= 0.8" 817 | } 818 | }, 819 | "node_modules/https-proxy-agent": { 820 | "version": "5.0.1", 821 | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", 822 | "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", 823 | "dependencies": { 824 | "agent-base": "6", 825 | "debug": "4" 826 | }, 827 | "engines": { 828 | "node": ">= 6" 829 | } 830 | }, 831 | "node_modules/https-proxy-agent/node_modules/debug": { 832 | "version": "4.3.4", 833 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 834 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 835 | "dependencies": { 836 | "ms": "2.1.2" 837 | }, 838 | "engines": { 839 | "node": ">=6.0" 840 | }, 841 | "peerDependenciesMeta": { 842 | "supports-color": { 843 | "optional": true 844 | } 845 | } 846 | }, 847 | "node_modules/https-proxy-agent/node_modules/ms": { 848 | "version": "2.1.2", 849 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 850 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 851 | }, 852 | "node_modules/iconv-lite": { 853 | "version": "0.4.24", 854 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 855 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 856 | "dependencies": { 857 | "safer-buffer": ">= 2.1.2 < 3" 858 | }, 859 | "engines": { 860 | "node": ">=0.10.0" 861 | } 862 | }, 863 | "node_modules/ignore-by-default": { 864 | "version": "1.0.1", 865 | "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", 866 | "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", 867 | "dev": true 868 | }, 869 | "node_modules/inflight": { 870 | "version": "1.0.6", 871 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 872 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", 873 | "dependencies": { 874 | "once": "^1.3.0", 875 | "wrappy": "1" 876 | } 877 | }, 878 | "node_modules/inherits": { 879 | "version": "2.0.4", 880 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 881 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 882 | }, 883 | "node_modules/ipaddr.js": { 884 | "version": "1.9.1", 885 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 886 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", 887 | "engines": { 888 | "node": ">= 0.10" 889 | } 890 | }, 891 | "node_modules/is-binary-path": { 892 | "version": "2.1.0", 893 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 894 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 895 | "dev": true, 896 | "dependencies": { 897 | "binary-extensions": "^2.0.0" 898 | }, 899 | "engines": { 900 | "node": ">=8" 901 | } 902 | }, 903 | "node_modules/is-extglob": { 904 | "version": "2.1.1", 905 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 906 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 907 | "dev": true, 908 | "engines": { 909 | "node": ">=0.10.0" 910 | } 911 | }, 912 | "node_modules/is-fullwidth-code-point": { 913 | "version": "3.0.0", 914 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 915 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 916 | "engines": { 917 | "node": ">=8" 918 | } 919 | }, 920 | "node_modules/is-glob": { 921 | "version": "4.0.3", 922 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 923 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 924 | "dev": true, 925 | "dependencies": { 926 | "is-extglob": "^2.1.1" 927 | }, 928 | "engines": { 929 | "node": ">=0.10.0" 930 | } 931 | }, 932 | "node_modules/is-number": { 933 | "version": "7.0.0", 934 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 935 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 936 | "dev": true, 937 | "engines": { 938 | "node": ">=0.12.0" 939 | } 940 | }, 941 | "node_modules/jose": { 942 | "version": "5.1.1", 943 | "resolved": "https://registry.npmjs.org/jose/-/jose-5.1.1.tgz", 944 | "integrity": "sha512-bfB+lNxowY49LfrBO0ITUn93JbUhxUN8I11K6oI5hJu/G6PO6fEUddVLjqdD0cQ9SXIHWXuWh7eJYwZF7Z0N/g==", 945 | "funding": { 946 | "url": "https://github.com/sponsors/panva" 947 | } 948 | }, 949 | "node_modules/kareem": { 950 | "version": "2.5.1", 951 | "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.5.1.tgz", 952 | "integrity": "sha512-7jFxRVm+jD+rkq3kY0iZDJfsO2/t4BBPeEb2qKn2lR/9KhuksYk5hxzfRYWMPV8P/x2d0kHD306YyWLzjjH+uA==", 953 | "engines": { 954 | "node": ">=12.0.0" 955 | } 956 | }, 957 | "node_modules/lru-cache": { 958 | "version": "6.0.0", 959 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", 960 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", 961 | "dependencies": { 962 | "yallist": "^4.0.0" 963 | }, 964 | "engines": { 965 | "node": ">=10" 966 | } 967 | }, 968 | "node_modules/make-dir": { 969 | "version": "3.1.0", 970 | "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", 971 | "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", 972 | "dependencies": { 973 | "semver": "^6.0.0" 974 | }, 975 | "engines": { 976 | "node": ">=8" 977 | }, 978 | "funding": { 979 | "url": "https://github.com/sponsors/sindresorhus" 980 | } 981 | }, 982 | "node_modules/make-dir/node_modules/semver": { 983 | "version": "6.3.1", 984 | "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", 985 | "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", 986 | "bin": { 987 | "semver": "bin/semver.js" 988 | } 989 | }, 990 | "node_modules/media-typer": { 991 | "version": "0.3.0", 992 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 993 | "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", 994 | "engines": { 995 | "node": ">= 0.6" 996 | } 997 | }, 998 | "node_modules/memory-pager": { 999 | "version": "1.5.0", 1000 | "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", 1001 | "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==" 1002 | }, 1003 | "node_modules/merge-descriptors": { 1004 | "version": "1.0.1", 1005 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 1006 | "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" 1007 | }, 1008 | "node_modules/methods": { 1009 | "version": "1.1.2", 1010 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 1011 | "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", 1012 | "engines": { 1013 | "node": ">= 0.6" 1014 | } 1015 | }, 1016 | "node_modules/mime": { 1017 | "version": "1.6.0", 1018 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 1019 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", 1020 | "bin": { 1021 | "mime": "cli.js" 1022 | }, 1023 | "engines": { 1024 | "node": ">=4" 1025 | } 1026 | }, 1027 | "node_modules/mime-db": { 1028 | "version": "1.52.0", 1029 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 1030 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 1031 | "engines": { 1032 | "node": ">= 0.6" 1033 | } 1034 | }, 1035 | "node_modules/mime-types": { 1036 | "version": "2.1.35", 1037 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 1038 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 1039 | "dependencies": { 1040 | "mime-db": "1.52.0" 1041 | }, 1042 | "engines": { 1043 | "node": ">= 0.6" 1044 | } 1045 | }, 1046 | "node_modules/minimatch": { 1047 | "version": "3.1.2", 1048 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 1049 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 1050 | "dependencies": { 1051 | "brace-expansion": "^1.1.7" 1052 | }, 1053 | "engines": { 1054 | "node": "*" 1055 | } 1056 | }, 1057 | "node_modules/minipass": { 1058 | "version": "5.0.0", 1059 | "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", 1060 | "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", 1061 | "engines": { 1062 | "node": ">=8" 1063 | } 1064 | }, 1065 | "node_modules/minizlib": { 1066 | "version": "2.1.2", 1067 | "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", 1068 | "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", 1069 | "dependencies": { 1070 | "minipass": "^3.0.0", 1071 | "yallist": "^4.0.0" 1072 | }, 1073 | "engines": { 1074 | "node": ">= 8" 1075 | } 1076 | }, 1077 | "node_modules/minizlib/node_modules/minipass": { 1078 | "version": "3.3.6", 1079 | "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", 1080 | "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", 1081 | "dependencies": { 1082 | "yallist": "^4.0.0" 1083 | }, 1084 | "engines": { 1085 | "node": ">=8" 1086 | } 1087 | }, 1088 | "node_modules/mkdirp": { 1089 | "version": "1.0.4", 1090 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", 1091 | "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", 1092 | "bin": { 1093 | "mkdirp": "bin/cmd.js" 1094 | }, 1095 | "engines": { 1096 | "node": ">=10" 1097 | } 1098 | }, 1099 | "node_modules/mongodb": { 1100 | "version": "6.2.0", 1101 | "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.2.0.tgz", 1102 | "integrity": "sha512-d7OSuGjGWDZ5usZPqfvb36laQ9CPhnWkAGHT61x5P95p/8nMVeH8asloMwW6GcYFeB0Vj4CB/1wOTDG2RA9BFA==", 1103 | "dependencies": { 1104 | "@mongodb-js/saslprep": "^1.1.0", 1105 | "bson": "^6.2.0", 1106 | "mongodb-connection-string-url": "^2.6.0" 1107 | }, 1108 | "engines": { 1109 | "node": ">=16.20.1" 1110 | }, 1111 | "peerDependencies": { 1112 | "@aws-sdk/credential-providers": "^3.188.0", 1113 | "@mongodb-js/zstd": "^1.1.0", 1114 | "gcp-metadata": "^5.2.0", 1115 | "kerberos": "^2.0.1", 1116 | "mongodb-client-encryption": ">=6.0.0 <7", 1117 | "snappy": "^7.2.2", 1118 | "socks": "^2.7.1" 1119 | }, 1120 | "peerDependenciesMeta": { 1121 | "@aws-sdk/credential-providers": { 1122 | "optional": true 1123 | }, 1124 | "@mongodb-js/zstd": { 1125 | "optional": true 1126 | }, 1127 | "gcp-metadata": { 1128 | "optional": true 1129 | }, 1130 | "kerberos": { 1131 | "optional": true 1132 | }, 1133 | "mongodb-client-encryption": { 1134 | "optional": true 1135 | }, 1136 | "snappy": { 1137 | "optional": true 1138 | }, 1139 | "socks": { 1140 | "optional": true 1141 | } 1142 | } 1143 | }, 1144 | "node_modules/mongodb-connection-string-url": { 1145 | "version": "2.6.0", 1146 | "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz", 1147 | "integrity": "sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==", 1148 | "dependencies": { 1149 | "@types/whatwg-url": "^8.2.1", 1150 | "whatwg-url": "^11.0.0" 1151 | } 1152 | }, 1153 | "node_modules/mongodb-connection-string-url/node_modules/tr46": { 1154 | "version": "3.0.0", 1155 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", 1156 | "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", 1157 | "dependencies": { 1158 | "punycode": "^2.1.1" 1159 | }, 1160 | "engines": { 1161 | "node": ">=12" 1162 | } 1163 | }, 1164 | "node_modules/mongodb-connection-string-url/node_modules/webidl-conversions": { 1165 | "version": "7.0.0", 1166 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", 1167 | "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", 1168 | "engines": { 1169 | "node": ">=12" 1170 | } 1171 | }, 1172 | "node_modules/mongodb-connection-string-url/node_modules/whatwg-url": { 1173 | "version": "11.0.0", 1174 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", 1175 | "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", 1176 | "dependencies": { 1177 | "tr46": "^3.0.0", 1178 | "webidl-conversions": "^7.0.0" 1179 | }, 1180 | "engines": { 1181 | "node": ">=12" 1182 | } 1183 | }, 1184 | "node_modules/mongoose": { 1185 | "version": "8.0.1", 1186 | "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.0.1.tgz", 1187 | "integrity": "sha512-O3TJrtLCt4H1eGf2HoHGcnOcCTWloQkpmIP3hA9olybX3OX2KUjdIIq135HD5paGjZEDJYKn9fw4eH5N477zqQ==", 1188 | "dependencies": { 1189 | "bson": "^6.2.0", 1190 | "kareem": "2.5.1", 1191 | "mongodb": "6.2.0", 1192 | "mpath": "0.9.0", 1193 | "mquery": "5.0.0", 1194 | "ms": "2.1.3", 1195 | "sift": "16.0.1" 1196 | }, 1197 | "engines": { 1198 | "node": ">=16.20.1" 1199 | }, 1200 | "funding": { 1201 | "type": "opencollective", 1202 | "url": "https://opencollective.com/mongoose" 1203 | } 1204 | }, 1205 | "node_modules/mongoose/node_modules/ms": { 1206 | "version": "2.1.3", 1207 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 1208 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 1209 | }, 1210 | "node_modules/mpath": { 1211 | "version": "0.9.0", 1212 | "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", 1213 | "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", 1214 | "engines": { 1215 | "node": ">=4.0.0" 1216 | } 1217 | }, 1218 | "node_modules/mquery": { 1219 | "version": "5.0.0", 1220 | "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", 1221 | "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", 1222 | "dependencies": { 1223 | "debug": "4.x" 1224 | }, 1225 | "engines": { 1226 | "node": ">=14.0.0" 1227 | } 1228 | }, 1229 | "node_modules/mquery/node_modules/debug": { 1230 | "version": "4.3.4", 1231 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 1232 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 1233 | "dependencies": { 1234 | "ms": "2.1.2" 1235 | }, 1236 | "engines": { 1237 | "node": ">=6.0" 1238 | }, 1239 | "peerDependenciesMeta": { 1240 | "supports-color": { 1241 | "optional": true 1242 | } 1243 | } 1244 | }, 1245 | "node_modules/mquery/node_modules/ms": { 1246 | "version": "2.1.2", 1247 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1248 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 1249 | }, 1250 | "node_modules/ms": { 1251 | "version": "2.0.0", 1252 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 1253 | "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" 1254 | }, 1255 | "node_modules/nanoid": { 1256 | "version": "5.0.3", 1257 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.3.tgz", 1258 | "integrity": "sha512-I7X2b22cxA4LIHXPSqbBCEQSL+1wv8TuoefejsX4HFWyC6jc5JG7CEaxOltiKjc1M+YCS2YkrZZcj4+dytw9GA==", 1259 | "funding": [ 1260 | { 1261 | "type": "github", 1262 | "url": "https://github.com/sponsors/ai" 1263 | } 1264 | ], 1265 | "bin": { 1266 | "nanoid": "bin/nanoid.js" 1267 | }, 1268 | "engines": { 1269 | "node": "^18 || >=20" 1270 | } 1271 | }, 1272 | "node_modules/negotiator": { 1273 | "version": "0.6.3", 1274 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", 1275 | "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", 1276 | "engines": { 1277 | "node": ">= 0.6" 1278 | } 1279 | }, 1280 | "node_modules/node-addon-api": { 1281 | "version": "5.1.0", 1282 | "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", 1283 | "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" 1284 | }, 1285 | "node_modules/node-fetch": { 1286 | "version": "2.7.0", 1287 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", 1288 | "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", 1289 | "dependencies": { 1290 | "whatwg-url": "^5.0.0" 1291 | }, 1292 | "engines": { 1293 | "node": "4.x || >=6.0.0" 1294 | }, 1295 | "peerDependencies": { 1296 | "encoding": "^0.1.0" 1297 | }, 1298 | "peerDependenciesMeta": { 1299 | "encoding": { 1300 | "optional": true 1301 | } 1302 | } 1303 | }, 1304 | "node_modules/nodemon": { 1305 | "version": "3.0.1", 1306 | "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.1.tgz", 1307 | "integrity": "sha512-g9AZ7HmkhQkqXkRc20w+ZfQ73cHLbE8hnPbtaFbFtCumZsjyMhKk9LajQ07U5Ux28lvFjZ5X7HvWR1xzU8jHVw==", 1308 | "dev": true, 1309 | "dependencies": { 1310 | "chokidar": "^3.5.2", 1311 | "debug": "^3.2.7", 1312 | "ignore-by-default": "^1.0.1", 1313 | "minimatch": "^3.1.2", 1314 | "pstree.remy": "^1.1.8", 1315 | "semver": "^7.5.3", 1316 | "simple-update-notifier": "^2.0.0", 1317 | "supports-color": "^5.5.0", 1318 | "touch": "^3.1.0", 1319 | "undefsafe": "^2.0.5" 1320 | }, 1321 | "bin": { 1322 | "nodemon": "bin/nodemon.js" 1323 | }, 1324 | "engines": { 1325 | "node": ">=10" 1326 | }, 1327 | "funding": { 1328 | "type": "opencollective", 1329 | "url": "https://opencollective.com/nodemon" 1330 | } 1331 | }, 1332 | "node_modules/nodemon/node_modules/debug": { 1333 | "version": "3.2.7", 1334 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", 1335 | "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", 1336 | "dev": true, 1337 | "dependencies": { 1338 | "ms": "^2.1.1" 1339 | } 1340 | }, 1341 | "node_modules/nodemon/node_modules/ms": { 1342 | "version": "2.1.3", 1343 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 1344 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 1345 | "dev": true 1346 | }, 1347 | "node_modules/nopt": { 1348 | "version": "5.0.0", 1349 | "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", 1350 | "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", 1351 | "dependencies": { 1352 | "abbrev": "1" 1353 | }, 1354 | "bin": { 1355 | "nopt": "bin/nopt.js" 1356 | }, 1357 | "engines": { 1358 | "node": ">=6" 1359 | } 1360 | }, 1361 | "node_modules/normalize-path": { 1362 | "version": "3.0.0", 1363 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 1364 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 1365 | "dev": true, 1366 | "engines": { 1367 | "node": ">=0.10.0" 1368 | } 1369 | }, 1370 | "node_modules/npmlog": { 1371 | "version": "5.0.1", 1372 | "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", 1373 | "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", 1374 | "dependencies": { 1375 | "are-we-there-yet": "^2.0.0", 1376 | "console-control-strings": "^1.1.0", 1377 | "gauge": "^3.0.0", 1378 | "set-blocking": "^2.0.0" 1379 | } 1380 | }, 1381 | "node_modules/object-assign": { 1382 | "version": "4.1.1", 1383 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 1384 | "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", 1385 | "engines": { 1386 | "node": ">=0.10.0" 1387 | } 1388 | }, 1389 | "node_modules/object-inspect": { 1390 | "version": "1.13.1", 1391 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", 1392 | "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", 1393 | "funding": { 1394 | "url": "https://github.com/sponsors/ljharb" 1395 | } 1396 | }, 1397 | "node_modules/on-finished": { 1398 | "version": "2.4.1", 1399 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", 1400 | "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", 1401 | "dependencies": { 1402 | "ee-first": "1.1.1" 1403 | }, 1404 | "engines": { 1405 | "node": ">= 0.8" 1406 | } 1407 | }, 1408 | "node_modules/on-headers": { 1409 | "version": "1.0.2", 1410 | "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", 1411 | "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", 1412 | "engines": { 1413 | "node": ">= 0.8" 1414 | } 1415 | }, 1416 | "node_modules/once": { 1417 | "version": "1.4.0", 1418 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1419 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 1420 | "dependencies": { 1421 | "wrappy": "1" 1422 | } 1423 | }, 1424 | "node_modules/parseurl": { 1425 | "version": "1.3.3", 1426 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 1427 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", 1428 | "engines": { 1429 | "node": ">= 0.8" 1430 | } 1431 | }, 1432 | "node_modules/path-is-absolute": { 1433 | "version": "1.0.1", 1434 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1435 | "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", 1436 | "engines": { 1437 | "node": ">=0.10.0" 1438 | } 1439 | }, 1440 | "node_modules/path-to-regexp": { 1441 | "version": "0.1.7", 1442 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 1443 | "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" 1444 | }, 1445 | "node_modules/picomatch": { 1446 | "version": "2.3.1", 1447 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 1448 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 1449 | "dev": true, 1450 | "engines": { 1451 | "node": ">=8.6" 1452 | }, 1453 | "funding": { 1454 | "url": "https://github.com/sponsors/jonschlinkert" 1455 | } 1456 | }, 1457 | "node_modules/proxy-addr": { 1458 | "version": "2.0.7", 1459 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", 1460 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", 1461 | "dependencies": { 1462 | "forwarded": "0.2.0", 1463 | "ipaddr.js": "1.9.1" 1464 | }, 1465 | "engines": { 1466 | "node": ">= 0.10" 1467 | } 1468 | }, 1469 | "node_modules/pstree.remy": { 1470 | "version": "1.1.8", 1471 | "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", 1472 | "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", 1473 | "dev": true 1474 | }, 1475 | "node_modules/punycode": { 1476 | "version": "2.3.1", 1477 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", 1478 | "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", 1479 | "engines": { 1480 | "node": ">=6" 1481 | } 1482 | }, 1483 | "node_modules/qs": { 1484 | "version": "6.11.0", 1485 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", 1486 | "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", 1487 | "dependencies": { 1488 | "side-channel": "^1.0.4" 1489 | }, 1490 | "engines": { 1491 | "node": ">=0.6" 1492 | }, 1493 | "funding": { 1494 | "url": "https://github.com/sponsors/ljharb" 1495 | } 1496 | }, 1497 | "node_modules/range-parser": { 1498 | "version": "1.2.1", 1499 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 1500 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", 1501 | "engines": { 1502 | "node": ">= 0.6" 1503 | } 1504 | }, 1505 | "node_modules/raw-body": { 1506 | "version": "2.5.1", 1507 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", 1508 | "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", 1509 | "dependencies": { 1510 | "bytes": "3.1.2", 1511 | "http-errors": "2.0.0", 1512 | "iconv-lite": "0.4.24", 1513 | "unpipe": "1.0.0" 1514 | }, 1515 | "engines": { 1516 | "node": ">= 0.8" 1517 | } 1518 | }, 1519 | "node_modules/raw-body/node_modules/bytes": { 1520 | "version": "3.1.2", 1521 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", 1522 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", 1523 | "engines": { 1524 | "node": ">= 0.8" 1525 | } 1526 | }, 1527 | "node_modules/readable-stream": { 1528 | "version": "3.6.2", 1529 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", 1530 | "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", 1531 | "dependencies": { 1532 | "inherits": "^2.0.3", 1533 | "string_decoder": "^1.1.1", 1534 | "util-deprecate": "^1.0.1" 1535 | }, 1536 | "engines": { 1537 | "node": ">= 6" 1538 | } 1539 | }, 1540 | "node_modules/readdirp": { 1541 | "version": "3.6.0", 1542 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 1543 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 1544 | "dev": true, 1545 | "dependencies": { 1546 | "picomatch": "^2.2.1" 1547 | }, 1548 | "engines": { 1549 | "node": ">=8.10.0" 1550 | } 1551 | }, 1552 | "node_modules/rimraf": { 1553 | "version": "3.0.2", 1554 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", 1555 | "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", 1556 | "dependencies": { 1557 | "glob": "^7.1.3" 1558 | }, 1559 | "bin": { 1560 | "rimraf": "bin.js" 1561 | }, 1562 | "funding": { 1563 | "url": "https://github.com/sponsors/isaacs" 1564 | } 1565 | }, 1566 | "node_modules/safe-buffer": { 1567 | "version": "5.1.2", 1568 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 1569 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 1570 | }, 1571 | "node_modules/safer-buffer": { 1572 | "version": "2.1.2", 1573 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 1574 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 1575 | }, 1576 | "node_modules/semver": { 1577 | "version": "7.5.4", 1578 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", 1579 | "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", 1580 | "dependencies": { 1581 | "lru-cache": "^6.0.0" 1582 | }, 1583 | "bin": { 1584 | "semver": "bin/semver.js" 1585 | }, 1586 | "engines": { 1587 | "node": ">=10" 1588 | } 1589 | }, 1590 | "node_modules/send": { 1591 | "version": "0.18.0", 1592 | "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", 1593 | "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", 1594 | "dependencies": { 1595 | "debug": "2.6.9", 1596 | "depd": "2.0.0", 1597 | "destroy": "1.2.0", 1598 | "encodeurl": "~1.0.2", 1599 | "escape-html": "~1.0.3", 1600 | "etag": "~1.8.1", 1601 | "fresh": "0.5.2", 1602 | "http-errors": "2.0.0", 1603 | "mime": "1.6.0", 1604 | "ms": "2.1.3", 1605 | "on-finished": "2.4.1", 1606 | "range-parser": "~1.2.1", 1607 | "statuses": "2.0.1" 1608 | }, 1609 | "engines": { 1610 | "node": ">= 0.8.0" 1611 | } 1612 | }, 1613 | "node_modules/send/node_modules/ms": { 1614 | "version": "2.1.3", 1615 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 1616 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 1617 | }, 1618 | "node_modules/serve-static": { 1619 | "version": "1.15.0", 1620 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", 1621 | "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", 1622 | "dependencies": { 1623 | "encodeurl": "~1.0.2", 1624 | "escape-html": "~1.0.3", 1625 | "parseurl": "~1.3.3", 1626 | "send": "0.18.0" 1627 | }, 1628 | "engines": { 1629 | "node": ">= 0.8.0" 1630 | } 1631 | }, 1632 | "node_modules/set-blocking": { 1633 | "version": "2.0.0", 1634 | "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", 1635 | "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" 1636 | }, 1637 | "node_modules/set-function-length": { 1638 | "version": "1.1.1", 1639 | "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", 1640 | "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", 1641 | "dependencies": { 1642 | "define-data-property": "^1.1.1", 1643 | "get-intrinsic": "^1.2.1", 1644 | "gopd": "^1.0.1", 1645 | "has-property-descriptors": "^1.0.0" 1646 | }, 1647 | "engines": { 1648 | "node": ">= 0.4" 1649 | } 1650 | }, 1651 | "node_modules/setprototypeof": { 1652 | "version": "1.2.0", 1653 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", 1654 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" 1655 | }, 1656 | "node_modules/side-channel": { 1657 | "version": "1.0.4", 1658 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", 1659 | "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", 1660 | "dependencies": { 1661 | "call-bind": "^1.0.0", 1662 | "get-intrinsic": "^1.0.2", 1663 | "object-inspect": "^1.9.0" 1664 | }, 1665 | "funding": { 1666 | "url": "https://github.com/sponsors/ljharb" 1667 | } 1668 | }, 1669 | "node_modules/sift": { 1670 | "version": "16.0.1", 1671 | "resolved": "https://registry.npmjs.org/sift/-/sift-16.0.1.tgz", 1672 | "integrity": "sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ==" 1673 | }, 1674 | "node_modules/signal-exit": { 1675 | "version": "3.0.7", 1676 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", 1677 | "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" 1678 | }, 1679 | "node_modules/simple-update-notifier": { 1680 | "version": "2.0.0", 1681 | "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", 1682 | "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", 1683 | "dev": true, 1684 | "dependencies": { 1685 | "semver": "^7.5.3" 1686 | }, 1687 | "engines": { 1688 | "node": ">=10" 1689 | } 1690 | }, 1691 | "node_modules/sparse-bitfield": { 1692 | "version": "3.0.3", 1693 | "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", 1694 | "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", 1695 | "dependencies": { 1696 | "memory-pager": "^1.0.2" 1697 | } 1698 | }, 1699 | "node_modules/statuses": { 1700 | "version": "2.0.1", 1701 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", 1702 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", 1703 | "engines": { 1704 | "node": ">= 0.8" 1705 | } 1706 | }, 1707 | "node_modules/string_decoder": { 1708 | "version": "1.3.0", 1709 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", 1710 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 1711 | "dependencies": { 1712 | "safe-buffer": "~5.2.0" 1713 | } 1714 | }, 1715 | "node_modules/string_decoder/node_modules/safe-buffer": { 1716 | "version": "5.2.1", 1717 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 1718 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 1719 | "funding": [ 1720 | { 1721 | "type": "github", 1722 | "url": "https://github.com/sponsors/feross" 1723 | }, 1724 | { 1725 | "type": "patreon", 1726 | "url": "https://www.patreon.com/feross" 1727 | }, 1728 | { 1729 | "type": "consulting", 1730 | "url": "https://feross.org/support" 1731 | } 1732 | ] 1733 | }, 1734 | "node_modules/string-width": { 1735 | "version": "4.2.3", 1736 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 1737 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 1738 | "dependencies": { 1739 | "emoji-regex": "^8.0.0", 1740 | "is-fullwidth-code-point": "^3.0.0", 1741 | "strip-ansi": "^6.0.1" 1742 | }, 1743 | "engines": { 1744 | "node": ">=8" 1745 | } 1746 | }, 1747 | "node_modules/strip-ansi": { 1748 | "version": "6.0.1", 1749 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 1750 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 1751 | "dependencies": { 1752 | "ansi-regex": "^5.0.1" 1753 | }, 1754 | "engines": { 1755 | "node": ">=8" 1756 | } 1757 | }, 1758 | "node_modules/supports-color": { 1759 | "version": "5.5.0", 1760 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 1761 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 1762 | "dev": true, 1763 | "dependencies": { 1764 | "has-flag": "^3.0.0" 1765 | }, 1766 | "engines": { 1767 | "node": ">=4" 1768 | } 1769 | }, 1770 | "node_modules/tar": { 1771 | "version": "6.2.0", 1772 | "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", 1773 | "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==", 1774 | "dependencies": { 1775 | "chownr": "^2.0.0", 1776 | "fs-minipass": "^2.0.0", 1777 | "minipass": "^5.0.0", 1778 | "minizlib": "^2.1.1", 1779 | "mkdirp": "^1.0.3", 1780 | "yallist": "^4.0.0" 1781 | }, 1782 | "engines": { 1783 | "node": ">=10" 1784 | } 1785 | }, 1786 | "node_modules/to-regex-range": { 1787 | "version": "5.0.1", 1788 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 1789 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 1790 | "dev": true, 1791 | "dependencies": { 1792 | "is-number": "^7.0.0" 1793 | }, 1794 | "engines": { 1795 | "node": ">=8.0" 1796 | } 1797 | }, 1798 | "node_modules/toidentifier": { 1799 | "version": "1.0.1", 1800 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", 1801 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", 1802 | "engines": { 1803 | "node": ">=0.6" 1804 | } 1805 | }, 1806 | "node_modules/touch": { 1807 | "version": "3.1.0", 1808 | "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", 1809 | "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", 1810 | "dev": true, 1811 | "dependencies": { 1812 | "nopt": "~1.0.10" 1813 | }, 1814 | "bin": { 1815 | "nodetouch": "bin/nodetouch.js" 1816 | } 1817 | }, 1818 | "node_modules/touch/node_modules/nopt": { 1819 | "version": "1.0.10", 1820 | "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", 1821 | "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", 1822 | "dev": true, 1823 | "dependencies": { 1824 | "abbrev": "1" 1825 | }, 1826 | "bin": { 1827 | "nopt": "bin/nopt.js" 1828 | }, 1829 | "engines": { 1830 | "node": "*" 1831 | } 1832 | }, 1833 | "node_modules/tr46": { 1834 | "version": "0.0.3", 1835 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", 1836 | "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" 1837 | }, 1838 | "node_modules/type-is": { 1839 | "version": "1.6.18", 1840 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 1841 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 1842 | "dependencies": { 1843 | "media-typer": "0.3.0", 1844 | "mime-types": "~2.1.24" 1845 | }, 1846 | "engines": { 1847 | "node": ">= 0.6" 1848 | } 1849 | }, 1850 | "node_modules/undefsafe": { 1851 | "version": "2.0.5", 1852 | "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", 1853 | "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", 1854 | "dev": true 1855 | }, 1856 | "node_modules/undici-types": { 1857 | "version": "5.26.5", 1858 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", 1859 | "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" 1860 | }, 1861 | "node_modules/unpipe": { 1862 | "version": "1.0.0", 1863 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 1864 | "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", 1865 | "engines": { 1866 | "node": ">= 0.8" 1867 | } 1868 | }, 1869 | "node_modules/util-deprecate": { 1870 | "version": "1.0.2", 1871 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 1872 | "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" 1873 | }, 1874 | "node_modules/utils-merge": { 1875 | "version": "1.0.1", 1876 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 1877 | "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", 1878 | "engines": { 1879 | "node": ">= 0.4.0" 1880 | } 1881 | }, 1882 | "node_modules/vary": { 1883 | "version": "1.1.2", 1884 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 1885 | "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", 1886 | "engines": { 1887 | "node": ">= 0.8" 1888 | } 1889 | }, 1890 | "node_modules/webidl-conversions": { 1891 | "version": "3.0.1", 1892 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", 1893 | "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" 1894 | }, 1895 | "node_modules/whatwg-url": { 1896 | "version": "5.0.0", 1897 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", 1898 | "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", 1899 | "dependencies": { 1900 | "tr46": "~0.0.3", 1901 | "webidl-conversions": "^3.0.0" 1902 | } 1903 | }, 1904 | "node_modules/wide-align": { 1905 | "version": "1.1.5", 1906 | "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", 1907 | "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", 1908 | "dependencies": { 1909 | "string-width": "^1.0.2 || 2 || 3 || 4" 1910 | } 1911 | }, 1912 | "node_modules/wrappy": { 1913 | "version": "1.0.2", 1914 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1915 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" 1916 | }, 1917 | "node_modules/yallist": { 1918 | "version": "4.0.0", 1919 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", 1920 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" 1921 | } 1922 | } 1923 | } 1924 | -------------------------------------------------------------------------------- /node-server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.mjs", 6 | "scripts": { 7 | "start": "node index.mjs", 8 | "dev": "nodemon index.mjs", 9 | "test": "jest" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "ISC", 14 | "dependencies": { 15 | "bcrypt": "^5.1.1", 16 | "compression": "^1.7.4", 17 | "cors": "^2.8.5", 18 | "crypto-js": "^4.2.0", 19 | "dotenv": "^16.3.1", 20 | "express": "^4.18.2", 21 | "jose": "^5.1.1", 22 | "mongoose": "^8.0.1", 23 | "nanoid": "^5.0.3" 24 | }, 25 | "devDependencies": { 26 | "nodemon": "^3.0.1" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /node-server/routes/index.mjs: -------------------------------------------------------------------------------- 1 | export { default as postRoutes } from './post.mjs' -------------------------------------------------------------------------------- /node-server/routes/post.mjs: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import PostController from '../controllers/post.mjs'; 3 | import { authenticateUser } from '../middleware/index.mjs'; 4 | 5 | const router = express.Router(); 6 | 7 | 8 | router.get('/posts', authenticateUser, PostController.getPostsController ) 9 | 10 | export default router -------------------------------------------------------------------------------- /node-server/services/index.mjs: -------------------------------------------------------------------------------- 1 | export { default as PostService } from './post.mjs' -------------------------------------------------------------------------------- /node-server/services/post.mjs: -------------------------------------------------------------------------------- 1 | import PostModel from "../models/post.mjs" 2 | import UserModel from "../models/user.mjs" 3 | 4 | const getAllPosts = async() =>{ 5 | try { 6 | const result = await PostModel.find({}).populate({path: "user", select: ["name", "email", "avatar", "_id"], model: UserModel}).lean().exec() 7 | const data = result?.map(post => ({...post, id: post._id?.toString(), user: {...post.user, id: post?.user?._id.toString()} })) 8 | return {isError: false, data, message: "success", status: 200 } 9 | } catch (error) { 10 | return {isError: false, data: null, message: error.message, status: 500 } 11 | } 12 | } 13 | 14 | const PostService = { getAllPosts} 15 | 16 | export default PostService -------------------------------------------------------------------------------- /node-server/utils/index.mjs: -------------------------------------------------------------------------------- 1 | import * as joseJwt from "jose" 2 | import CryptoJS from "crypto-js" 3 | 4 | 5 | export const verifyJwtToken = async(token) => { 6 | try { 7 | const privateKey = process.env.ACCESS_TOKEN_PRIVATE_KEY 8 | const result = await joseJwt.jwtVerify(token, new TextEncoder().encode(privateKey)) 9 | return {data: result.payload, isError: false, message: "success" } 10 | } catch (error) { 11 | return { data: null, isError: true, message:error.message } 12 | } 13 | } 14 | 15 | export const decryptString = (hexString) => { 16 | const secretKey = CryptoJS.enc.Utf8.parse(process.env.CRYPTO_ENCRYPTION_KEY) 17 | const iv = CryptoJS.enc.Utf8.parse(process.env.CRYPTO_IV_KEY) 18 | const ciphertext = CryptoJS.enc.Hex.parse(hexString) 19 | const decrypted = CryptoJS.AES.decrypt({ciphertext}, secretKey, {iv}).toString(CryptoJS.enc.Utf8) 20 | console.log("decrypted: ", decrypted) 21 | return JSON.parse(decrypted) 22 | } 23 | --------------------------------------------------------------------------------