| @@ -0,0 +1,92 @@ | |||
| const validator = require('validator'); | |||
| import { ProductData as IProduct } from '../utils/interface/productInterface'; | |||
| import { OrderData as IOrder } from '../utils/interface/orderInterface'; | |||
| import { Schema, model, Types } from 'mongoose'; | |||
| const OrderSchema = new Schema<IOrder>( | |||
| { | |||
| products: Array<IProduct>, | |||
| time: { | |||
| type: Date, | |||
| required: [true, 'Please provide a date.'], | |||
| validate(value: Date) { | |||
| if (!validator.isDate(value)) { | |||
| throw new Error('Not a date'); | |||
| } | |||
| }, | |||
| }, | |||
| shippingAddress: { | |||
| country: { | |||
| type: String, | |||
| required: [true, 'Please provide a country.'], | |||
| trim: true, | |||
| }, | |||
| city: { | |||
| type: String, | |||
| required: [true, 'Please provide a city.'], | |||
| trim: true, | |||
| }, | |||
| address: { | |||
| type: String, | |||
| required: [true, 'Please provide an address.'], | |||
| trim: true, | |||
| }, | |||
| address2: { | |||
| type: String, | |||
| trim: true, | |||
| }, | |||
| postcode: { | |||
| type: String, | |||
| required: [true, 'Please provide a postal code.'], | |||
| }, | |||
| email: { | |||
| type: String, | |||
| required: [true, 'Please provide an email.'], | |||
| }, | |||
| fullName: { | |||
| type: String, | |||
| required: [true, 'Please provide a name.'], | |||
| }, | |||
| }, | |||
| totalPrice: { | |||
| type: Number, | |||
| required: [true, 'Please provide a total price.'], | |||
| validate(value: number) { | |||
| if (value < 0) { | |||
| throw new Error('Total price must be a postive number'); | |||
| } | |||
| }, | |||
| }, | |||
| numberOfItems: { | |||
| type: Number, | |||
| required: [true, 'Please provide a total number of items.'], | |||
| validate(value: number) { | |||
| if (value < 0) { | |||
| throw new Error('Number of items must be a postive number'); | |||
| } | |||
| }, | |||
| }, | |||
| fulfilled: { | |||
| type: Boolean, | |||
| default: false, | |||
| }, | |||
| owner: { | |||
| type: Types.ObjectId, | |||
| required: [true, 'Please provide an owner.'], | |||
| ref: 'User', | |||
| }, | |||
| stripeCheckoutId: { | |||
| type: String, | |||
| required: [true, 'Please provide a stripe checkout id.'], | |||
| }, | |||
| }, | |||
| { | |||
| toJSON: { virtuals: true }, // So `res.json()` and other `JSON.stringify()` functions include virtuals | |||
| toObject: { virtuals: true }, // So `console.log()` and other functions that use `toObject()` include virtuals | |||
| } | |||
| ); | |||
| const Order = model<IOrder>('Order', OrderSchema, 'Order'); | |||
| module.exports = Order; | |||
| @@ -0,0 +1,73 @@ | |||
| import { ProductData as IProduct } from '../utils/interface/productInterface'; | |||
| import { Schema, model } from 'mongoose'; | |||
| const ProductSchema = new Schema<IProduct>({ | |||
| category: { | |||
| type: String, | |||
| required: [true, 'Please provide a category.'], | |||
| maxlength: 100, | |||
| trim: true, | |||
| }, | |||
| name: { | |||
| type: String, | |||
| required: [true, 'Please provide a name.'], | |||
| maxlength: 100, | |||
| trim: true, | |||
| }, | |||
| image: { | |||
| type: String, | |||
| required: [true, 'Please provide an image.'], | |||
| }, | |||
| description: { | |||
| type: String, | |||
| required: [true, 'Please provide a description.'], | |||
| trim: true, | |||
| }, | |||
| place: { | |||
| type: String, | |||
| trim: true, | |||
| }, | |||
| people: { | |||
| type: String, | |||
| trim: true, | |||
| }, | |||
| process: { | |||
| type: String, | |||
| trim: true, | |||
| }, | |||
| pairing: { | |||
| type: String, | |||
| trim: true, | |||
| }, | |||
| available: { | |||
| type: Boolean, | |||
| default: true, | |||
| }, | |||
| isFeatured: { | |||
| type: Boolean, | |||
| default: false, | |||
| }, | |||
| price: { | |||
| type: Number, | |||
| required: [true, 'Please provide a price.'], | |||
| validate(value: number) { | |||
| if (value < 0) { | |||
| throw new Error('Price must be a postive number'); | |||
| } | |||
| }, | |||
| }, | |||
| customID: { | |||
| type: String, | |||
| required: [true, 'Please provide a custom id.'], | |||
| unique: true, | |||
| }, | |||
| stripeID: { | |||
| type: String, | |||
| required: [true, 'Please provide a stripe id.'], | |||
| unique: true, | |||
| }, | |||
| }); | |||
| const Product = model<IProduct>('Product', ProductSchema); | |||
| module.exports = Product; | |||
| @@ -0,0 +1,39 @@ | |||
| import { Schema, model } from 'mongoose'; | |||
| import { QuestionData as IQusetion } from '../utils/interface/questionInterface'; | |||
| const validator = require('validator'); | |||
| const QuestionSchema = new Schema<IQusetion>({ | |||
| firstName: { | |||
| type: String, | |||
| required: [true, 'Please provide a name.'], | |||
| maxlength: [60, 'Name cannot be more than 60 characters'], | |||
| trim: true, | |||
| }, | |||
| lastName: { | |||
| type: String, | |||
| required: [true, 'Please provide a last name.'], | |||
| maxlength: [60, 'Name cannot be more than 60 characters'], | |||
| trim: true, | |||
| }, | |||
| email: { | |||
| type: String, | |||
| required: [true, 'Please provide an email.'], | |||
| trim: true, | |||
| lowercase: true, | |||
| unique: false, | |||
| validate(value: string) { | |||
| if (!validator.isEmail(value)) { | |||
| throw new Error('Email is invalid'); | |||
| } | |||
| }, | |||
| }, | |||
| message: { | |||
| type: String, | |||
| required: [true, 'Please provide a message/question.'], | |||
| trim: true, | |||
| }, | |||
| }); | |||
| const Question = model<IQusetion>('Question', QuestionSchema, 'Questions'); | |||
| module.exports = Question; | |||
| @@ -0,0 +1,128 @@ | |||
| import { Schema, model, Model } from 'mongoose'; | |||
| import { | |||
| hashPassword, | |||
| verifyPassword, | |||
| } from '../utils/helpers/hashPasswordHelpers'; | |||
| import { IUser } from '../utils/interface/userInterface'; | |||
| const validator = require('validator'); | |||
| interface UserModel extends Model<IUser, {}, {}> { | |||
| findByCredentials(username: string, password: string): object; | |||
| } | |||
| const UserSchema = new Schema<IUser, UserModel>( | |||
| { | |||
| fullName: { | |||
| type: String, | |||
| required: [true, 'Please provide a name.'], | |||
| maxlength: [60, 'Name cannot be more than 60 characters'], | |||
| trim: true, | |||
| }, | |||
| username: { | |||
| type: String, | |||
| required: [true, 'Please provide an username.'], | |||
| unique: true, | |||
| maxlength: [60, 'Name cannot be more than 60 characters'], | |||
| trim: true, | |||
| }, | |||
| email: { | |||
| type: String, | |||
| unique: true, | |||
| required: [true, 'Please provide an email.'], | |||
| trim: true, | |||
| lowercase: true, | |||
| validate(value: string) { | |||
| if (!validator.isEmail(value)) { | |||
| throw new Error('Email is invalid'); | |||
| } | |||
| }, | |||
| }, | |||
| password: { | |||
| type: String, | |||
| required: [true, 'Please provide a password.'], | |||
| minlength: 7, | |||
| trim: true, | |||
| validate(value: string) { | |||
| if (value.toLowerCase().includes('password')) { | |||
| throw new Error('Password cannot contain "password"'); | |||
| } | |||
| }, | |||
| }, | |||
| country: { | |||
| type: String, | |||
| required: [true, 'Please provide a country.'], | |||
| trim: true, | |||
| }, | |||
| city: { | |||
| type: String, | |||
| required: [true, 'Please provide a city.'], | |||
| trim: true, | |||
| }, | |||
| address: { | |||
| type: String, | |||
| required: [true, 'Please provide an address.'], | |||
| trim: true, | |||
| }, | |||
| address2: { | |||
| type: String, | |||
| trim: true, | |||
| }, | |||
| postcode: { | |||
| type: String, | |||
| required: [true, 'Please provide a postal code.'], | |||
| }, | |||
| }, | |||
| { | |||
| toJSON: { virtuals: true }, // So `res.json()` and other `JSON.stringify()` functions include virtuals | |||
| toObject: { virtuals: true }, // So `console.log()` and other functions that use `toObject()` include virtuals | |||
| } | |||
| ); | |||
| UserSchema.virtual('orders', { | |||
| ref: 'Order', | |||
| localField: '_id', | |||
| foreignField: 'owner', | |||
| }); | |||
| UserSchema.static( | |||
| 'findByCredentials', | |||
| async function findByCredentials(username: string, password: string) { | |||
| const user = await User.findOne({ username }); | |||
| if (!user) { | |||
| throw new Error('Unable to login'); | |||
| } | |||
| const isMatch = await verifyPassword(password, user.password); | |||
| if (!isMatch) { | |||
| throw new Error('Unable to login'); | |||
| } | |||
| const userData = { | |||
| fullName: user.fullName, | |||
| email: user.email, | |||
| address: user.address, | |||
| address2: user.address2, | |||
| city: user.city, | |||
| country: user.country, | |||
| postcode: user.postcode, | |||
| orders: user.orders, | |||
| _id: user._id, | |||
| }; | |||
| return userData; | |||
| } | |||
| ); | |||
| UserSchema.pre('save', async function (next) { | |||
| const user = this; | |||
| if (user.isModified('password')) { | |||
| user.password = await hashPassword(user.password); | |||
| } | |||
| next(); | |||
| }); | |||
| const User = model<IUser, UserModel>('User', UserSchema, 'User'); | |||
| module.exports = User; | |||
| @@ -9,10 +9,17 @@ | |||
| "lint": "next lint" | |||
| }, | |||
| "dependencies": { | |||
| "@sendgrid/mail": "^7.7.0", | |||
| "@types/bcryptjs": "^2.4.2", | |||
| "@types/mongodb": "^4.0.7", | |||
| "@types/validator": "^13.7.7", | |||
| "bcryptjs": "^2.4.3", | |||
| "mongoose": "^6.6.5", | |||
| "next": "12.3.1", | |||
| "next-auth": "^4.13.0", | |||
| "react": "18.2.0", | |||
| "react-dom": "18.2.0" | |||
| "react-dom": "18.2.0", | |||
| "validator": "^13.7.0" | |||
| }, | |||
| "devDependencies": { | |||
| "@types/node": "18.8.3", | |||
| @@ -0,0 +1,40 @@ | |||
| import NextAuth from 'next-auth'; | |||
| import Credentials from 'next-auth/providers/credentials'; | |||
| import dbConnect from '../../../utils/helpers/dbHelpers'; | |||
| const User = require('../../../models/user'); | |||
| export default NextAuth({ | |||
| session: { | |||
| strategy: 'jwt', | |||
| }, | |||
| callbacks: { | |||
| async jwt({ token, user, account, profile, isNewUser }) { | |||
| return { ...token, ...user }; | |||
| }, | |||
| async session({ session, token, user }) { | |||
| return session; | |||
| }, | |||
| }, | |||
| providers: [ | |||
| Credentials({ | |||
| name: 'Credentials', | |||
| credentials: { | |||
| username: { label: 'Username', type: 'text' }, | |||
| password: { label: 'Password', type: 'password' }, | |||
| }, | |||
| // @ts-ignore | |||
| async authorize(credentials) { | |||
| if (credentials) { | |||
| await dbConnect(); | |||
| const userData = await User.findByCredentials( | |||
| credentials.username, | |||
| credentials.password | |||
| ); | |||
| return { user: userData }; | |||
| } | |||
| return null; | |||
| }, | |||
| }), | |||
| ], | |||
| }); | |||
| @@ -0,0 +1,34 @@ | |||
| import type { NextApiRequest, NextApiResponse } from 'next'; | |||
| import dbConnect from '../../../utils/helpers/dbHelpers'; | |||
| import { UpdateResponse, IUser } from '../../../utils/interface/userInterface'; | |||
| const User = require('../../../models/user'); | |||
| async function handler( | |||
| req: NextApiRequest, | |||
| res: NextApiResponse<UpdateResponse> | |||
| ) { | |||
| const { method } = req; | |||
| await dbConnect(); | |||
| switch (method) { | |||
| case 'POST': { | |||
| try { | |||
| const user: IUser = await User.create(req.body); | |||
| res | |||
| .status(201) | |||
| .json({ message: `User (${user.fullName}) created sucessfully!` }); | |||
| } catch (error) { | |||
| if (error instanceof Error) | |||
| res.status(400).json({ message: error.message }); | |||
| else res.status(400).json({ message: 'Unexpected error' + error }); | |||
| } | |||
| break; | |||
| } | |||
| default: | |||
| res.status(405).json({ message: 'Method not allowed' }); | |||
| break; | |||
| } | |||
| } | |||
| export default handler; | |||
| @@ -1,13 +0,0 @@ | |||
| // Next.js API route support: https://nextjs.org/docs/api-routes/introduction | |||
| import type { NextApiRequest, NextApiResponse } from 'next' | |||
| type Data = { | |||
| name: string | |||
| } | |||
| export default function handler( | |||
| req: NextApiRequest, | |||
| res: NextApiResponse<Data> | |||
| ) { | |||
| res.status(200).json({ name: 'John Doe' }) | |||
| } | |||
| @@ -0,0 +1,66 @@ | |||
| const Order = require('../../../models/order'); | |||
| import dbConnect from '../../../utils/helpers/dbHelpers'; | |||
| import { Types } from 'mongoose'; | |||
| import type { NextApiRequest, NextApiResponse } from 'next'; | |||
| import { | |||
| OrderResponse, | |||
| OrderDataDB, | |||
| } from '../../../utils/interface/orderInterface'; | |||
| async function handler( | |||
| req: NextApiRequest, | |||
| res: NextApiResponse<OrderResponse> | |||
| ) { | |||
| const { method } = req; | |||
| const ownerID = req.query.ownerID as string; | |||
| await dbConnect(); | |||
| switch (method) { | |||
| case 'POST': { | |||
| try { | |||
| const order: OrderDataDB = await Order.create( | |||
| req.body | |||
| ); /* create a new model in the database */ | |||
| res | |||
| .status(201) | |||
| .json({ message: 'Your order was submitted successfully!', order }); | |||
| } catch (error) { | |||
| if (error instanceof Error) | |||
| res.status(400).json({ message: error.message }); | |||
| else res.status(400).json({ message: 'Unexpected error' + error }); | |||
| } | |||
| break; | |||
| } | |||
| case 'GET': { | |||
| try { | |||
| const objectId = new Types.ObjectId(ownerID); | |||
| const orders: Array<OrderDataDB> = await Order.find({ | |||
| owner: objectId, | |||
| }); | |||
| if (!orders) { | |||
| res.status(200).json({ | |||
| message: | |||
| 'There are currently no orders in our database for the selected owner.', | |||
| orders: [], | |||
| }); | |||
| } | |||
| res.status(200).json({ | |||
| message: | |||
| 'All orders from our database for the selected owner were fetched successfully.', | |||
| orders, | |||
| }); | |||
| } catch (error) { | |||
| if (error instanceof Error) | |||
| res.status(400).json({ message: error.message }); | |||
| else res.status(400).json({ message: 'Unexpected error' + error }); | |||
| } | |||
| break; | |||
| } | |||
| default: | |||
| res.status(405).json({ message: 'Method not allowed' }); | |||
| break; | |||
| } | |||
| } | |||
| export default handler; | |||
| @@ -0,0 +1,57 @@ | |||
| const Product = require('../../../models/product'); | |||
| import dbConnect from '../../../utils/helpers/dbHelpers'; | |||
| import type { NextApiRequest, NextApiResponse } from 'next'; | |||
| import { | |||
| ProductDataDB, | |||
| SingleProductResponse, | |||
| } from '../../../utils/interface/productInterface'; | |||
| async function handler( | |||
| req: NextApiRequest, | |||
| res: NextApiResponse<SingleProductResponse> | |||
| ) { | |||
| const { method } = req; | |||
| await dbConnect(); | |||
| switch (method) { | |||
| case 'GET': { | |||
| try { | |||
| const productId = req.query.productId; | |||
| const product: ProductDataDB = await Product.findOne({ | |||
| customID: productId, | |||
| }); | |||
| if (!product) { | |||
| throw new Error('The product with this id does not exist!'); | |||
| } | |||
| const similarProducts: Array<ProductDataDB> = await Product.find({ | |||
| category: product.category, | |||
| customID: { $ne: product.customID }, | |||
| }); | |||
| const shuffled = similarProducts | |||
| .sort(() => 0.5 - Math.random()) | |||
| .slice(0, 3); | |||
| res.status(200).json({ | |||
| message: 'The product you requested was fetched successfully.', | |||
| product, | |||
| similarProducts: shuffled, | |||
| }); | |||
| } catch (error) { | |||
| if (error instanceof Error) | |||
| res.status(400).json({ message: error.message }); | |||
| else res.status(400).json({ message: 'Unexpected error' + error }); | |||
| } | |||
| break; | |||
| } | |||
| default: | |||
| res.status(405).json({ message: 'Method not allowed' }); | |||
| break; | |||
| } | |||
| } | |||
| export default handler; | |||
| @@ -0,0 +1,48 @@ | |||
| const Product = require('../../../models/product'); | |||
| import dbConnect from '../../../utils/helpers/dbHelpers'; | |||
| import type { NextApiRequest, NextApiResponse } from 'next'; | |||
| import { | |||
| ProductDataDB, | |||
| FeaturedProductsResponse, | |||
| } from '../../../utils/interface/productInterface'; | |||
| async function handler( | |||
| req: NextApiRequest, | |||
| res: NextApiResponse<FeaturedProductsResponse> | |||
| ) { | |||
| const { method } = req; | |||
| await dbConnect(); | |||
| switch (method) { | |||
| case 'GET': { | |||
| try { | |||
| const featuredProducts: Array<ProductDataDB> = await Product.find({ | |||
| isFeatured: true, | |||
| }); | |||
| if (!featuredProducts) { | |||
| res.status(200).json({ | |||
| message: 'There are no featured products right now.', | |||
| featuredProducts: [], | |||
| }); | |||
| } | |||
| res.status(200).json({ | |||
| message: 'Featured products were fetched successfully.', | |||
| featuredProducts, | |||
| }); | |||
| } catch (error) { | |||
| if (error instanceof Error) | |||
| res.status(400).json({ message: error.message }); | |||
| else res.status(400).json({ message: 'Unexpected error' + error }); | |||
| } | |||
| break; | |||
| } | |||
| default: | |||
| res.status(405).json({ message: 'Method not allowed' }); | |||
| break; | |||
| } | |||
| } | |||
| export default handler; | |||
| @@ -0,0 +1,103 @@ | |||
| const Product = require('../../../models/product'); | |||
| import dbConnect from '../../../utils/helpers/dbHelpers'; | |||
| import type { NextApiRequest, NextApiResponse } from 'next'; | |||
| import { | |||
| ProductDataDB, | |||
| ProductsResponse, | |||
| } from '../../../utils/interface/productInterface'; | |||
| async function handler( | |||
| req: NextApiRequest, | |||
| res: NextApiResponse<ProductsResponse> | |||
| ) { | |||
| const { method } = req; | |||
| await dbConnect(); | |||
| const pageIndex = parseInt(req.query.pageIndex as string); | |||
| const category = req.query.category === 'All' ? '' : req.query.category; | |||
| const filterType = req.query.filterType; | |||
| if (pageIndex < 1) { | |||
| res.status(422).json({ | |||
| message: 'Page does not exist ', | |||
| }); | |||
| return; | |||
| } | |||
| switch (method) { | |||
| case 'GET': { | |||
| try { | |||
| const productCount: number = await Product.find({ | |||
| category: { $regex: category }, | |||
| }).countDocuments(); | |||
| if (productCount === 0) { | |||
| res.status(200).json({ | |||
| message: 'There are currently no products in our database.', | |||
| product: [], | |||
| productCount: 0, | |||
| next: '', | |||
| previous: '', | |||
| }); | |||
| break; | |||
| } | |||
| if ((pageIndex - 1) * 9 >= productCount) { | |||
| throw new Error('Page does not exist!'); | |||
| } | |||
| const product: Array<ProductDataDB> = await Product.find({ | |||
| category: { $regex: category }, | |||
| }) | |||
| .skip((pageIndex - 1) * 9) | |||
| .limit(9) | |||
| .sort(filterType === 'asc' ? 'name' : '-name '); | |||
| if (!product) { | |||
| throw new Error('There are currently no products in our database.'); | |||
| } | |||
| const previousUrl = | |||
| pageIndex > 1 | |||
| ? `https://localhost:3000/api/product?pageIndex=${pageIndex - 1}` | |||
| : ''; | |||
| const nextUrl = | |||
| pageIndex * 9 < productCount | |||
| ? `https://localhost:3000/api/product?pageIndex=${pageIndex + 1}` | |||
| : ''; | |||
| res.status(200).json({ | |||
| message: 'All products from our database were fetched successfully.', | |||
| product, | |||
| productCount, | |||
| next: nextUrl, | |||
| previous: previousUrl, | |||
| }); | |||
| } catch (error) { | |||
| if (error instanceof Error) | |||
| res.status(400).json({ message: error.message }); | |||
| else res.status(400).json({ message: 'Unexpected error' + error }); | |||
| } | |||
| break; | |||
| } | |||
| case 'POST': { | |||
| try { | |||
| const product: ProductDataDB = await Product.create(req.body); | |||
| res | |||
| .status(201) | |||
| .json({ message: 'Your product was created and stored!', product }); | |||
| } catch (error) { | |||
| if (error instanceof Error) | |||
| res.status(400).json({ message: error.message }); | |||
| else res.status(400).json({ message: 'Unexpected error' + error }); | |||
| } | |||
| break; | |||
| } | |||
| default: | |||
| res.status(405).json({ message: 'Method not allowed' }); | |||
| break; | |||
| } | |||
| } | |||
| export default handler; | |||
| @@ -0,0 +1,54 @@ | |||
| const Question = require('../../../models/question'); | |||
| import dbConnect from '../../../utils/helpers/dbHelpers'; | |||
| const sgMail = require('@sendgrid/mail'); | |||
| import type { NextApiRequest, NextApiResponse } from 'next'; | |||
| import { | |||
| QuestionDataDB, | |||
| QuestionResponse, | |||
| } from '../../../utils/interface/questionInterface'; | |||
| async function handler( | |||
| req: NextApiRequest, | |||
| res: NextApiResponse<QuestionResponse> | |||
| ) { | |||
| const { method } = req; | |||
| await dbConnect(); | |||
| switch (method) { | |||
| case 'POST': { | |||
| try { | |||
| const question: QuestionDataDB = await Question.create(req.body); | |||
| sgMail.setApiKey(process.env.NEXT_PUBLIC_SEND_GRID); | |||
| const msg = { | |||
| to: '[email protected]', //req.body.email, // Change to your recipient | |||
| from: '[email protected]', // Change to your verified sender | |||
| subject: 'Question submitted', | |||
| text: 'Your question was submitted successfully, we will contact you via email shortly. Thank you!', | |||
| html: '<strong>Your question was submitted successfully, we will contact you via email shortly. Thank you!</strong>', | |||
| }; | |||
| sgMail.send(msg).then(() => { | |||
| console.log('Email sent'); | |||
| }); | |||
| res.status(201).json({ | |||
| message: | |||
| 'Your message/question was submitted successfully, check your mail for confirmation.', | |||
| question, | |||
| }); | |||
| } catch (error) { | |||
| if (error instanceof Error) | |||
| res.status(400).json({ message: error.message }); | |||
| else res.status(400).json({ message: 'Unexpected error' + error }); | |||
| } | |||
| break; | |||
| } | |||
| default: | |||
| res.status(405).json({ message: 'Method not allowed' }); | |||
| break; | |||
| } | |||
| } | |||
| export default handler; | |||
| @@ -0,0 +1,51 @@ | |||
| const User = require('../../../models/user'); | |||
| import dbConnect from '../../../utils/helpers/dbHelpers'; | |||
| import type { NextApiRequest, NextApiResponse } from 'next'; | |||
| import { UpdateResponse } from '../../../utils/interface/userInterface'; | |||
| async function handler( | |||
| req: NextApiRequest, | |||
| res: NextApiResponse<UpdateResponse> | |||
| ) { | |||
| const { method } = req; | |||
| await dbConnect(); | |||
| switch (method) { | |||
| case 'PATCH': { | |||
| const updates = Object.keys(req.body.userData); | |||
| const allowedUpdates = [ | |||
| 'fullName', | |||
| 'email', | |||
| 'address', | |||
| 'address2', | |||
| 'city', | |||
| 'country', | |||
| 'postcode', | |||
| ]; | |||
| const isValidOperation = updates.every((update) => | |||
| allowedUpdates.includes(update) | |||
| ); | |||
| if (!isValidOperation) { | |||
| return res.status(400).send({ message: 'Invalid updates!' }); | |||
| } | |||
| try { | |||
| const user = await User.findOne({ _id: req.body._id }); | |||
| updates.forEach((update) => (user[update] = req.body.userData[update])); | |||
| await user.save(); | |||
| res.send({ | |||
| message: 'User profile updated successfully.', | |||
| }); | |||
| } catch (error) { | |||
| if (error instanceof Error) | |||
| res.status(400).json({ message: error.message }); | |||
| else res.status(400).json({ message: 'Unexpected error' + error }); | |||
| } | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| export default handler; | |||
| @@ -1,23 +1,14 @@ | |||
| import apiEndpoints from '../apiEndpoints'; | |||
| import { OrderData } from '../../utils/interface/orderInterface'; | |||
| import { OrderResponseGet } from '../../utils/interface/orderInterface'; | |||
| interface OrderDataDB extends OrderData { | |||
| id: string; | |||
| _id: string; | |||
| __v: number; | |||
| } | |||
| interface OrderResponse { | |||
| message: string; | |||
| orders: Array<OrderDataDB>; | |||
| } | |||
| export const getOrdersForOwner = async (id: string): Promise<OrderResponse> => { | |||
| export const getOrdersForOwner = async ( | |||
| id: string | |||
| ): Promise<OrderResponseGet> => { | |||
| const response = await fetch( | |||
| `http://localhost:3000${apiEndpoints.order}?ownerID=${id}` | |||
| ); | |||
| const data: OrderResponse = await response.json(); | |||
| const data: OrderResponseGet = await response.json(); | |||
| if (!response.ok) { | |||
| throw new Error(data.message || 'Something went wrong!'); | |||
| @@ -1,19 +1,12 @@ | |||
| import apiEndpoints from '../apiEndpoints'; | |||
| import { OrderData } from '../../utils/interface/orderInterface'; | |||
| import { | |||
| OrderResponsePost, | |||
| OrderData, | |||
| } from '../../utils/interface/orderInterface'; | |||
| interface OrderDataDB extends OrderData { | |||
| id: string; | |||
| _id: string; | |||
| __v: number; | |||
| } | |||
| interface OrderResponse { | |||
| message: string; | |||
| order: OrderDataDB; | |||
| } | |||
| export const postOrder = async ( | |||
| orderData: OrderData | |||
| ): Promise<OrderResponse> => { | |||
| ): Promise<OrderResponsePost> => { | |||
| const response = await fetch(`http://localhost:3000${apiEndpoints.order}`, { | |||
| method: 'POST', | |||
| body: JSON.stringify(orderData), | |||
| @@ -22,7 +15,7 @@ export const postOrder = async ( | |||
| }, | |||
| }); | |||
| const data: OrderResponse = await response.json(); | |||
| const data: OrderResponsePost = await response.json(); | |||
| if (!response.ok) { | |||
| throw new Error(data.message || 'Something went wrong!'); | |||
| @@ -1,17 +1,6 @@ | |||
| import { ProductData } from '../../utils/interface/productInterface'; | |||
| import { FeaturedProductsResponse } from '../../utils/interface/productInterface'; | |||
| import apiEndpoints from '../apiEndpoints'; | |||
| interface ProductDataDB extends ProductData { | |||
| id: string; | |||
| _id: string; | |||
| __v: number; | |||
| } | |||
| interface FeaturedProductsResponse { | |||
| message: string; | |||
| featuredProducts: Array<ProductDataDB>; | |||
| } | |||
| export const getFeaturedProducts = | |||
| async (): Promise<FeaturedProductsResponse> => { | |||
| const response = await fetch( | |||
| @@ -1,11 +1,6 @@ | |||
| import { SingleProductResponse } from '../../utils/interface/productInterface'; | |||
| import apiEndpoints from '../apiEndpoints'; | |||
| interface SingleProductResponse { | |||
| message: string; | |||
| product: object; | |||
| similarProducts: Array<object>; | |||
| } | |||
| export const getProductData = async ( | |||
| productId: string | |||
| ): Promise<SingleProductResponse> => { | |||
| @@ -1,13 +1,6 @@ | |||
| import { ProductsResponse } from '../../utils/interface/productInterface'; | |||
| import apiEndpoints from '../apiEndpoints'; | |||
| interface ProductsResponse { | |||
| message: string; | |||
| product: object; | |||
| productCount: number; | |||
| next: string; | |||
| previous: string; | |||
| } | |||
| export const getAllProducts = async ( | |||
| pageIndex: number, | |||
| category = 'All', | |||
| @@ -1,10 +1,8 @@ | |||
| import apiEndpoints from '../apiEndpoints'; | |||
| import { QuestionData } from '../../utils/interface/questionInterface'; | |||
| interface QuestionResponse { | |||
| message: string; | |||
| question: { [key: string]: string }; | |||
| } | |||
| import { | |||
| QuestionData, | |||
| QuestionResponse, | |||
| } from '../../utils/interface/questionInterface'; | |||
| export const postQuestion = async ( | |||
| questionData: QuestionData | |||
| @@ -1,9 +1,5 @@ | |||
| import apiEndpoints from '../apiEndpoints'; | |||
| import { UserData } from '../../utils/interface/userInterface'; | |||
| interface UpdateResponse { | |||
| message: string; | |||
| } | |||
| import { UserData, UpdateResponse } from '../../utils/interface/userInterface'; | |||
| export const updateUser = async ( | |||
| userData: UserData, | |||
| @@ -0,0 +1,51 @@ | |||
| import { MongoClient } from 'mongodb'; | |||
| import mongoose from 'mongoose'; | |||
| export async function connectToDatabase() { | |||
| const client = await MongoClient.connect(process.env.MONGODB_URI!); | |||
| return client; | |||
| } | |||
| declare global { | |||
| var mongoose: any; | |||
| } | |||
| const MONGODB_URI = process.env.MONGODB_URI; | |||
| if (!MONGODB_URI) { | |||
| throw new Error( | |||
| 'Please define the MONGODB_URI environment variable inside .env.local' | |||
| ); | |||
| } | |||
| /** | |||
| * Global is used here to maintain a cached connection across hot reloads | |||
| * in development. This prevents connections growing exponentially | |||
| * during API Route usage. | |||
| */ | |||
| let cached = global.mongoose; | |||
| if (!cached) { | |||
| cached = global.mongoose = { conn: null, promise: null }; | |||
| } | |||
| async function dbConnect() { | |||
| if (cached.conn) { | |||
| return cached.conn; | |||
| } | |||
| if (!cached.promise) { | |||
| const opts = { | |||
| bufferCommands: false, | |||
| }; | |||
| cached.promise = mongoose.connect(MONGODB_URI!, opts).then((mongoose) => { | |||
| return mongoose; | |||
| }); | |||
| } | |||
| cached.conn = await cached.promise; | |||
| return cached.conn; | |||
| } | |||
| export default dbConnect; | |||
| @@ -0,0 +1,11 @@ | |||
| import { hash, compare } from 'bcryptjs'; | |||
| export async function hashPassword(password: string) { | |||
| const hashedPassword = await hash(password, 12); | |||
| return hashedPassword; | |||
| } | |||
| export async function verifyPassword(password: string, hashedPassword: string) { | |||
| const isValid = await compare(password, hashedPassword); | |||
| return isValid; | |||
| } | |||
| @@ -16,3 +16,28 @@ export interface OrderData { | |||
| owner: ObjectId; | |||
| stripeCheckoutId: string; | |||
| } | |||
| export interface OrderDataDB extends OrderData { | |||
| id: string; | |||
| _id: string; | |||
| __v: number; | |||
| } | |||
| export interface OrderResponseGet { | |||
| message: string; | |||
| orders: Array<OrderDataDB>; | |||
| } | |||
| export interface OrderResponsePost { | |||
| message: string; | |||
| order: OrderDataDB; | |||
| } | |||
| export interface OrderResponseError { | |||
| message: string; | |||
| } | |||
| export type OrderResponse = | |||
| | OrderResponseGet | |||
| | OrderResponsePost | |||
| | OrderResponseError; | |||
| @@ -13,3 +13,50 @@ export interface ProductData { | |||
| customID: string; | |||
| stripeID: string; | |||
| } | |||
| export interface ProductDataDB extends ProductData { | |||
| id: string; | |||
| _id: string; | |||
| __v: number; | |||
| } | |||
| export interface FeaturedProductsResponseGet { | |||
| message: string; | |||
| featuredProducts: Array<ProductDataDB>; | |||
| } | |||
| export interface ProductsResponseGet { | |||
| message: string; | |||
| product: Array<ProductData>; | |||
| productCount: number; | |||
| next: string; | |||
| previous: string; | |||
| } | |||
| export interface ProductsResponseError { | |||
| message: string; | |||
| } | |||
| export interface ProductsResponsePost { | |||
| message: string; | |||
| product: ProductData; | |||
| } | |||
| interface SingleProductResponseGet { | |||
| message: string; | |||
| product: ProductDataDB; | |||
| similarProducts: Array<ProductDataDB>; | |||
| } | |||
| export type SingleProductResponse = | |||
| | SingleProductResponseGet | |||
| | ProductsResponseError; | |||
| export type ProductsResponse = | |||
| | ProductsResponseGet | |||
| | ProductsResponsePost | |||
| | ProductsResponseError; | |||
| export type FeaturedProductsResponse = | |||
| | FeaturedProductsResponseGet | |||
| | ProductsResponseError; | |||
| @@ -4,3 +4,20 @@ export interface QuestionData { | |||
| lastName: string; | |||
| message: string; | |||
| } | |||
| export interface QuestionDataDB extends QuestionData { | |||
| id: string; | |||
| _id: string; | |||
| __v: number; | |||
| } | |||
| export interface QuestionResponsePost { | |||
| message: string; | |||
| question: QuestionData; | |||
| } | |||
| export interface QuestionError { | |||
| message: string; | |||
| } | |||
| export type QuestionResponse = QuestionResponsePost | QuestionError; | |||
| @@ -6,3 +6,14 @@ export interface UserData { | |||
| fullName: string; | |||
| postcode: string; | |||
| } | |||
| export interface IUser extends UserData { | |||
| email: string; | |||
| username: string; | |||
| password: string; | |||
| orders?: Array<object>; | |||
| } | |||
| export interface UpdateResponse { | |||
| message: string; | |||
| } | |||
| @@ -17,6 +17,13 @@ | |||
| dependencies: | |||
| regenerator-runtime "^0.13.4" | |||
| "@babel/runtime@^7.16.3": | |||
| version "7.19.4" | |||
| resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.19.4.tgz#a42f814502ee467d55b38dd1c256f53a7b885c78" | |||
| integrity sha512-EXpLCrk55f+cYqmHsSR+yD/0gAIMxxA9QK9lnQWzhMCvt+YmoBN7Zx94s++Kv0+unHk39vxNO8t+CMA2WSS3wA== | |||
| dependencies: | |||
| regenerator-runtime "^0.13.4" | |||
| "@eslint/eslintrc@^1.3.3": | |||
| version "1.3.3" | |||
| resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.3.tgz#2b044ab39fdfa75b4688184f9e573ce3c5b0ff95" | |||
| @@ -149,11 +156,39 @@ | |||
| "@nodelib/fs.scandir" "2.1.5" | |||
| fastq "^1.6.0" | |||
| "@panva/hkdf@^1.0.1": | |||
| version "1.0.2" | |||
| resolved "https://registry.yarnpkg.com/@panva/hkdf/-/hkdf-1.0.2.tgz#bab0f09d09de9fd83628220d496627681bc440d6" | |||
| integrity sha512-MSAs9t3Go7GUkMhpKC44T58DJ5KGk2vBo+h1cqQeqlMfdGkxaVB78ZWpv9gYi/g2fa4sopag9gJsNvS8XGgWJA== | |||
| "@rushstack/eslint-patch@^1.1.3": | |||
| version "1.2.0" | |||
| resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz#8be36a1f66f3265389e90b5f9c9962146758f728" | |||
| integrity sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg== | |||
| "@sendgrid/client@^7.7.0": | |||
| version "7.7.0" | |||
| resolved "https://registry.yarnpkg.com/@sendgrid/client/-/client-7.7.0.tgz#f8f67abd604205a0d0b1af091b61517ef465fdbf" | |||
| integrity sha512-SxH+y8jeAQSnDavrTD0uGDXYIIkFylCo+eDofVmZLQ0f862nnqbC3Vd1ej6b7Le7lboyzQF6F7Fodv02rYspuA== | |||
| dependencies: | |||
| "@sendgrid/helpers" "^7.7.0" | |||
| axios "^0.26.0" | |||
| "@sendgrid/helpers@^7.7.0": | |||
| version "7.7.0" | |||
| resolved "https://registry.yarnpkg.com/@sendgrid/helpers/-/helpers-7.7.0.tgz#93fb4b6e2f0dc65080440d6a784cc93e8e148757" | |||
| integrity sha512-3AsAxfN3GDBcXoZ/y1mzAAbKzTtUZ5+ZrHOmWQ279AuaFXUNCh9bPnRpN504bgveTqoW+11IzPg3I0WVgDINpw== | |||
| dependencies: | |||
| deepmerge "^4.2.2" | |||
| "@sendgrid/mail@^7.7.0": | |||
| version "7.7.0" | |||
| resolved "https://registry.yarnpkg.com/@sendgrid/mail/-/mail-7.7.0.tgz#aba09f5ce2e9d8ceee92284c3ea8b4a90b0e38fe" | |||
| integrity sha512-5+nApPE9wINBvHSUxwOxkkQqM/IAAaBYoP9hw7WwgDNQPxraruVqHizeTitVtKGiqWCKm2mnjh4XGN3fvFLqaw== | |||
| dependencies: | |||
| "@sendgrid/client" "^7.7.0" | |||
| "@sendgrid/helpers" "^7.7.0" | |||
| "@swc/[email protected]": | |||
| version "0.4.11" | |||
| resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.4.11.tgz#db23a376761b3d31c26502122f349a21b592c8de" | |||
| @@ -161,6 +196,11 @@ | |||
| dependencies: | |||
| tslib "^2.4.0" | |||
| "@types/bcryptjs@^2.4.2": | |||
| version "2.4.2" | |||
| resolved "https://registry.yarnpkg.com/@types/bcryptjs/-/bcryptjs-2.4.2.tgz#e3530eac9dd136bfdfb0e43df2c4c5ce1f77dfae" | |||
| integrity sha512-LiMQ6EOPob/4yUL66SZzu6Yh77cbzJFYll+ZfaPiPPFswtIlA/Fs1MzdKYA7JApHU49zQTbJGX3PDmCpIdDBRQ== | |||
| "@types/json5@^0.0.29": | |||
| version "0.0.29" | |||
| resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" | |||
| @@ -204,6 +244,11 @@ | |||
| resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" | |||
| integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== | |||
| "@types/validator@^13.7.7": | |||
| version "13.7.7" | |||
| resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.7.7.tgz#e87cf34dd08522d21acf30130fd8941f433b81b5" | |||
| integrity sha512-jiEw2kTUJ8Jsh4A1K4b5Pkjj9Xz6FktLLOQ36ZVLRkmxFbpTvAV2VRoKMojz8UlZxNg/2dZqzpigH4JYn1bkQg== | |||
| "@types/webidl-conversions@*": | |||
| version "7.0.0" | |||
| resolved "https://registry.yarnpkg.com/@types/webidl-conversions/-/webidl-conversions-7.0.0.tgz#2b8e60e33906459219aa587e9d1a612ae994cfe7" | |||
| @@ -352,6 +397,13 @@ axe-core@^4.4.3: | |||
| resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.4.3.tgz#11c74d23d5013c0fa5d183796729bc3482bd2f6f" | |||
| integrity sha512-32+ub6kkdhhWick/UjvEwRchgoetXqTK14INLqbGm5U2TzBkBNF3nQtLYm8ovxSkQWArjEQvftCKryjZaATu3w== | |||
| axios@^0.26.0: | |||
| version "0.26.1" | |||
| resolved "https://registry.yarnpkg.com/axios/-/axios-0.26.1.tgz#1ede41c51fcf51bbbd6fd43669caaa4f0495aaa9" | |||
| integrity sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA== | |||
| dependencies: | |||
| follow-redirects "^1.14.8" | |||
| axobject-query@^2.2.0: | |||
| version "2.2.0" | |||
| resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be" | |||
| @@ -367,6 +419,11 @@ base64-js@^1.3.1: | |||
| resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" | |||
| integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== | |||
| bcryptjs@^2.4.3: | |||
| version "2.4.3" | |||
| resolved "https://registry.yarnpkg.com/bcryptjs/-/bcryptjs-2.4.3.tgz#9ab5627b93e60621ff7cdac5da9733027df1d0cb" | |||
| integrity sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ== | |||
| brace-expansion@^1.1.7: | |||
| version "1.1.11" | |||
| resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" | |||
| @@ -382,7 +439,7 @@ braces@^3.0.2: | |||
| dependencies: | |||
| fill-range "^7.0.1" | |||
| bson@^4.7.0: | |||
| bson@^4.6.5, bson@^4.7.0: | |||
| version "4.7.0" | |||
| resolved "https://registry.yarnpkg.com/bson/-/bson-4.7.0.tgz#7874a60091ffc7a45c5dd2973b5cad7cded9718a" | |||
| integrity sha512-VrlEE4vuiO1WTpfof4VmaVolCVYkYTgB9iWgYNOrVlnifpME/06fhFRmONgBhClD5pFC1t9ZWqFUQEQAzY43bA== | |||
| @@ -440,6 +497,11 @@ [email protected]: | |||
| resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" | |||
| integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== | |||
| cookie@^0.5.0: | |||
| version "0.5.0" | |||
| resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" | |||
| integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== | |||
| core-js-pure@^3.25.1: | |||
| version "3.25.5" | |||
| resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.25.5.tgz#79716ba54240c6aa9ceba6eee08cf79471ba184d" | |||
| @@ -464,6 +526,13 @@ damerau-levenshtein@^1.0.8: | |||
| resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7" | |||
| integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA== | |||
| [email protected], debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: | |||
| version "4.3.4" | |||
| resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" | |||
| integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== | |||
| dependencies: | |||
| ms "2.1.2" | |||
| debug@^2.6.9: | |||
| version "2.6.9" | |||
| resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" | |||
| @@ -478,18 +547,16 @@ debug@^3.2.7: | |||
| dependencies: | |||
| ms "^2.1.1" | |||
| debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: | |||
| version "4.3.4" | |||
| resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" | |||
| integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== | |||
| dependencies: | |||
| ms "2.1.2" | |||
| deep-is@^0.1.3: | |||
| version "0.1.4" | |||
| resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" | |||
| integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== | |||
| deepmerge@^4.2.2: | |||
| version "4.2.2" | |||
| resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" | |||
| integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== | |||
| define-properties@^1.1.3, define-properties@^1.1.4: | |||
| version "1.1.4" | |||
| resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1" | |||
| @@ -854,6 +921,11 @@ flatted@^3.1.0: | |||
| resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" | |||
| integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== | |||
| follow-redirects@^1.14.8: | |||
| version "1.15.2" | |||
| resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" | |||
| integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== | |||
| fs.realpath@^1.0.0: | |||
| version "1.0.0" | |||
| resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" | |||
| @@ -1148,6 +1220,11 @@ isexe@^2.0.0: | |||
| resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" | |||
| integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== | |||
| jose@^4.1.4, jose@^4.9.3: | |||
| version "4.10.0" | |||
| resolved "https://registry.yarnpkg.com/jose/-/jose-4.10.0.tgz#2e0b7bcc80dd0775f8a4588e55beb9460c37d60a" | |||
| integrity sha512-KEhB/eLGLomWGPTb+/RNbYsTjIyx03JmbqAyIyiXBuNSa7CmNrJd5ysFhblayzs/e/vbOPMUaLnjHUMhGp4yLw== | |||
| js-sdsl@^4.1.4: | |||
| version "4.1.5" | |||
| resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.1.5.tgz#1ff1645e6b4d1b028cd3f862db88c9d887f26e2a" | |||
| @@ -1190,6 +1267,11 @@ json5@^1.0.1: | |||
| array-includes "^3.1.5" | |||
| object.assign "^4.1.3" | |||
| [email protected]: | |||
| version "2.4.1" | |||
| resolved "https://registry.yarnpkg.com/kareem/-/kareem-2.4.1.tgz#7d81ec518204a48c1cb16554af126806c3cd82b0" | |||
| integrity sha512-aJ9opVoXroQUPfovYP5kaj2lM7Jn02Gw13bL0lg9v0V7SaUc0qavPs0Eue7d2DcC3NjqI6QAUElXNsuZSeM+EA== | |||
| language-subtag-registry@~0.3.2: | |||
| version "0.3.22" | |||
| resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz#2e1500861b2e457eba7e7ae86877cbd08fa1fd1d" | |||
| @@ -1286,6 +1368,43 @@ mongodb@*: | |||
| optionalDependencies: | |||
| saslprep "^1.0.3" | |||
| [email protected]: | |||
| version "4.9.1" | |||
| resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-4.9.1.tgz#0c769448228bcf9a6aa7d16daa3625b48312479e" | |||
| integrity sha512-ZhgI/qBf84fD7sI4waZBoLBNJYPQN5IOC++SBCiPiyhzpNKOxN/fi0tBHvH2dEC42HXtNEbFB0zmNz4+oVtorQ== | |||
| dependencies: | |||
| bson "^4.7.0" | |||
| denque "^2.1.0" | |||
| mongodb-connection-string-url "^2.5.3" | |||
| socks "^2.7.0" | |||
| optionalDependencies: | |||
| saslprep "^1.0.3" | |||
| mongoose@^6.6.5: | |||
| version "6.6.5" | |||
| resolved "https://registry.yarnpkg.com/mongoose/-/mongoose-6.6.5.tgz#fcc7ba9594db711b3d4d7a1b3522c5dbc8f0bf52" | |||
| integrity sha512-iA/oDpWOc+K2QYzA4Eq7Z1oUBQOz9FGDmUwPLgw872Bfs/qizA5Db+gJorAn+TnnGu3VoCK8iP4Y+TECUelwjA== | |||
| dependencies: | |||
| bson "^4.6.5" | |||
| kareem "2.4.1" | |||
| mongodb "4.9.1" | |||
| mpath "0.9.0" | |||
| mquery "4.0.3" | |||
| ms "2.1.3" | |||
| sift "16.0.0" | |||
| [email protected]: | |||
| version "0.9.0" | |||
| resolved "https://registry.yarnpkg.com/mpath/-/mpath-0.9.0.tgz#0c122fe107846e31fc58c75b09c35514b3871904" | |||
| integrity sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew== | |||
| [email protected]: | |||
| version "4.0.3" | |||
| resolved "https://registry.yarnpkg.com/mquery/-/mquery-4.0.3.tgz#4d15f938e6247d773a942c912d9748bd1965f89d" | |||
| integrity sha512-J5heI+P08I6VJ2Ky3+33IpCdAvlYGTSUjwTPxkAr8i8EoduPMBX2OY/wa3IKZIQl7MU4SbFk8ndgSKyB/cl1zA== | |||
| dependencies: | |||
| debug "4.x" | |||
| [email protected]: | |||
| version "2.0.0" | |||
| resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" | |||
| @@ -1296,7 +1415,7 @@ [email protected]: | |||
| resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" | |||
| integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== | |||
| ms@^2.1.1: | |||
| [email protected], ms@^2.1.1: | |||
| version "2.1.3" | |||
| resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" | |||
| integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== | |||
| @@ -1311,6 +1430,21 @@ natural-compare@^1.4.0: | |||
| resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" | |||
| integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== | |||
| next-auth@^4.13.0: | |||
| version "4.13.0" | |||
| resolved "https://registry.yarnpkg.com/next-auth/-/next-auth-4.13.0.tgz#93d312cec2513ac3c5eb583ee0665da50059a902" | |||
| integrity sha512-FtkPpeb9Bax6yKDaxcaGIvZZjvr10JaU2AsBYv1yv4N6rP86Xa7/4Ro1Aq94YGwsYzk+YKS52CRjD2DqCcSmVA== | |||
| dependencies: | |||
| "@babel/runtime" "^7.16.3" | |||
| "@panva/hkdf" "^1.0.1" | |||
| cookie "^0.5.0" | |||
| jose "^4.9.3" | |||
| oauth "^0.9.15" | |||
| openid-client "^5.1.0" | |||
| preact "^10.6.3" | |||
| preact-render-to-string "^5.1.19" | |||
| uuid "^8.3.2" | |||
| [email protected]: | |||
| version "12.3.1" | |||
| resolved "https://registry.yarnpkg.com/next/-/next-12.3.1.tgz#127b825ad2207faf869b33393ec8c75fe61e50f1" | |||
| @@ -1337,11 +1471,21 @@ [email protected]: | |||
| "@next/swc-win32-ia32-msvc" "12.3.1" | |||
| "@next/swc-win32-x64-msvc" "12.3.1" | |||
| oauth@^0.9.15: | |||
| version "0.9.15" | |||
| resolved "https://registry.yarnpkg.com/oauth/-/oauth-0.9.15.tgz#bd1fefaf686c96b75475aed5196412ff60cfb9c1" | |||
| integrity sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA== | |||
| object-assign@^4.1.1: | |||
| version "4.1.1" | |||
| resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" | |||
| integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== | |||
| object-hash@^2.0.1: | |||
| version "2.2.0" | |||
| resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.2.0.tgz#5ad518581eefc443bd763472b8ff2e9c2c0d54a5" | |||
| integrity sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw== | |||
| object-inspect@^1.12.2, object-inspect@^1.9.0: | |||
| version "1.12.2" | |||
| resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea" | |||
| @@ -1397,6 +1541,11 @@ object.values@^1.1.5: | |||
| define-properties "^1.1.3" | |||
| es-abstract "^1.19.1" | |||
| oidc-token-hash@^5.0.1: | |||
| version "5.0.1" | |||
| resolved "https://registry.yarnpkg.com/oidc-token-hash/-/oidc-token-hash-5.0.1.tgz#ae6beec3ec20f0fd885e5400d175191d6e2f10c6" | |||
| integrity sha512-EvoOtz6FIEBzE+9q253HsLCVRiK/0doEJ2HCvvqMQb3dHZrP3WlJKYtJ55CRTw4jmYomzH4wkPuCj/I3ZvpKxQ== | |||
| once@^1.3.0: | |||
| version "1.4.0" | |||
| resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" | |||
| @@ -1404,6 +1553,16 @@ once@^1.3.0: | |||
| dependencies: | |||
| wrappy "1" | |||
| openid-client@^5.1.0: | |||
| version "5.1.10" | |||
| resolved "https://registry.yarnpkg.com/openid-client/-/openid-client-5.1.10.tgz#add6044878b9be75ffdd09abfcaae6feff376b1f" | |||
| integrity sha512-KYAtkxTuUwTvjAmH0QMFFP3i9l0+XhP2/blct6Q9kn+DUJ/lu8/g/bI8ghSgxz9dJLm/9cpB/1uLVGTcGGY0hw== | |||
| dependencies: | |||
| jose "^4.1.4" | |||
| lru-cache "^6.0.0" | |||
| object-hash "^2.0.1" | |||
| oidc-token-hash "^5.0.1" | |||
| optionator@^0.9.1: | |||
| version "0.9.1" | |||
| resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" | |||
| @@ -1481,11 +1640,28 @@ [email protected]: | |||
| picocolors "^1.0.0" | |||
| source-map-js "^1.0.2" | |||
| preact-render-to-string@^5.1.19: | |||
| version "5.2.5" | |||
| resolved "https://registry.yarnpkg.com/preact-render-to-string/-/preact-render-to-string-5.2.5.tgz#359b14a45bea2a7b5c0ed2a9c6eb7ea915cf7d5a" | |||
| integrity sha512-rEBn42C3Wh+AjPxXUbDkb6xw0cTJQgxdYlp6ytUR1uBZF647Wn6ykkopMeQlRl7ggX+qnYYjZ4Hs1abZENl7ww== | |||
| dependencies: | |||
| pretty-format "^3.8.0" | |||
| preact@^10.6.3: | |||
| version "10.11.1" | |||
| resolved "https://registry.yarnpkg.com/preact/-/preact-10.11.1.tgz#35fdad092de8b2ad29df3a0bef9af1f4fdd2256b" | |||
| integrity sha512-1Wz5PCRm6Fg+6BTXWJHhX4wRK9MZbZBHuwBqfZlOdVm2NqPe8/rjYpufvYCwJSGb9layyzB2jTTXfpCTynLqFQ== | |||
| prelude-ls@^1.2.1: | |||
| version "1.2.1" | |||
| resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" | |||
| integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== | |||
| pretty-format@^3.8.0: | |||
| version "3.8.0" | |||
| resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-3.8.0.tgz#bfbed56d5e9a776645f4b1ff7aa1a3ac4fa3c385" | |||
| integrity sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew== | |||
| prop-types@^15.8.1: | |||
| version "15.8.1" | |||
| resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" | |||
| @@ -1642,6 +1818,11 @@ side-channel@^1.0.4: | |||
| get-intrinsic "^1.0.2" | |||
| object-inspect "^1.9.0" | |||
| [email protected]: | |||
| version "16.0.0" | |||
| resolved "https://registry.yarnpkg.com/sift/-/sift-16.0.0.tgz#447991577db61f1a8fab727a8a98a6db57a23eb8" | |||
| integrity sha512-ILTjdP2Mv9V1kIxWMXeMTIRbOBrqKc4JAXmFMnFq3fKeyQ2Qwa3Dw1ubcye3vR+Y6ofA0b9gNDr/y2t6eUeIzQ== | |||
| slash@^3.0.0: | |||
| version "3.0.0" | |||
| resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" | |||
| @@ -1823,6 +2004,16 @@ [email protected]: | |||
| resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" | |||
| integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== | |||
| uuid@^8.3.2: | |||
| version "8.3.2" | |||
| resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" | |||
| integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== | |||
| validator@^13.7.0: | |||
| version "13.7.0" | |||
| resolved "https://registry.yarnpkg.com/validator/-/validator-13.7.0.tgz#4f9658ba13ba8f3d82ee881d3516489ea85c0857" | |||
| integrity sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw== | |||
| webidl-conversions@^7.0.0: | |||
| version "7.0.0" | |||
| resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" | |||